Files
cell/internal/bootstrap.cm

138 lines
3.5 KiB
Plaintext

// Hidden vars (os, args, core_path, use_mcode) come from env
// 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 = name + ".mach"
var data = null
if (fd.is_file(mach_path)) {
data = fd.slurp(mach_path)
return mach_load(data, env)
}
data = text(fd.slurp(name + ".ast.json"))
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)
// Optionally load mcode compiler module
var mcode_mod = null
if (use_mcode) {
mcode_mod = boot_load("mcode", boot_env)
}
// 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
}
// Run AST through either mcode or mach pipeline
function run_ast(name, ast, env) {
var compiled = null
if (use_mcode) {
compiled = mcode_mod(ast)
return mcode_run(name, json.encode(compiled), env)
}
return mach_eval_ast(name, json.encode(ast), env)
}
// use() with ƿit pipeline for .cm modules
function use(path) {
var file_path = path + '.cm'
var script = null
var ast = null
var result = null
if (use_cache[path])
return use_cache[path]
// Check CWD first, then core_path
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})
use_cache[path] = result
return result
}
// Fallback to embedded C module
result = use_embed(replace(path, '/', '_'))
use_cache[path] = result
return result
}
// Load and run the user's program
var program = args[0]
var script_file = program
// Add .ce extension if not already present
if (!ends_with(script_file, '.ce') && !ends_with(script_file, '.cm'))
script_file = program + '.ce'
var user_args = []
var _j = 1
while (_j < length(args)) {
push(user_args, args[_j])
_j = _j + 1
}
var script = text(fd.slurp(script_file))
var ast = analyze(script, script_file)
run_ast(program, ast, {use: use, args: user_args, json: json})