// Hidden vars come from env: // CLI mode (cell_init): os, args, core_path, shop_path, emit_qbe // Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path // args[0] = script name, args[1..] = user args var load_internal = os.load_internal function use_embed(name) { return load_internal("js_" + name + "_use") } var fd = use_embed('fd') var json = use_embed('json') var use_cache = {} use_cache['fd'] = fd use_cache['os'] = os use_cache['json'] = json // Bootstrap: load tokenize.cm, parse.cm, fold.cm from pre-compiled mach bytecode function use_basic(path) { if (use_cache[path]) return use_cache[path] var result = use_embed(replace(path, '/', '_')) use_cache[path] = result return result } // Load a module from .mach bytecode, falling back to .ast.json function boot_load(name, env) { var mach_path = core_path + '/' + name + ".mach" var data = null if (fd.is_file(mach_path)) { data = fd.slurp(mach_path) return mach_load(data, env) } var ast_path = core_path + '/' + name + ".ast.json" data = text(fd.slurp(ast_path)) return mach_eval_ast(name, data, env) } var boot_env = {use: use_basic} var tokenize_mod = boot_load("tokenize", boot_env) var parse_mod = boot_load("parse", boot_env) var fold_mod = boot_load("fold", boot_env) use_cache['tokenize'] = tokenize_mod use_cache['parse'] = parse_mod use_cache['fold'] = fold_mod // Always load mcode compiler module var mcode_mod = boot_load("mcode", boot_env) use_cache['mcode'] = mcode_mod var streamline_mod = null var qbe_emit_mod = null // Warn if any .cm source is newer than its .mach bytecode function check_mach_stale() { var pairs = [ ["tokenize.cm", "tokenize.mach"], ["parse.cm", "parse.mach"], ["fold.cm", "fold.mach"], ["mcode.cm", "mcode.mach"], ["streamline.cm", "streamline.mach"], ["qbe.cm", "qbe.mach"], ["qbe_emit.cm", "qbe_emit.mach"], ["internal/bootstrap.cm", "internal/bootstrap.mach"], ["internal/engine.cm", "internal/engine.mach"] ] var stale = [] var _i = 0 var cm_path = null var mach_path = null var cm_stat = null var mach_stat = null while (_i < length(pairs)) { cm_path = core_path + '/' + pairs[_i][0] mach_path = core_path + '/' + pairs[_i][1] if (fd.is_file(cm_path) && fd.is_file(mach_path)) { cm_stat = fd.stat(cm_path) mach_stat = fd.stat(mach_path) if (cm_stat.mtime > mach_stat.mtime) { push(stale, pairs[_i][0]) } } _i = _i + 1 } if (length(stale) > 0) { print("warning: bytecode is stale for: " + text(stale, ", ") + "\n") print("run 'make regen' or './cell --core . regen.cm' to update\n") } } check_mach_stale() // analyze: tokenize + parse, check for errors function analyze(src, filename) { var tok_result = tokenize_mod(src, filename) var ast = parse_mod(tok_result.tokens, src, filename, tokenize_mod) var _i = 0 var prev_line = -1 var prev_msg = null var e = null var msg = null var line = null var col = null var has_errors = ast.errors != null && length(ast.errors) > 0 if (has_errors) { while (_i < length(ast.errors)) { e = ast.errors[_i] msg = e.message line = e.line col = e.column if (msg != prev_msg || line != prev_line) { if (line != null && col != null) { print(`${filename}:${text(line)}:${text(col)}: error: ${msg}`) } else { print(`${filename}: error: ${msg}`) } } prev_line = line prev_msg = msg _i = _i + 1 } disrupt } ast = fold_mod(ast) return ast } // Load a module from .mach bytecode, falling back to source compilation function load_module(name, env) { var mach_path = core_path + '/' + name + ".mach" var data = null var src_path = null var src = null var ast = null if (fd.is_file(mach_path)) { data = fd.slurp(mach_path) return mach_load(data, env) } src_path = core_path + '/' + name + ".cm" src = text(fd.slurp(src_path)) ast = analyze(src, src_path) return mach_eval_ast(name, json.encode(ast), env) } // Load optimization pipeline modules (needs analyze to be defined) var qbe_macros = null streamline_mod = load_module("streamline", boot_env) use_cache['streamline'] = streamline_mod if (emit_qbe) { qbe_macros = load_module("qbe", boot_env) qbe_emit_mod = load_module("qbe_emit", boot_env) use_cache['qbe'] = qbe_macros use_cache['qbe_emit'] = qbe_emit_mod } // Run AST through mcode pipeline → register VM function run_ast(name, ast, env) { var compiled = mcode_mod(ast) var optimized = streamline_mod(compiled) var qbe_il = null if (emit_qbe) { qbe_il = qbe_emit_mod(optimized, qbe_macros) print(qbe_il) return null } return mach_eval_mcode(name, json.encode(optimized), env) } // use() with ƿit pipeline for .cm modules function use_fn(path) { var file_path = null var mach_path = null var data = null var script = null var ast = null var result = null if (use_cache[path]) return use_cache[path] // Try .mach bytecode first (CWD then core_path) mach_path = path + '.mach' if (!fd.is_file(mach_path)) mach_path = core_path + '/' + path + '.mach' if (fd.is_file(mach_path)) { data = fd.slurp(mach_path) result = mach_load(data, {use: use_fn}) use_cache[path] = result return result } // Try .cm source (CWD then core_path) file_path = path + '.cm' if (!fd.is_file(file_path)) file_path = core_path + '/' + path + '.cm' if (fd.is_file(file_path)) { script = text(fd.slurp(file_path)) ast = analyze(script, file_path) result = run_ast(path, ast, {use: use_fn}) use_cache[path] = result return result } // Fallback to embedded C module result = use_embed(replace(path, '/', '_')) use_cache[path] = result return result } // 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)) { data = fd.slurp(engine_path) return mach_load(data, env) } engine_path = core_path + '/internal/engine.cm' 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 — parse args program = args[0] _j = 1 while (_j < length(args)) { push(user_args, args[_j]) _j = _j + 1 } // Resolve script file: try .cm then .ce in CWD then core_path script_file = program if (!ends_with(script_file, '.ce') && !ends_with(script_file, '.cm')) script_file = program + '.cm' 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' if (ends_with(script_file, '.ce')) { // Actor script — delegate to engine load_engine({ os: os, actorsym: actorsym, init: {program: program, arg: user_args}, core_path: core_path, shop_path: shop_path, json: json, analyze: analyze, run_ast_fn: run_ast }) } else { // Module script — run directly 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 load_engine({ os: os, actorsym: actorsym, init: init, core_path: core_path, shop_path: shop_path, json: json, nota: nota, wota: wota, analyze: analyze, run_ast_fn: run_ast }) }