bootstrap
This commit is contained in:
@@ -123,37 +123,51 @@ function use_fn(path) {
|
||||
// Helper to load engine.cm and run it with given env
|
||||
function load_engine(env) {
|
||||
var engine_path = core_path + '/internal/engine.mach'
|
||||
var data = null
|
||||
var engine_src = null
|
||||
var engine_ast = null
|
||||
if (fd.is_file(engine_path)) {
|
||||
var data = fd.slurp(engine_path)
|
||||
data = fd.slurp(engine_path)
|
||||
return mach_load(data, env)
|
||||
}
|
||||
engine_path = core_path + '/internal/engine.cm'
|
||||
var engine_src = text(fd.slurp(engine_path))
|
||||
var engine_ast = analyze(engine_src, engine_path)
|
||||
engine_src = text(fd.slurp(engine_path))
|
||||
engine_ast = analyze(engine_src, engine_path)
|
||||
return run_ast('engine', engine_ast, env)
|
||||
}
|
||||
|
||||
// Detect mode and route
|
||||
// CLI mode has 'args'; actor spawn mode has 'init'
|
||||
var program = null
|
||||
var user_args = []
|
||||
var _j = 0
|
||||
var script_file = null
|
||||
var script = null
|
||||
var ast = null
|
||||
|
||||
if (args != null) {
|
||||
// CLI mode — run script directly
|
||||
var program = args[0]
|
||||
var user_args = []
|
||||
var _j = 1
|
||||
program = args[0]
|
||||
_j = 1
|
||||
while (_j < length(args)) {
|
||||
push(user_args, args[_j])
|
||||
_j = _j + 1
|
||||
}
|
||||
|
||||
var script_file = program
|
||||
script_file = program
|
||||
if (!ends_with(script_file, '.ce') && !ends_with(script_file, '.cm'))
|
||||
script_file = program + '.cm'
|
||||
|
||||
// Search CWD then core_path, trying .cm then .ce
|
||||
if (!fd.is_file(script_file))
|
||||
script_file = core_path + '/' + program + '.cm'
|
||||
if (!fd.is_file(script_file))
|
||||
script_file = program + '.ce'
|
||||
if (!fd.is_file(script_file))
|
||||
script_file = core_path + '/' + program + '.ce'
|
||||
|
||||
var script = text(fd.slurp(script_file))
|
||||
var ast = analyze(script, script_file)
|
||||
script = text(fd.slurp(script_file))
|
||||
ast = analyze(script, script_file)
|
||||
run_ast(program, ast, {use: use_fn, args: user_args, json: json})
|
||||
} else {
|
||||
// Actor spawn mode — load engine.cm with full actor env
|
||||
|
||||
Binary file not shown.
@@ -6,17 +6,13 @@ var SYSYM = '__SYSTEM__'
|
||||
var _cell = {}
|
||||
var need_stop = false
|
||||
|
||||
var dylib_ext
|
||||
|
||||
var cases = {
|
||||
Windows: '.dll',
|
||||
macOS: '.dylib',
|
||||
Linux: '.so'
|
||||
}
|
||||
|
||||
print(os.platform())
|
||||
|
||||
dylib_ext = cases[os.platform()]
|
||||
var dylib_ext = cases[os.platform()]
|
||||
|
||||
var MOD_EXT = '.cm'
|
||||
var ACTOR_EXT = '.ce'
|
||||
@@ -67,11 +63,14 @@ function use_core(path) {
|
||||
return use_cache[cache_key]
|
||||
|
||||
var sym = use_embed(replace(path, '/', '_'))
|
||||
var result = null
|
||||
var script = null
|
||||
var ast = null
|
||||
|
||||
// Check for pre-compiled .mach file first
|
||||
var mach_path = core_path + '/' + path + '.mach'
|
||||
if (fd.is_file(mach_path)) {
|
||||
var result = mach_load(fd.slurp(mach_path), {use: use_core})
|
||||
result = mach_load(fd.slurp(mach_path), {use: use_core})
|
||||
use_cache[cache_key] = result
|
||||
return result
|
||||
}
|
||||
@@ -79,9 +78,9 @@ function use_core(path) {
|
||||
// Fall back to source .cm file — compile at runtime
|
||||
var file_path = core_path + '/' + path + MOD_EXT
|
||||
if (fd.is_file(file_path)) {
|
||||
var script = text(fd.slurp(file_path))
|
||||
var ast = analyze(script, file_path)
|
||||
var result = run_ast_fn('core:' + path, ast, {use: use_core})
|
||||
script = text(fd.slurp(file_path))
|
||||
ast = analyze(script, file_path)
|
||||
result = run_ast_fn('core:' + path, ast, {use: use_core})
|
||||
use_cache[cache_key] = result
|
||||
return result
|
||||
}
|
||||
@@ -108,21 +107,24 @@ function is_actor(value) {
|
||||
var ENETSERVICE = 0.1
|
||||
var REPLYTIMEOUT = 60 // seconds before replies are ignored
|
||||
|
||||
function caller_data(depth = 0)
|
||||
function caller_data(depth)
|
||||
{
|
||||
var _depth = depth == null ? 0 : depth
|
||||
var file = "nofile"
|
||||
var line = 0
|
||||
|
||||
var caller = array(Error().stack, "\n")[1+depth]
|
||||
var md = null
|
||||
var m = null
|
||||
|
||||
var caller = array(Error().stack, "\n")[1+_depth]
|
||||
if (caller) {
|
||||
var md = extract(caller, /\((.*)\:/)
|
||||
var m = md ? md[1] : "SCRIPT"
|
||||
md = extract(caller, /\((.*)\:/)
|
||||
m = md ? md[1] : "SCRIPT"
|
||||
if (m) file = m
|
||||
md = extract(caller, /\:(\d*)\)/)
|
||||
m = md ? md[1] : 0
|
||||
if (m) line = m
|
||||
}
|
||||
|
||||
|
||||
return {file,line}
|
||||
}
|
||||
|
||||
@@ -152,6 +154,9 @@ function log(name, args) {
|
||||
|
||||
function actor_die(err)
|
||||
{
|
||||
var reason = null
|
||||
var unders = null
|
||||
|
||||
if (err && is_function(err.toString)) {
|
||||
os.print(err.toString())
|
||||
os.print("\n")
|
||||
@@ -161,14 +166,14 @@ function actor_die(err)
|
||||
if (overling) {
|
||||
if (err) {
|
||||
// with an err, this is a forceful disrupt
|
||||
var reason = (is_proto(err, Error)) ? err.stack : err
|
||||
reason = (is_proto(err, Error)) ? err.stack : err
|
||||
report_to_overling({type:'disrupt', reason})
|
||||
} else
|
||||
report_to_overling({type:'stop'})
|
||||
}
|
||||
|
||||
if (underlings) {
|
||||
var unders = array(underlings)
|
||||
unders = array(underlings)
|
||||
arrfor(unders, function(id, index) {
|
||||
log.console(`calling on ${id} to disrupt too`)
|
||||
$_.stop(create_actor({id}))
|
||||
@@ -192,9 +197,10 @@ function actor_die(err)
|
||||
_cell.args = init != null ? init : {}
|
||||
_cell.id = "newguy"
|
||||
|
||||
function create_actor(desc = {id:guid()}) {
|
||||
function create_actor(desc) {
|
||||
var _desc = desc == null ? {id:guid()} : desc
|
||||
var actor = {}
|
||||
actor[ACTORDATA] = desc
|
||||
actor[ACTORDATA] = _desc
|
||||
return actor
|
||||
}
|
||||
|
||||
@@ -343,9 +349,10 @@ REPLYTIMEOUT = config.reply_timeout
|
||||
}
|
||||
*/
|
||||
|
||||
function guid(bits = 256)
|
||||
function guid(bits)
|
||||
{
|
||||
var guid = blob(bits, os.random)
|
||||
var _bits = bits == null ? 256 : bits
|
||||
var guid = blob(_bits, os.random)
|
||||
stone(guid)
|
||||
return text(guid,'h')
|
||||
}
|
||||
@@ -427,13 +434,16 @@ $_.portal = function(fn, port) {
|
||||
}
|
||||
|
||||
function handle_host(e) {
|
||||
var queue = null
|
||||
var data = null
|
||||
|
||||
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)
|
||||
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`)
|
||||
log.system(`sent queue out of queue`)
|
||||
peer_queue.delete(e.peer)
|
||||
}
|
||||
} else if (e.type == "disconnect") {
|
||||
@@ -443,27 +453,28 @@ function handle_host(e) {
|
||||
})
|
||||
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port)
|
||||
} else if (e.type == "receive") {
|
||||
var data = nota.decode(e.data)
|
||||
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)
|
||||
if (data.data) populate_actor_addresses(data.data, e)
|
||||
turn(data)
|
||||
}
|
||||
}
|
||||
|
||||
function populate_actor_addresses(obj, e) {
|
||||
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], e)
|
||||
})
|
||||
}
|
||||
|
||||
// takes a callback function, an actor object, and a configuration record for getting information about the status of a connection to the actor. The configuration record is used to request the sort of information that needs to be communicated. This can include latency, bandwidth, activity, congestion, cost, partitions. The callback is given a record containing the requested information.
|
||||
$_.contact = function(callback, record) {
|
||||
send(create_actor(record), record, callback)
|
||||
@@ -512,12 +523,13 @@ $_.unneeded = function unneeded(fn, seconds) {
|
||||
}
|
||||
|
||||
// schedules the invocation of a function after a specified amount of time.
|
||||
$_.delay = function delay(fn, seconds = 0) {
|
||||
$_.delay = function delay(fn, seconds) {
|
||||
var _seconds = seconds == null ? 0 : seconds
|
||||
function delay_turn() {
|
||||
fn()
|
||||
send_messages()
|
||||
}
|
||||
var id = actor_mod.delay(delay_turn, seconds)
|
||||
var id = actor_mod.delay(delay_turn, _seconds)
|
||||
return function() { actor_mod.removetimer(id) }
|
||||
}
|
||||
|
||||
@@ -542,6 +554,9 @@ function actor_send_immediate(actor, send) {
|
||||
}
|
||||
|
||||
function actor_send(actor, message) {
|
||||
var wota_blob = null
|
||||
var peer = null
|
||||
|
||||
if (actor[HEADER] && !actor[HEADER].replycc) // attempting to respond to a message but sender is not expecting; silently drop
|
||||
return
|
||||
|
||||
@@ -560,22 +575,21 @@ function actor_send(actor, message) {
|
||||
if (receive_fn) receive_fn(message.data)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// message to actor in same flock
|
||||
if (actor[ACTORDATA].id && actor_mod.mailbox_exist(actor[ACTORDATA].id)) {
|
||||
var wota_blob = wota.encode(message)
|
||||
// log.console(`sending wota blob of ${length(wota_blob)/8} bytes`)
|
||||
wota_blob = wota.encode(message)
|
||||
actor_mod.mailbox_push(actor[ACTORDATA].id, wota_blob)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (actor[ACTORDATA].address) {
|
||||
if (actor[ACTORDATA].id)
|
||||
message.target = actor[ACTORDATA].id
|
||||
else
|
||||
message.type = "contact"
|
||||
|
||||
var peer = peers[actor[ACTORDATA].address + ":" + actor[ACTORDATA].port]
|
||||
|
||||
peer = peers[actor[ACTORDATA].address + ":" + actor[ACTORDATA].port]
|
||||
if (!peer) {
|
||||
if (!portal) {
|
||||
log.system(`creating a contactor ...`)
|
||||
@@ -619,6 +633,11 @@ function send_messages() {
|
||||
var replies = {}
|
||||
|
||||
function send(actor, message, reply) {
|
||||
var send_msg = null
|
||||
var target = null
|
||||
var header = null
|
||||
var id = null
|
||||
|
||||
if (!is_object(actor)) {
|
||||
log.error(`Must send to an actor object. Provided: ${actor}`)
|
||||
disrupt
|
||||
@@ -628,11 +647,11 @@ function send(actor, message, reply) {
|
||||
log.error('Message must be an object')
|
||||
disrupt
|
||||
}
|
||||
var send_msg = {type:"user", data: message}
|
||||
var target = actor
|
||||
send_msg = {type:"user", data: message}
|
||||
target = actor
|
||||
|
||||
if (actor[HEADER] && actor[HEADER].replycc) {
|
||||
var header = actor[HEADER]
|
||||
header = 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
|
||||
@@ -643,7 +662,7 @@ function send(actor, message, reply) {
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
var id = guid()
|
||||
id = guid()
|
||||
replies[id] = reply
|
||||
$_.delay(_ => {
|
||||
if (replies[id]) {
|
||||
@@ -728,18 +747,21 @@ function handle_actor_disconnect(id) {
|
||||
|
||||
function handle_sysym(msg)
|
||||
{
|
||||
var from
|
||||
var from = null
|
||||
var greeter = null
|
||||
var letter2 = null
|
||||
|
||||
if (msg.kind == 'stop') {
|
||||
actor_die("got stop message")
|
||||
} else if (msg.kind == 'underling') {
|
||||
from = msg.from
|
||||
var greeter = greeters[from[ACTORDATA].id]
|
||||
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 = msg.data
|
||||
letter2[HEADER] = msg
|
||||
delete msg.data
|
||||
portal_fn(letter2)
|
||||
@@ -756,13 +778,16 @@ function handle_sysym(msg)
|
||||
}
|
||||
|
||||
function handle_message(msg) {
|
||||
var letter = null
|
||||
var fn = null
|
||||
|
||||
if (msg[SYSYM]) {
|
||||
handle_sysym(msg[SYSYM], msg.from)
|
||||
return
|
||||
}
|
||||
|
||||
if (msg.type == "user") {
|
||||
var letter = msg.data // what the sender really sent
|
||||
letter = msg.data // what the sender really sent
|
||||
_ObjectDefineProperty(letter, HEADER, {
|
||||
value: msg, enumerable: false
|
||||
})
|
||||
@@ -771,7 +796,7 @@ function handle_message(msg) {
|
||||
})
|
||||
|
||||
if (msg.return) {
|
||||
var fn = replies[msg.return]
|
||||
fn = replies[msg.return]
|
||||
if (fn) fn(letter)
|
||||
delete replies[msg.return]
|
||||
return
|
||||
@@ -801,14 +826,16 @@ var package = use_core('package')
|
||||
|
||||
// Find the .ce file
|
||||
var prog_path = prog + ".ce"
|
||||
var pkg_dir = null
|
||||
var core_dir = null
|
||||
if (!fd.is_file(prog_path)) {
|
||||
var pkg_dir = package.find_package_dir(prog_path)
|
||||
pkg_dir = package.find_package_dir(prog_path)
|
||||
if (pkg_dir)
|
||||
prog_path = pkg_dir + '/' + prog + '.ce'
|
||||
}
|
||||
if (!fd.is_file(prog_path)) {
|
||||
// Check core packages
|
||||
var core_dir = core_path
|
||||
core_dir = core_path
|
||||
prog_path = core_dir + '/' + prog + '.ce'
|
||||
}
|
||||
if (!fd.is_file(prog_path)) {
|
||||
@@ -824,9 +851,11 @@ $_.clock(_ => {
|
||||
var env = {}
|
||||
arrfor(array(runtime_env), function(k) { env[k] = runtime_env[k] })
|
||||
var _ki = 0
|
||||
var inj = null
|
||||
var key = null
|
||||
while (_ki < length(inject)) {
|
||||
var inj = inject[_ki]
|
||||
var key = inj
|
||||
inj = inject[_ki]
|
||||
key = inj
|
||||
if (key && key[0] == '$') key = text(key, 1)
|
||||
if (key == 'fd') env['$fd'] = fd
|
||||
else env['$' + key] = $_[key]
|
||||
|
||||
BIN
internal/engine.mach
Normal file
BIN
internal/engine.mach
Normal file
Binary file not shown.
42
regen.cm
Normal file
42
regen.cm
Normal file
@@ -0,0 +1,42 @@
|
||||
// regen.cm — regenerate .mach bytecode files
|
||||
// Run with: ./cell --core . regen.cm
|
||||
|
||||
var fd = use("fd")
|
||||
var json = use("json")
|
||||
var tokenize = use("tokenize")
|
||||
var parse = use("parse")
|
||||
var fold = use("fold")
|
||||
|
||||
var files = [
|
||||
{src: "tokenize.cm", name: "tokenize", out: "tokenize.mach"},
|
||||
{src: "parse.cm", name: "parse", out: "parse.mach"},
|
||||
{src: "fold.cm", name: "fold", out: "fold.mach"},
|
||||
{src: "mcode.cm", name: "mcode", out: "mcode.mach"},
|
||||
{src: "internal/bootstrap.cm", name: "bootstrap", out: "internal/bootstrap.mach"},
|
||||
{src: "internal/engine.cm", name: "engine", out: "internal/engine.mach"}
|
||||
]
|
||||
|
||||
var i = 0
|
||||
var entry = null
|
||||
var src = null
|
||||
var tok_result = null
|
||||
var ast = null
|
||||
var folded = null
|
||||
var ast_json = null
|
||||
var bytecode = null
|
||||
var f = null
|
||||
|
||||
while (i < length(files)) {
|
||||
entry = files[i]
|
||||
src = text(fd.slurp(entry.src))
|
||||
tok_result = tokenize(src, entry.src)
|
||||
ast = parse(tok_result.tokens, src, entry.src, tokenize)
|
||||
folded = fold(ast)
|
||||
ast_json = json.encode(folded)
|
||||
bytecode = mach_compile_ast(entry.name, ast_json)
|
||||
f = fd.open(entry.out, "w")
|
||||
fd.write(f, bytecode)
|
||||
fd.close(f)
|
||||
print(`wrote ${entry.out}`)
|
||||
i = i + 1
|
||||
}
|
||||
@@ -217,8 +217,8 @@ void script_startup(cell_rt *prt)
|
||||
|
||||
if (core_path)
|
||||
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
|
||||
if (shop_path)
|
||||
JS_SetPropertyStr(js, hidden_env, "shop_path", JS_NewString(js, shop_path));
|
||||
JS_SetPropertyStr(js, hidden_env, "shop_path",
|
||||
shop_path ? JS_NewString(js, shop_path) : JS_NULL);
|
||||
|
||||
// Stone the environment
|
||||
hidden_env = JS_Stone(js, hidden_env);
|
||||
@@ -413,8 +413,8 @@ int cell_init(int argc, char **argv)
|
||||
JSValue hidden_env = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
|
||||
if (shop_path)
|
||||
JS_SetPropertyStr(ctx, hidden_env, "shop_path", JS_NewString(ctx, shop_path));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "shop_path",
|
||||
shop_path ? JS_NewString(ctx, shop_path) : JS_NULL);
|
||||
JS_SetPropertyStr(ctx, hidden_env, "use_mcode", JS_NewBool(ctx, use_mcode));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));
|
||||
|
||||
Reference in New Issue
Block a user