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
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.

View File

@@ -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

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)
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));