From 4deb0e257741682c01ed8239e3718278e04e94a1 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Feb 2026 11:03:01 -0600 Subject: [PATCH] new syntax for internals --- internal/engine.cm | 1 + internal/engine.mach | Bin 22470 -> 22539 bytes internal/shop.cm | 245 ++++++++++++++++++++++------------- internal/testlib.cm | 15 ++- link.cm | 6 +- package.cm | 106 +++++++++------ test.ce | 301 ++++++++++++++++++++++++++----------------- time.cm | 5 +- toml.cm | 96 +++++++++----- 9 files changed, 483 insertions(+), 292 deletions(-) diff --git a/internal/engine.cm b/internal/engine.cm index b2d04872..39d1ee97 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -865,6 +865,7 @@ $_.clock(_ => { var pkg = file_info ? file_info.package : null env.use = function(path) { return shop.use(path, pkg) } env.args = _cell.args.arg + env.log = log var script = text(fd.slurp(prog_path)) var ast = analyze(script, prog_path) diff --git a/internal/engine.mach b/internal/engine.mach index e740337fdeb5f513ba81a37ceff3ae70a8eb3d28..245e07eb68d8809c5068c9e8a7d8612617513344 100644 GIT binary patch delta 279 zcmZY4zYYOG5C-s>o7yDQPK3Wl0bQoP~1ORCkTRF~Eb~t9~#De2zg F`T^tPDRlq< delta 226 zcmZX~F%E)26a>)!Sr!x6UC?M^1Cc;sFfn$NQre@@Rue$IfoGs|4`LUp} zc*$gD>IPpAY@!@TI?ikT&T~Aqv+V*8p|BuUIF3EcQiYOB1&Ki9rhrf=;L_~&ED;8B zyX#ztB`&$Lfx8xc8FEL!+@YVqHK-w^0=@RRTj8-_9u#asf= 1; i--) { - var pkg_candidate = text(array(parts, 0, i), '/') - var mod_path = text(array(parts, i), '/') + var i = 0 + var pkg_candidate = null + var mod_path = null + var candidate_dir = null + for (i = length(parts) - 1; i >= 1; i--) { + pkg_candidate = text(array(parts, 0, i), '/') + mod_path = text(array(parts, i), '/') if (!mod_path || length(mod_path) == 0) continue - var candidate_dir = get_packages_dir() + '/' + safe_package_path(pkg_candidate) + candidate_dir = get_packages_dir() + '/' + safe_package_path(pkg_candidate) if (fd.is_file(candidate_dir + '/cell.toml')) return {package: pkg_candidate, path: mod_path} @@ -158,8 +163,9 @@ function abs_path_to_package(package_dir) return 'core' } // Also check if core_dir is a symlink pointing to package_dir + var core_target = null if (fd.is_link(core_dir)) { - var core_target = fd.readlink(core_dir) + core_target = fd.readlink(core_dir) if (core_target == package_dir || fd.realpath(core_dir) == package_dir) { return 'core' } @@ -181,9 +187,11 @@ function abs_path_to_package(package_dir) // For local directories (e.g., linked targets), read the package name from cell.toml var _toml_path = package_dir + '/cell.toml' + var content = null + var cfg = null if (fd.is_file(_toml_path)) { - var content = text(fd.slurp(_toml_path)) - var cfg = toml.decode(content) + content = text(fd.slurp(_toml_path)) + cfg = toml.decode(content) if (cfg.package) return cfg.package } @@ -316,12 +324,16 @@ Shop.verify_package_name = function(pkg) { // Convert module package to download URL Shop.get_download_url = function(pkg, commit_hash) { var info = Shop.resolve_package_info(pkg) + var parts = null + var host = null + var user = null + var repo = null if (info == 'gitea') { - var parts = array(pkg, '/') - var host = parts[0] - var user = parts[1] - var repo = parts[2] + parts = array(pkg, '/') + host = parts[0] + user = parts[1] + repo = parts[2] return 'https://' + host + '/' + user + '/' + repo + '/archive/' + commit_hash + '.zip' } @@ -332,12 +344,16 @@ Shop.get_download_url = function(pkg, commit_hash) { // Get the API URL for checking remote git commits Shop.get_api_url = function(pkg) { var info = Shop.resolve_package_info(pkg) + var parts = null + var host = null + var user = null + var repo = null if (info == 'gitea') { - var parts = array(pkg, '/') - var host = parts[0] - var user = parts[1] - var repo = parts[2] + parts = array(pkg, '/') + host = parts[0] + user = parts[1] + repo = parts[2] return 'https://' + host + '/api/v1/repos/' + user + '/' + repo + '/branches/' } @@ -393,9 +409,12 @@ function inject_env(inject) { } // Add capability injections - for (var i = 0; i < length(inject); i++) { - var inj = inject[i] - var key = trim(inj, '$') + var i = 0 + var inj = null + var key = null + for (i = 0; i < length(inject); i++) { + inj = inject[i] + key = trim(inj, '$') if (key == 'fd') env[key] = fd else env[key] = my$_[key] } @@ -409,15 +428,19 @@ function inject_bindings_code(inject) { var runtime_fns = ['logical', 'some', 'every', 'starts_with', 'ends_with', 'actor', 'is_actor', 'log', 'send', 'fallback', 'parallel', 'race', 'sequence'] - for (var i = 0; i < length(runtime_fns); i++) { - var fn = runtime_fns[i] + var i = 0 + var fn = null + var inj = null + var key = null + for (i = 0; i < length(runtime_fns); i++) { + fn = runtime_fns[i] push(lines, `var ${fn} = env["${fn}"];`) } // Capability bindings ($delay, $start, etc.) - for (var i = 0; i < length(inject); i++) { - var inj = inject[i] - var key = trim(inj, '$') + for (i = 0; i < length(inject); i++) { + inj = inject[i] + key = trim(inj, '$') push(lines, `var $${key} = env["${key}"];`) } return text(lines, '\n') @@ -474,26 +497,34 @@ function resolve_mod_fn(path, pkg) { // given a path and a package context // return module info about where it was found function resolve_locator(path, ctx) -{ +{ var explicit = split_explicit_package_import(path) + var explicit_path = null + var fn = null + var core_dir = null + var core_file_path = null + var is_core = null + var scope = null + var alias_path = null + if (explicit) { if (is_internal_path(explicit.path) && ctx && explicit.package != ctx) explicit = null } if (explicit) { - var explicit_path = get_packages_dir() + '/' + safe_package_path(explicit.package) + '/' + explicit.path + explicit_path = get_packages_dir() + '/' + safe_package_path(explicit.package) + '/' + explicit.path if (fd.is_file(explicit_path)) { - var fn = resolve_mod_fn(explicit_path, explicit.package) + fn = resolve_mod_fn(explicit_path, explicit.package) return {path: explicit_path, scope: SCOPE_PACKAGE, symbol: fn} } } // 1. If no context, resolve from core only if (!ctx) { - var core_dir = Shop.get_core_dir() - var core_file_path = core_dir + '/' + path + core_dir = Shop.get_core_dir() + core_file_path = core_dir + '/' + path if (fd.is_file(core_file_path)) { - var fn = resolve_mod_fn(core_file_path, 'core') + fn = resolve_mod_fn(core_file_path, 'core') return {path: core_file_path, scope: SCOPE_CORE, symbol: fn} } return null @@ -502,7 +533,7 @@ function resolve_locator(path, ctx) // check in ctx package // If ctx is an absolute path (starts with /), use it directly // Otherwise, look it up in the packages directory - var ctx_dir + var ctx_dir = null if (starts_with(ctx, '/')) { ctx_dir = ctx } else { @@ -511,10 +542,10 @@ function resolve_locator(path, ctx) var ctx_path = ctx_dir + '/' + path if (fd.is_file(ctx_path)) { - var fn = resolve_mod_fn(ctx_path, ctx) + fn = resolve_mod_fn(ctx_path, ctx) // Check if ctx is the core package (either by name or by path) - var is_core = (ctx == 'core') || (ctx_dir == Shop.get_core_dir()) - var scope = is_core ? SCOPE_CORE : SCOPE_LOCAL + is_core = (ctx == 'core') || (ctx_dir == Shop.get_core_dir()) + scope = is_core ? SCOPE_CORE : SCOPE_LOCAL return {path: ctx_path, scope: scope, symbol: fn} } @@ -524,24 +555,24 @@ function resolve_locator(path, ctx) // check for aliased dependency var alias = pkg_tools.split_alias(ctx, path) if (alias) { - var alias_path = get_packages_dir() + '/' + safe_package_path(alias.package) + '/' + alias.path + alias_path = get_packages_dir() + '/' + safe_package_path(alias.package) + '/' + alias.path if (fd.is_file(alias_path)) { - var fn = resolve_mod_fn(alias_path, ctx) + fn = resolve_mod_fn(alias_path, ctx) return {path: alias_path, scope:SCOPE_PACKAGE, symbol:fn} } } - var package_path = get_packages_dir() + '/' + safe_package_path(path) + var package_path = get_packages_dir() + '/' + safe_package_path(path) if (fd.is_file(package_path)) { - var fn = resolve_mod_fn(package_path, ctx) + fn = resolve_mod_fn(package_path, ctx) return {path: package_path, scope: SCOPE_PACKAGE, symbol: fn} } // 4. Check core as fallback - var core_dir = Shop.get_core_dir() - var core_file_path = core_dir + '/' + path + core_dir = Shop.get_core_dir() + core_file_path = core_dir + '/' + path if (fd.is_file(core_file_path)) { - var fn = resolve_mod_fn(core_file_path, 'core') + fn = resolve_mod_fn(core_file_path, 'core') return {path: core_file_path, scope: SCOPE_CORE, symbol: fn} } @@ -573,7 +604,7 @@ Shop.open_package_dylib = function(pkg) { var link_target = link.get_target(pkg) var resolved_pkg = link_target ? link_target : pkg - var pkg_dir; + var pkg_dir = null if (starts_with(resolved_pkg, '/')) { pkg_dir = resolved_pkg } else { @@ -581,9 +612,11 @@ Shop.open_package_dylib = function(pkg) { } var toml_path = pkg_dir + '/cell.toml' + var content = null + var cfg = null if (fd.is_file(toml_path)) { - var content = text(fd.slurp(toml_path)) - var cfg = toml.decode(content) + content = text(fd.slurp(toml_path)) + cfg = toml.decode(content) if (cfg.dependencies) { arrfor(array(cfg.dependencies), function(alias, i) { var dep_pkg = cfg.dependencies[alias] @@ -606,12 +639,19 @@ Shop.open_package_dylib = function(pkg) { // Core is never loaded as a dynamic library via dlopen function resolve_c_symbol(path, package_context) { var explicit = split_explicit_package_import(path) + var sym = null + var dl_path = null + var _path = null + var core_sym = null + var canon_pkg = null + var mod_name = null + if (explicit) { if (is_internal_path(explicit.path) && package_context && explicit.package != package_context) explicit = null } if (explicit) { - var sym = make_c_symbol(explicit.package, explicit.path) + sym = make_c_symbol(explicit.package, explicit.path) if (os.internal_exists(sym)) { return { symbol: function() { return os.load_internal(sym) }, @@ -622,7 +662,7 @@ function resolve_c_symbol(path, package_context) { } Shop.open_package_dylib(explicit.package) - var dl_path = get_lib_path(explicit.package) + dl_path = get_lib_path(explicit.package) if (open_dls[dl_path] && os.dylib_has_symbol(open_dls[dl_path], sym)) { return { symbol: function() { return os.dylib_symbol(open_dls[dl_path], sym) }, @@ -635,8 +675,8 @@ function resolve_c_symbol(path, package_context) { // If no package context, only check core internal symbols if (!package_context || package_context == 'core') { - var _path = replace(path, '/', '_') - var core_sym = `js_${_path}_use` + _path = replace(path, '/', '_') + core_sym = `js_${_path}_use` if (os.internal_exists(core_sym)) { return { symbol: function() { return os.load_internal(core_sym) }, @@ -648,7 +688,7 @@ function resolve_c_symbol(path, package_context) { } // 1. Check own package first (internal, then dylib) - var sym = make_c_symbol(package_context, path) + sym = make_c_symbol(package_context, path) if (os.internal_exists(sym)) { return { symbol: function() { return os.load_internal(sym) }, @@ -658,7 +698,7 @@ function resolve_c_symbol(path, package_context) { } Shop.open_package_dylib(package_context) - var dl_path = get_lib_path(package_context) + dl_path = get_lib_path(package_context) if (open_dls[dl_path] && os.dylib_has_symbol(open_dls[dl_path], sym)) { return { @@ -674,10 +714,10 @@ function resolve_c_symbol(path, package_context) { // 2. Check aliased package imports (e.g. 'prosperon/sprite') var pkg_alias = get_import_package(path) if (pkg_alias) { - var canon_pkg = get_aliased_package(path, package_context) + canon_pkg = get_aliased_package(path, package_context) if (canon_pkg) { - var mod_name = get_import_name(path) - var sym = make_c_symbol(canon_pkg, mod_name) + mod_name = get_import_name(path) + sym = make_c_symbol(canon_pkg, mod_name) // Check internal first if (os.internal_exists(sym)) { @@ -691,7 +731,7 @@ function resolve_c_symbol(path, package_context) { // Then check dylib Shop.open_package_dylib(canon_pkg) - var dl_path = get_lib_path(canon_pkg) + dl_path = get_lib_path(canon_pkg) if (open_dls[dl_path] && os.dylib_has_symbol(open_dls[dl_path], sym)) { return { symbol: function() { return os.dylib_symbol(open_dls[dl_path], sym) }, @@ -704,7 +744,7 @@ function resolve_c_symbol(path, package_context) { } // 3. Check core internal symbols (core is never a dynamic library) - var core_sym = `js_${replace(path, '/', '_')}_use` + core_sym = `js_${replace(path, '/', '_')}_use` if (os.internal_exists(core_sym)) { return { symbol: function() { return os.load_internal(core_sym) }, @@ -732,31 +772,37 @@ function resolve_module_info(path, package_context) { if (min_scope == 999) return null - var cache_key + var cache_key = null + var real_path = null + var real_info = null + var pkg_alias = null + var canon_pkg = null + var mod_name = null + if (mod_resolve.scope == SCOPE_CORE) { cache_key = 'core/' + path } else if (mod_resolve.scope < 900 && mod_resolve.path) { - var real_path = fd.realpath(mod_resolve.path) + real_path = fd.realpath(mod_resolve.path) if (real_path) { - var real_info = Shop.file_info(real_path) + real_info = Shop.file_info(real_path) if (real_info.package && real_info.name) cache_key = real_info.package + '/' + real_info.name else cache_key = real_path } } - + if (!cache_key) { if (min_scope == SCOPE_CORE) cache_key = 'core/' + path else if (min_scope == SCOPE_LOCAL && package_context) cache_key = package_context + '/' + path else if (min_scope == SCOPE_PACKAGE) { - var pkg_alias = get_import_package(path) + pkg_alias = get_import_package(path) if (pkg_alias) { - var canon_pkg = get_canonical_package(pkg_alias, package_context) + canon_pkg = get_canonical_package(pkg_alias, package_context) if (canon_pkg) { - var mod_name = get_import_name(path) + mod_name = get_import_name(path) cache_key = canon_pkg + '/' + mod_name } else cache_key = path @@ -807,20 +853,26 @@ function execute_module(info) var c_resolve = info.c_resolve var mod_resolve = info.mod_resolve - var used + var used = null + var context = null + var file_info = null + var inject = null + var env = null + var pkg = null + var use_fn = null if (mod_resolve.scope < 900) { - var context = null + context = null if (c_resolve.scope < 900) { context = call_c_module(c_resolve) } // Get file info to determine inject list - var file_info = Shop.file_info(mod_resolve.path) - var inject = Shop.script_inject_for(file_info) - var env = inject_env(inject) - var pkg = file_info.package - var use_fn = make_use_fn(pkg) + file_info = Shop.file_info(mod_resolve.path) + inject = Shop.script_inject_for(file_info) + env = inject_env(inject) + pkg = file_info.package + use_fn = make_use_fn(pkg) // Call with signature: setup_module(args, use, env) // args is null for module loading @@ -942,17 +994,18 @@ Shop.fetch = function(pkg) { // Check if we have the zip cached var zip_blob = get_cached_zip(pkg, commit) + var actual_hash = null if (zip_blob) { // If we have a hash on record, verify it if (expected_hash) { - var actual_hash = text(crypto.blake2(zip_blob), 'h') + actual_hash = text(crypto.blake2(zip_blob), 'h') if (actual_hash == expected_hash) { return { status: 'cached' } } log.console("Zip hash mismatch for " + pkg + ", re-fetching...") } else { // No hash stored yet - compute and store it - var actual_hash = text(crypto.blake2(zip_blob), 'h') + actual_hash = text(crypto.blake2(zip_blob), 'h') lock_entry.zip_hash = actual_hash Shop.save_lock(lock) return { status: 'cached' } @@ -1004,10 +1057,12 @@ Shop.extract = function(pkg) { // Check if already extracted at correct commit var lock = Shop.load_lock() var lock_entry = lock[pkg] + var extracted_commit_file = null + var extracted_commit = null if (lock_entry && lock_entry.commit) { - var extracted_commit_file = target_dir + '/.cell_commit' + extracted_commit_file = target_dir + '/.cell_commit' if (fd.is_file(extracted_commit_file)) { - var extracted_commit = trim(text(fd.slurp(extracted_commit_file))) + extracted_commit = trim(text(fd.slurp(extracted_commit_file))) if (extracted_commit == lock_entry.commit) { // Already extracted at this commit, skip return true @@ -1059,6 +1114,7 @@ Shop.update = function(pkg) { log.console(`checking ${pkg}`) + var new_entry = null if (info == 'local') { // Check if local path exists if (!fd.is_dir(pkg)) { @@ -1066,7 +1122,7 @@ Shop.update = function(pkg) { return null } // Local packages always get a lock entry - var new_entry = { + new_entry = { type: 'local', updated: time.number() } @@ -1089,7 +1145,7 @@ Shop.update = function(pkg) { if (local_commit == remote_commit) return null - var new_entry = { + new_entry = { type: info, commit: remote_commit, updated: time.number() @@ -1114,21 +1170,28 @@ function install_zip(zip_blob, target_dir) { var count = zip.count() var created_dirs = {} - for (var i = 0; i < count; i++) { + var i = 0 + var filename = null + var slash_pos = null + var rel_path = null + var full_path = null + var dir_path = null + var file_data = null + for (i = 0; i < count; i++) { if (zip.is_directory(i)) continue - var filename = zip.get_filename(i) - var slash_pos = search(filename, '/') + filename = zip.get_filename(i) + slash_pos = search(filename, '/') if (slash_pos == null) continue if (slash_pos + 1 >= length(filename)) continue - var rel_path = text(filename, slash_pos + 1) - var full_path = target_dir + '/' + rel_path - var dir_path = fd.dirname(full_path) + rel_path = text(filename, slash_pos + 1) + full_path = target_dir + '/' + rel_path + dir_path = fd.dirname(full_path) if (!created_dirs[dir_path]) { ensure_dir(dir_path) created_dirs[dir_path] = true } - var file_data = zip.slurp(filename) + file_data = zip.slurp(filename) stone(file_data) @@ -1151,14 +1214,16 @@ Shop.remove = function(pkg) { Shop.get = function(pkg) { var lock = Shop.load_lock() - + var info = null + var commit = null + if (!lock[pkg]) { - var info = Shop.resolve_package_info(pkg) + info = Shop.resolve_package_info(pkg) if (!info) { print("Invalid package: " + pkg); disrupt } - - var commit = null + + commit = null if (info != 'local') { commit = fetch_remote_hash(pkg) if (!commit) { @@ -1216,9 +1281,11 @@ function get_package_scripts(package) { var files = pkg_tools.list_files(package) var scripts = [] - - for (var i = 0; i < length(files); i++) { - var file = files[i] + + var i = 0 + var file = null + for (i = 0; i < length(files); i++) { + file = files[i] if (ends_with(file, '.cm') || ends_with(file, '.ce')) { push(scripts, file) } diff --git a/internal/testlib.cm b/internal/testlib.cm index abac1d9f..743db329 100644 --- a/internal/testlib.cm +++ b/internal/testlib.cm @@ -4,19 +4,21 @@ var pkg = use('package') // Check if current directory is a valid cell package function is_valid_package(dir) { - if (!dir) dir = '.' - return fd.is_file(dir + '/cell.toml') + var _dir = dir == null ? '.' : dir + if (!_dir) _dir = '.' + return fd.is_file(_dir + '/cell.toml') } // Get current package name from cell.toml or null function get_current_package_name() { if (!is_valid_package('.')) return null - try { + var _load = function() { var config = pkg.load_config(null) return config.package || 'local' - } catch (e) { + } disruption { return 'local' } + return _load() } // Get the directory for a package @@ -37,9 +39,10 @@ function ensure_dir(path) { var parts = array(path, '/') var current = starts_with(path, '/') ? '/' : '' - for (var i = 0; i < length(parts); i++) { + var i = 0 + for (i = 0; i < length(parts); i++) { if (parts[i] == '') continue - current += parts[i] + '/' + current = current + parts[i] + '/' if (!fd.is_dir(current)) { fd.mkdir(current) } diff --git a/link.cm b/link.cm index 529ac995..d02feb2d 100644 --- a/link.cm +++ b/link.cm @@ -34,7 +34,8 @@ function ensure_dir(path) { if (fd.stat(path).isDirectory) return var parts = array(path, '/') var current = starts_with(path, '/') ? '/' : '' - for (var i = 0; i < length(parts); i++) { + var i = 0 + for (i = 0; i < length(parts); i++) { if (parts[i] == '') continue current = current + parts[i] + '/' if (!fd.stat(current).isDirectory) { @@ -119,8 +120,9 @@ Link.add = function(canonical, target, shop) { // Read the target's cell.toml to find its dependencies var target_path = starts_with(target, '/') ? target : get_package_abs_dir(target) var toml_path = target_path + '/cell.toml' + var _install_deps = null if (fd.is_file(toml_path)) { - var _install_deps = function() { + _install_deps = function() { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg && cfg.dependencies) { diff --git a/package.cm b/package.cm index a10c004b..8483267d 100644 --- a/package.cm +++ b/package.cm @@ -101,11 +101,12 @@ package.alias_to_package = function(name, alias) } // alias is optional -package.add_dependency = function(name, locator, alias = locator) +package.add_dependency = function(name, locator, alias) { + var _alias = alias == null ? locator : alias var config = package.load_config(name) if (!config.dependencies) config.dependencies = {} - config.dependencies[alias] = locator + config.dependencies[_alias] = locator package.save_config(name, config) } @@ -115,10 +116,11 @@ package.remove_dependency = function(name, locator) var config = package.load_config(name) if (!config.dependencies) return + var alias = null if (config.dependencies[locator]) delete config.dependencies[locator] else { - var alias = package.find_alias(name, locator) + alias = package.find_alias(name, locator) if (alias) delete config.dependencies[alias] } @@ -133,8 +135,9 @@ package.find_package_dir = function(file) if (fd.is_file(dir)) dir = fd.dirname(dir) + var toml_path = null while (dir && length(dir) > 0) { - var toml_path = dir + '/cell.toml' + toml_path = dir + '/cell.toml' if (fd.is_file(toml_path)) { return dir } @@ -163,9 +166,11 @@ package.split_alias = function(name, path) if (!config) return null var deps = config.dependencies + var dep_locator = null + var remaining_path = null if (deps && deps[first_part]) { - var dep_locator = deps[first_part] - var remaining_path = text(array(parts, 1), '/') + dep_locator = deps[first_part] + remaining_path = text(array(parts, 1), '/') return { package: dep_locator, path: remaining_path } } return null @@ -208,18 +213,23 @@ package.list_files = function(pkg) { var walk = function(current_dir, current_prefix) { var list = fd.readdir(current_dir) if (!list) return - - for (var i = 0; i < length(list); i++) { - var item = list[i] + + var i = 0 + var item = null + var full_path = null + var rel_path = null + var st = null + for (i = 0; i < length(list); i++) { + item = list[i] if (item == '.' || item == '..') continue - if (starts_with(item, '.')) continue - + if (starts_with(item, '.')) continue + // Skip build directories in root - var full_path = current_dir + "/" + item - var rel_path = current_prefix ? current_prefix + "/" + item : item - - var st = fd.stat(full_path) + full_path = current_dir + "/" + item + rel_path = current_prefix ? current_prefix + "/" + item : item + + st = fd.stat(full_path) if (st.isDirectory) { walk(full_path, rel_path) } else { @@ -237,7 +247,8 @@ package.list_files = function(pkg) { package.list_modules = function(name) { var files = package.list_files(name) var modules = [] - for (var i = 0; i < length(files); i++) { + var i = 0 + for (i = 0; i < length(files); i++) { if (ends_with(files[i], '.cm')) { push(modules, text(files[i], 0, -3)) } @@ -248,7 +259,8 @@ package.list_modules = function(name) { package.list_programs = function(name) { var files = package.list_files(name) var programs = [] - for (var i = 0; i < length(files); i++) { + var i = 0 + for (i = 0; i < length(files); i++) { if (ends_with(files[i], '.ce')) { push(programs, text(files[i], 0, -3)) } @@ -265,14 +277,16 @@ package.get_flags = function(name, flag_type, target) { var flags = [] // Base flags + var base = null + var target_flags = null if (config.compilation && config.compilation[flag_type]) { - var base = config.compilation[flag_type] + base = config.compilation[flag_type] flags = array(flags, filter(array(base, /\s+/), function(f) { return length(f) > 0 })) } - + // Target-specific flags if (target && config.compilation && config.compilation[target] && config.compilation[target][flag_type]) { - var target_flags = config.compilation[target][flag_type] + target_flags = config.compilation[target][flag_type] flags = array(flags, filter(array(target_flags, /\s+/), function(f) { return length(f) > 0 })) } @@ -290,23 +304,36 @@ package.get_c_files = function(name, target, exclude_main) { // Group files by their base name (without target suffix) var groups = {} // base_key -> { generic: file, variants: { target: file } } - for (var i = 0; i < length(files); i++) { - var file = files[i] + var i = 0 + var file = null + var ext = null + var base = null + var name_part = null + var dir_part = null + var dir = null + var is_variant = null + var variant_target = null + var generic_name = null + var t = 0 + var suffix = null + var group_key = null + for (i = 0; i < length(files); i++) { + file = files[i] if (!ends_with(file, '.c') && !ends_with(file, '.cpp')) continue - - var ext = ends_with(file, '.cpp') ? '.cpp' : '.c' - var base = text(file, 0, -length(ext)) - var name_part = fd.basename(base) - var dir_part = fd.dirname(base) - var dir = (dir_part && dir_part != '.') ? dir_part + '/' : '' - + + ext = ends_with(file, '.cpp') ? '.cpp' : '.c' + base = text(file, 0, -length(ext)) + name_part = fd.basename(base) + dir_part = fd.dirname(base) + dir = (dir_part && dir_part != '.') ? dir_part + '/' : '' + // Check for target suffix - var is_variant = false - var variant_target = null - var generic_name = name_part - - for (var t = 0; t < length(known_targets); t++) { - var suffix = '_' + known_targets[t] + is_variant = false + variant_target = null + generic_name = name_part + + for (t = 0; t < length(known_targets); t++) { + suffix = '_' + known_targets[t] if (ends_with(name_part, suffix)) { is_variant = true variant_target = known_targets[t] @@ -315,7 +342,7 @@ package.get_c_files = function(name, target, exclude_main) { } } - var group_key = dir + generic_name + ext + group_key = dir + generic_name + ext if (!groups[group_key]) { groups[group_key] = { generic: null, variants: {} } } @@ -332,18 +359,19 @@ package.get_c_files = function(name, target, exclude_main) { arrfor(array(groups), function(key) { var group = groups[key] var selected = null - + var basename = null + // Prefer target-specific variant if available if (target && group.variants[target]) { selected = group.variants[target] } else if (group.generic) { selected = group.generic } - + if (selected) { // Skip main.c if requested if (exclude_main) { - var basename = fd.basename(selected) + basename = fd.basename(selected) if (basename == 'main.c' || starts_with(basename, 'main_')) return } push(result, selected) diff --git a/test.ce b/test.ce index f92174d4..34ca1b35 100644 --- a/test.ce +++ b/test.ce @@ -8,7 +8,7 @@ var dbg = use('js') // run gc with dbg.gc() -if (!args) args = [] +var _args = args == null ? [] : args var target_pkg = null // null = current package var target_test = null // null = all tests, otherwise specific test file @@ -22,19 +22,20 @@ var actor_test_results = [] // Check if current directory is a valid cell package function is_valid_package(dir) { - if (!dir) dir = '.' - return fd.is_file(dir + '/cell.toml') + var _dir = dir == null ? '.' : dir + return fd.is_file(_dir + '/cell.toml') } // Get current package name from cell.toml or null function get_current_package_name() { if (!is_valid_package('.')) return null - try { + var _load = function() { var config = pkg.load_config(null) return config.package || 'local' - } catch (e) { + } disruption { return 'local' } + return _load() } // Parse arguments @@ -48,16 +49,21 @@ function get_current_package_name() { function parse_args() { var cleaned_args = [] - for (var i = 0; i < length(args); i++) { - if (args[i] == '-g') { + var i = 0 + var name = null + var lock = null + var resolved = null + var test_path = null + for (i = 0; i < length(_args); i++) { + if (_args[i] == '-g') { gc_after_each_test = true } else { - push(cleaned_args, args[i]) + push(cleaned_args, _args[i]) } } - args = cleaned_args + _args = cleaned_args - if (length(args) == 0) { + if (length(_args) == 0) { // cell test - run all tests for current package if (!is_valid_package('.')) { log.console('No cell.toml found in current directory') @@ -67,7 +73,7 @@ function parse_args() { return true } - if (args[0] == 'all') { + if (_args[0] == 'all') { // cell test all - run all tests for current package if (!is_valid_package('.')) { log.console('No cell.toml found in current directory') @@ -77,14 +83,14 @@ function parse_args() { return true } - if (args[0] == 'package') { - if (length(args) < 2) { + if (_args[0] == 'package') { + if (length(_args) < 2) { log.console('Usage: cell test package [test]') log.console(' cell test package all') return false } - if (args[1] == 'all') { + if (_args[1] == 'all') { // cell test package all - run tests from all packages all_pkgs = true log.console('Testing all packages...') @@ -92,18 +98,19 @@ function parse_args() { } // cell test package [test] - var name = args[1] + name = _args[1] // Check if package exists in lock or is a local path - var lock = shop.load_lock() + lock = shop.load_lock() if (lock[name]) { target_pkg = name } else if (starts_with(name, '/') && is_valid_package(name)) { target_pkg = name } else { // Try to resolve as dependency alias from current package + resolved = null if (is_valid_package('.')) { - var resolved = pkg.alias_to_package(null, name) + resolved = pkg.alias_to_package(null, name) if (resolved) { target_pkg = resolved } else { @@ -116,9 +123,9 @@ function parse_args() { } } - if (length(args) >= 3) { + if (length(_args) >= 3) { // cell test package - target_test = args[2] + target_test = _args[2] } log.console(`Testing package: ${target_pkg}`) @@ -126,7 +133,7 @@ function parse_args() { } // cell test tests/suite or cell test - specific test file - var test_path = args[0] + test_path = _args[0] // Normalize path - add tests/ prefix if not present and doesn't start with / if (!starts_with(test_path, 'tests/') && !starts_with(test_path, '/')) { @@ -160,9 +167,10 @@ function ensure_dir(path) { var parts = array(path, '/') var current = starts_with(path, '/') ? '/' : '' - for (var i = 0; i < length(parts); i++) { + var i = 0 + for (i = 0; i < length(parts); i++) { if (parts[i] == '') continue - current += parts[i] + '/' + current = current + parts[i] + '/' if (!fd.is_dir(current)) fd.mkdir(current) } @@ -189,23 +197,29 @@ function collect_actor_tests(package_name, specific_test) { var files = pkg.list_files(package_name) var actor_tests = [] - for (var i = 0; i < length(files); i++) { - var f = files[i] + var i = 0 + var f = null + var test_name = null + var match_name = null + var test_base = null + var match_base = null + for (i = 0; i < length(files); i++) { + f = files[i] // Check if file is in tests/ folder and is a .ce actor if (starts_with(f, "tests/") && ends_with(f, ".ce")) { // If specific test requested, filter if (specific_test) { - var test_name = text(f, 0, -3) // remove .ce - var match_name = specific_test + test_name = text(f, 0, -3) // remove .ce + match_name = specific_test if (!starts_with(match_name, 'tests/')) match_name = 'tests/' + match_name if (!ends_with(match_name, '.ce')) match_name = match_name // Match without extension - var test_base = test_name - var match_base = ends_with(match_name, '.ce') ? text(match_name, 0, -3) : match_name + test_base = test_name + match_base = ends_with(match_name, '.ce') ? text(match_name, 0, -3) : match_name if (test_base != match_base) continue } - push(actor_tests,{ + push(actor_tests, { package: package_name || "local", file: f, path: prefix + '/' + f @@ -229,19 +243,19 @@ function spawn_actor_test(test_info) { actor: null } - try { - // Spawn the actor test - it should send back results + var _spawn = function() { var actor_path = text(test_info.path, 0, -3) // remove .ce entry.actor = $start(actor_path) push(pending_actor_tests, entry) - } catch (e) { + } disruption { entry.status = "failed" - entry.error = { message: `Failed to spawn actor: ${e}` } + entry.error = { message: `Failed to spawn actor` } entry.duration_ns = 0 push(actor_test_results, entry) log.console(` FAIL ${test_name}: `) - log.error(e) + log.error() } + _spawn() } function run_tests(package_name, specific_test) { @@ -260,17 +274,24 @@ function run_tests(package_name, specific_test) { var files = pkg.list_files(package_name) var test_files = [] - for (var i = 0; i < length(files); i++) { - var f = files[i] + var i = 0 + var f = null + var test_name = null + var match_name = null + var match_base = null + var mod_path = null + var file_result = null + for (i = 0; i < length(files); i++) { + f = files[i] // Check if file is in tests/ folder and is a .cm module (not .ce - those are actor tests) if (starts_with(f, "tests/") && ends_with(f, ".cm")) { // If specific test requested, filter if (specific_test) { - var test_name = text(f, 0, -3) // remove .cm - var match_name = specific_test + test_name = text(f, 0, -3) // remove .cm + match_name = specific_test if (!starts_with(match_name, 'tests/')) match_name = 'tests/' + match_name // Match without extension - var match_base = ends_with(match_name, '.cm') ? text(match_name, 0, -3) : match_name + match_base = ends_with(match_name, '.cm') ? text(match_name, 0, -3) : match_name if (test_name != match_base) continue } push(test_files, f) @@ -282,24 +303,31 @@ function run_tests(package_name, specific_test) { else log.console(`Running tests for local package`) } - for (var i = 0; i < length(test_files); i++) { - var f = test_files[i] - var mod_path = text(f, 0, -3) // remove .cm + var _load_file = null + for (i = 0; i < length(test_files); i++) { + f = test_files[i] + mod_path = text(f, 0, -3) // remove .cm - var file_result = { + file_result = { name: f, tests: [], passed: 0, failed: 0 } - try { - var test_mod - // For local packages (null), use the current directory as package context + _load_file = function() { + var test_mod = null var use_pkg = package_name ? package_name : fd.realpath('.') test_mod = shop.use(mod_path, use_pkg) var tests = [] + var j = 0 + var t = null + var test_entry = null + var start_time = null + var _test_error = null + var end_time = null + var _run_one = null if (is_function(test_mod)) { push(tests, {name: 'main', fn: test_mod}) } else if (is_object(test_mod)) { @@ -312,50 +340,55 @@ function run_tests(package_name, specific_test) { if (length(tests) > 0) { log.console(` ${f}`) - for (var j = 0; j < length(tests); j++) { - var t = tests[j] - var test_entry = { + for (j = 0; j < length(tests); j++) { + t = tests[j] + test_entry = { package: pkg_result.package, test: t.name, status: "pending", duration_ns: 0 } - var start_time = time.number() - try { + start_time = time.number() + _test_error = null + _run_one = function() { var ret = t.fn() if (is_text(ret)) { - throw Error(ret) + _test_error = Error(ret) + disrupt } else if (ret && (is_text(ret.message) || is_proto(ret, Error))) { - throw ret + _test_error = ret + disrupt } test_entry.status = "passed" log.console(` PASS ${t.name}`) pkg_result.passed++ file_result.passed++ - } catch (e) { + } disruption { + var e = _test_error test_entry.status = "failed" test_entry.error = { message: e, - stack: e.stack || "" + stack: (e && e.stack) ? e.stack : "" } - if (e.name) test_entry.error.name = e.name + if (e && e.name) test_entry.error.name = e.name if (is_object(e) && e.message) { - test_entry.error.message = e.message + test_entry.error.message = e.message } log.console(` FAIL ${t.name} ${test_entry.error.message}`) if (test_entry.error.stack) { - log.console(` ${text(array(test_entry.error.stack, '\n'), '\n ')}`) + log.console(` ${text(array(test_entry.error.stack, '\n'), '\n ')}`) } pkg_result.failed++ file_result.failed++ } - var end_time = time.number() + _run_one() + end_time = time.number() test_entry.duration_ns = round((end_time - start_time) * 1000000000) push(file_result.tests, test_entry) @@ -366,15 +399,15 @@ function run_tests(package_name, specific_test) { } } - } catch (e) { - log.console(` Error loading ${f}: ${e}`) - var test_entry = { + } disruption { + var test_entry = { package: pkg_result.package, test: "load_module", status: "failed", duration_ns: 0, - error: { message: `Error loading module: ${e}` } + error: { message: `Error loading module` } } + log.console(` Error loading ${f}`) push(file_result.tests, test_entry) pkg_result.failed++ file_result.failed++ @@ -383,6 +416,7 @@ function run_tests(package_name, specific_test) { dbg.gc() } } + _load_file() push(pkg_result.files, file_result) } return pkg_result @@ -390,6 +424,8 @@ function run_tests(package_name, specific_test) { var all_results = [] var all_actor_tests = [] +var packages = null +var i = 0 if (all_pkgs) { // Run local first if we're in a valid package @@ -399,8 +435,8 @@ if (all_pkgs) { } // Then all packages in lock - var packages = shop.list_packages() - for (var i = 0; i < length(packages); i++) { + packages = shop.list_packages() + for (i = 0; i < length(packages); i++) { push(all_results, run_tests(packages[i], null)) all_actor_tests = array(all_actor_tests, collect_actor_tests(packages[i], null)) } @@ -412,7 +448,7 @@ if (all_pkgs) { // Spawn actor tests if any if (length(all_actor_tests) > 0) { log.console(`Running ${length(all_actor_tests)} actor test(s)...`) - for (var i = 0; i < length(all_actor_tests); i++) { + for (i = 0; i < length(all_actor_tests); i++) { spawn_actor_test(all_actor_tests[i]) } } @@ -421,7 +457,10 @@ if (length(all_actor_tests) > 0) { function handle_actor_message(msg) { var sender = msg.$sender var found_idx = -1 - for (var i = 0; i < length(pending_actor_tests); i++) { + var i = 0 + var res = null + var entry = null + for (i = 0; i < length(pending_actor_tests); i++) { if (pending_actor_tests[i].actor == sender) { found_idx = i break @@ -445,9 +484,9 @@ function handle_actor_message(msg) { results = [msg] } - for (var i = 0; i < length(results); i++) { - var res = results[i] || {} - var entry = { + for (i = 0; i < length(results); i++) { + res = results[i] || {} + entry = { package: base_entry.package, file: base_entry.file, test: res.test || base_entry.test + (length(results) > 1 ? `#${i+1}` : ""), @@ -481,18 +520,22 @@ function handle_actor_message(msg) { function check_timeouts() { var now = time.number() var timed_out = [] + var i = 0 + var entry = null + var elapsed_ms = null + var idx = null - for (var i = length(pending_actor_tests) - 1; i >= 0; i--) { - var entry = pending_actor_tests[i] - var elapsed_ms = (now - entry.start_time) * 1000 + for (i = length(pending_actor_tests) - 1; i >= 0; i--) { + entry = pending_actor_tests[i] + elapsed_ms = (now - entry.start_time) * 1000 if (elapsed_ms > ACTOR_TEST_TIMEOUT) { push(timed_out, i) } } - for (var i = 0; i < length(timed_out); i++) { - var idx = timed_out[i] - var entry = pending_actor_tests[idx] + for (i = 0; i < length(timed_out); i++) { + idx = timed_out[i] + entry = pending_actor_tests[idx] pending_actor_tests = array(array(pending_actor_tests, 0, idx), array(pending_actor_tests, idx + 1)) entry.status = "failed" @@ -519,11 +562,17 @@ function check_completion() { } function finalize_results() { + var i = 0 + var j = 0 + var r = null + var pkg_result = null + var file_result = null + // Add actor test results to all_results - for (var i = 0; i < length(actor_test_results); i++) { - var r = actor_test_results[i] - var pkg_result = null - for (var j = 0; j < length(all_results); j++) { + for (i = 0; i < length(actor_test_results); i++) { + r = actor_test_results[i] + pkg_result = null + for (j = 0; j < length(all_results); j++) { if (all_results[j].package == r.package) { pkg_result = all_results[j] break @@ -534,8 +583,8 @@ function finalize_results() { push(all_results, pkg_result) } - var file_result = null - for (var j = 0; j < length(pkg_result.files); j++) { + file_result = null + for (j = 0; j < length(pkg_result.files); j++) { if (pkg_result.files[j].name == r.file) { file_result = pkg_result.files[j] break @@ -559,7 +608,7 @@ function finalize_results() { // Calculate totals var totals = { total: 0, passed: 0, failed: 0 } - for (var i = 0; i < length(all_results); i++) { + for (i = 0; i < length(all_results); i++) { totals.total += all_results[i].total totals.passed += all_results[i].passed totals.failed += all_results[i].failed @@ -573,10 +622,10 @@ function finalize_results() { } // If no actor tests, finalize immediately -var totals +var totals = null if (length(all_actor_tests) == 0) { totals = { total: 0, passed: 0, failed: 0 } - for (var i = 0; i < length(all_results); i++) { + for (i = 0; i < length(all_results); i++) { totals.total += all_results[i].total totals.passed += all_results[i].passed totals.failed += all_results[i].failed @@ -593,6 +642,16 @@ function generate_reports(totals) { var timestamp = text(floor(time.number())) var report_dir = shop.get_reports_dir() + '/test_' + timestamp ensure_dir(report_dir) + var i = 0 + var j = 0 + var k = 0 + var pkg_res = null + var f = null + var status = null + var t = null + var dur = null + var pkg_tests = null + var json_path = null var txt_report = `TEST REPORT Date: ${time.text(time.number())} @@ -600,53 +659,53 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed} === SUMMARY === ` - for (var i = 0; i < length(all_results); i++) { - var pkg_res = all_results[i] + for (i = 0; i < length(all_results); i++) { + pkg_res = all_results[i] if (pkg_res.total == 0) continue - txt_report += `Package: ${pkg_res.package}\n` - for (var j = 0; j < length(pkg_res.files); j++) { - var f = pkg_res.files[j] - var status = f.failed == 0 ? "PASS" : "FAIL" - txt_report += ` [${status}] ${f.name} (${f.passed}/${length(f.tests)})\n` + txt_report = txt_report + `Package: ${pkg_res.package}\n` + for (j = 0; j < length(pkg_res.files); j++) { + f = pkg_res.files[j] + status = f.failed == 0 ? "PASS" : "FAIL" + txt_report = txt_report + ` [${status}] ${f.name} (${f.passed}/${length(f.tests)})\n` } } - txt_report += `\n=== FAILURES ===\n` + txt_report = txt_report + `\n=== FAILURES ===\n` var has_failures = false - for (var i = 0; i < length(all_results); i++) { - var pkg_res = all_results[i] - for (var j = 0; j < length(pkg_res.files); j++) { - var f = pkg_res.files[j] - for (var k = 0; k < length(f.tests); k++) { - var t = f.tests[k] + for (i = 0; i < length(all_results); i++) { + pkg_res = all_results[i] + for (j = 0; j < length(pkg_res.files); j++) { + f = pkg_res.files[j] + for (k = 0; k < length(f.tests); k++) { + t = f.tests[k] if (t.status == "failed") { has_failures = true - txt_report += `FAIL: ${pkg_res.package} :: ${f.name} :: ${t.test}\n` + txt_report = txt_report + `FAIL: ${pkg_res.package} :: ${f.name} :: ${t.test}\n` if (t.error) { - txt_report += ` Message: ${t.error.message}\n` + txt_report = txt_report + ` Message: ${t.error.message}\n` if (t.error.stack) { - txt_report += ` Stack:\n${text(array(array(t.error.stack, '\n'), l => ` ${l}`), '\n')}\n` + txt_report = txt_report + ` Stack:\n${text(array(array(t.error.stack, '\n'), l => ` ${l}`), '\n')}\n` } } - txt_report += `\n` + txt_report = txt_report + `\n` } } } } - if (!has_failures) txt_report += `None\n` + if (!has_failures) txt_report = txt_report + `None\n` - txt_report += `\n=== DETAILED RESULTS ===\n` - for (var i = 0; i < length(all_results); i++) { - var pkg_res = all_results[i] + txt_report = txt_report + `\n=== DETAILED RESULTS ===\n` + for (i = 0; i < length(all_results); i++) { + pkg_res = all_results[i] if (pkg_res.total == 0) continue - for (var j = 0; j < length(pkg_res.files); j++) { - var f = pkg_res.files[j] - for (var k = 0; k < length(f.tests); k++) { - var t = f.tests[k] - var dur = `${t.duration_ns || 0}ns` - var status = t.status == "passed" ? "PASS" : "FAIL" - txt_report += `[${status}] ${pkg_res.package} ${t.test} (${dur})\n` + for (j = 0; j < length(pkg_res.files); j++) { + f = pkg_res.files[j] + for (k = 0; k < length(f.tests); k++) { + t = f.tests[k] + dur = `${t.duration_ns || 0}ns` + status = t.status == "passed" ? "PASS" : "FAIL" + txt_report = txt_report + `[${status}] ${pkg_res.package} ${t.test} (${dur})\n` } } } @@ -655,19 +714,19 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed} log.console(`Report written to ${report_dir}/test.txt`) // Generate JSON per package - for (var i = 0; i < length(all_results); i++) { - var pkg_res = all_results[i] + for (i = 0; i < length(all_results); i++) { + pkg_res = all_results[i] if (pkg_res.total == 0) continue - var pkg_tests = [] - for (var j = 0; j < length(pkg_res.files); j++) { - var f = pkg_res.files[j] - for (var k = 0; k < length(f.tests); k++) { + pkg_tests = [] + for (j = 0; j < length(pkg_res.files); j++) { + f = pkg_res.files[j] + for (k = 0; k < length(f.tests); k++) { push(pkg_tests, f.tests[k]) } } - var json_path = `${report_dir}/${replace(pkg_res.package, /\//, '_')}.json` + json_path = `${report_dir}/${replace(pkg_res.package, /\//, '_')}.json` fd.slurpwrite(json_path, stone(blob(json.encode(pkg_tests)))) } } diff --git a/time.cm b/time.cm index bf71b9a7..a54ba85b 100644 --- a/time.cm +++ b/time.cm @@ -124,14 +124,15 @@ function time_number(_rec) { var dst = r.dst ? 1 : 0 var yday = r.yday || 0 + var i = 0 if (year > time.epoch) { - var i = time.epoch + 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 + i = time.epoch - 1 while (i > year) { c = c + time.day * time.yearsize(i) i = i - 1 diff --git a/toml.cm b/toml.cm index eb356784..bcea35fc 100644 --- a/toml.cm +++ b/toml.cm @@ -26,23 +26,33 @@ function parse_toml(toml_text) { var current_section = result var current_section_name = '' - for (var i = 0; i < length(lines); i++) { - var line = trim(lines[i]) + var i = 0 + var line = null + var inner = null + var section_path = null + var j = 0 + var key = null + var eq_index = null + var key_part = null + var value = null + var unquoted = null + for (i = 0; i < length(lines); i++) { + line = trim(lines[i]) if (line == null) line = lines[i] // Skip empty lines and comments if (!line || starts_with(line, '#')) continue // Section header if (starts_with(line, '[') && ends_with(line, ']')) { - var inner = text(line, 1, -1) - var section_path = parse_key_path(inner) + inner = text(line, 1, -1) + section_path = parse_key_path(inner) if (section_path == null) return null current_section = result current_section_name = text(section_path, '.') - for (var j = 0; j < length(section_path); j++) { - var key = section_path[j] + for (j = 0; j < length(section_path); j++) { + key = section_path[j] // Only treat null as "missing"; do not clobber false/0/"" if (current_section[key] == null) { @@ -58,18 +68,18 @@ function parse_toml(toml_text) { } // Key-value pair - var eq_index = search(line, '=') + eq_index = search(line, '=') if (eq_index != null && eq_index > 0) { - var key_part = trim(text(line, 0, eq_index)) - var value = trim(text(line, eq_index + 1)) + key_part = trim(text(line, 0, eq_index)) + value = trim(text(line, eq_index + 1)) if (key_part == null) key_part = trim(text(line, 0, eq_index)) if (value == null) value = trim(text(line, eq_index + 1)) - var key = parse_key(key_part) + key = parse_key(key_part) if (key == null) return null if (starts_with(value, '"') && ends_with(value, '"')) { - var unquoted = text(value, 1, -1) + unquoted = text(value, 1, -1) current_section[key] = toml_unescape(unquoted) if (current_section[key] == null) return null } else if (starts_with(value, '[') && ends_with(value, ']')) { @@ -91,9 +101,9 @@ function parse_toml(toml_text) { function parse_key(str) { if (!is_text(str)) return null - + var inner = null if (starts_with(str, '"') && ends_with(str, '"')) { - var inner = text(str, 1, -1) + inner = text(str, 1, -1) return toml_unescape(inner) } return str @@ -107,12 +117,15 @@ function parse_key_path(str) { var current = '' var in_quote = false - for (var i = 0; i < length(str); i++) { - var c = str[i] + var i = 0 + var c = null + var piece = null + for (i = 0; i < length(str); i++) { + c = str[i] if (c == '"' && (i == 0 || str[i - 1] != '\\')) { in_quote = !in_quote } else if (c == '.' && !in_quote) { - var piece = trim(current) + piece = trim(current) if (piece == null) piece = trim(current) push(parts, parse_key(piece)) current = '' @@ -140,14 +153,17 @@ function parse_array(str) { var current = '' var in_quotes = false - for (var i = 0; i < length(s); i++) { - var ch = s[i] + var i = 0 + var ch = null + var piece = null + for (i = 0; i < length(s); i++) { + ch = s[i] if (ch == '"' && (i == 0 || s[i - 1] != '\\')) { in_quotes = !in_quotes current = current + ch } else if (ch == ',' && !in_quotes) { - var piece = trim(current) + piece = trim(current) if (piece == null) piece = trim(current) push(items, parse_value(piece)) current = '' @@ -181,12 +197,14 @@ function encode_toml(obj) { var result = [] function encode_value(value) { + var items = null + var i = 0 if (is_text(value)) return '"' + toml_escape(value) + '"' if (is_logical(value)) return value ? 'true' : 'false' if (is_number(value)) return text(value) if (is_array(value)) { - var items = [] - for (var i = 0; i < length(value); i++) push(items, encode_value(value[i])) + items = [] + for (i = 0; i < length(value); i++) push(items, encode_value(value[i])) return '[' + text(items, ', ') + ']' } return text(value) @@ -201,29 +219,41 @@ function encode_toml(obj) { // First pass: encode top-level simple values var keys = array(obj) - for (var i = 0; i < length(keys); i++) { - var key = keys[i] - var value = obj[key] + var i = 0 + var key = null + var value = null + for (i = 0; i < length(keys); i++) { + key = keys[i] + value = obj[key] if (!is_object(value)) push(result, quote_key(key) + ' = ' + encode_value(value)) } // Second pass: encode nested objects function encode_section(o, path) { var keys = array(o) - for (var i = 0; i < length(keys); i++) { - var key = keys[i] - var value = o[key] + var i = 0 + var key = null + var value = null + var quoted = null + var section_path = null + var section_keys = null + var j = 0 + var sk = null + var sv = null + for (i = 0; i < length(keys); i++) { + key = keys[i] + value = o[key] if (is_object(value)) { - var quoted = quote_key(key) - var section_path = path ? path + '.' + quoted : quoted + quoted = quote_key(key) + section_path = path ? path + '.' + quoted : quoted push(result, '[' + section_path + ']') // Direct properties - var section_keys = array(value) - for (var j = 0; j < length(section_keys); j++) { - var sk = section_keys[j] - var sv = value[sk] + section_keys = array(value) + for (j = 0; j < length(section_keys); j++) { + sk = section_keys[j] + sv = value[sk] if (!is_object(sv)) push(result, quote_key(sk) + ' = ' + encode_value(sv)) }