bootstrap

This commit is contained in:
2026-02-10 09:53:41 -06:00
parent 120ce9d30c
commit 178837b88d
6 changed files with 155 additions and 70 deletions

View File

@@ -123,37 +123,51 @@ function use_fn(path) {
// Helper to load engine.cm and run it with given env // Helper to load engine.cm and run it with given env
function load_engine(env) { function load_engine(env) {
var engine_path = core_path + '/internal/engine.mach' 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)) { if (fd.is_file(engine_path)) {
var data = fd.slurp(engine_path) data = fd.slurp(engine_path)
return mach_load(data, env) return mach_load(data, env)
} }
engine_path = core_path + '/internal/engine.cm' engine_path = core_path + '/internal/engine.cm'
var engine_src = text(fd.slurp(engine_path)) engine_src = text(fd.slurp(engine_path))
var engine_ast = analyze(engine_src, engine_path) engine_ast = analyze(engine_src, engine_path)
return run_ast('engine', engine_ast, env) return run_ast('engine', engine_ast, env)
} }
// Detect mode and route // Detect mode and route
// CLI mode has 'args'; actor spawn mode has 'init' // 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) { if (args != null) {
// CLI mode — run script directly // CLI mode — run script directly
var program = args[0] program = args[0]
var user_args = [] _j = 1
var _j = 1
while (_j < length(args)) { while (_j < length(args)) {
push(user_args, args[_j]) push(user_args, args[_j])
_j = _j + 1 _j = _j + 1
} }
var script_file = program script_file = program
if (!ends_with(script_file, '.ce') && !ends_with(script_file, '.cm')) if (!ends_with(script_file, '.ce') && !ends_with(script_file, '.cm'))
script_file = program + '.cm' script_file = program + '.cm'
// Search CWD then core_path, trying .cm then .ce
if (!fd.is_file(script_file)) if (!fd.is_file(script_file))
script_file = core_path + '/' + program + '.cm' 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)) script = text(fd.slurp(script_file))
var ast = analyze(script, script_file) ast = analyze(script, script_file)
run_ast(program, ast, {use: use_fn, args: user_args, json: json}) run_ast(program, ast, {use: use_fn, args: user_args, json: json})
} else { } else {
// Actor spawn mode — load engine.cm with full actor env // Actor spawn mode — load engine.cm with full actor env

Binary file not shown.

View File

@@ -6,17 +6,13 @@ var SYSYM = '__SYSTEM__'
var _cell = {} var _cell = {}
var need_stop = false var need_stop = false
var dylib_ext
var cases = { var cases = {
Windows: '.dll', Windows: '.dll',
macOS: '.dylib', macOS: '.dylib',
Linux: '.so' Linux: '.so'
} }
print(os.platform()) var dylib_ext = cases[os.platform()]
dylib_ext = cases[os.platform()]
var MOD_EXT = '.cm' var MOD_EXT = '.cm'
var ACTOR_EXT = '.ce' var ACTOR_EXT = '.ce'
@@ -67,11 +63,14 @@ function use_core(path) {
return use_cache[cache_key] return use_cache[cache_key]
var sym = use_embed(replace(path, '/', '_')) var sym = use_embed(replace(path, '/', '_'))
var result = null
var script = null
var ast = null
// Check for pre-compiled .mach file first // Check for pre-compiled .mach file first
var mach_path = core_path + '/' + path + '.mach' var mach_path = core_path + '/' + path + '.mach'
if (fd.is_file(mach_path)) { 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 use_cache[cache_key] = result
return result return result
} }
@@ -79,9 +78,9 @@ function use_core(path) {
// Fall back to source .cm file — compile at runtime // Fall back to source .cm file — compile at runtime
var file_path = core_path + '/' + path + MOD_EXT var file_path = core_path + '/' + path + MOD_EXT
if (fd.is_file(file_path)) { if (fd.is_file(file_path)) {
var script = text(fd.slurp(file_path)) script = text(fd.slurp(file_path))
var ast = analyze(script, file_path) ast = analyze(script, file_path)
var result = run_ast_fn('core:' + path, ast, {use: use_core}) result = run_ast_fn('core:' + path, ast, {use: use_core})
use_cache[cache_key] = result use_cache[cache_key] = result
return result return result
} }
@@ -108,15 +107,18 @@ function is_actor(value) {
var ENETSERVICE = 0.1 var ENETSERVICE = 0.1
var REPLYTIMEOUT = 60 // seconds before replies are ignored 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 file = "nofile"
var line = 0 var line = 0
var md = null
var m = null
var caller = array(Error().stack, "\n")[1+depth] var caller = array(Error().stack, "\n")[1+_depth]
if (caller) { if (caller) {
var md = extract(caller, /\((.*)\:/) md = extract(caller, /\((.*)\:/)
var m = md ? md[1] : "SCRIPT" m = md ? md[1] : "SCRIPT"
if (m) file = m if (m) file = m
md = extract(caller, /\:(\d*)\)/) md = extract(caller, /\:(\d*)\)/)
m = md ? md[1] : 0 m = md ? md[1] : 0
@@ -152,6 +154,9 @@ function log(name, args) {
function actor_die(err) function actor_die(err)
{ {
var reason = null
var unders = null
if (err && is_function(err.toString)) { if (err && is_function(err.toString)) {
os.print(err.toString()) os.print(err.toString())
os.print("\n") os.print("\n")
@@ -161,14 +166,14 @@ function actor_die(err)
if (overling) { if (overling) {
if (err) { if (err) {
// with an err, this is a forceful disrupt // 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}) report_to_overling({type:'disrupt', reason})
} else } else
report_to_overling({type:'stop'}) report_to_overling({type:'stop'})
} }
if (underlings) { if (underlings) {
var unders = array(underlings) unders = array(underlings)
arrfor(unders, function(id, index) { arrfor(unders, function(id, index) {
log.console(`calling on ${id} to disrupt too`) log.console(`calling on ${id} to disrupt too`)
$_.stop(create_actor({id})) $_.stop(create_actor({id}))
@@ -192,9 +197,10 @@ function actor_die(err)
_cell.args = init != null ? init : {} _cell.args = init != null ? init : {}
_cell.id = "newguy" _cell.id = "newguy"
function create_actor(desc = {id:guid()}) { function create_actor(desc) {
var _desc = desc == null ? {id:guid()} : desc
var actor = {} var actor = {}
actor[ACTORDATA] = desc actor[ACTORDATA] = _desc
return actor 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) stone(guid)
return text(guid,'h') return text(guid,'h')
} }
@@ -427,13 +434,16 @@ $_.portal = function(fn, port) {
} }
function handle_host(e) { function handle_host(e) {
var queue = null
var data = null
if (e.type == "connect") { if (e.type == "connect") {
log.system(`connected a new peer: ${e.peer.address}:${e.peer.port}`) log.system(`connected a new peer: ${e.peer.address}:${e.peer.port}`)
peers[`${e.peer.address}:${e.peer.port}`] = e.peer peers[`${e.peer.address}:${e.peer.port}`] = e.peer
var queue = peer_queue.get(e.peer) queue = peer_queue.get(e.peer)
if (queue) { if (queue) {
arrfor(queue, (msg, index) => e.peer.send(nota.encode(msg))) 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) peer_queue.delete(e.peer)
} }
} else if (e.type == "disconnect") { } else if (e.type == "disconnect") {
@@ -443,12 +453,17 @@ function handle_host(e) {
}) })
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port) log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port)
} else if (e.type == "receive") { } else if (e.type == "receive") {
var data = nota.decode(e.data) data = nota.decode(e.data)
if (data.replycc && !data.replycc.address) { if (data.replycc && !data.replycc.address) {
data.replycc[ACTORDATA].address = e.peer.address data.replycc[ACTORDATA].address = e.peer.address
data.replycc[ACTORDATA].port = e.peer.port data.replycc[ACTORDATA].port = e.peer.port
} }
function populate_actor_addresses(obj) { if (data.data) populate_actor_addresses(data.data, e)
turn(data)
}
}
function populate_actor_addresses(obj, e) {
if (!is_object(obj)) return if (!is_object(obj)) return
if (obj[ACTORDATA] && !obj[ACTORDATA].address) { if (obj[ACTORDATA] && !obj[ACTORDATA].address) {
obj[ACTORDATA].address = e.peer.address obj[ACTORDATA].address = e.peer.address
@@ -456,12 +471,8 @@ function handle_host(e) {
} }
arrfor(array(obj), function(key, index) { arrfor(array(obj), function(key, index) {
if (key in obj) if (key in obj)
populate_actor_addresses(obj[key]) populate_actor_addresses(obj[key], e)
}) })
}
if (data.data) populate_actor_addresses(data.data)
turn(data)
}
} }
// 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. // 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.
@@ -512,12 +523,13 @@ $_.unneeded = function unneeded(fn, seconds) {
} }
// schedules the invocation of a function after a specified amount of time. // 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() { function delay_turn() {
fn() fn()
send_messages() send_messages()
} }
var id = actor_mod.delay(delay_turn, seconds) var id = actor_mod.delay(delay_turn, _seconds)
return function() { actor_mod.removetimer(id) } return function() { actor_mod.removetimer(id) }
} }
@@ -542,6 +554,9 @@ function actor_send_immediate(actor, send) {
} }
function actor_send(actor, message) { 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 if (actor[HEADER] && !actor[HEADER].replycc) // attempting to respond to a message but sender is not expecting; silently drop
return return
@@ -563,8 +578,7 @@ function actor_send(actor, message) {
// message to actor in same flock // message to actor in same flock
if (actor[ACTORDATA].id && actor_mod.mailbox_exist(actor[ACTORDATA].id)) { if (actor[ACTORDATA].id && actor_mod.mailbox_exist(actor[ACTORDATA].id)) {
var wota_blob = wota.encode(message) wota_blob = wota.encode(message)
// log.console(`sending wota blob of ${length(wota_blob)/8} bytes`)
actor_mod.mailbox_push(actor[ACTORDATA].id, wota_blob) actor_mod.mailbox_push(actor[ACTORDATA].id, wota_blob)
return return
} }
@@ -575,7 +589,7 @@ function actor_send(actor, message) {
else else
message.type = "contact" message.type = "contact"
var peer = peers[actor[ACTORDATA].address + ":" + actor[ACTORDATA].port] peer = peers[actor[ACTORDATA].address + ":" + actor[ACTORDATA].port]
if (!peer) { if (!peer) {
if (!portal) { if (!portal) {
log.system(`creating a contactor ...`) log.system(`creating a contactor ...`)
@@ -619,6 +633,11 @@ function send_messages() {
var replies = {} var replies = {}
function send(actor, message, reply) { function send(actor, message, reply) {
var send_msg = null
var target = null
var header = null
var id = null
if (!is_object(actor)) { if (!is_object(actor)) {
log.error(`Must send to an actor object. Provided: ${actor}`) log.error(`Must send to an actor object. Provided: ${actor}`)
disrupt disrupt
@@ -628,11 +647,11 @@ function send(actor, message, reply) {
log.error('Message must be an object') log.error('Message must be an object')
disrupt disrupt
} }
var send_msg = {type:"user", data: message} send_msg = {type:"user", data: message}
var target = actor target = actor
if (actor[HEADER] && actor[HEADER].replycc) { if (actor[HEADER] && actor[HEADER].replycc) {
var header = actor[HEADER] header = actor[HEADER]
if (!header.replycc || !is_actor(header.replycc)) { if (!header.replycc || !is_actor(header.replycc)) {
log.error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`) log.error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`)
disrupt disrupt
@@ -643,7 +662,7 @@ function send(actor, message, reply) {
} }
if (reply) { if (reply) {
var id = guid() id = guid()
replies[id] = reply replies[id] = reply
$_.delay(_ => { $_.delay(_ => {
if (replies[id]) { if (replies[id]) {
@@ -728,18 +747,21 @@ function handle_actor_disconnect(id) {
function handle_sysym(msg) function handle_sysym(msg)
{ {
var from var from = null
var greeter = null
var letter2 = null
if (msg.kind == 'stop') { if (msg.kind == 'stop') {
actor_die("got stop message") actor_die("got stop message")
} else if (msg.kind == 'underling') { } else if (msg.kind == 'underling') {
from = msg.from from = msg.from
var greeter = greeters[from[ACTORDATA].id] greeter = greeters[from[ACTORDATA].id]
if (greeter) greeter(msg.message) if (greeter) greeter(msg.message)
if (msg.message.type == 'disrupt') if (msg.message.type == 'disrupt')
delete underlings[from[ACTORDATA].id] delete underlings[from[ACTORDATA].id]
} else if (msg.kind == 'contact') { } else if (msg.kind == 'contact') {
if (portal_fn) { if (portal_fn) {
var letter2 = msg.data letter2 = msg.data
letter2[HEADER] = msg letter2[HEADER] = msg
delete msg.data delete msg.data
portal_fn(letter2) portal_fn(letter2)
@@ -756,13 +778,16 @@ function handle_sysym(msg)
} }
function handle_message(msg) { function handle_message(msg) {
var letter = null
var fn = null
if (msg[SYSYM]) { if (msg[SYSYM]) {
handle_sysym(msg[SYSYM], msg.from) handle_sysym(msg[SYSYM], msg.from)
return return
} }
if (msg.type == "user") { if (msg.type == "user") {
var letter = msg.data // what the sender really sent letter = msg.data // what the sender really sent
_ObjectDefineProperty(letter, HEADER, { _ObjectDefineProperty(letter, HEADER, {
value: msg, enumerable: false value: msg, enumerable: false
}) })
@@ -771,7 +796,7 @@ function handle_message(msg) {
}) })
if (msg.return) { if (msg.return) {
var fn = replies[msg.return] fn = replies[msg.return]
if (fn) fn(letter) if (fn) fn(letter)
delete replies[msg.return] delete replies[msg.return]
return return
@@ -801,14 +826,16 @@ var package = use_core('package')
// Find the .ce file // Find the .ce file
var prog_path = prog + ".ce" var prog_path = prog + ".ce"
var pkg_dir = null
var core_dir = null
if (!fd.is_file(prog_path)) { 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) if (pkg_dir)
prog_path = pkg_dir + '/' + prog + '.ce' prog_path = pkg_dir + '/' + prog + '.ce'
} }
if (!fd.is_file(prog_path)) { if (!fd.is_file(prog_path)) {
// Check core packages // Check core packages
var core_dir = core_path core_dir = core_path
prog_path = core_dir + '/' + prog + '.ce' prog_path = core_dir + '/' + prog + '.ce'
} }
if (!fd.is_file(prog_path)) { if (!fd.is_file(prog_path)) {
@@ -824,9 +851,11 @@ $_.clock(_ => {
var env = {} var env = {}
arrfor(array(runtime_env), function(k) { env[k] = runtime_env[k] }) arrfor(array(runtime_env), function(k) { env[k] = runtime_env[k] })
var _ki = 0 var _ki = 0
var inj = null
var key = null
while (_ki < length(inject)) { while (_ki < length(inject)) {
var inj = inject[_ki] inj = inject[_ki]
var key = inj key = inj
if (key && key[0] == '$') key = text(key, 1) if (key && key[0] == '$') key = text(key, 1)
if (key == 'fd') env['$fd'] = fd if (key == 'fd') env['$fd'] = fd
else env['$' + key] = $_[key] else env['$' + key] = $_[key]

BIN
internal/engine.mach Normal file

Binary file not shown.

42
regen.cm Normal file
View 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
}

View File

@@ -217,8 +217,8 @@ void script_startup(cell_rt *prt)
if (core_path) if (core_path)
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, 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_SetPropertyStr(js, hidden_env, "shop_path", JS_NewString(js, shop_path)); shop_path ? JS_NewString(js, shop_path) : JS_NULL);
// Stone the environment // Stone the environment
hidden_env = JS_Stone(js, hidden_env); hidden_env = JS_Stone(js, hidden_env);
@@ -413,8 +413,8 @@ int cell_init(int argc, char **argv)
JSValue hidden_env = JS_NewObject(ctx); JSValue hidden_env = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx)); JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path)); JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
if (shop_path) JS_SetPropertyStr(ctx, hidden_env, "shop_path",
JS_SetPropertyStr(ctx, hidden_env, "shop_path", JS_NewString(ctx, 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, "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, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx)); JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));