intrinsics rewritten without ++, --, etc
This commit is contained in:
@@ -12,7 +12,8 @@ var files = [
|
||||
{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/bootstrap.cm", name: "bootstrap", out: "internal/bootstrap.mach"},
|
||||
{src: "internal/engine.cm", name: "engine", out: "internal/engine.mach"}
|
||||
]
|
||||
|
||||
var i = 0
|
||||
|
||||
17
fd.cm
17
fd.cm
@@ -12,11 +12,11 @@ function last_pos(str, sep) {
|
||||
|
||||
// Helper to join paths
|
||||
function join_paths(base, rel) {
|
||||
base = replace(base, /\/+$/, "")
|
||||
rel = replace(rel, /^\/+/, "")
|
||||
if (!base) return rel
|
||||
if (!rel) return base
|
||||
return base + "/" + rel
|
||||
var b = replace(base, /\/+$/, "")
|
||||
var r = replace(rel, /^\/+/, "")
|
||||
if (!b) return r
|
||||
if (!r) return b
|
||||
return b + "/" + r
|
||||
}
|
||||
|
||||
fd.join_paths = join_paths
|
||||
@@ -39,7 +39,8 @@ fd.stem = function stem(path) {
|
||||
}
|
||||
|
||||
fd.globfs = function(globs, dir) {
|
||||
if (dir == null) dir = "."
|
||||
var _dir = dir
|
||||
if (_dir == null) _dir = "."
|
||||
var results = []
|
||||
|
||||
function check_neg(path) {
|
||||
@@ -88,9 +89,9 @@ fd.globfs = function(globs, dir) {
|
||||
});
|
||||
}
|
||||
|
||||
var st = fd.stat(dir)
|
||||
var st = fd.stat(_dir)
|
||||
if (st && st.isDirectory) {
|
||||
visit(dir, "")
|
||||
visit(_dir, "")
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Hidden vars (os, args, core_path, use_mcode) come from env
|
||||
// args[0] = script name, args[1..] = user args
|
||||
// Hidden vars come from env:
|
||||
// CLI mode (cell_init): os, args, core_path, use_mcode
|
||||
// Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path
|
||||
var load_internal = os.load_internal
|
||||
function use_embed(name) {
|
||||
return load_internal("js_" + name + "_use")
|
||||
@@ -24,13 +25,14 @@ function use_basic(path) {
|
||||
|
||||
// Load a module from .mach bytecode, falling back to .ast.json
|
||||
function boot_load(name, env) {
|
||||
var mach_path = name + ".mach"
|
||||
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)
|
||||
}
|
||||
data = text(fd.slurp(name + ".ast.json"))
|
||||
var ast_path = core_path + '/' + name + ".ast.json"
|
||||
data = text(fd.slurp(ast_path))
|
||||
return mach_eval_ast(name, data, env)
|
||||
}
|
||||
|
||||
@@ -91,7 +93,7 @@ function run_ast(name, ast, env) {
|
||||
}
|
||||
|
||||
// use() with ƿit pipeline for .cm modules
|
||||
function use(path) {
|
||||
function use_fn(path) {
|
||||
var file_path = path + '.cm'
|
||||
var script = null
|
||||
var ast = null
|
||||
@@ -106,7 +108,7 @@ function use(path) {
|
||||
if (fd.is_file(file_path)) {
|
||||
script = text(fd.slurp(file_path))
|
||||
ast = analyze(script, file_path)
|
||||
result = run_ast(path, ast, {use: use})
|
||||
result = run_ast(path, ast, {use: use_fn})
|
||||
use_cache[path] = result
|
||||
return result
|
||||
}
|
||||
@@ -117,21 +119,46 @@ function use(path) {
|
||||
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
|
||||
// Helper to load engine.cm and run it with given env
|
||||
function load_engine(env) {
|
||||
var engine_path = core_path + '/internal/engine.mach'
|
||||
if (fd.is_file(engine_path)) {
|
||||
var 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)
|
||||
return run_ast('engine', engine_ast, env)
|
||||
}
|
||||
|
||||
var script = text(fd.slurp(script_file))
|
||||
var ast = analyze(script, script_file)
|
||||
run_ast(program, ast, {use: use, args: user_args, json: json})
|
||||
// Detect mode and route
|
||||
// CLI mode has 'args'; actor spawn mode has 'init'
|
||||
if (args != null) {
|
||||
// CLI mode — run script directly
|
||||
var program = args[0]
|
||||
var user_args = []
|
||||
var _j = 1
|
||||
while (_j < length(args)) {
|
||||
push(user_args, args[_j])
|
||||
_j = _j + 1
|
||||
}
|
||||
|
||||
var 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'
|
||||
|
||||
var script = text(fd.slurp(script_file))
|
||||
var 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, json: json, nota: nota, wota: wota,
|
||||
analyze: analyze, run_ast_fn: run_ast
|
||||
})
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
// Hidden vars (os, actorsym, init, core_path) come from env
|
||||
// Hidden vars (os, actorsym, init, core_path, analyze, run_ast_fn, json) come from env
|
||||
// In actor spawn mode, also: nota, wota
|
||||
var ACTORDATA = actorsym
|
||||
var SYSYM = '__SYSTEM__'
|
||||
|
||||
@@ -49,23 +50,12 @@ function ends_with(str, suffix) {
|
||||
return search(str, suffix, -length(suffix)) != null
|
||||
}
|
||||
|
||||
var js = use_embed('js')
|
||||
var fd = use_embed('fd')
|
||||
var js = use_embed('js')
|
||||
|
||||
// Get the shop path from HOME environment
|
||||
var home = os.getenv('HOME') || os.getenv('USERPROFILE')
|
||||
if (!home) {
|
||||
os.print('Could not determine home directory\n')
|
||||
os.exit(1)
|
||||
}
|
||||
var shop_path = home + '/.cell'
|
||||
var packages_path = shop_path + '/packages'
|
||||
var core_path = packages_path + '/core'
|
||||
|
||||
if (!fd.is_dir(core_path)) {
|
||||
os.print('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.\n')
|
||||
os.exit(1)
|
||||
}
|
||||
// Derive shop path from core_path (core_path = ~/.cell/packages/core)
|
||||
var packages_path = core_path + '/..'
|
||||
var shop_path = packages_path + '/..'
|
||||
|
||||
var use_cache = {}
|
||||
use_cache['core/os'] = os
|
||||
@@ -74,25 +64,31 @@ use_cache['core/os'] = os
|
||||
function use_core(path) {
|
||||
var cache_key = 'core/' + path
|
||||
if (use_cache[cache_key])
|
||||
return use_cache[cache_key];
|
||||
return use_cache[cache_key]
|
||||
|
||||
var sym = use_embed(replace(path, '/', '_'))
|
||||
|
||||
// Core scripts are in packages/core/
|
||||
var file_path = core_path + '/' + path + MOD_EXT
|
||||
|
||||
if (fd.is_file(file_path)) {
|
||||
var script_blob = fd.slurp(file_path)
|
||||
var script = text(script_blob)
|
||||
var mod = `(function setup_module(use){${script}})`
|
||||
var fn = mach_eval('core:' + path, mod)
|
||||
var result = call(fn,sym, [use_core])
|
||||
use_cache[cache_key] = result;
|
||||
return result;
|
||||
// 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})
|
||||
use_cache[cache_key] = result
|
||||
return result
|
||||
}
|
||||
|
||||
use_cache[cache_key] = sym;
|
||||
return sym;
|
||||
// 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})
|
||||
use_cache[cache_key] = result
|
||||
return result
|
||||
}
|
||||
|
||||
// Embedded C module only
|
||||
use_cache[cache_key] = sym
|
||||
return sym
|
||||
}
|
||||
|
||||
var blob = use_core('blob')
|
||||
@@ -191,7 +187,7 @@ function actor_die(err)
|
||||
|
||||
|
||||
|
||||
actor_mod.on_exception(actor_die)
|
||||
//actor_mod.on_exception(actor_die)
|
||||
|
||||
_cell.args = init != null ? init : {}
|
||||
_cell.id = "newguy"
|
||||
@@ -208,10 +204,12 @@ $_.self = create_actor()
|
||||
os.use_cache = use_cache
|
||||
os.global_shop_path = shop_path
|
||||
os.$_ = $_
|
||||
os.analyze = analyze
|
||||
os.run_ast_fn = run_ast_fn
|
||||
os.json = json
|
||||
use_cache['core/json'] = json
|
||||
|
||||
var shop = use_core('internal/shop')
|
||||
|
||||
var json = use_core('json')
|
||||
var time = use_core('time')
|
||||
|
||||
var pronto = use_core('pronto')
|
||||
@@ -801,41 +799,49 @@ var prog = _cell.args.program
|
||||
|
||||
var package = use_core('package')
|
||||
|
||||
var locator = shop.resolve_locator(_cell.args.program + ".ce", null)
|
||||
|
||||
if (!locator) {
|
||||
var pkg = package.find_package_dir(_cell.args.program + ".ce")
|
||||
locator = shop.resolve_locator(_cell.args.program + ".ce", pkg)
|
||||
// Find the .ce file
|
||||
var prog_path = prog + ".ce"
|
||||
if (!fd.is_file(prog_path)) {
|
||||
var pkg_dir = package.find_package_dir(prog_path)
|
||||
if (pkg_dir)
|
||||
prog_path = pkg_dir + '/' + prog + '.ce'
|
||||
}
|
||||
|
||||
if (!locator) {
|
||||
os.print(`Main program ${_cell.args.program} could not be found\n`)
|
||||
if (!fd.is_file(prog_path)) {
|
||||
// Check core packages
|
||||
var core_dir = core_path
|
||||
prog_path = core_dir + '/' + prog + '.ce'
|
||||
}
|
||||
if (!fd.is_file(prog_path)) {
|
||||
os.print(`Main program ${prog} could not be found\n`)
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
$_.clock(_ => {
|
||||
// Get capabilities for the main program
|
||||
var file_info = shop.file_info ? shop.file_info(locator.path) : null
|
||||
var file_info = shop.file_info ? shop.file_info(prog_path) : null
|
||||
var inject = shop.script_inject_for ? shop.script_inject_for(file_info) : []
|
||||
|
||||
// Build env object for injection
|
||||
// Build env with runtime functions + capability injections
|
||||
var env = {}
|
||||
for (var i = 0; i < length(inject); i++) {
|
||||
var key = inject[i]
|
||||
arrfor(array(runtime_env), function(k) { env[k] = runtime_env[k] })
|
||||
var _ki = 0
|
||||
while (_ki < length(inject)) {
|
||||
var inj = inject[_ki]
|
||||
var key = inj
|
||||
if (key && key[0] == '$') key = text(key, 1)
|
||||
if (key == 'fd') env[key] = fd
|
||||
else env[key] = $_[key]
|
||||
if (key == 'fd') env['$fd'] = fd
|
||||
else env['$' + key] = $_[key]
|
||||
_ki = _ki + 1
|
||||
}
|
||||
|
||||
// Create use function bound to the program's package
|
||||
var pkg = file_info ? file_info.package : null
|
||||
var use_fn = function(path) { return shop.use(path, pkg) }
|
||||
env.use = function(path) { return shop.use(path, pkg) }
|
||||
env.args = _cell.args.arg
|
||||
|
||||
// Call with signature: setup_module(args, use, env)
|
||||
// The script wrapper binds $delay, $start, etc. from env
|
||||
var val = call(locator.symbol, null, [_cell.args.arg, use_fn, env])
|
||||
|
||||
if (val)
|
||||
var script = text(fd.slurp(prog_path))
|
||||
var ast = analyze(script, prog_path)
|
||||
var val = run_ast_fn(prog, ast, env)
|
||||
if (val) {
|
||||
log.error('Program must not return anything')
|
||||
disrupt
|
||||
}
|
||||
})
|
||||
|
||||
130
internal/shop.cm
130
internal/shop.cm
@@ -5,7 +5,6 @@ var fd = use('fd')
|
||||
var http = use('http')
|
||||
var miniz = use('miniz')
|
||||
var time = use('time')
|
||||
var js = use('js')
|
||||
var crypto = use('crypto')
|
||||
var blob = use('blob')
|
||||
|
||||
@@ -13,6 +12,10 @@ var pkg_tools = use('package')
|
||||
var os = use('os')
|
||||
var link = use('link')
|
||||
|
||||
var analyze = os.analyze
|
||||
var run_ast_fn = os.run_ast_fn
|
||||
var shop_json = os.json
|
||||
|
||||
var core = "core"
|
||||
|
||||
function pull_from_cache(content)
|
||||
@@ -34,7 +37,7 @@ function ensure_dir(path) {
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
for (var i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current += parts[i] + '/'
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.stat(current).isDirectory) {
|
||||
fd.mkdir(current)
|
||||
}
|
||||
@@ -142,8 +145,10 @@ function package_in_shop(package) {
|
||||
|
||||
function abs_path_to_package(package_dir)
|
||||
{
|
||||
if (!fd.is_file(package_dir + '/cell.toml'))
|
||||
throw Error('Not a valid package directory (no cell.toml): ' + package_dir)
|
||||
if (!fd.is_file(package_dir + '/cell.toml')) {
|
||||
print('Not a valid package directory (no cell.toml): ' + package_dir)
|
||||
disrupt
|
||||
}
|
||||
|
||||
var packages_prefix = get_packages_dir() + '/'
|
||||
var core_dir = packages_prefix + core_package
|
||||
@@ -175,13 +180,12 @@ function abs_path_to_package(package_dir)
|
||||
return package_dir
|
||||
|
||||
// For local directories (e.g., linked targets), read the package name from cell.toml
|
||||
try {
|
||||
var content = text(fd.slurp(package_dir + '/cell.toml'))
|
||||
var _toml_path = package_dir + '/cell.toml'
|
||||
if (fd.is_file(_toml_path)) {
|
||||
var content = text(fd.slurp(_toml_path))
|
||||
var cfg = toml.decode(content)
|
||||
if (cfg.package)
|
||||
return cfg.package
|
||||
} catch (e) {
|
||||
// Fall through
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -299,12 +303,14 @@ Shop.resolve_package_info = function(pkg) {
|
||||
|
||||
// Verify if a package name is valid and return status
|
||||
Shop.verify_package_name = function(pkg) {
|
||||
if (!pkg) throw Error("Empty package name")
|
||||
if (pkg == 'local') throw Error("local is not a valid package name")
|
||||
if (pkg == 'core') throw Error("core is not a valid package name")
|
||||
|
||||
if (search(pkg, '://') != null)
|
||||
throw Error(`Invalid package name: ${pkg}; did you mean ${array(pkg, '://')[1]}?`)
|
||||
if (!pkg) { print("Empty package name"); disrupt }
|
||||
if (pkg == 'local') { print("local is not a valid package name"); disrupt }
|
||||
if (pkg == 'core') { print("core is not a valid package name"); disrupt }
|
||||
|
||||
if (search(pkg, '://') != null) {
|
||||
print(`Invalid package name: ${pkg}; did you mean ${array(pkg, '://')[1]}?`)
|
||||
disrupt
|
||||
}
|
||||
}
|
||||
|
||||
// Convert module package to download URL
|
||||
@@ -383,9 +389,7 @@ function inject_env(inject) {
|
||||
var env = {}
|
||||
var rt = my$_.os ? my$_.os.runtime_env : null
|
||||
if (rt) {
|
||||
for (var k in rt) {
|
||||
env[k] = rt[k]
|
||||
}
|
||||
arrfor(array(rt), function(k) { env[k] = rt[k] })
|
||||
}
|
||||
|
||||
// Add capability injections
|
||||
@@ -441,28 +445,30 @@ ${script}
|
||||
// Resolve module function, hashing it in the process
|
||||
// path is the exact path to the script file
|
||||
function resolve_mod_fn(path, pkg) {
|
||||
if (!fd.is_file(path)) throw Error(`path ${path} is not a file`)
|
||||
if (!fd.is_file(path)) { print(`path ${path} is not a file`); disrupt }
|
||||
|
||||
var file_info = Shop.file_info(path)
|
||||
var file_pkg = file_info.package
|
||||
var inject = Shop.script_inject_for(file_info)
|
||||
var content = text(fd.slurp(path))
|
||||
var script = script_form(path, content, file_pkg, inject);
|
||||
var script = script_form(path, content, file_pkg, inject)
|
||||
|
||||
var obj = pull_from_cache(stone(blob(script)))
|
||||
if (obj) {
|
||||
var fn = js.compile_unblob(obj)
|
||||
return js.integrate(fn, null)
|
||||
// Check cache for pre-compiled .mach blob
|
||||
var cached = pull_from_cache(stone(blob(script)))
|
||||
if (cached) {
|
||||
return mach_load(cached)
|
||||
}
|
||||
|
||||
// Compile name is just for debug/stack traces
|
||||
var compile_name = path
|
||||
// Compile via new pipeline
|
||||
var ast = analyze(script, path)
|
||||
var ast_json = shop_json.encode(ast)
|
||||
|
||||
var fn = js.compile(compile_name, script)
|
||||
// Cache compiled binary
|
||||
var compiled = mach_compile_ast(path, ast_json)
|
||||
put_into_cache(stone(blob(script)), compiled)
|
||||
|
||||
put_into_cache(stone(blob(script)), js.compile_blob(fn))
|
||||
|
||||
return js.integrate(fn, null)
|
||||
// Evaluate to get the function object
|
||||
return mach_eval_ast(path, ast_json)
|
||||
}
|
||||
|
||||
// given a path and a package context
|
||||
@@ -576,33 +582,20 @@ Shop.open_package_dylib = function(pkg) {
|
||||
|
||||
var toml_path = pkg_dir + '/cell.toml'
|
||||
if (fd.is_file(toml_path)) {
|
||||
try {
|
||||
var content = text(fd.slurp(toml_path))
|
||||
var cfg = toml.decode(content)
|
||||
if (cfg.dependencies) {
|
||||
arrfor(array(cfg.dependencies), function(alias, i) {
|
||||
var dep_pkg = cfg.dependencies[alias]
|
||||
try {
|
||||
Shop.open_package_dylib(dep_pkg)
|
||||
} catch (dep_e) {
|
||||
// Dependency dylib load failed, continue with others
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
// Error reading toml, continue
|
||||
var content = text(fd.slurp(toml_path))
|
||||
var cfg = toml.decode(content)
|
||||
if (cfg.dependencies) {
|
||||
arrfor(array(cfg.dependencies), function(alias, i) {
|
||||
var dep_pkg = cfg.dependencies[alias]
|
||||
Shop.open_package_dylib(dep_pkg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var dl_path = get_lib_path(pkg)
|
||||
if (fd.is_file(dl_path)) {
|
||||
if (!open_dls[dl_path]) {
|
||||
try {
|
||||
open_dls[dl_path] = os.dylib_open(dl_path)
|
||||
} catch (e) {
|
||||
dylib_visited[pkg] = false
|
||||
throw e
|
||||
}
|
||||
open_dls[dl_path] = os.dylib_open(dl_path)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -642,8 +635,8 @@ function resolve_c_symbol(path, package_context) {
|
||||
|
||||
// If no package context, only check core internal symbols
|
||||
if (!package_context || package_context == 'core') {
|
||||
path = replace(path, '/', '_')
|
||||
var core_sym = `js_${path}_use`
|
||||
var _path = replace(path, '/', '_')
|
||||
var core_sym = `js_${_path}_use`
|
||||
if (os.internal_exists(core_sym)) {
|
||||
return {
|
||||
symbol: function() { return os.load_internal(core_sym) },
|
||||
@@ -836,14 +829,13 @@ function execute_module(info)
|
||||
// C only
|
||||
used = call_c_module(c_resolve)
|
||||
} else {
|
||||
throw Error(`Module ${info.path} could not be found`)
|
||||
print(`Module ${info.path} could not be found`); disrupt
|
||||
}
|
||||
|
||||
// if (is_function(used))
|
||||
// throw Error('C module loader returned a function; did you forget to call it?')
|
||||
|
||||
if (!used)
|
||||
throw Error(`Module ${info} returned null`)
|
||||
if (!used) { print(`Module ${info} returned null`); disrupt }
|
||||
|
||||
// stone(used)
|
||||
return used
|
||||
@@ -852,16 +844,14 @@ function execute_module(info)
|
||||
function get_module(path, package_context) {
|
||||
var info = resolve_module_info(path, package_context)
|
||||
|
||||
if (!info)
|
||||
throw Error(`Module ${path} could not be found in ${package_context}`)
|
||||
if (!info) { print(`Module ${path} could not be found in ${package_context}`); disrupt }
|
||||
|
||||
return execute_module(info)
|
||||
}
|
||||
|
||||
Shop.use = function use(path, package_context) {
|
||||
var info = resolve_module_info(path, package_context)
|
||||
if (!info)
|
||||
throw Error(`Module ${path} could not be found in ${package_context}`)
|
||||
if (!info) { print(`Module ${path} could not be found in ${package_context}`); disrupt }
|
||||
|
||||
if (use_cache[info.cache_key])
|
||||
return use_cache[info.cache_key]
|
||||
@@ -889,13 +879,13 @@ function fetch_remote_hash(pkg) {
|
||||
if (!api_url) return null
|
||||
|
||||
|
||||
try {
|
||||
var _fetch_hash = function() {
|
||||
var resp = http.fetch(api_url)
|
||||
return Shop.extract_commit_hash(pkg, text(resp))
|
||||
} catch (e) {
|
||||
log.console("Warning: Could not check for updates for " + pkg)
|
||||
} disruption {
|
||||
return null
|
||||
}
|
||||
return _fetch_hash()
|
||||
}
|
||||
|
||||
// Download a zip for a package at a specific commit and cache it
|
||||
@@ -909,14 +899,14 @@ function download_zip(pkg, commit_hash) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
var _download = function() {
|
||||
var zip_blob = http.fetch(download_url)
|
||||
fd.slurpwrite(cache_path, zip_blob)
|
||||
return zip_blob
|
||||
} catch (e) {
|
||||
log.error("Download failed for " + pkg + ": " + e)
|
||||
} disruption {
|
||||
return null
|
||||
}
|
||||
return _download()
|
||||
}
|
||||
|
||||
// Get zip from cache, returns null if not cached
|
||||
@@ -1028,7 +1018,7 @@ Shop.extract = function(pkg) {
|
||||
var zip_blob = get_package_zip(pkg)
|
||||
|
||||
if (!zip_blob)
|
||||
throw Error("No zip blob available for " + pkg)
|
||||
print("No zip blob available for " + pkg); disrupt
|
||||
|
||||
// Extract zip for remote package
|
||||
install_zip(zip_blob, target_dir)
|
||||
@@ -1113,7 +1103,7 @@ Shop.update = function(pkg) {
|
||||
|
||||
function install_zip(zip_blob, target_dir) {
|
||||
var zip = miniz.read(zip_blob)
|
||||
if (!zip) throw Error("Failed to read zip archive")
|
||||
if (!zip) { print("Failed to read zip archive"); disrupt }
|
||||
|
||||
if (fd.is_link(target_dir)) fd.unlink(target_dir)
|
||||
if (fd.is_dir(target_dir)) fd.rmdir(target_dir, 1)
|
||||
@@ -1165,14 +1155,14 @@ Shop.get = function(pkg) {
|
||||
if (!lock[pkg]) {
|
||||
var info = Shop.resolve_package_info(pkg)
|
||||
if (!info) {
|
||||
throw Error("Invalid package: " + pkg)
|
||||
print("Invalid package: " + pkg); disrupt
|
||||
}
|
||||
|
||||
var commit = null
|
||||
if (info != 'local') {
|
||||
commit = fetch_remote_hash(pkg)
|
||||
if (!commit) {
|
||||
throw Error("Could not resolve commit for " + pkg)
|
||||
print("Could not resolve commit for " + pkg); disrupt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1188,8 +1178,6 @@ Shop.get = function(pkg) {
|
||||
// Compile a module
|
||||
// List all files in a package
|
||||
|
||||
var debug = use('debug')
|
||||
|
||||
Shop.file_reload = function(file)
|
||||
{
|
||||
var info = Shop.file_info(file)
|
||||
|
||||
70
link.cm
70
link.cm
@@ -36,7 +36,7 @@ function ensure_dir(path) {
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
for (var i = 0; i < length(parts); i++) {
|
||||
if (parts[i] == '') continue
|
||||
current += parts[i] + '/'
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.stat(current).isDirectory) {
|
||||
fd.mkdir(current)
|
||||
}
|
||||
@@ -66,14 +66,16 @@ Link.load = function() {
|
||||
return link_cache
|
||||
}
|
||||
|
||||
try {
|
||||
var _load = function() {
|
||||
var content = text(fd.slurp(path))
|
||||
var cfg = toml.decode(content)
|
||||
link_cache = cfg.links || {}
|
||||
} catch (e) {
|
||||
log.console("Warning: Failed to load link.toml: " + e)
|
||||
if (cfg && cfg.links) link_cache = cfg.links
|
||||
else link_cache = {}
|
||||
} disruption {
|
||||
print("Warning: Failed to load link.toml\n")
|
||||
link_cache = {}
|
||||
}
|
||||
_load()
|
||||
return link_cache
|
||||
}
|
||||
|
||||
@@ -90,14 +92,16 @@ Link.add = function(canonical, target, shop) {
|
||||
// Validate canonical package exists in shop
|
||||
var lock = shop.load_lock()
|
||||
if (!lock[canonical]) {
|
||||
throw Error('Package ' + canonical + ' is not installed. Install it first with: cell get ' + canonical)
|
||||
print('Package ' + canonical + ' is not installed. Install it first with: cell get ' + canonical + '\n')
|
||||
disrupt
|
||||
}
|
||||
|
||||
// Validate target is a valid package
|
||||
if (starts_with(target, '/')) {
|
||||
// Local path - must have cell.toml
|
||||
if (!fd.is_file(target + '/cell.toml')) {
|
||||
throw Error('Target ' + target + ' is not a valid package (no cell.toml)')
|
||||
print('Target ' + target + ' is not a valid package (no cell.toml)\n')
|
||||
disrupt
|
||||
}
|
||||
} else {
|
||||
// Remote package target - ensure it's installed
|
||||
@@ -116,33 +120,34 @@ Link.add = function(canonical, target, shop) {
|
||||
var target_path = starts_with(target, '/') ? target : get_package_abs_dir(target)
|
||||
var toml_path = target_path + '/cell.toml'
|
||||
if (fd.is_file(toml_path)) {
|
||||
try {
|
||||
var _install_deps = function() {
|
||||
var content = text(fd.slurp(toml_path))
|
||||
var cfg = toml.decode(content)
|
||||
if (cfg.dependencies) {
|
||||
if (cfg && cfg.dependencies) {
|
||||
arrfor(array(cfg.dependencies), function(alias) {
|
||||
var dep_locator = cfg.dependencies[alias]
|
||||
// Skip local dependencies that don't exist
|
||||
if (starts_with(dep_locator, '/') && !fd.is_dir(dep_locator)) {
|
||||
log.console(" Skipping missing local dependency: " + dep_locator)
|
||||
print(" Skipping missing local dependency: " + dep_locator + "\n")
|
||||
return
|
||||
}
|
||||
// Install the dependency if not already in shop
|
||||
try {
|
||||
var _get_dep = function() {
|
||||
shop.get(dep_locator)
|
||||
shop.extract(dep_locator)
|
||||
} catch (e) {
|
||||
log.console(` Warning: Could not install dependency ${dep_locator}: ${e.message}`)
|
||||
log.error(e)
|
||||
} disruption {
|
||||
print(` Warning: Could not install dependency ${dep_locator}\n`)
|
||||
}
|
||||
_get_dep()
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
log.console(` Warning: Could not read dependencies from ${toml_path}`)
|
||||
} disruption {
|
||||
print(` Warning: Could not read dependencies from ${toml_path}\n`)
|
||||
}
|
||||
_install_deps()
|
||||
}
|
||||
|
||||
log.console("Linked " + canonical + " -> " + target)
|
||||
print("Linked " + canonical + " -> " + target + "\n")
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -154,12 +159,12 @@ Link.remove = function(canonical) {
|
||||
var target_dir = get_package_abs_dir(canonical)
|
||||
if (fd.is_link(target_dir)) {
|
||||
fd.unlink(target_dir)
|
||||
log.console("Removed symlink at " + target_dir)
|
||||
print("Removed symlink at " + target_dir + "\n")
|
||||
}
|
||||
|
||||
|
||||
delete links[canonical]
|
||||
Link.save(links)
|
||||
log.console("Unlinked " + canonical)
|
||||
print("Unlinked " + canonical + "\n")
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -174,7 +179,7 @@ Link.clear = function() {
|
||||
})
|
||||
|
||||
Link.save({})
|
||||
log.console("Cleared all links")
|
||||
print("Cleared all links\n")
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -218,7 +223,7 @@ Link.sync_all = function(shop) {
|
||||
|
||||
arrfor(array(links), function(canonical) {
|
||||
var target = links[canonical]
|
||||
try {
|
||||
var _sync = function() {
|
||||
// Validate target exists
|
||||
var link_target = resolve_link_target(target)
|
||||
if (!fd.is_dir(link_target)) {
|
||||
@@ -234,10 +239,10 @@ Link.sync_all = function(shop) {
|
||||
|
||||
// Install dependencies of the linked package
|
||||
var toml_path = link_target + '/cell.toml'
|
||||
try {
|
||||
var _install = function() {
|
||||
var content = text(fd.slurp(toml_path))
|
||||
var cfg = toml.decode(content)
|
||||
if (cfg.dependencies) {
|
||||
if (cfg && cfg.dependencies) {
|
||||
arrfor(array(cfg.dependencies), function(alias) {
|
||||
var dep_locator = cfg.dependencies[alias]
|
||||
// Skip local dependencies that don't exist
|
||||
@@ -245,22 +250,25 @@ Link.sync_all = function(shop) {
|
||||
return
|
||||
}
|
||||
// Install the dependency if not already in shop
|
||||
try {
|
||||
var _get = function() {
|
||||
shop.get(dep_locator)
|
||||
shop.extract(dep_locator)
|
||||
} catch (e) {
|
||||
} disruption {
|
||||
// Silently continue - dependency may already be installed
|
||||
}
|
||||
_get()
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
} disruption {
|
||||
// Could not read dependencies - continue anyway
|
||||
}
|
||||
_install()
|
||||
|
||||
count++
|
||||
} catch (e) {
|
||||
push(errors, canonical + ': ' + e.message)
|
||||
count = count + 1
|
||||
} disruption {
|
||||
push(errors, canonical + ': sync failed')
|
||||
}
|
||||
_sync()
|
||||
})
|
||||
|
||||
return { synced: count, errors: errors }
|
||||
@@ -269,7 +277,7 @@ Link.sync_all = function(shop) {
|
||||
// Check if a package is currently linked
|
||||
Link.is_linked = function(canonical) {
|
||||
var links = Link.load()
|
||||
return canonical in links
|
||||
return links[canonical] != null
|
||||
}
|
||||
|
||||
// Get the link target for a package (or null if not linked)
|
||||
|
||||
12
package.cm
12
package.cm
@@ -51,7 +51,7 @@ package.load_config = function(name)
|
||||
return config_cache[config_path]
|
||||
|
||||
if (!fd.is_file(config_path)) {
|
||||
throw Error(`${config_path} does not exist`)
|
||||
print(`${config_path} does not exist`); disrupt
|
||||
}
|
||||
|
||||
var content = text(fd.slurp(config_path))
|
||||
@@ -158,7 +158,7 @@ package.split_alias = function(name, path)
|
||||
var parts = array(path, '/')
|
||||
var first_part = parts[0]
|
||||
|
||||
try {
|
||||
var _split = function() {
|
||||
var config = package.load_config(name)
|
||||
if (!config) return null
|
||||
|
||||
@@ -168,11 +168,11 @@ package.split_alias = function(name, path)
|
||||
var remaining_path = text(array(parts, 1), '/')
|
||||
return { package: dep_locator, path: remaining_path }
|
||||
}
|
||||
} catch (e) {
|
||||
// Config doesn't exist or couldn't be loaded
|
||||
return null
|
||||
} disruption {
|
||||
return null
|
||||
}
|
||||
|
||||
return null
|
||||
return _split()
|
||||
}
|
||||
|
||||
package.gather_dependencies = function(name)
|
||||
|
||||
204
pronto.cm
204
pronto.cm
@@ -4,9 +4,9 @@
|
||||
// Time is in seconds.
|
||||
|
||||
function make_reason(factory, excuse, evidence) {
|
||||
def reason = Error(`pronto.${factory}${excuse ? ': ' + excuse : ''}`)
|
||||
reason.evidence = evidence
|
||||
return reason
|
||||
var msg = 'pronto.' + factory
|
||||
if (excuse) msg = msg + ': ' + excuse
|
||||
return { message: msg, evidence: evidence }
|
||||
}
|
||||
|
||||
function is_requestor(fn) {
|
||||
@@ -14,21 +14,27 @@ function is_requestor(fn) {
|
||||
}
|
||||
|
||||
function check_requestors(list, factory) {
|
||||
if (!is_array(list) || some(list, r => !is_requestor(r)))
|
||||
throw make_reason(factory, 'Bad requestor array.', list)
|
||||
if (!is_array(list) || some(list, r => !is_requestor(r))) {
|
||||
print(make_reason(factory, 'Bad requestor array.', list).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
}
|
||||
|
||||
function check_callback(cb, factory) {
|
||||
if (!is_function(cb) || length(cb) != 2)
|
||||
throw make_reason(factory, 'Not a callback.', cb)
|
||||
if (!is_function(cb) || length(cb) != 2) {
|
||||
print(make_reason(factory, 'Not a callback.', cb).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
}
|
||||
|
||||
// fallback(requestor_array)
|
||||
// Tries each requestor in order until one succeeds.
|
||||
function fallback(requestor_array) {
|
||||
def factory = 'fallback'
|
||||
if (!is_array(requestor_array) || length(requestor_array) == 0)
|
||||
throw make_reason(factory, 'Empty requestor array.')
|
||||
if (!is_array(requestor_array) || length(requestor_array) == 0) {
|
||||
print(make_reason(factory, 'Empty requestor array.').message + '\n')
|
||||
disrupt
|
||||
}
|
||||
check_requestors(requestor_array, factory)
|
||||
|
||||
return function fallback_requestor(callback, value) {
|
||||
@@ -40,7 +46,8 @@ function fallback(requestor_array) {
|
||||
function cancel(reason) {
|
||||
cancelled = true
|
||||
if (current_cancel) {
|
||||
try { current_cancel(reason) } catch (_) {}
|
||||
var _c = function() { current_cancel(reason) } disruption {}
|
||||
_c()
|
||||
current_cancel = null
|
||||
}
|
||||
}
|
||||
@@ -53,9 +60,9 @@ function fallback(requestor_array) {
|
||||
}
|
||||
|
||||
def requestor = requestor_array[index]
|
||||
index += 1
|
||||
index = index + 1
|
||||
|
||||
try {
|
||||
var _run = function() {
|
||||
current_cancel = requestor(function(val, reason) {
|
||||
if (cancelled) return
|
||||
current_cancel = null
|
||||
@@ -65,9 +72,10 @@ function fallback(requestor_array) {
|
||||
try_next()
|
||||
}
|
||||
}, value)
|
||||
} catch (ex) {
|
||||
} disruption {
|
||||
try_next()
|
||||
}
|
||||
_run()
|
||||
}
|
||||
|
||||
try_next()
|
||||
@@ -79,25 +87,32 @@ function fallback(requestor_array) {
|
||||
// Runs requestors in parallel, collecting all results.
|
||||
function parallel(requestor_array, throttle, need) {
|
||||
def factory = 'parallel'
|
||||
if (!is_array(requestor_array))
|
||||
throw make_reason(factory, 'Not an array.', requestor_array)
|
||||
if (!is_array(requestor_array)) {
|
||||
print(make_reason(factory, 'Not an array.', requestor_array).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
check_requestors(requestor_array, factory)
|
||||
|
||||
def length = length(requestor_array)
|
||||
if (length == 0)
|
||||
def len = length(requestor_array)
|
||||
if (len == 0)
|
||||
return function(callback, value) { callback([]) }
|
||||
|
||||
if (need == null) need = length
|
||||
if (!is_number(need) || need < 0 || need > length)
|
||||
throw make_reason(factory, 'Bad need.', need)
|
||||
var _need = need
|
||||
if (_need == null) _need = len
|
||||
if (!is_number(_need) || _need < 0 || _need > len) {
|
||||
print(make_reason(factory, 'Bad need.', _need).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
|
||||
if (throttle != null && (!is_number(throttle) || throttle < 1))
|
||||
throw make_reason(factory, 'Bad throttle.', throttle)
|
||||
if (throttle != null && (!is_number(throttle) || throttle < 1)) {
|
||||
print(make_reason(factory, 'Bad throttle.', throttle).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
|
||||
return function parallel_requestor(callback, value) {
|
||||
check_callback(callback, factory)
|
||||
def results = array(length)
|
||||
def cancel_list = array(length)
|
||||
def results = array(len)
|
||||
def cancel_list = array(len)
|
||||
var next_index = 0
|
||||
var successes = 0
|
||||
var failures = 0
|
||||
@@ -107,33 +122,34 @@ function parallel(requestor_array, throttle, need) {
|
||||
if (finished) return
|
||||
finished = true
|
||||
arrfor(cancel_list, c => {
|
||||
try { if (is_function(c)) c(reason) } catch (_) {}
|
||||
var _c = function() { if (is_function(c)) c(reason) } disruption {}
|
||||
_c()
|
||||
})
|
||||
}
|
||||
|
||||
function start_one() {
|
||||
if (finished || next_index >= length) return
|
||||
if (finished || next_index >= len) return
|
||||
def idx = next_index
|
||||
next_index += 1
|
||||
next_index = next_index + 1
|
||||
def requestor = requestor_array[idx]
|
||||
|
||||
try {
|
||||
var _run = function() {
|
||||
cancel_list[idx] = requestor(function(val, reason) {
|
||||
if (finished) return
|
||||
cancel_list[idx] = null
|
||||
|
||||
if (val != null) {
|
||||
results[idx] = val
|
||||
successes += 1
|
||||
if (successes >= need) {
|
||||
successes = successes + 1
|
||||
if (successes >= _need) {
|
||||
finished = true
|
||||
cancel(make_reason(factory, 'Finished.'))
|
||||
callback(results)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
failures += 1
|
||||
if (failures > length - need) {
|
||||
failures = failures + 1
|
||||
if (failures > len - _need) {
|
||||
cancel(reason)
|
||||
callback(null, reason || make_reason(factory, 'Too many failures.'))
|
||||
return
|
||||
@@ -142,20 +158,21 @@ function parallel(requestor_array, throttle, need) {
|
||||
|
||||
start_one()
|
||||
}, value)
|
||||
} catch (ex) {
|
||||
failures += 1
|
||||
if (failures > length - need) {
|
||||
cancel(ex)
|
||||
callback(null, ex)
|
||||
} disruption {
|
||||
failures = failures + 1
|
||||
if (failures > len - _need) {
|
||||
cancel(make_reason(factory, 'Requestor threw.'))
|
||||
callback(null, make_reason(factory, 'Requestor threw.'))
|
||||
return
|
||||
}
|
||||
start_one()
|
||||
}
|
||||
_run()
|
||||
}
|
||||
|
||||
|
||||
def concurrent = throttle ? min(throttle, length) : length
|
||||
for (var i = 0; i < concurrent; i++) start_one()
|
||||
def concurrent = throttle ? min(throttle, len) : len
|
||||
var i = 0
|
||||
while (i < concurrent) { start_one(); i = i + 1 }
|
||||
|
||||
return cancel
|
||||
}
|
||||
@@ -165,22 +182,29 @@ function parallel(requestor_array, throttle, need) {
|
||||
// Runs requestors in parallel, returns first success(es).
|
||||
function race(requestor_array, throttle, need) {
|
||||
def factory = 'race'
|
||||
if (!is_array(requestor_array) || length(requestor_array) == 0)
|
||||
throw make_reason(factory, 'Empty requestor array.')
|
||||
if (!is_array(requestor_array) || length(requestor_array) == 0) {
|
||||
print(make_reason(factory, 'Empty requestor array.').message + '\n')
|
||||
disrupt
|
||||
}
|
||||
check_requestors(requestor_array, factory)
|
||||
|
||||
def length = length(requestor_array)
|
||||
if (need == null) need = 1
|
||||
if (!is_number(need) || need < 1 || need > length)
|
||||
throw make_reason(factory, 'Bad need.', need)
|
||||
def len = length(requestor_array)
|
||||
var _need = need
|
||||
if (_need == null) _need = 1
|
||||
if (!is_number(_need) || _need < 1 || _need > len) {
|
||||
print(make_reason(factory, 'Bad need.', _need).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
|
||||
if (throttle != null && (!is_number(throttle) || throttle < 1))
|
||||
throw make_reason(factory, 'Bad throttle.', throttle)
|
||||
if (throttle != null && (!is_number(throttle) || throttle < 1)) {
|
||||
print(make_reason(factory, 'Bad throttle.', throttle).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
|
||||
return function race_requestor(callback, value) {
|
||||
check_callback(callback, factory)
|
||||
def results = array(length)
|
||||
def cancel_list = array(length)
|
||||
def results = array(len)
|
||||
def cancel_list = array(len)
|
||||
var next_index = 0
|
||||
var successes = 0
|
||||
var failures = 0
|
||||
@@ -190,27 +214,28 @@ function race(requestor_array, throttle, need) {
|
||||
if (finished) return
|
||||
finished = true
|
||||
arrfor(cancel_list, c => {
|
||||
try { if (is_function(c)) c(reason) } catch (_) {}
|
||||
var _c = function() { if (is_function(c)) c(reason) } disruption {}
|
||||
_c()
|
||||
})
|
||||
}
|
||||
|
||||
function start_one() {
|
||||
if (finished || next_index >= length) return
|
||||
if (finished || next_index >= len) return
|
||||
def idx = next_index
|
||||
next_index += 1
|
||||
next_index = next_index + 1
|
||||
def requestor = requestor_array[idx]
|
||||
|
||||
try {
|
||||
var _run = function() {
|
||||
cancel_list[idx] = requestor(function(val, reason) {
|
||||
if (finished) return
|
||||
cancel_list[idx] = null
|
||||
|
||||
if (val != null) {
|
||||
results[idx] = val
|
||||
successes += 1
|
||||
if (successes >= need) {
|
||||
successes = successes + 1
|
||||
if (successes >= _need) {
|
||||
cancel(make_reason(factory, 'Winner.'))
|
||||
if (need == 1) {
|
||||
if (_need == 1) {
|
||||
callback(val)
|
||||
} else {
|
||||
callback(results)
|
||||
@@ -218,8 +243,8 @@ function race(requestor_array, throttle, need) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
failures += 1
|
||||
if (failures > length - need) {
|
||||
failures = failures + 1
|
||||
if (failures > len - _need) {
|
||||
cancel(reason)
|
||||
callback(null, reason || make_reason(factory, 'All failed.'))
|
||||
return
|
||||
@@ -228,19 +253,21 @@ function race(requestor_array, throttle, need) {
|
||||
|
||||
start_one()
|
||||
}, value)
|
||||
} catch (ex) {
|
||||
failures += 1
|
||||
if (failures > length - need) {
|
||||
cancel(ex)
|
||||
callback(null, ex)
|
||||
} disruption {
|
||||
failures = failures + 1
|
||||
if (failures > len - _need) {
|
||||
cancel(make_reason(factory, 'Requestor threw.'))
|
||||
callback(null, make_reason(factory, 'Requestor threw.'))
|
||||
return
|
||||
}
|
||||
start_one()
|
||||
}
|
||||
_run()
|
||||
}
|
||||
|
||||
def concurrent = throttle ? min(throttle, length) : length
|
||||
for (var i = 0; i < concurrent; i++) start_one()
|
||||
def concurrent = throttle ? min(throttle, len) : len
|
||||
var i = 0
|
||||
while (i < concurrent) { start_one(); i = i + 1 }
|
||||
|
||||
return cancel
|
||||
}
|
||||
@@ -250,8 +277,10 @@ function race(requestor_array, throttle, need) {
|
||||
// Runs requestors one at a time, passing result to next.
|
||||
function sequence(requestor_array) {
|
||||
def factory = 'sequence'
|
||||
if (!is_array(requestor_array))
|
||||
throw make_reason(factory, 'Not an array.', requestor_array)
|
||||
if (!is_array(requestor_array)) {
|
||||
print(make_reason(factory, 'Not an array.', requestor_array).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
check_requestors(requestor_array, factory)
|
||||
|
||||
if (length(requestor_array) == 0)
|
||||
@@ -266,7 +295,8 @@ function sequence(requestor_array) {
|
||||
function cancel(reason) {
|
||||
cancelled = true
|
||||
if (current_cancel) {
|
||||
try { current_cancel(reason) } catch (_) {}
|
||||
var _c = function() { current_cancel(reason) } disruption {}
|
||||
_c()
|
||||
current_cancel = null
|
||||
}
|
||||
}
|
||||
@@ -279,9 +309,9 @@ function sequence(requestor_array) {
|
||||
}
|
||||
|
||||
def requestor = requestor_array[index]
|
||||
index += 1
|
||||
index = index + 1
|
||||
|
||||
try {
|
||||
var _run = function() {
|
||||
current_cancel = requestor(function(result, reason) {
|
||||
if (cancelled) return
|
||||
current_cancel = null
|
||||
@@ -291,9 +321,10 @@ function sequence(requestor_array) {
|
||||
run_next(result)
|
||||
}
|
||||
}, val)
|
||||
} catch (ex) {
|
||||
callback(null, ex)
|
||||
} disruption {
|
||||
callback(null, make_reason(factory, 'Requestor threw.'))
|
||||
}
|
||||
_run()
|
||||
}
|
||||
|
||||
run_next(value)
|
||||
@@ -305,26 +336,29 @@ function sequence(requestor_array) {
|
||||
// Converts a unary function into a requestor.
|
||||
function requestorize(unary) {
|
||||
def factory = 'requestorize'
|
||||
if (!is_function(unary))
|
||||
throw make_reason(factory, 'Not a function.', unary)
|
||||
if (!is_function(unary)) {
|
||||
print(make_reason(factory, 'Not a function.', unary).message + '\n')
|
||||
disrupt
|
||||
}
|
||||
|
||||
return function requestorized(callback, value) {
|
||||
check_callback(callback, factory)
|
||||
try {
|
||||
var _run = function() {
|
||||
def result = unary(value)
|
||||
callback(result == null ? true : result)
|
||||
} catch (ex) {
|
||||
callback(null, ex)
|
||||
} disruption {
|
||||
callback(null, make_reason(factory, 'Function threw.'))
|
||||
}
|
||||
_run()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fallback,
|
||||
parallel,
|
||||
race,
|
||||
sequence,
|
||||
requestorize,
|
||||
is_requestor,
|
||||
check_callback
|
||||
fallback: fallback,
|
||||
parallel: parallel,
|
||||
race: race,
|
||||
sequence: sequence,
|
||||
requestorize: requestorize,
|
||||
is_requestor: is_requestor,
|
||||
check_callback: check_callback
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#define BOOTSTRAP_MACH "internal/bootstrap.mach"
|
||||
#define BOOTSTRAP_AST "internal/bootstrap.ast.json"
|
||||
#define BOOTSTRAP_SRC "internal/bootstrap.cm"
|
||||
#define CELL_SHOP_DIR ".cell"
|
||||
#define CELL_CORE_DIR "packages/core"
|
||||
|
||||
@@ -183,6 +184,10 @@ void script_startup(cell_rt *prt)
|
||||
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
|
||||
}
|
||||
|
||||
// Set args and use_mcode to null/false for actor spawn (not CLI mode)
|
||||
JS_SetPropertyStr(js, hidden_env, "args", JS_NULL);
|
||||
JS_SetPropertyStr(js, hidden_env, "use_mcode", JS_NewBool(js, 0));
|
||||
|
||||
if (core_path) {
|
||||
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
|
||||
}
|
||||
@@ -324,6 +329,11 @@ int cell_init(int argc, char **argv)
|
||||
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, "use_mcode", JS_NewBool(ctx, use_mcode));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_NewObject(ctx));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "nota", js_nota_use(ctx));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "wota", js_wota_use(ctx));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "init", JS_NULL);
|
||||
JSValue args_arr = JS_NewArray(ctx);
|
||||
for (int i = arg_start; i < argc; i++) {
|
||||
JSValue str = JS_NewString(ctx, argv[i]);
|
||||
|
||||
343
time.cm
343
time.cm
@@ -1,204 +1,211 @@
|
||||
// epoch = 0000-01-01 00:00:00 +0000
|
||||
var time = this;
|
||||
var time = this
|
||||
|
||||
/* -------- host helpers -------------------------------------------------- */
|
||||
var now = time.now; // seconds since Misty epoch (C stub)
|
||||
var computer_zone = time.computer_zone; // integral hours, no DST
|
||||
var computer_dst = time.computer_dst; // true ↔ DST in effect
|
||||
var now = time.now
|
||||
var computer_zone = time.computer_zone
|
||||
var computer_dst = time.computer_dst
|
||||
|
||||
delete time.now;
|
||||
delete time.computer_zone;
|
||||
delete time.computer_dst;
|
||||
delete time.now
|
||||
delete time.computer_zone
|
||||
delete time.computer_dst
|
||||
|
||||
/* -------- units & static tables ----------------------------------------- */
|
||||
|
||||
time.second = 1;
|
||||
time.minute = 60;
|
||||
time.hour = 3_600;
|
||||
time.day = 86_400;
|
||||
time.week = 604_800;
|
||||
time.second = 1
|
||||
time.minute = 60
|
||||
time.hour = 3600
|
||||
time.day = 86400
|
||||
time.week = 604800
|
||||
|
||||
time.weekdays = [
|
||||
"Sunday", "Monday", "Tuesday",
|
||||
"Wednesday", "Thursday", "Friday", "Saturday"
|
||||
];
|
||||
]
|
||||
|
||||
time.monthstr = [
|
||||
"January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December"
|
||||
];
|
||||
]
|
||||
|
||||
time.epoch = 1970;
|
||||
time.epoch = 1970
|
||||
|
||||
/* ratios (kept K&R-style) */
|
||||
time.hour2minute = function() { return time.hour / time.minute; };
|
||||
time.day2hour = function() { return time.day / time.hour; };
|
||||
time.minute2second = function() { return time.minute / time.second; };
|
||||
time.week2day = function() { return time.week / time.day; };
|
||||
time.hour2minute = function() { return time.hour / time.minute }
|
||||
time.day2hour = function() { return time.day / time.hour }
|
||||
time.minute2second = function() { return time.minute / time.second }
|
||||
time.week2day = function() { return time.week / time.day }
|
||||
|
||||
/* leap-year helpers */
|
||||
time.yearsize = function yearsize(y)
|
||||
{
|
||||
if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) return 366;
|
||||
return 365;
|
||||
};
|
||||
time.isleap = function(y) { return time.yearsize(y) == 366; };
|
||||
time.yearsize = function yearsize(y) {
|
||||
if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) return 366
|
||||
return 365
|
||||
}
|
||||
time.isleap = function(y) { return time.yearsize(y) == 366 }
|
||||
|
||||
/* timecode utility */
|
||||
time.timecode = function(t, fps = 24)
|
||||
{
|
||||
var s = whole(t);
|
||||
var frac = t - s;
|
||||
return `${s}:${whole(frac * fps)}`;
|
||||
};
|
||||
|
||||
/* per-month day counts (non-leap) */
|
||||
time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
|
||||
// convert seconds-since-epoch → broken-down record
|
||||
function time_record(num = now(),
|
||||
zone = computer_zone(),
|
||||
dst = computer_dst())
|
||||
{
|
||||
if (is_object(num)) return num;
|
||||
|
||||
var monthdays = array(time.monthdays);
|
||||
var rec = {
|
||||
second : 0, minute : 0, hour : 0,
|
||||
yday : 0, year : 0,
|
||||
weekday: 0, month : 0, day : 0,
|
||||
zone : zone, dst : !!dst,
|
||||
ce : "AD"
|
||||
};
|
||||
|
||||
/* include DST in the effective offset */
|
||||
var offset = zone + (dst ? 1 : 0);
|
||||
num += offset * time.hour;
|
||||
|
||||
/* split into day + seconds-of-day */
|
||||
var hms = num % time.day;
|
||||
var day = floor(num / time.day);
|
||||
if (hms < 0) { hms += time.day; day--; }
|
||||
|
||||
rec.second = hms % time.minute;
|
||||
var tmp = floor(hms / time.minute);
|
||||
rec.minute = tmp % time.minute;
|
||||
rec.hour = floor(tmp / time.minute);
|
||||
rec.weekday = (day + 4_503_599_627_370_496 + 2) % 7; /* 2 → 1970-01-01 was Thursday */
|
||||
|
||||
/* year & day-of-year */
|
||||
var y = time.epoch;
|
||||
if (day >= 0) {
|
||||
for (y = time.epoch; day >= time.yearsize(y); y++)
|
||||
day -= time.yearsize(y);
|
||||
} else {
|
||||
for (y = time.epoch; day < 0; y--)
|
||||
day += time.yearsize(y - 1);
|
||||
}
|
||||
rec.year = y;
|
||||
rec.ce = (y <= 0) ? "BC" : "AD";
|
||||
rec.yday = day;
|
||||
|
||||
/* month & month-day */
|
||||
if (time.yearsize(y) == 366) monthdays[1] = 29;
|
||||
var m = 0;
|
||||
for (; day >= monthdays[m]; m++) day -= monthdays[m];
|
||||
rec.month = m;
|
||||
rec.day = day + 1;
|
||||
|
||||
return rec;
|
||||
time.timecode = function(t) {
|
||||
var fps = 24
|
||||
var s = whole(t)
|
||||
var frac = t - s
|
||||
return `${s}:${whole(frac * fps)}`
|
||||
}
|
||||
|
||||
function time_number(rec = now())
|
||||
{
|
||||
if (is_number(rec)) return rec;
|
||||
time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
|
||||
log.console(rec)
|
||||
log.console(rec.minute)
|
||||
// convert seconds-since-epoch -> broken-down record
|
||||
function time_record(_num, _zone, _dst) {
|
||||
var n = _num
|
||||
var z = _zone
|
||||
var d = _dst
|
||||
if (n == null) n = now()
|
||||
if (z == null) z = computer_zone()
|
||||
if (d == null) d = computer_dst()
|
||||
|
||||
var c = 0;
|
||||
var year = rec.year || 0;
|
||||
var hour = rec.hour || 0;
|
||||
var minute = rec.minute || 0;
|
||||
var second = rec.second || 0;
|
||||
var zone = rec.zone || 0;
|
||||
var dst = rec.dst ? 1 : 0;
|
||||
var yday = rec.yday || 0;
|
||||
if (is_object(n)) return n
|
||||
|
||||
if (year > time.epoch) {
|
||||
for (var i = time.epoch; i < year; i++)
|
||||
c += time.day * time.yearsize(i);
|
||||
} else if (year < time.epoch) {
|
||||
for (var i = time.epoch - 1; i > year; i--)
|
||||
c += time.day * time.yearsize(i);
|
||||
c += (time.yearsize(year) - yday - 1) * time.day;
|
||||
c += (time.day2hour() - hour - 1) * time.hour;
|
||||
c += (time.hour2minute() - minute - 1) * time.minute;
|
||||
c += time.minute2second() - second;
|
||||
c += (zone + dst) * time.hour;
|
||||
return -c; /* BCE */
|
||||
var monthdays = array(time.monthdays)
|
||||
var rec = {
|
||||
second: 0, minute: 0, hour: 0,
|
||||
yday: 0, year: 0,
|
||||
weekday: 0, month: 0, day: 0,
|
||||
zone: z, dst: !!d,
|
||||
ce: "AD"
|
||||
}
|
||||
|
||||
c = second;
|
||||
c += minute * time.minute;
|
||||
c += hour * time.hour;
|
||||
c += yday * time.day;
|
||||
c -= (zone + dst) * time.hour;
|
||||
var offset = z + (d ? 1 : 0)
|
||||
n = n + offset * time.hour
|
||||
|
||||
return c;
|
||||
var hms = n % time.day
|
||||
var day = floor(n / time.day)
|
||||
if (hms < 0) { hms = hms + time.day; day = day - 1 }
|
||||
|
||||
rec.second = hms % time.minute
|
||||
var tmp = floor(hms / time.minute)
|
||||
rec.minute = tmp % time.minute
|
||||
rec.hour = floor(tmp / time.minute)
|
||||
rec.weekday = (day + 4503599627370496 + 2) % 7
|
||||
|
||||
var y = time.epoch
|
||||
if (day >= 0) {
|
||||
y = time.epoch
|
||||
while (day >= time.yearsize(y)) {
|
||||
day = day - time.yearsize(y)
|
||||
y = y + 1
|
||||
}
|
||||
} else {
|
||||
y = time.epoch
|
||||
while (day < 0) {
|
||||
y = y - 1
|
||||
day = day + time.yearsize(y)
|
||||
}
|
||||
}
|
||||
rec.year = y
|
||||
rec.ce = (y <= 0) ? "BC" : "AD"
|
||||
rec.yday = day
|
||||
|
||||
if (time.yearsize(y) == 366) monthdays[1] = 29
|
||||
var m = 0
|
||||
while (day >= monthdays[m]) {
|
||||
day = day - monthdays[m]
|
||||
m = m + 1
|
||||
}
|
||||
rec.month = m
|
||||
rec.day = day + 1
|
||||
|
||||
return rec
|
||||
}
|
||||
|
||||
function time_number(_rec) {
|
||||
var r = _rec
|
||||
if (r == null) r = now()
|
||||
if (is_number(r)) return r
|
||||
|
||||
var c = 0
|
||||
var year = r.year || 0
|
||||
var hour = r.hour || 0
|
||||
var minute = r.minute || 0
|
||||
var second = r.second || 0
|
||||
var zone = r.zone || 0
|
||||
var dst = r.dst ? 1 : 0
|
||||
var yday = r.yday || 0
|
||||
|
||||
if (year > time.epoch) {
|
||||
var i = time.epoch
|
||||
while (i < year) {
|
||||
c = c + time.day * time.yearsize(i)
|
||||
i = i + 1
|
||||
}
|
||||
} else if (year < time.epoch) {
|
||||
var i = time.epoch - 1
|
||||
while (i > year) {
|
||||
c = c + time.day * time.yearsize(i)
|
||||
i = i - 1
|
||||
}
|
||||
c = c + (time.yearsize(year) - yday - 1) * time.day
|
||||
c = c + (time.day2hour() - hour - 1) * time.hour
|
||||
c = c + (time.hour2minute() - minute - 1) * time.minute
|
||||
c = c + time.minute2second() - second
|
||||
c = c + (zone + dst) * time.hour
|
||||
return -c
|
||||
}
|
||||
|
||||
c = second
|
||||
c = c + minute * time.minute
|
||||
c = c + hour * time.hour
|
||||
c = c + yday * time.day
|
||||
c = c - (zone + dst) * time.hour
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// text formatting
|
||||
var default_fmt = "vB mB d hh:nn:ss a z y c"; /* includes new DST token */
|
||||
var default_fmt = "vB mB d hh:nn:ss a z y c"
|
||||
|
||||
function time_text(num = now(),
|
||||
fmt = default_fmt,
|
||||
zone = computer_zone(),
|
||||
dst = computer_dst())
|
||||
{
|
||||
var rec = is_number(num) ? time_record(num, zone, dst) : num;
|
||||
zone = rec.zone;
|
||||
dst = rec.dst;
|
||||
function time_text(_num, _fmt, _zone, _dst) {
|
||||
var n = _num
|
||||
var f = _fmt
|
||||
var z = _zone
|
||||
var d = _dst
|
||||
if (n == null) n = now()
|
||||
if (f == null) f = default_fmt
|
||||
if (z == null) z = computer_zone()
|
||||
if (d == null) d = computer_dst()
|
||||
|
||||
/* am/pm */
|
||||
if (search(fmt, "a") != null) {
|
||||
if (rec.hour >= 13) { rec.hour -= 12; fmt = replace(fmt, "a", "PM"); }
|
||||
else if (rec.hour == 12) { fmt = replace(fmt, "a", "PM"); }
|
||||
else if (rec.hour == 0) { rec.hour = 12; fmt = replace(fmt, "a", "AM"); }
|
||||
else fmt = replace(fmt, "a", "AM");
|
||||
var rec = is_number(n) ? time_record(n, z, d) : n
|
||||
z = rec.zone
|
||||
d = rec.dst
|
||||
|
||||
if (search(f, "a") != null) {
|
||||
if (rec.hour >= 13) { rec.hour = rec.hour - 12; f = replace(f, "a", "PM") }
|
||||
else if (rec.hour == 12) { f = replace(f, "a", "PM") }
|
||||
else if (rec.hour == 0) { rec.hour = 12; f = replace(f, "a", "AM") }
|
||||
else f = replace(f, "a", "AM")
|
||||
}
|
||||
|
||||
/* BCE/CE */
|
||||
var year = rec.year > 0 ? rec.year : rec.year - 1;
|
||||
if (search(fmt, "c") != null) {
|
||||
if (year < 0) { year = abs(year); fmt = replace(fmt, "c", "BC"); }
|
||||
else fmt = replace(fmt, "c", "AD");
|
||||
var year = rec.year > 0 ? rec.year : rec.year - 1
|
||||
if (search(f, "c") != null) {
|
||||
if (year < 0) { year = abs(year); f = replace(f, "c", "BC") }
|
||||
else f = replace(f, "c", "AD")
|
||||
}
|
||||
|
||||
/* substitutions */
|
||||
var full_offset = zone + (dst ? 1 : 0);
|
||||
fmt = replace(fmt, "yyyy", text(year, "i4"))
|
||||
fmt = replace(fmt, "y", year);
|
||||
fmt = replace(fmt, "eee", rec.yday + 1);
|
||||
fmt = replace(fmt, "dd", text(rec.day, "i2"))
|
||||
fmt = replace(fmt, "d", rec.day);
|
||||
fmt = replace(fmt, "hh", text(rec.hour, "i2"));
|
||||
fmt = replace(fmt, "h", rec.hour);
|
||||
fmt = replace(fmt, "nn", text(rec.minute, "i2"));
|
||||
fmt = replace(fmt, "n", rec.minute);
|
||||
fmt = replace(fmt, "ss", text(rec.second, "i2"));
|
||||
fmt = replace(fmt, "s", rec.second);
|
||||
fmt = replace(fmt, "x", dst ? "DST" : ""); /* new */
|
||||
fmt = replace(fmt, "z", (full_offset >= 0 ? "+" : "") + text(full_offset));
|
||||
fmt = replace(fmt, /mm[^bB]/g, rec.month + 1);
|
||||
fmt = replace(fmt, /m[^bB]/g, rec.month + 1);
|
||||
fmt = replace(fmt, /v[^bB]/g, rec.weekday);
|
||||
fmt = replace(fmt, "mb", text(time.monthstr[rec.month], 0, 3));
|
||||
fmt = replace(fmt, "mB", time.monthstr[rec.month]);
|
||||
fmt = replace(fmt, "vB", time.weekdays[rec.weekday]);
|
||||
fmt = replace(fmt, "vb", text(time.weekdays[rec.weekday], 0, 3));
|
||||
var full_offset = z + (d ? 1 : 0)
|
||||
f = replace(f, "yyyy", text(year, "i4"))
|
||||
f = replace(f, "y", year)
|
||||
f = replace(f, "eee", rec.yday + 1)
|
||||
f = replace(f, "dd", text(rec.day, "i2"))
|
||||
f = replace(f, "d", rec.day)
|
||||
f = replace(f, "hh", text(rec.hour, "i2"))
|
||||
f = replace(f, "h", rec.hour)
|
||||
f = replace(f, "nn", text(rec.minute, "i2"))
|
||||
f = replace(f, "n", rec.minute)
|
||||
f = replace(f, "ss", text(rec.second, "i2"))
|
||||
f = replace(f, "s", rec.second)
|
||||
f = replace(f, "x", d ? "DST" : "")
|
||||
f = replace(f, "z", (full_offset >= 0 ? "+" : "") + text(full_offset))
|
||||
f = replace(f, /mm[^bB]/g, rec.month + 1)
|
||||
f = replace(f, /m[^bB]/g, rec.month + 1)
|
||||
f = replace(f, /v[^bB]/g, rec.weekday)
|
||||
f = replace(f, "mb", text(time.monthstr[rec.month], 0, 3))
|
||||
f = replace(f, "mB", time.monthstr[rec.month])
|
||||
f = replace(f, "vB", time.weekdays[rec.weekday])
|
||||
f = replace(f, "vb", text(time.weekdays[rec.weekday], 0, 3))
|
||||
|
||||
return fmt;
|
||||
return f
|
||||
}
|
||||
|
||||
return { record: time_record, number: time_number, text: time_text };
|
||||
return { record: time_record, number: time_number, text: time_text }
|
||||
|
||||
35
toml.cm
35
toml.cm
@@ -3,21 +3,16 @@
|
||||
|
||||
function toml_unescape(s) {
|
||||
if (!is_text(s)) return null
|
||||
// Order matters:
|
||||
// "\\\"" (backslash + quote) should become "\"", not just '"'
|
||||
// So: unescape \" first, then unescape \\.
|
||||
s = replace(s, '\\"', '"')
|
||||
s = replace(s, '\\\\', '\\')
|
||||
return s
|
||||
var r = replace(s, '\\"', '"')
|
||||
r = replace(r, '\\\\', '\\')
|
||||
return r
|
||||
}
|
||||
|
||||
function toml_escape(s) {
|
||||
if (!is_text(s)) return null
|
||||
// Order matters:
|
||||
// escape backslashes first, otherwise escaping quotes introduces new backslashes that would get double-escaped.
|
||||
s = replace(s, '\\', '\\\\')
|
||||
s = replace(s, '"', '\\"')
|
||||
return s
|
||||
var r = replace(s, '\\', '\\\\')
|
||||
r = replace(r, '"', '\\"')
|
||||
return r
|
||||
}
|
||||
|
||||
function parse_toml(toml_text) {
|
||||
@@ -123,7 +118,7 @@ function parse_key_path(str) {
|
||||
current = ''
|
||||
continue
|
||||
}
|
||||
current += c
|
||||
current = current + c
|
||||
}
|
||||
|
||||
var tail = trim(current)
|
||||
@@ -137,27 +132,27 @@ function parse_array(str) {
|
||||
if (!is_text(str)) return null
|
||||
|
||||
// Remove brackets and trim
|
||||
str = text(str, 1, -1)
|
||||
str = trim(str)
|
||||
if (!str) return []
|
||||
var s = text(str, 1, -1)
|
||||
s = trim(s)
|
||||
if (!s) return []
|
||||
|
||||
var items = []
|
||||
var current = ''
|
||||
var in_quotes = false
|
||||
|
||||
for (var i = 0; i < length(str); i++) {
|
||||
var ch = str[i]
|
||||
for (var i = 0; i < length(s); i++) {
|
||||
var ch = s[i]
|
||||
|
||||
if (ch == '"' && (i == 0 || str[i - 1] != '\\')) {
|
||||
if (ch == '"' && (i == 0 || s[i - 1] != '\\')) {
|
||||
in_quotes = !in_quotes
|
||||
current += ch
|
||||
current = current + ch
|
||||
} else if (ch == ',' && !in_quotes) {
|
||||
var piece = trim(current)
|
||||
if (piece == null) piece = trim(current)
|
||||
push(items, parse_value(piece))
|
||||
current = ''
|
||||
} else {
|
||||
current += ch
|
||||
current = current + ch
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
baseURL = 'https://pit-lang.org/'
|
||||
baseURL = 'https://crumbpit.org/'
|
||||
languageCode = 'en-us'
|
||||
title = 'ƿit'
|
||||
theme = 'knr'
|
||||
|
||||
Reference in New Issue
Block a user