diff --git a/bootstrap.ce b/bootstrap.ce index e0d70785..5ef34250 100644 --- a/bootstrap.ce +++ b/bootstrap.ce @@ -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 diff --git a/fd.cm b/fd.cm index 8790cfad..01a618fc 100644 --- a/fd.cm +++ b/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 diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm index ac532e0a..a8cc77a5 100644 --- a/internal/bootstrap.cm +++ b/internal/bootstrap.cm @@ -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 + }) +} diff --git a/internal/bootstrap.mach b/internal/bootstrap.mach index 441628ba..14cb5527 100644 Binary files a/internal/bootstrap.mach and b/internal/bootstrap.mach differ diff --git a/internal/engine.cm b/internal/engine.cm index d49c9589..3304f090 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -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 + } }) diff --git a/internal/shop.cm b/internal/shop.cm index 6b5fd360..d22373cf 100644 --- a/internal/shop.cm +++ b/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) diff --git a/link.cm b/link.cm index 42b5c6cf..529ac995 100644 --- a/link.cm +++ b/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) diff --git a/package.cm b/package.cm index 44c5fa2a..a10c004b 100644 --- a/package.cm +++ b/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) diff --git a/pronto.cm b/pronto.cm index 1c44efa4..1695890e 100644 --- a/pronto.cm +++ b/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 } diff --git a/source/cell.c b/source/cell.c index 4a421748..ea659b11 100644 --- a/source/cell.c +++ b/source/cell.c @@ -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]); diff --git a/time.cm b/time.cm index 770a33c1..bf71b9a7 100644 --- a/time.cm +++ b/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 } diff --git a/toml.cm b/toml.cm index 4479a326..eb356784 100644 --- a/toml.cm +++ b/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 } } diff --git a/website/hugo.toml b/website/hugo.toml index 9b628d13..7af65c40 100644 --- a/website/hugo.toml +++ b/website/hugo.toml @@ -1,4 +1,4 @@ -baseURL = 'https://pit-lang.org/' +baseURL = 'https://crumbpit.org/' languageCode = 'en-us' title = 'ƿit' theme = 'knr'