Merge branch 'fix_imports' into fix_libs
This commit is contained in:
@@ -73,7 +73,21 @@ function ensure_build_dir() {
|
|||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a pipeline module from cache, with boot/ seed fallback
|
// Load a boot seed module (for compiling pipeline modules on cache miss)
|
||||||
|
function boot_load(name) {
|
||||||
|
var mcode_path = core_path + '/boot/' + name + '.cm.mcode'
|
||||||
|
var mcode_blob = null
|
||||||
|
var mach_blob = null
|
||||||
|
if (!fd.is_file(mcode_path)) {
|
||||||
|
print("error: missing boot seed: " + name + "\n")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
mcode_blob = fd.slurp(mcode_path)
|
||||||
|
mach_blob = mach_compile_mcode_bin(name, text(mcode_blob))
|
||||||
|
return mach_load(mach_blob, {use: use_embed})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a pipeline module from cache; on miss compile from source via boot chain
|
||||||
function load_pipeline_module(name, env) {
|
function load_pipeline_module(name, env) {
|
||||||
var source_path = core_path + '/' + name + '.cm'
|
var source_path = core_path + '/' + name + '.cm'
|
||||||
var source_blob = null
|
var source_blob = null
|
||||||
@@ -82,14 +96,48 @@ function load_pipeline_module(name, env) {
|
|||||||
var mcode_path = null
|
var mcode_path = null
|
||||||
var mcode_blob = null
|
var mcode_blob = null
|
||||||
var mach_blob = null
|
var mach_blob = null
|
||||||
|
var src = null
|
||||||
|
var boot_tok = null
|
||||||
|
var boot_par = null
|
||||||
|
var boot_fld = null
|
||||||
|
var boot_mc = null
|
||||||
|
var tok_result = null
|
||||||
|
var ast = null
|
||||||
|
var compiled = null
|
||||||
|
var mcode_json = null
|
||||||
if (fd.is_file(source_path)) {
|
if (fd.is_file(source_path)) {
|
||||||
source_blob = fd.slurp(source_path)
|
source_blob = fd.slurp(source_path)
|
||||||
hash = content_hash(source_blob)
|
hash = content_hash(source_blob)
|
||||||
cached = cache_path(hash)
|
cached = cache_path(hash)
|
||||||
if (cached && fd.is_file(cached))
|
if (cached && fd.is_file(cached))
|
||||||
return mach_load(fd.slurp(cached), env)
|
return mach_load(fd.slurp(cached), env)
|
||||||
|
|
||||||
|
// Cache miss: compile from source using boot seed pipeline
|
||||||
|
mcode_path = core_path + '/boot/' + name + '.cm.mcode'
|
||||||
|
if (fd.is_file(mcode_path)) {
|
||||||
|
boot_tok = boot_load("tokenize")
|
||||||
|
boot_par = boot_load("parse")
|
||||||
|
boot_fld = boot_load("fold")
|
||||||
|
boot_mc = boot_load("mcode")
|
||||||
|
src = text(source_blob)
|
||||||
|
tok_result = boot_tok(src, source_path)
|
||||||
|
ast = boot_par(tok_result.tokens, src, source_path, boot_tok)
|
||||||
|
if (ast.errors != null && length(ast.errors) > 0) {
|
||||||
|
print("error: failed to compile pipeline module: " + name + "\n")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
ast = boot_fld(ast)
|
||||||
|
compiled = boot_mc(ast)
|
||||||
|
mcode_json = json.encode(compiled)
|
||||||
|
mach_blob = mach_compile_mcode_bin(name, mcode_json)
|
||||||
|
if (cached) {
|
||||||
|
ensure_build_dir()
|
||||||
|
fd.slurpwrite(cached, mach_blob)
|
||||||
|
}
|
||||||
|
return mach_load(mach_blob, env)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Boot seed fallback
|
// Last resort: boot seed as runtime (no source file found)
|
||||||
mcode_path = core_path + '/boot/' + name + '.cm.mcode'
|
mcode_path = core_path + '/boot/' + name + '.cm.mcode'
|
||||||
if (fd.is_file(mcode_path)) {
|
if (fd.is_file(mcode_path)) {
|
||||||
mcode_blob = fd.slurp(mcode_path)
|
mcode_blob = fd.slurp(mcode_path)
|
||||||
|
|||||||
37
mcode.cm
37
mcode.cm
@@ -22,6 +22,12 @@ var mcode = function(ast) {
|
|||||||
"~!": "bitnot", "[]!": "load"
|
"~!": "bitnot", "[]!": "load"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var binop_sym = {
|
||||||
|
add: "+", subtract: "-", multiply: "*", divide: "/",
|
||||||
|
modulo: "%", pow: "**",
|
||||||
|
lt: "<", le: "<=", gt: ">", ge: ">="
|
||||||
|
}
|
||||||
|
|
||||||
var compound_map = {
|
var compound_map = {
|
||||||
"+=": "add", "-=": "subtract", "*=": "multiply", "/=": "divide",
|
"+=": "add", "-=": "subtract", "*=": "multiply", "/=": "divide",
|
||||||
"%=": "modulo", "&=": "bitand", "|=": "bitor", "^=": "bitxor",
|
"%=": "modulo", "&=": "bitand", "|=": "bitor", "^=": "bitxor",
|
||||||
@@ -67,6 +73,7 @@ var mcode = function(ast) {
|
|||||||
var _bp_right = 0
|
var _bp_right = 0
|
||||||
var _bp_ln = null
|
var _bp_ln = null
|
||||||
var _bp_rn = null
|
var _bp_rn = null
|
||||||
|
var _bp_op_sym = null
|
||||||
|
|
||||||
// State save/restore for nested function compilation
|
// State save/restore for nested function compilation
|
||||||
var save_state = function() {
|
var save_state = function() {
|
||||||
@@ -240,6 +247,27 @@ var mcode = function(ast) {
|
|||||||
emit_1("null", dest)
|
emit_1("null", dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var emit_log_error = function(msg) {
|
||||||
|
var log_slot = alloc_slot()
|
||||||
|
add_instr(["access", log_slot, {kind: "name", name: "log", make: "intrinsic"}])
|
||||||
|
var name_slot = alloc_slot()
|
||||||
|
emit_const_str(name_slot, "error")
|
||||||
|
var msg_slot = alloc_slot()
|
||||||
|
emit_const_str(msg_slot, msg)
|
||||||
|
var args_arr = alloc_slot()
|
||||||
|
add_instr(["array", args_arr, 0])
|
||||||
|
emit_2("push", args_arr, msg_slot)
|
||||||
|
var result = alloc_slot()
|
||||||
|
var frame_slot = alloc_slot()
|
||||||
|
emit_3("frame", frame_slot, log_slot, 2)
|
||||||
|
var null_slot = alloc_slot()
|
||||||
|
emit_1("null", null_slot)
|
||||||
|
emit_3("setarg", frame_slot, 0, null_slot)
|
||||||
|
emit_3("setarg", frame_slot, 1, name_slot)
|
||||||
|
emit_3("setarg", frame_slot, 2, args_arr)
|
||||||
|
emit_2("invoke", frame_slot, result)
|
||||||
|
}
|
||||||
|
|
||||||
var emit_jump = function(label) {
|
var emit_jump = function(label) {
|
||||||
add_instr(["jump", label])
|
add_instr(["jump", label])
|
||||||
}
|
}
|
||||||
@@ -321,6 +349,7 @@ var mcode = function(ast) {
|
|||||||
emit_jump(done)
|
emit_jump(done)
|
||||||
|
|
||||||
emit_label(err)
|
emit_label(err)
|
||||||
|
emit_log_error("cannot apply '+': operands must both be text or both be numbers")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(done)
|
emit_label(done)
|
||||||
return null
|
return null
|
||||||
@@ -345,6 +374,7 @@ var mcode = function(ast) {
|
|||||||
emit_jump(done)
|
emit_jump(done)
|
||||||
|
|
||||||
emit_label(err)
|
emit_label(err)
|
||||||
|
emit_log_error("cannot apply '" + _bp_op_sym + "': operands must be numbers")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(done)
|
emit_label(done)
|
||||||
return null
|
return null
|
||||||
@@ -569,6 +599,7 @@ var mcode = function(ast) {
|
|||||||
emit_jump(done)
|
emit_jump(done)
|
||||||
|
|
||||||
emit_label(err)
|
emit_label(err)
|
||||||
|
emit_log_error("cannot compare with '" + _bp_op_sym + "': operands must be same type")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(done)
|
emit_label(done)
|
||||||
return null
|
return null
|
||||||
@@ -589,6 +620,7 @@ var mcode = function(ast) {
|
|||||||
emit_jump(done)
|
emit_jump(done)
|
||||||
|
|
||||||
emit_label(err)
|
emit_label(err)
|
||||||
|
emit_log_error("cannot negate: operand must be a number")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(done)
|
emit_label(done)
|
||||||
return null
|
return null
|
||||||
@@ -607,6 +639,7 @@ var mcode = function(ast) {
|
|||||||
_bp_dest = dest
|
_bp_dest = dest
|
||||||
_bp_left = left
|
_bp_left = left
|
||||||
_bp_right = right
|
_bp_right = right
|
||||||
|
_bp_op_sym = binop_sym[op_str] || op_str
|
||||||
if (op_str == "add") {
|
if (op_str == "add") {
|
||||||
emit_add_decomposed()
|
emit_add_decomposed()
|
||||||
} else if (op_str == "eq") {
|
} else if (op_str == "eq") {
|
||||||
@@ -760,6 +793,7 @@ var mcode = function(ast) {
|
|||||||
|
|
||||||
// Error path: non-text key on function disrupts
|
// Error path: non-text key on function disrupts
|
||||||
emit_label(error_path)
|
emit_label(error_path)
|
||||||
|
emit_log_error("cannot access: key must be text")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_jump(done_label)
|
emit_jump(done_label)
|
||||||
|
|
||||||
@@ -1427,6 +1461,7 @@ var mcode = function(ast) {
|
|||||||
emit_2("push", arr_slot, val_slot)
|
emit_2("push", arr_slot, val_slot)
|
||||||
emit_jump(guard_done)
|
emit_jump(guard_done)
|
||||||
emit_label(guard_err)
|
emit_label(guard_err)
|
||||||
|
emit_log_error("cannot push: target must be an array")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(guard_done)
|
emit_label(guard_done)
|
||||||
return val_slot
|
return val_slot
|
||||||
@@ -1785,6 +1820,7 @@ var mcode = function(ast) {
|
|||||||
emit_2("push", a0, a1)
|
emit_2("push", a0, a1)
|
||||||
emit_jump(guard_done)
|
emit_jump(guard_done)
|
||||||
emit_label(guard_err)
|
emit_label(guard_err)
|
||||||
|
emit_log_error("cannot push: target must be an array")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(guard_done)
|
emit_label(guard_done)
|
||||||
return a1
|
return a1
|
||||||
@@ -2156,6 +2192,7 @@ var mcode = function(ast) {
|
|||||||
emit_2("pop", local_slot, arr_slot)
|
emit_2("pop", local_slot, arr_slot)
|
||||||
emit_jump(guard_done)
|
emit_jump(guard_done)
|
||||||
emit_label(guard_err)
|
emit_label(guard_err)
|
||||||
|
emit_log_error("cannot pop: target must be an array")
|
||||||
emit_0("disrupt")
|
emit_0("disrupt")
|
||||||
emit_label(guard_done)
|
emit_label(guard_done)
|
||||||
}
|
}
|
||||||
|
|||||||
9
parse.cm
9
parse.cm
@@ -437,7 +437,14 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
}
|
}
|
||||||
push(list, pair)
|
push(list, pair)
|
||||||
if (tok.kind == ",") advance()
|
if (tok.kind == ",") advance()
|
||||||
else break
|
else if (tok.kind == "{") {
|
||||||
|
if (right && right.kind == "(") {
|
||||||
|
parse_error(tok, "unexpected '{' after property value; use method shorthand `name(args) { ... }` or `name: function(args) { ... }`")
|
||||||
|
} else {
|
||||||
|
parse_error(tok, "expected ',' or '}' in object literal")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else break
|
||||||
}
|
}
|
||||||
ast_node_end(node)
|
ast_node_end(node)
|
||||||
if (tok.kind == "}") advance()
|
if (tok.kind == "}") advance()
|
||||||
|
|||||||
@@ -2124,41 +2124,45 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
if (code->disruption_pc > 0 && frame_pc < code->disruption_pc) {
|
if (code->disruption_pc > 0 && frame_pc < code->disruption_pc) {
|
||||||
env = fn->u.reg.env_record;
|
env = fn->u.reg.env_record;
|
||||||
pc = code->disruption_pc;
|
pc = code->disruption_pc;
|
||||||
|
ctx->disruption_reported = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (JS_IsNull(frame->caller)) {
|
if (JS_IsNull(frame->caller)) {
|
||||||
const char *fn_name = code->name_cstr ? code->name_cstr : "<anonymous>";
|
if (!ctx->disruption_reported) {
|
||||||
const char *file = code->filename_cstr ? code->filename_cstr : "<unknown>";
|
const char *fn_name = code->name_cstr ? code->name_cstr : "<anonymous>";
|
||||||
uint16_t line = 0, col = 0;
|
const char *file = code->filename_cstr ? code->filename_cstr : "<unknown>";
|
||||||
if (code->line_table && frame_pc > 0 && frame_pc - 1 < code->instr_count) {
|
uint16_t line = 0, col = 0;
|
||||||
line = code->line_table[frame_pc - 1].line;
|
if (code->line_table && frame_pc > 0 && frame_pc - 1 < code->instr_count) {
|
||||||
col = code->line_table[frame_pc - 1].col;
|
line = code->line_table[frame_pc - 1].line;
|
||||||
}
|
col = code->line_table[frame_pc - 1].col;
|
||||||
fprintf(stderr, "unhandled disruption in %s (%s:%u:%u)\n", fn_name, file, line, col);
|
|
||||||
/* Walk and print the frame chain as a stack trace */
|
|
||||||
{
|
|
||||||
JSFrameRegister *trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
|
||||||
int first = 1;
|
|
||||||
while (trace_frame) {
|
|
||||||
if (!mist_is_function(trace_frame->function)) break;
|
|
||||||
JSFunction *trace_fn = JS_VALUE_GET_FUNCTION(trace_frame->function);
|
|
||||||
if (trace_fn->kind == JS_FUNC_KIND_REGISTER && trace_fn->u.reg.code) {
|
|
||||||
JSCodeRegister *tc = trace_fn->u.reg.code;
|
|
||||||
uint32_t tpc = first ? (frame_pc > 0 ? frame_pc - 1 : 0)
|
|
||||||
: (uint32_t)(JS_VALUE_GET_INT(trace_frame->address) >> 16);
|
|
||||||
uint16_t tl = 0, tcol = 0;
|
|
||||||
if (tc->line_table && tpc < tc->instr_count) {
|
|
||||||
tl = tc->line_table[tpc].line;
|
|
||||||
tcol = tc->line_table[tpc].col;
|
|
||||||
}
|
|
||||||
fprintf(stderr, " at %s (%s:%u:%u)\n",
|
|
||||||
tc->name_cstr ? tc->name_cstr : "<anonymous>",
|
|
||||||
tc->filename_cstr ? tc->filename_cstr : "<unknown>", tl, tcol);
|
|
||||||
}
|
|
||||||
if (JS_IsNull(trace_frame->caller)) break;
|
|
||||||
trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(trace_frame->caller);
|
|
||||||
first = 0;
|
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "unhandled disruption in %s (%s:%u:%u)\n", fn_name, file, line, col);
|
||||||
|
/* Walk and print the frame chain as a stack trace */
|
||||||
|
{
|
||||||
|
JSFrameRegister *trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
int first = 1;
|
||||||
|
while (trace_frame) {
|
||||||
|
if (!mist_is_function(trace_frame->function)) break;
|
||||||
|
JSFunction *trace_fn = JS_VALUE_GET_FUNCTION(trace_frame->function);
|
||||||
|
if (trace_fn->kind == JS_FUNC_KIND_REGISTER && trace_fn->u.reg.code) {
|
||||||
|
JSCodeRegister *tc = trace_fn->u.reg.code;
|
||||||
|
uint32_t tpc = first ? (frame_pc > 0 ? frame_pc - 1 : 0)
|
||||||
|
: (uint32_t)(JS_VALUE_GET_INT(trace_frame->address) >> 16);
|
||||||
|
uint16_t tl = 0, tcol = 0;
|
||||||
|
if (tc->line_table && tpc < tc->instr_count) {
|
||||||
|
tl = tc->line_table[tpc].line;
|
||||||
|
tcol = tc->line_table[tpc].col;
|
||||||
|
}
|
||||||
|
fprintf(stderr, " at %s (%s:%u:%u)\n",
|
||||||
|
tc->name_cstr ? tc->name_cstr : "<anonymous>",
|
||||||
|
tc->filename_cstr ? tc->filename_cstr : "<unknown>", tl, tcol);
|
||||||
|
}
|
||||||
|
if (JS_IsNull(trace_frame->caller)) break;
|
||||||
|
trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(trace_frame->caller);
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->disruption_reported = TRUE;
|
||||||
}
|
}
|
||||||
result = JS_Throw(ctx, JS_NULL);
|
result = JS_Throw(ctx, JS_NULL);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
|||||||
@@ -1147,6 +1147,8 @@ struct JSContext {
|
|||||||
|
|
||||||
JSValue current_exception;
|
JSValue current_exception;
|
||||||
|
|
||||||
|
JS_BOOL disruption_reported;
|
||||||
|
|
||||||
/* Actor identity key — used by wota/nota PRIVATE serialization */
|
/* Actor identity key — used by wota/nota PRIVATE serialization */
|
||||||
JSValue actor_sym;
|
JSValue actor_sym;
|
||||||
|
|
||||||
|
|||||||
153
tests/errors.cm
Normal file
153
tests/errors.cm
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
// Runtime type error tests — verify each type mismatch disrupts correctly
|
||||||
|
return {
|
||||||
|
test_text_plus_array_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = "hello" + [1, 2, 3]
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "text + array should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_number_plus_text_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = 1 + "hello"
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "number + text should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_number_plus_array_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = 1 + [1, 2]
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "number + array should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_text_multiply_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = "hello" * 2
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "text * number should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_text_divide_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = "hello" / 2
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "text / number should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_text_modulo_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = "hello" % 2
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "text % number should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_array_subtract_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = [1] - 2
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "array - number should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_negate_text_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var s = "hello"
|
||||||
|
var fn = function() {
|
||||||
|
return -s
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "negate text should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_push_on_non_array_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = "hello"
|
||||||
|
x[] = 1
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "push on non-array should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_pop_on_non_array_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var s = "hello"
|
||||||
|
var fn = function() {
|
||||||
|
var v = s[]
|
||||||
|
return v
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "pop on non-array should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_comparison_type_mismatch_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
var x = "hello" < [1]
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "text < array should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_explicit_disrupt_works: function() {
|
||||||
|
var caught = false
|
||||||
|
var fn = function() {
|
||||||
|
disrupt
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
fn()
|
||||||
|
if (!caught) return "explicit disrupt should be caught"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_valid_add_text: function() {
|
||||||
|
var x = "hello" + " world"
|
||||||
|
if (x != "hello world") return "text + text should work"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_valid_add_numbers: function() {
|
||||||
|
var x = 1 + 2
|
||||||
|
if (x != 3) return "number + number should work"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_valid_comparison: function() {
|
||||||
|
if (!(1 < 2)) return "1 < 2 should be true"
|
||||||
|
if (!("a" < "b")) return "a < b should be true"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user