Compare commits
6 Commits
8f9d026b9b
...
d18ea1b330
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d18ea1b330 | ||
|
|
4de0659474 | ||
|
|
27a9b72b07 | ||
|
|
a3622bd5bd | ||
|
|
2f6700415e | ||
|
|
243d92f7f3 |
@@ -1,17 +1,21 @@
|
||||
(function engine() {
|
||||
// Hidden vars (os, actorsym, init, core_path) come from env
|
||||
var ACTORDATA = actorsym
|
||||
var SYSYM = '__SYSTEM__'
|
||||
|
||||
var _cell = {}
|
||||
var need_stop = false
|
||||
|
||||
var dylib_ext
|
||||
|
||||
switch(os.platform()) {
|
||||
case 'Windows': dylib_ext = '.dll'; break;
|
||||
case 'macOS': dylib_ext = '.dylib'; break;
|
||||
case 'Linux': dylib_ext = '.so'; break;
|
||||
}
|
||||
var cases = {
|
||||
Windows: '.dll',
|
||||
macOS: '.dylib',
|
||||
Linux: '.so'
|
||||
}
|
||||
|
||||
print(os.platform())
|
||||
|
||||
dylib_ext = cases[os.platform()]
|
||||
|
||||
var MOD_EXT = '.cm'
|
||||
var ACTOR_EXT = '.ce'
|
||||
@@ -51,14 +55,16 @@ var fd = use_embed('fd')
|
||||
// Get the shop path from HOME environment
|
||||
var home = os.getenv('HOME') || os.getenv('USERPROFILE')
|
||||
if (!home) {
|
||||
throw Error('Could not determine home directory')
|
||||
os.print('Could not determine home directory\n')
|
||||
os.exit(1)
|
||||
}
|
||||
var shop_path = home + '/.cell'
|
||||
var packages_path = shop_path + '/packages'
|
||||
var core_path = packages_path + '/core'
|
||||
|
||||
if (!fd.is_dir(core_path)) {
|
||||
throw Error('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.')
|
||||
os.print('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.\n')
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
var use_cache = {}
|
||||
@@ -79,7 +85,7 @@ function use_core(path) {
|
||||
var script_blob = fd.slurp(file_path)
|
||||
var script = text(script_blob)
|
||||
var mod = `(function setup_module(use){${script}})`
|
||||
var fn = js.eval('core:' + path, mod)
|
||||
var fn = mach_eval('core:' + path, mod)
|
||||
var result = call(fn,sym, [use_core])
|
||||
use_cache[cache_key] = result;
|
||||
return result;
|
||||
@@ -133,32 +139,27 @@ function log(name, args) {
|
||||
var caller = caller_data(1)
|
||||
var msg = args[0]
|
||||
|
||||
switch(name) {
|
||||
case 'console':
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
case 'error':
|
||||
msg = msg ?? Error()
|
||||
if (is_proto(msg, Error))
|
||||
msg = msg.name + ": " + msg.message + "\n" + msg.stack
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
case 'system':
|
||||
msg = "[SYSTEM] " + msg
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
default:
|
||||
log.console(`unknown log type: ${name}`)
|
||||
break
|
||||
if (name == 'console') {
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
} else if (name == 'error') {
|
||||
if (msg == null) msg = Error()
|
||||
if (is_proto(msg, Error))
|
||||
msg = msg.name + ": " + msg.message + "\n" + msg.stack
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
} else if (name == 'system') {
|
||||
msg = "[SYSTEM] " + msg
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
} else {
|
||||
log.console(`unknown log type: ${name}`)
|
||||
}
|
||||
}
|
||||
|
||||
function disrupt(err)
|
||||
function actor_die(err)
|
||||
{
|
||||
if (is_function(err.toString)) {
|
||||
os.print(err.toString())
|
||||
os.print("\n")
|
||||
os.print(err.stack)
|
||||
if (err && is_function(err.toString)) {
|
||||
os.print(err.toString())
|
||||
os.print("\n")
|
||||
if (err.stack) os.print(err.stack)
|
||||
}
|
||||
|
||||
if (overling) {
|
||||
@@ -185,14 +186,14 @@ function disrupt(err)
|
||||
log.console(err.stack)
|
||||
}
|
||||
|
||||
actor_mod.disrupt()
|
||||
actor_mod["disrupt"]()
|
||||
}
|
||||
|
||||
|
||||
|
||||
actor_mod.on_exception(disrupt)
|
||||
actor_mod.on_exception(actor_die)
|
||||
|
||||
_cell.args = init ?? {}
|
||||
_cell.args = init != null ? init : {}
|
||||
_cell.id = "newguy"
|
||||
|
||||
function create_actor(desc = {id:guid()}) {
|
||||
@@ -241,11 +242,15 @@ os.runtime_env = runtime_env
|
||||
|
||||
$_.time_limit = function(requestor, seconds)
|
||||
{
|
||||
if (!pronto.is_requestor(requestor))
|
||||
throw Error('time_limit: first argument must be a requestor');
|
||||
if (!is_number(seconds) || seconds <= 0)
|
||||
throw Error('time_limit: seconds must be a positive number');
|
||||
|
||||
if (!pronto.is_requestor(requestor)) {
|
||||
log.error('time_limit: first argument must be a requestor')
|
||||
disrupt
|
||||
}
|
||||
if (!is_number(seconds) || seconds <= 0) {
|
||||
log.error('time_limit: seconds must be a positive number')
|
||||
disrupt
|
||||
}
|
||||
|
||||
return function time_limit_requestor(callback, value) {
|
||||
pronto.check_callback(callback, 'time_limit')
|
||||
var finished = false
|
||||
@@ -260,7 +265,14 @@ $_.time_limit = function(requestor, seconds)
|
||||
timer_cancel = null
|
||||
}
|
||||
if (requestor_cancel) {
|
||||
try { pronto.requestor_cancel(reason) } catch (_) {}
|
||||
requestor_cancel(reason)
|
||||
requestor_cancel = null
|
||||
}
|
||||
}
|
||||
|
||||
function safe_cancel_requestor(reason) {
|
||||
if (requestor_cancel) {
|
||||
requestor_cancel(reason)
|
||||
requestor_cancel = null
|
||||
}
|
||||
}
|
||||
@@ -268,15 +280,12 @@ $_.time_limit = function(requestor, seconds)
|
||||
timer_cancel = $_.delay(function() {
|
||||
if (finished) return
|
||||
def reason = make_reason(factory, 'Timeout.', seconds)
|
||||
if (requestor_cancel) {
|
||||
try { requestor_cancel(reason) } catch (_) {}
|
||||
requestor_cancel = null
|
||||
}
|
||||
safe_cancel_requestor(reason)
|
||||
finished = true
|
||||
callback(null, reason)
|
||||
}, seconds)
|
||||
|
||||
try {
|
||||
function do_request() {
|
||||
requestor_cancel = requestor(function(val, reason) {
|
||||
if (finished) return
|
||||
finished = true
|
||||
@@ -286,16 +295,14 @@ $_.time_limit = function(requestor, seconds)
|
||||
}
|
||||
callback(val, reason)
|
||||
}, value)
|
||||
} catch (ex) {
|
||||
cancel(ex)
|
||||
callback(null, ex)
|
||||
} disruption {
|
||||
cancel(Error('requestor failed'))
|
||||
callback(null, Error('requestor failed'))
|
||||
}
|
||||
do_request()
|
||||
|
||||
return function(reason) {
|
||||
if (requestor_cancel) {
|
||||
try { requestor_cancel(reason) } catch (_) {}
|
||||
requestor_cancel = null
|
||||
}
|
||||
safe_cancel_requestor(reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,52 +415,54 @@ var portal_fn = null
|
||||
|
||||
// takes a function input value that will eventually be called with the current time in number form.
|
||||
$_.portal = function(fn, port) {
|
||||
if (portal) throw Error(`Already started a portal listening on ${portal.port}`)
|
||||
if (!port) throw Error("Requires a valid port.")
|
||||
if (portal) {
|
||||
log.error(`Already started a portal listening on ${portal.port}`)
|
||||
disrupt
|
||||
}
|
||||
if (!port) {
|
||||
log.error("Requires a valid port.")
|
||||
disrupt
|
||||
}
|
||||
log.system(`starting a portal on port ${port}`)
|
||||
portal = enet.create_host({address: "any", port})
|
||||
portal_fn = fn
|
||||
}
|
||||
|
||||
function handle_host(e) {
|
||||
switch (e.type) {
|
||||
case "connect":
|
||||
log.system(`connected a new peer: ${e.peer.address}:${e.peer.port}`)
|
||||
peers[`${e.peer.address}:${e.peer.port}`] = e.peer
|
||||
var queue = peer_queue.get(e.peer)
|
||||
if (queue) {
|
||||
arrfor(queue, (msg, index) => e.peer.send(nota.encode(msg)))
|
||||
log.system(`sent ${msg} out of queue`)
|
||||
peer_queue.delete(e.peer)
|
||||
}
|
||||
break
|
||||
case "disconnect":
|
||||
if (e.type == "connect") {
|
||||
log.system(`connected a new peer: ${e.peer.address}:${e.peer.port}`)
|
||||
peers[`${e.peer.address}:${e.peer.port}`] = e.peer
|
||||
var queue = peer_queue.get(e.peer)
|
||||
if (queue) {
|
||||
arrfor(queue, (msg, index) => e.peer.send(nota.encode(msg)))
|
||||
log.system(`sent ${msg} out of queue`)
|
||||
peer_queue.delete(e.peer)
|
||||
arrfor(array(peers), function(id, index) {
|
||||
if (peers[id] == e.peer) delete peers[id]
|
||||
}
|
||||
} else if (e.type == "disconnect") {
|
||||
peer_queue.delete(e.peer)
|
||||
arrfor(array(peers), function(id, index) {
|
||||
if (peers[id] == e.peer) delete peers[id]
|
||||
})
|
||||
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port)
|
||||
} else if (e.type == "receive") {
|
||||
var data = nota.decode(e.data)
|
||||
if (data.replycc && !data.replycc.address) {
|
||||
data.replycc[ACTORDATA].address = e.peer.address
|
||||
data.replycc[ACTORDATA].port = e.peer.port
|
||||
}
|
||||
function populate_actor_addresses(obj) {
|
||||
if (!is_object(obj)) return
|
||||
if (obj[ACTORDATA] && !obj[ACTORDATA].address) {
|
||||
obj[ACTORDATA].address = e.peer.address
|
||||
obj[ACTORDATA].port = e.peer.port
|
||||
}
|
||||
arrfor(array(obj), function(key, index) {
|
||||
if (key in obj)
|
||||
populate_actor_addresses(obj[key])
|
||||
})
|
||||
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port)
|
||||
break
|
||||
case "receive":
|
||||
var data = nota.decode(e.data)
|
||||
if (data.replycc && !data.replycc.address) {
|
||||
data.replycc[ACTORDATA].address = e.peer.address
|
||||
data.replycc[ACTORDATA].port = e.peer.port
|
||||
}
|
||||
function populate_actor_addresses(obj) {
|
||||
if (!is_object(obj)) return
|
||||
if (obj[ACTORDATA] && !obj[ACTORDATA].address) {
|
||||
obj[ACTORDATA].address = e.peer.address
|
||||
obj[ACTORDATA].port = e.peer.port
|
||||
}
|
||||
arrfor(array(obj), function(key, index) {
|
||||
if (key in obj)
|
||||
populate_actor_addresses(obj[key])
|
||||
})
|
||||
}
|
||||
if (data.data) populate_actor_addresses(data.data)
|
||||
turn(data)
|
||||
break
|
||||
}
|
||||
if (data.data) populate_actor_addresses(data.data)
|
||||
turn(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,10 +496,14 @@ $_.stop = function stop(actor) {
|
||||
need_stop = true
|
||||
return
|
||||
}
|
||||
if (!is_actor(actor))
|
||||
throw Error('Can only call stop on an actor.')
|
||||
if (is_null(underlings[actor[ACTORDATA].id]))
|
||||
throw Error('Can only call stop on an underling or self.')
|
||||
if (!is_actor(actor)) {
|
||||
log.error('Can only call stop on an actor.')
|
||||
disrupt
|
||||
}
|
||||
if (is_null(underlings[actor[ACTORDATA].id])) {
|
||||
log.error('Can only call stop on an underling or self.')
|
||||
disrupt
|
||||
}
|
||||
|
||||
sys_msg(actor, {kind:"stop"})
|
||||
}
|
||||
@@ -527,20 +540,22 @@ function actor_prep(actor, send) {
|
||||
|
||||
// Send a message immediately without queuing
|
||||
function actor_send_immediate(actor, send) {
|
||||
try {
|
||||
actor_send(actor, send);
|
||||
} catch (err) {
|
||||
log.error("Failed to send immediate message:", err);
|
||||
}
|
||||
actor_send(actor, send)
|
||||
}
|
||||
|
||||
function actor_send(actor, message) {
|
||||
if (actor[HEADER] && !actor[HEADER].replycc) // attempting to respond to a message but sender is not expecting; silently drop
|
||||
return
|
||||
|
||||
if (!is_actor(actor) && !is_actor(actor.replycc)) throw Error(`Must send to an actor object. Attempted send to ${actor}`)
|
||||
|
||||
if (!is_object(message)) throw Error('Must send an object record.')
|
||||
if (!is_actor(actor) && !is_actor(actor.replycc)) {
|
||||
log.error(`Must send to an actor object. Attempted send to ${actor}`)
|
||||
disrupt
|
||||
}
|
||||
|
||||
if (!is_object(message)) {
|
||||
log.error('Must send an object record.')
|
||||
disrupt
|
||||
}
|
||||
|
||||
// message to self
|
||||
if (actor[ACTORDATA].id == _cell.id) {
|
||||
@@ -583,12 +598,10 @@ function actor_send(actor, message) {
|
||||
// Holds all messages queued during the current turn.
|
||||
var message_queue = []
|
||||
|
||||
var need_stop = false
|
||||
|
||||
function send_messages() {
|
||||
function send_messages() {
|
||||
// if we've been flagged to stop, bail out before doing anything
|
||||
if (need_stop) {
|
||||
disrupt()
|
||||
actor_die()
|
||||
message_queue = []
|
||||
return
|
||||
}
|
||||
@@ -608,19 +621,26 @@ var need_stop = false
|
||||
var replies = {}
|
||||
|
||||
function send(actor, message, reply) {
|
||||
if (!is_object(actor))
|
||||
throw Error(`Must send to an actor object. Provided: ${actor}`);
|
||||
if (!is_object(actor)) {
|
||||
log.error(`Must send to an actor object. Provided: ${actor}`)
|
||||
disrupt
|
||||
}
|
||||
|
||||
if (!is_object(message))
|
||||
throw Error('Message must be an object')
|
||||
if (!is_object(message)) {
|
||||
log.error('Message must be an object')
|
||||
disrupt
|
||||
}
|
||||
var send_msg = {type:"user", data: message}
|
||||
var target = actor
|
||||
|
||||
if (actor[HEADER] && actor[HEADER].replycc) {
|
||||
var header = actor[HEADER]
|
||||
if (!header.replycc || !is_actor(header.replycc))
|
||||
throw Error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`)
|
||||
if (!header.replycc || !is_actor(header.replycc)) {
|
||||
log.error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`)
|
||||
disrupt
|
||||
}
|
||||
|
||||
actor = header.replycc
|
||||
target = header.replycc
|
||||
send_msg.return = header.reply
|
||||
}
|
||||
|
||||
@@ -638,7 +658,7 @@ function send(actor, message, reply) {
|
||||
}
|
||||
|
||||
// Instead of sending immediately, queue it
|
||||
actor_prep(actor, send_msg);
|
||||
actor_prep(target, send_msg);
|
||||
}
|
||||
|
||||
stone(send)
|
||||
@@ -669,7 +689,7 @@ overling = _cell.args.overling
|
||||
$_.overling = overling
|
||||
|
||||
root = _cell.args.root
|
||||
root ??= $_.self
|
||||
if (root == null) root = $_.self
|
||||
|
||||
if (overling) {
|
||||
$_.couple(overling) // auto couple to overling
|
||||
@@ -705,36 +725,35 @@ function handle_actor_disconnect(id) {
|
||||
delete greeters[id]
|
||||
}
|
||||
log.system(`actor ${id} disconnected`)
|
||||
if (!is_null(couplings[id])) disrupt("coupled actor died") // couplings now disrupts instead of stop
|
||||
if (!is_null(couplings[id])) actor_die("coupled actor died") // couplings now disrupts instead of stop
|
||||
}
|
||||
|
||||
function handle_sysym(msg)
|
||||
{
|
||||
var from
|
||||
switch(msg.kind) {
|
||||
case 'stop':
|
||||
disrupt("got stop message")
|
||||
break
|
||||
case 'underling':
|
||||
from = msg.from
|
||||
var greeter = greeters[from[ACTORDATA].id]
|
||||
if (greeter) greeter(msg.message)
|
||||
if (msg.message.type == 'disrupt')
|
||||
delete underlings[from[ACTORDATA].id]
|
||||
break
|
||||
case 'contact':
|
||||
if (portal_fn) {
|
||||
var letter2 = msg.data
|
||||
letter2[HEADER] = msg
|
||||
delete msg.data
|
||||
portal_fn(letter2)
|
||||
} else throw Error('Got a contact message, but no portal is established.')
|
||||
break
|
||||
case 'couple': // from must be notified when we die
|
||||
from = msg.from
|
||||
underlings[from[ACTORDATA].id] = true
|
||||
log.system(`actor ${from} is coupled to me`)
|
||||
break
|
||||
if (msg.kind == 'stop') {
|
||||
actor_die("got stop message")
|
||||
} else if (msg.kind == 'underling') {
|
||||
from = msg.from
|
||||
var greeter = greeters[from[ACTORDATA].id]
|
||||
if (greeter) greeter(msg.message)
|
||||
if (msg.message.type == 'disrupt')
|
||||
delete underlings[from[ACTORDATA].id]
|
||||
} else if (msg.kind == 'contact') {
|
||||
if (portal_fn) {
|
||||
var letter2 = msg.data
|
||||
letter2[HEADER] = msg
|
||||
delete msg.data
|
||||
portal_fn(letter2)
|
||||
} else {
|
||||
log.error('Got a contact message, but no portal is established.')
|
||||
disrupt
|
||||
}
|
||||
} else if (msg.kind == 'couple') {
|
||||
// from must be notified when we die
|
||||
from = msg.from
|
||||
underlings[from[ACTORDATA].id] = true
|
||||
log.system(`actor ${from} is coupled to me`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,30 +763,27 @@ function handle_message(msg) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (msg.type) {
|
||||
case "user":
|
||||
var letter = msg.data // what the sender really sent
|
||||
_ObjectDefineProperty(letter, HEADER, {
|
||||
value: msg, enumerable: false
|
||||
})
|
||||
_ObjectDefineProperty(letter, ACTORDATA, { // this is so is_actor == true
|
||||
value: { reply: msg.reply }, enumerable: false
|
||||
})
|
||||
|
||||
if (msg.return) {
|
||||
var fn = replies[msg.return]
|
||||
if (fn) fn(letter)
|
||||
delete replies[msg.return]
|
||||
return
|
||||
}
|
||||
|
||||
if (receive_fn) receive_fn(letter)
|
||||
if (msg.type == "user") {
|
||||
var letter = msg.data // what the sender really sent
|
||||
_ObjectDefineProperty(letter, HEADER, {
|
||||
value: msg, enumerable: false
|
||||
})
|
||||
_ObjectDefineProperty(letter, ACTORDATA, { // this is so is_actor == true
|
||||
value: { reply: msg.reply }, enumerable: false
|
||||
})
|
||||
|
||||
if (msg.return) {
|
||||
var fn = replies[msg.return]
|
||||
if (fn) fn(letter)
|
||||
delete replies[msg.return]
|
||||
return
|
||||
case "stopped":
|
||||
handle_actor_disconnect(msg.id)
|
||||
break
|
||||
}
|
||||
|
||||
if (receive_fn) receive_fn(letter)
|
||||
} else if (msg.type == "stopped") {
|
||||
handle_actor_disconnect(msg.id)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function enet_check()
|
||||
{
|
||||
@@ -792,8 +808,10 @@ if (!locator) {
|
||||
locator = shop.resolve_locator(_cell.args.program + ".ce", pkg)
|
||||
}
|
||||
|
||||
if (!locator)
|
||||
throw Error(`Main program ${_cell.args.program} could not be found`)
|
||||
if (!locator) {
|
||||
os.print(`Main program ${_cell.args.program} could not be found\n`)
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
$_.clock(_ => {
|
||||
// Get capabilities for the main program
|
||||
@@ -818,7 +836,6 @@ $_.clock(_ => {
|
||||
var val = call(locator.symbol, null, [_cell.args.arg, use_fn, env])
|
||||
|
||||
if (val)
|
||||
throw Error('Program must not return anything');
|
||||
log.error('Program must not return anything')
|
||||
disrupt
|
||||
})
|
||||
|
||||
})()
|
||||
@@ -114,12 +114,19 @@ static int print_tree_errors(cJSON *root) {
|
||||
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
|
||||
if (cJSON_IsString(fname))
|
||||
filename = fname->valuestring;
|
||||
int prev_line = -1;
|
||||
const char *prev_msg = NULL;
|
||||
cJSON *e;
|
||||
cJSON_ArrayForEach(e, errors) {
|
||||
const char *msg = cJSON_GetStringValue(
|
||||
cJSON_GetObjectItemCaseSensitive(e, "message"));
|
||||
cJSON *line = cJSON_GetObjectItemCaseSensitive(e, "line");
|
||||
cJSON *col = cJSON_GetObjectItemCaseSensitive(e, "column");
|
||||
int cur_line = cJSON_IsNumber(line) ? (int)line->valuedouble : -1;
|
||||
if (prev_msg && msg && cur_line == prev_line && strcmp(msg, prev_msg) == 0)
|
||||
continue;
|
||||
prev_line = cur_line;
|
||||
prev_msg = msg;
|
||||
if (msg && cJSON_IsNumber(line) && cJSON_IsNumber(col))
|
||||
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
||||
filename, (int)line->valuedouble, (int)col->valuedouble, msg);
|
||||
@@ -168,7 +175,7 @@ void script_startup(cell_rt *prt)
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js, js_blob_use(js));
|
||||
|
||||
// Load and compile engine.cm
|
||||
// Load and parse engine.cm to AST
|
||||
size_t engine_size;
|
||||
char *data = load_core_file(ENGINE, &engine_size);
|
||||
if (!data) {
|
||||
@@ -176,10 +183,15 @@ void script_startup(cell_rt *prt)
|
||||
return;
|
||||
}
|
||||
|
||||
JSValue bytecode = JS_Compile(js, data, engine_size, ENGINE);
|
||||
cJSON *ast = JS_ASTTree(data, engine_size, ENGINE);
|
||||
free(data);
|
||||
if (JS_IsException(bytecode)) {
|
||||
uncaught_exception(js, bytecode);
|
||||
if (!ast) {
|
||||
printf("ERROR: Failed to parse %s\n", ENGINE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (print_tree_errors(ast)) {
|
||||
cJSON_Delete(ast);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,9 +221,10 @@ void script_startup(cell_rt *prt)
|
||||
// Stone the environment
|
||||
hidden_env = JS_Stone(js, hidden_env);
|
||||
|
||||
// Integrate and run
|
||||
// Run through MACH VM
|
||||
crt->state = ACTOR_RUNNING;
|
||||
JSValue v = JS_Integrate(js, bytecode, hidden_env);
|
||||
JSValue v = JS_RunMachTree(js, ast, hidden_env);
|
||||
cJSON_Delete(ast);
|
||||
uncaught_exception(js, v);
|
||||
crt->state = ACTOR_IDLE;
|
||||
set_actor_state(crt);
|
||||
|
||||
1036
source/quickjs.c
1036
source/quickjs.c
File diff suppressed because it is too large
Load Diff
@@ -604,6 +604,48 @@ run("self-referencing object", function() {
|
||||
assert_eq(o.self.self.name, "root", "cycle access")
|
||||
})
|
||||
|
||||
// === IDENTIFIER ? AND ! ===
|
||||
|
||||
run("question mark in identifier", function() {
|
||||
var nil? = (x) => x == null
|
||||
assert_eq(nil?(null), true, "nil? null")
|
||||
assert_eq(nil?(42), false, "nil? 42")
|
||||
})
|
||||
|
||||
run("bang in identifier", function() {
|
||||
var set! = (x) => x + 1
|
||||
assert_eq(set!(5), 6, "set! call")
|
||||
})
|
||||
|
||||
run("question mark mid identifier", function() {
|
||||
var is?valid = (x) => x > 0
|
||||
assert_eq(is?valid(3), true, "is?valid true")
|
||||
assert_eq(is?valid(-1), false, "is?valid false")
|
||||
})
|
||||
|
||||
run("bang mid identifier", function() {
|
||||
var do!stuff = () => 42
|
||||
assert_eq(do!stuff(), 42, "do!stuff call")
|
||||
})
|
||||
|
||||
run("ternary after question ident", function() {
|
||||
var nil? = (x) => x == null
|
||||
var a = nil?(null) ? "yes" : "no"
|
||||
assert_eq(a, "yes", "ternary true branch")
|
||||
var b = nil?(42) ? "yes" : "no"
|
||||
assert_eq(b, "no", "ternary false branch")
|
||||
})
|
||||
|
||||
run("bang not confused with logical not", function() {
|
||||
assert_eq(!true, false, "logical not true")
|
||||
assert_eq(!false, true, "logical not false")
|
||||
})
|
||||
|
||||
run("inequality not confused with bang ident", function() {
|
||||
assert_eq(1 != 2, true, "inequality true")
|
||||
assert_eq(1 != 1, false, "inequality false")
|
||||
})
|
||||
|
||||
// === SUMMARY ===
|
||||
|
||||
print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed))
|
||||
|
||||
176
vm_suite.ce
176
vm_suite.ce
@@ -3369,6 +3369,182 @@ run("gc object from keys function under pressure", function() {
|
||||
}
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// DEFAULT PARAMETER TESTS
|
||||
// ============================================================================
|
||||
|
||||
run("default param constant", function() {
|
||||
var f = function(a, b=10) { return a + b }
|
||||
assert_eq(f(1), 11, "default param constant")
|
||||
})
|
||||
|
||||
run("default param overridden", function() {
|
||||
var f = function(a, b=10) { return a + b }
|
||||
assert_eq(f(1, 2), 3, "default param overridden")
|
||||
})
|
||||
|
||||
run("default param uses earlier param", function() {
|
||||
var f = function(a, b=a+1) { return b }
|
||||
assert_eq(f(5), 6, "default param uses earlier param")
|
||||
})
|
||||
|
||||
run("default param uses earlier param overridden", function() {
|
||||
var f = function(a, b=a+1) { return b }
|
||||
assert_eq(f(5, 20), 20, "default param uses earlier param overridden")
|
||||
})
|
||||
|
||||
run("multiple default params", function() {
|
||||
var f = function(a, b=10, c=a+1) { return a + b + c }
|
||||
assert_eq(f(1), 13, "multiple defaults f(1)")
|
||||
assert_eq(f(1, 2), 5, "multiple defaults f(1,2)")
|
||||
assert_eq(f(1, 2, 3), 6, "multiple defaults f(1,2,3)")
|
||||
})
|
||||
|
||||
run("arrow function default param", function() {
|
||||
var g = (x, y=100) => x + y
|
||||
assert_eq(g(5), 105, "arrow default param")
|
||||
assert_eq(g(5, 20), 25, "arrow default param overridden")
|
||||
})
|
||||
|
||||
run("default param null passed explicitly", function() {
|
||||
var f = function(a, b=10) { return b }
|
||||
assert_eq(f(1, null), 10, "explicit null triggers default")
|
||||
})
|
||||
|
||||
run("default param with string", function() {
|
||||
var f = function(a, b="hello") { return b }
|
||||
assert_eq(f(1), "hello", "default string param")
|
||||
assert_eq(f(1, "world"), "world", "default string overridden")
|
||||
})
|
||||
|
||||
run("default param first param has no default", function() {
|
||||
var f = function(a, b=42) { return a }
|
||||
assert_eq(f(7), 7, "first param no default")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// FUNCTINO TESTS
|
||||
// ============================================================================
|
||||
|
||||
run("functino +! addition", function() {
|
||||
assert_eq(+!(3, 4), 7, "+! addition")
|
||||
})
|
||||
|
||||
run("functino -! subtraction", function() {
|
||||
assert_eq(-!(10, 3), 7, "-! subtraction")
|
||||
})
|
||||
|
||||
run("functino *! multiplication", function() {
|
||||
assert_eq(*!(5, 6), 30, "*! multiplication")
|
||||
})
|
||||
|
||||
run("functino /! division", function() {
|
||||
assert_eq(/!(10, 2), 5, "/! division")
|
||||
})
|
||||
|
||||
run("functino %! modulo", function() {
|
||||
assert_eq(%!(10, 3), 1, "%! modulo")
|
||||
})
|
||||
|
||||
run("functino **! power", function() {
|
||||
assert_eq(**!(2, 10), 1024, "**! power")
|
||||
})
|
||||
|
||||
run("functino <! less than", function() {
|
||||
assert_eq(<!(3, 5), true, "<! true")
|
||||
assert_eq(<!(5, 3), false, "<! false")
|
||||
assert_eq(<!(3, 3), false, "<! equal")
|
||||
})
|
||||
|
||||
run("functino >! greater than", function() {
|
||||
assert_eq(>!(5, 3), true, ">! true")
|
||||
assert_eq(>!(3, 5), false, ">! false")
|
||||
assert_eq(>!(3, 3), false, ">! equal")
|
||||
})
|
||||
|
||||
run("functino <=! less or equal", function() {
|
||||
assert_eq(<=!(3, 5), true, "<=! less")
|
||||
assert_eq(<=!(3, 3), true, "<=! equal")
|
||||
assert_eq(<=!(5, 3), false, "<=! greater")
|
||||
})
|
||||
|
||||
run("functino >=! greater or equal", function() {
|
||||
assert_eq(>=!(5, 3), true, ">=! greater")
|
||||
assert_eq(>=!(3, 3), true, ">=! equal")
|
||||
assert_eq(>=!(3, 5), false, ">=! less")
|
||||
})
|
||||
|
||||
run("functino =! equality", function() {
|
||||
assert_eq(=!(5, 5), true, "=! true")
|
||||
assert_eq(=!(5, 3), false, "=! false")
|
||||
})
|
||||
|
||||
run("functino !=! inequality", function() {
|
||||
assert_eq(!=!(5, 3), true, "!=! true")
|
||||
assert_eq(!=!(5, 5), false, "!=! false")
|
||||
})
|
||||
|
||||
run("functino =! with tolerance", function() {
|
||||
assert_eq(=!(1.0, 1.0001, 0.001), true, "=! within tolerance")
|
||||
assert_eq(=!(1.0, 1.01, 0.001), false, "=! outside tolerance")
|
||||
})
|
||||
|
||||
run("functino !=! with tolerance", function() {
|
||||
assert_eq(!=!(1.0, 1.01, 0.001), true, "!=! outside tolerance")
|
||||
assert_eq(!=!(1.0, 1.0001, 0.001), false, "!=! within tolerance")
|
||||
})
|
||||
|
||||
run("functino &! bitwise and", function() {
|
||||
assert_eq(&!(0xff, 0x0f), 0x0f, "&! bitwise and")
|
||||
})
|
||||
|
||||
run("functino |! bitwise or", function() {
|
||||
assert_eq(|!(0xf0, 0x0f), 0xff, "|! bitwise or")
|
||||
})
|
||||
|
||||
run("functino ^! bitwise xor", function() {
|
||||
assert_eq(^!(0xff, 0x0f), 0xf0, "^! bitwise xor")
|
||||
})
|
||||
|
||||
run("functino <<! shift left", function() {
|
||||
assert_eq(<<!(1, 4), 16, "<<! shift left")
|
||||
})
|
||||
|
||||
run("functino >>! shift right", function() {
|
||||
assert_eq(>>!(16, 4), 1, ">>! shift right")
|
||||
})
|
||||
|
||||
run("functino ~! bitwise not", function() {
|
||||
assert_eq(~!(0), -1, "~! bitwise not 0")
|
||||
})
|
||||
|
||||
run("functino []! array index", function() {
|
||||
var arr = [10, 20, 30]
|
||||
assert_eq([]!(arr, 0), 10, "[]! index 0")
|
||||
assert_eq([]!(arr, 1), 20, "[]! index 1")
|
||||
assert_eq([]!(arr, 2), 30, "[]! index 2")
|
||||
})
|
||||
|
||||
run("functino &&! logical and", function() {
|
||||
assert_eq(&&!(true, true), true, "&&! true true")
|
||||
assert_eq(&&!(true, false), false, "&&! true false")
|
||||
assert_eq(&&!(false, true), false, "&&! false true")
|
||||
assert_eq(&&!(1, 2), 2, "&&! truthy returns right")
|
||||
assert_eq(&&!(0, 2), 0, "&&! falsy returns left")
|
||||
})
|
||||
|
||||
run("functino ||! logical or", function() {
|
||||
assert_eq(||!(false, true), true, "||! false true")
|
||||
assert_eq(||!(true, false), true, "||! true false")
|
||||
assert_eq(||!(false, false), false, "||! false false")
|
||||
assert_eq(||!(0, 5), 5, "||! falsy returns right")
|
||||
assert_eq(||!(3, 5), 3, "||! truthy returns left")
|
||||
})
|
||||
|
||||
run("functino >>>! unsigned shift right", function() {
|
||||
assert_eq(>>>!(-1, 28), 15, ">>>! unsigned shift right")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// SUMMARY
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user