rm top level fns

This commit is contained in:
2026-01-15 17:56:33 -06:00
parent 5018901acb
commit ac91495679
30 changed files with 340 additions and 451 deletions

View File

@@ -25,8 +25,8 @@ def MAX_BATCH_SIZE = 100000000 // 100M iterations max per batch
// Statistical functions
function median(arr) {
if (arr.length == 0) return 0
var sorted = arr.slice().sort(function(a, b) { return a - b })
var mid = number.floor(arr.length / 2)
var sorted = sort(arr)
var mid = floor(arr.length / 2)
if (arr.length % 2 == 0) {
return (sorted[mid - 1] + sorted[mid]) / 2
}
@@ -54,8 +54,8 @@ function stddev(arr, mean_val) {
function percentile(arr, p) {
if (arr.length == 0) return 0
var sorted = arr.slice().sort(function(a, b) { return a - b })
var idx = number.floor(arr.length * p / 100)
var sorted = sort(arr)
var idx = floor(arr.length * p / 100)
if (idx >= arr.length) idx = arr.length - 1
return sorted[idx]
}
@@ -228,7 +228,7 @@ function calibrate_batch_size(bench_fn, is_batch) {
if (dt > 0 && dt < TARGET_SAMPLE_NS && is_number(n) && is_number(dt)) {
var calc = n * TARGET_SAMPLE_NS / dt
if (is_number(calc) && calc > 0) {
var target_n = number.floor(calc)
var target_n = floor(calc)
// Check if floor returned a valid number
if (is_number(target_n) && target_n > 0) {
if (target_n > MAX_BATCH_SIZE) target_n = MAX_BATCH_SIZE
@@ -376,20 +376,20 @@ function run_single_bench(bench_fn, bench_name) {
// Calculate ops/s from median
var ops_per_sec = 0
if (median_ns > 0) {
ops_per_sec = number.floor(1000000000 / median_ns)
ops_per_sec = floor(1000000000 / median_ns)
}
return {
name: bench_name,
batch_size: batch_size,
samples: SAMPLES,
mean_ns: number.round(mean_ns),
median_ns: number.round(median_ns),
min_ns: number.round(min_ns),
max_ns: number.round(max_ns),
stddev_ns: number.round(stddev_ns),
p95_ns: number.round(p95_ns),
p99_ns: number.round(p99_ns),
mean_ns: round(mean_ns),
median_ns: round(median_ns),
min_ns: round(min_ns),
max_ns: round(max_ns),
stddev_ns: round(stddev_ns),
p95_ns: round(p95_ns),
p99_ns: round(p99_ns),
ops_per_sec: ops_per_sec
}
}
@@ -397,17 +397,17 @@ function run_single_bench(bench_fn, bench_name) {
// Format nanoseconds for display
function format_ns(ns) {
if (ns < 1000) return `${ns}ns`
if (ns < 1000000) return `${number.round(ns / 1000 * 100) / 100}µs`
if (ns < 1000000000) return `${number.round(ns / 1000000 * 100) / 100}ms`
return `${number.round(ns / 1000000000 * 100) / 100}s`
if (ns < 1000000) return `${round(ns / 1000 * 100) / 100}µs`
if (ns < 1000000000) return `${round(ns / 1000000 * 100) / 100}ms`
return `${round(ns / 1000000000 * 100) / 100}s`
}
// Format ops/sec for display
function format_ops(ops) {
if (ops < 1000) return `${ops} ops/s`
if (ops < 1000000) return `${number.round(ops / 1000 * 100) / 100}K ops/s`
if (ops < 1000000000) return `${number.round(ops / 1000000 * 100) / 100}M ops/s`
return `${number.round(ops / 1000000000 * 100) / 100}G ops/s`
if (ops < 1000000) return `${round(ops / 1000 * 100) / 100}K ops/s`
if (ops < 1000000000) return `${round(ops / 1000000 * 100) / 100}M ops/s`
return `${round(ops / 1000000000 * 100) / 100}G ops/s`
}
// Run benchmarks for a package
@@ -525,7 +525,7 @@ log.console(`Benchmarks: ${total_benches} total`)
// Generate reports
function generate_reports() {
var timestamp = text(number.floor(time.number()))
var timestamp = text(floor(time.number()))
var report_dir = shop.get_reports_dir() + '/bench_' + timestamp
testlib.ensure_dir(report_dir)

View File

@@ -1,5 +1,5 @@
function mainThread() {
var maxDepth = number.max(6, Number(arg[0] || 16));
var maxDepth = max(6, Number(arg[0] || 16));
var stretchDepth = maxDepth + 1;
var check = itemCheck(bottomUpTree(stretchDepth));

View File

@@ -3,7 +3,7 @@ var math = use('math/radians')
function eratosthenes (n) {
var sieve = blob(n, true)
var sqrtN = number.whole(math.sqrt(n));
var sqrtN = whole(math.sqrt(n));
for (i = 2; i <= sqrtN; i++)
if (sieve.read_logical(i))

View File

@@ -204,7 +204,7 @@ function benchStringOps() {
var joinTime = measureTime(function() {
for (var i = 0; i < iterations.complex; i++) {
var result = strings.join(",");
var result = text(strings, ",");
}
});
@@ -239,7 +239,7 @@ function benchArithmetic() {
var result = 1.5;
for (var i = 0; i < iterations.simple; i++) {
result = math.sine(result) + math.cosine(i * 0.01);
result = math.sqrt(number.abs(result)) + 0.1;
result = math.sqrt(abs(result)) + 0.1;
}
});

View File

@@ -44,8 +44,8 @@ for (var i = 0; i < 100; i++) {
// Calculate statistics
function getStats(arr) {
def avg = arr.reduce((a, b) => a + b) / arr.length;
def min = number.min(...arr);
def max = number.max(...arr);
def min = min(...arr);
def max = max(...arr);
return { avg, min, max };
}

View File

@@ -63,7 +63,7 @@ def benchmarks = [
{
name: "Large Array (1k numbers)",
// A thousand random numbers
data: [ Array.from({length:1000}, (_, i) => i * 0.5) ],
data: [ array(1000, i => i *0.5) ],
iterations: 1000
},
{

View File

@@ -98,7 +98,7 @@ def benchmarks = [
},
{
name: "large_array",
data: [ Array.from({length:1000}, (_, i) => i) ],
data: [ array(1000, i => i) ],
iterations: 1000
},
];
@@ -171,13 +171,13 @@ var bench = benchmarks.find(b => b.name == scenario_name);
if (!lib) {
log.console('Unknown library:', lib_name);
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
log.console('Available libraries:', text(libraries.map(l => l.name), ', '));
$stop()
}
if (!bench) {
log.console('Unknown scenario:', scenario_name);
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
log.console('Available scenarios:', text(benchmarks.map(b => b.name), ', '));
$stop()
}

View File

@@ -80,7 +80,7 @@ if (!target) {
if (target && !build.has_target(target)) {
log.error('Invalid target: ' + target)
log.console('Available targets: ' + build.list_targets().join(', '))
log.console('Available targets: ' + text(build.list_targets(), ', '))
$stop()
}

View File

@@ -197,7 +197,7 @@ Build.build_package = function(pkg, target = Build.detect_host_target(), exclude
// Compute link key from all inputs that affect the dylib output
function compute_link_key(objects, ldflags, target_ldflags, target, cc) {
// Sort objects for deterministic hash
var sorted_objects = objects.slice().sort()
var sorted_objects = sort(objects)
// Build a string representing all link inputs
var parts = []
@@ -214,7 +214,7 @@ function compute_link_key(objects, ldflags, target_ldflags, target, cc) {
parts.push('target_ldflag:' + target_ldflags[i])
}
return content_hash(parts.join('\n'))
return content_hash(text(parts, '\n'))
}
// Build a dynamic library for a package
@@ -372,7 +372,7 @@ Build.build_static = function(packages, target = Build.detect_host_target(), out
var pkg_dir = shop.get_package_dir(pkg)
// Deduplicate based on the entire LDFLAGS string for this package
var ldflags_key = pkg + ':' + ldflags.join(' ')
var ldflags_key = pkg + ':' + text(ldflags, ' ')
if (!seen_flags[ldflags_key]) {
seen_flags[ldflags_key] = true
for (var j = 0; j < ldflags.length; j++) {

View File

@@ -268,7 +268,7 @@ function stat(path) {
// Get search paths
function searchpath() {
return mounts.slice()
return array(mounts)
}
// Mount a package using the shop system

View File

@@ -97,7 +97,7 @@ try {
// Skip the first directory (repo-commit prefix)
parts.shift()
var rel_path = parts.join('/')
var rel_path = text(parts, '/')
var full_path = target_path + '/' + rel_path
var dir_path = full_path.substring(0, full_path.lastIndexOf('/'))

View File

@@ -162,7 +162,7 @@ switch (command) {
'reply_timeout', 'actor_max', 'stack_max'
]
if (!valid_system_keys.includes(path[1])) {
log.error("Invalid system key. Valid keys: " + valid_system_keys.join(', '))
log.error("Invalid system key. Valid keys: " + text(valid_system_keys, ', '))
$stop()
return
}

View File

@@ -35,7 +35,7 @@ cell hello
## Standard Library
- [text](library/text.md) — string manipulation
- [number](library/number.md) — numeric operations
- [number](library/number.md) — numeric operations (functions are global: `floor()`, `max()`, etc.)
- [array](library/array.md) — array utilities
- [object](library/object.md) — object utilities
- [blob](library/blob.md) — binary data

View File

@@ -46,98 +46,98 @@ number("0xff", "j") // 255
## Methods
### number.abs(n)
### abs(n)
Absolute value.
```javascript
number.abs(-5) // 5
number.abs(5) // 5
abs(-5) // 5
abs(5) // 5
```
### number.sign(n)
### sign(n)
Returns -1, 0, or 1.
```javascript
number.sign(-5) // -1
number.sign(0) // 0
number.sign(5) // 1
sign(-5) // -1
sign(0) // 0
sign(5) // 1
```
### number.floor(n, place)
### floor(n, place)
Round down.
```javascript
number.floor(4.9) // 4
number.floor(4.567, 2) // 4.56
floor(4.9) // 4
floor(4.567, 2) // 4.56
```
### number.ceiling(n, place)
### ceiling(n, place)
Round up.
```javascript
number.ceiling(4.1) // 5
number.ceiling(4.123, 2) // 4.13
ceiling(4.1) // 5
ceiling(4.123, 2) // 4.13
```
### number.round(n, place)
### round(n, place)
Round to nearest.
```javascript
number.round(4.5) // 5
number.round(4.567, 2) // 4.57
round(4.5) // 5
round(4.567, 2) // 4.57
```
### number.trunc(n, place)
### trunc(n, place)
Truncate toward zero.
```javascript
number.trunc(4.9) // 4
number.trunc(-4.9) // -4
trunc(4.9) // 4
trunc(-4.9) // -4
```
### number.whole(n)
### whole(n)
Get the integer part.
```javascript
number.whole(4.9) // 4
number.whole(-4.9) // -4
whole(4.9) // 4
whole(-4.9) // -4
```
### number.fraction(n)
### fraction(n)
Get the fractional part.
```javascript
number.fraction(4.75) // 0.75
fraction(4.75) // 0.75
```
### number.min(...values)
### min(...values)
Return the smallest value.
```javascript
number.min(3, 1, 4, 1, 5) // 1
min(3, 1, 4, 1, 5) // 1
```
### number.max(...values)
### max(...values)
Return the largest value.
```javascript
number.max(3, 1, 4, 1, 5) // 5
max(3, 1, 4, 1, 5) // 5
```
### number.remainder(dividend, divisor)
### remainder(dividend, divisor)
Compute remainder.
```javascript
number.remainder(17, 5) // 2
remainder(17, 5) // 2
```

View File

@@ -84,6 +84,6 @@ if (downloaded_count > 0) parts.push(`${text(downloaded_count)} downloaded`)
if (cached_count > 0) parts.push(`${text(cached_count)} cached`)
if (fail_count > 0) parts.push(`${text(fail_count)} failed`)
if (parts.length == 0) parts.push("nothing to fetch")
log.console("Fetch complete: " + parts.join(", "))
log.console("Fetch complete: " + text(parts, ", "))
$stop()

View File

@@ -118,7 +118,7 @@ function caller_data(depth = 0)
}
function console_rec(line, file, msg) {
return `[${_cell.id.slice(0,5)}] [${file}:${line}]: ${msg}\n`
return `[${text(_cell.id, 0, 5)}] [${file}:${line}]: ${msg}\n`
// time: [${time.text("mb d yyyy h:nn:ss")}]
}

View File

@@ -115,8 +115,8 @@ function split_explicit_package_import(path)
// Find the longest prefix that is an installed package
for (var i = parts.length - 1; i >= 1; i--) {
var pkg_candidate = parts.slice(0, i).join('/')
var mod_path = parts.slice(i).join('/')
var pkg_candidate = text(array(parts, 0, i), '/')
var mod_path = text(array(parts, i), '/')
if (!mod_path || mod_path.length == 0) continue
var candidate_dir = get_packages_dir() + '/' + safe_package_path(pkg_candidate)
@@ -220,7 +220,7 @@ function get_import_name(path)
{
var parts = path.split('/')
if (parts.length < 2) return null
return parts.slice(1).join('/')
return text(array(parts, 1), '/')
}
// Given a path like 'prosperon/sprite' and a package context,
@@ -383,7 +383,7 @@ Shop.get_script_capabilities = function(path) {
function inject_params(inject) {
if (!inject || !inject.length) return ''
return ', ' + inject.join(', ')
return ', ' + text(inject, ', ')
}
function inject_values(inject) {
@@ -707,7 +707,7 @@ function resolve_module_info(path, package_context) {
var c_resolve = resolve_c_symbol(path, package_context) || {scope:999}
var mod_resolve = resolve_locator(path + '.cm', package_context) || {scope:999}
var min_scope = number.min(c_resolve.scope, mod_resolve.scope)
var min_scope = min(c_resolve.scope, mod_resolve.scope)
if (min_scope == 999)
return null
@@ -1104,7 +1104,7 @@ function install_zip(zip_blob, target_dir) {
if (parts.length <= 1) continue
parts.shift()
var rel_path = parts.join('/')
var rel_path = text(parts, '/')
var full_path = target_dir + '/' + rel_path
var dir_path = full_path.substring(0, full_path.lastIndexOf('/'))

View File

@@ -94,7 +94,7 @@ function print_deps(ctx, indent) {
}
if (status.length > 0) {
line += " [" + status.join(", ") + "]"
line += " [" + text(status, ", ") + "]"
}
log.console(line)

View File

@@ -22,7 +22,7 @@ if (args.length < 1) {
log.error(' -t, --target <target> Cross-compile for target platform')
log.error(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.error('')
log.error('Available targets: ' + build.list_targets().join(', '))
log.error('Available targets: ' + text(build.list_targets(), ', '))
$stop()
return
}
@@ -63,7 +63,7 @@ for (var i = 1; i < args.length; i++) {
log.console(' -t, --target <target> Cross-compile for target platform')
log.console(' -b, --buildtype <type> Build type: release, debug, minsize (default: release)')
log.console('')
log.console('Available targets: ' + build.list_targets().join(', '))
log.console('Available targets: ' + text(build.list_targets(), ', '))
$stop()
} else {
log.error('Unknown option: ' + args[i])
@@ -109,7 +109,7 @@ for (var package of packages) {
shop.extract(package)
}
log.console('Building static binary from ' + text(packages.length) + ' packages: ' + packages.join(', '))
log.console('Building static binary from ' + text(packages.length) + ' packages: ' + text(packages, ', '))
try {
var result = build.build_static(packages, target, output_name, buildtype)

View File

@@ -6,9 +6,6 @@ var json = use('json')
var os = use('os')
var link = use('link')
// Cache for loaded configs to avoid toml re-parsing corruption
var config_cache = {}
// Convert package name to a safe directory name
// For absolute paths (local packages), replace / with _
// For remote packages, keep slashes as they use nested directories
@@ -47,10 +44,6 @@ package.load_config = function(name)
{
var config_path = get_path(name) + '/cell.toml'
// Return cached config if available
if (config_cache[config_path])
return config_cache[config_path]
if (!fd.is_file(config_path))
throw Error(`${config_path} does not exist`)
@@ -63,10 +56,6 @@ package.load_config = function(name)
return {}
}
// Deep copy to avoid toml module's shared state bug and cache it
result = json.decode(json.encode(result))
config_cache[config_path] = result
return result
}
@@ -169,7 +158,7 @@ package.split_alias = function(name, path)
var deps = config.dependencies
if (deps && deps[first_part]) {
var dep_locator = deps[first_part]
var remaining_path = parts.slice(1).join('/')
var remaining_path = text(array(parts, 1), '/')
return { package: dep_locator, path: remaining_path }
}
} catch (e) {

View File

@@ -153,7 +153,7 @@ function parallel(requestor_array, throttle, need) {
}
}
def concurrent = throttle ? number.min(throttle, length) : length
def concurrent = throttle ? min(throttle, length) : length
for (var i = 0; i < concurrent; i++) start_one()
return cancel
@@ -238,7 +238,7 @@ function race(requestor_array, throttle, need) {
}
}
def concurrent = throttle ? number.min(throttle, length) : length
def concurrent = throttle ? min(throttle, length) : length
for (var i = 0; i < concurrent; i++) start_one()
return cancel

View File

@@ -14,7 +14,7 @@ rnd.random_fit = function()
rnd.random_whole = function(num)
{
return number.floor(rnd.random() * num)
return floor(rnd.random() * num)
}
rnd.random_range = function(min,max)

View File

@@ -170,7 +170,7 @@ for (var i = 0; i < sorted.length; i++) {
}
if (status_parts.length > 0) {
line += " [" + status_parts.join(", ") + "]"
line += " [" + text(status_parts, ", ") + "]"
}
log.console(line)
@@ -183,10 +183,10 @@ for (var i = 0; i < sorted.length; i++) {
if (cflags.length > 0 || ldflags.length > 0) {
log.console(indent + " Compilation inputs:")
if (cflags.length > 0) {
log.console(indent + " CFLAGS: " + cflags.join(' '))
log.console(indent + " CFLAGS: " + text(cflags, ' '))
}
if (ldflags.length > 0) {
log.console(indent + " LDFLAGS: " + ldflags.join(' '))
log.console(indent + " LDFLAGS: " + text(ldflags, ' '))
}
}
} catch (e) {

View File

@@ -29533,170 +29533,6 @@ fail:
return JS_EXCEPTION;
}
static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// from(items, mapfn = void 0, this_arg = void 0)
JSValueConst items = argv[0], mapfn, this_arg;
JSValueConst args[2];
JSValue iter, r, v, v2, arrayLike, next_method, enum_obj;
int64_t k, len;
int done, mapping;
mapping = FALSE;
mapfn = JS_NULL;
this_arg = JS_NULL;
r = JS_NULL;
arrayLike = JS_NULL;
iter = JS_NULL;
enum_obj = JS_NULL;
next_method = JS_NULL;
if (argc > 1) {
mapfn = argv[1];
if (!JS_IsNull(mapfn)) {
if (check_function(ctx, mapfn))
goto exception;
mapping = 1;
if (argc > 2)
this_arg = argv[2];
}
}
iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
if (JS_IsException(iter))
goto exception;
if (!JS_IsNull(iter) && !JS_IsNull(iter)) {
if (!JS_IsFunction(ctx, iter)) {
JS_ThrowTypeError(ctx, "value is not iterable");
goto exception;
}
if (JS_IsConstructor(ctx, this_val))
r = JS_CallConstructor(ctx, this_val, 0, NULL);
else
r = JS_NewArray(ctx);
if (JS_IsException(r))
goto exception;
enum_obj = JS_GetIterator2(ctx, items, iter);
if (JS_IsException(enum_obj))
goto exception;
next_method = JS_GetProperty(ctx, enum_obj, JS_ATOM_next);
if (JS_IsException(next_method))
goto exception;
for (k = 0;; k++) {
v = JS_IteratorNext(ctx, enum_obj, next_method, 0, NULL, &done);
if (JS_IsException(v))
goto exception;
if (done)
break;
if (mapping) {
args[0] = v;
args[1] = JS_NewInt32(ctx, k);
v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
JS_FreeValue(ctx, v);
v = v2;
if (JS_IsException(v))
goto exception_close;
}
if (JS_DefinePropertyValueInt64(ctx, r, k, v,
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception_close;
}
} else {
arrayLike = JS_ToObject(ctx, items);
if (JS_IsException(arrayLike))
goto exception;
if (js_get_length64(ctx, &len, arrayLike) < 0)
goto exception;
v = JS_NewInt64(ctx, len);
args[0] = v;
if (JS_IsConstructor(ctx, this_val)) {
r = JS_CallConstructor(ctx, this_val, 1, args);
} else {
r = js_array_constructor(ctx, JS_NULL, 1, args);
}
JS_FreeValue(ctx, v);
if (JS_IsException(r))
goto exception;
for(k = 0; k < len; k++) {
v = JS_GetPropertyInt64(ctx, arrayLike, k);
if (JS_IsException(v))
goto exception;
if (mapping) {
args[0] = v;
args[1] = JS_NewInt32(ctx, k);
v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
JS_FreeValue(ctx, v);
v = v2;
if (JS_IsException(v))
goto exception;
}
if (JS_DefinePropertyValueInt64(ctx, r, k, v,
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
}
}
if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
goto exception;
goto done;
exception_close:
JS_IteratorClose(ctx, enum_obj, TRUE);
exception:
JS_FreeValue(ctx, r);
r = JS_EXCEPTION;
done:
JS_FreeValue(ctx, arrayLike);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, enum_obj);
JS_FreeValue(ctx, next_method);
return r;
}
static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue obj, args[1];
int i;
if (JS_IsConstructor(ctx, this_val)) {
args[0] = JS_NewInt32(ctx, argc);
obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
} else {
obj = JS_NewArray(ctx);
}
if (JS_IsException(obj))
return JS_EXCEPTION;
for(i = 0; i < argc; i++) {
if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
JS_PROP_THROW) < 0) {
goto fail;
}
}
if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
fail:
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
return obj;
}
static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int ret;
ret = JS_IsArray(ctx, argv[0]);
if (ret < 0)
return JS_EXCEPTION;
else
return JS_NewBool(ctx, ret);
}
static JSValue js_get_this(JSContext *ctx,
JSValueConst this_val)
{
return JS_DupValue(ctx, this_val);
}
static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
JSValueConst len_val)
{
@@ -29743,13 +29579,6 @@ static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
}
}
static const JSCFunctionListEntry js_array_funcs[] = {
JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
JS_CFUNC_DEF("from", 1, js_array_from ),
JS_CFUNC_DEF("of", 0, js_array_of ),
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
};
static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
{
JSValue val;
@@ -34587,7 +34416,6 @@ done:
static const JSCFunctionListEntry js_regexp_funcs[] = {
JS_CFUNC_DEF("escape", 1, js_regexp_escape ),
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
};
static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
@@ -35779,20 +35607,6 @@ static JSValue js_cell_number_remainder(JSContext *ctx, JSValueConst this_val,
return JS_NewFloat64(ctx, dividend - (trunc(dividend / divisor) * divisor));
}
static const JSCFunctionListEntry js_cell_number_funcs[] = {
JS_CFUNC_DEF("whole", 1, js_cell_number_whole),
JS_CFUNC_DEF("fraction", 1, js_cell_number_fraction),
JS_CFUNC_DEF("floor", 2, js_cell_number_floor),
JS_CFUNC_DEF("ceiling", 2, js_cell_number_ceiling),
JS_CFUNC_DEF("abs", 1, js_cell_number_abs),
JS_CFUNC_DEF("round", 2, js_cell_number_round),
JS_CFUNC_DEF("sign", 1, js_cell_number_sign),
JS_CFUNC_DEF("trunc", 2, js_cell_number_trunc),
JS_CFUNC_DEF("min", 0, js_cell_number_min),
JS_CFUNC_DEF("max", 0, js_cell_number_max),
JS_CFUNC_DEF("remainder", 2, js_cell_number_remainder),
};
/* ----------------------------------------------------------------------------
* text function and sub-functions
* ---------------------------------------------------------------------------- */
@@ -36093,22 +35907,34 @@ static JSValue js_cell_text(JSContext *ctx, JSValueConst this_val,
if (argc == 1)
return JS_DupValue(ctx, arg);
/* text(string, from, to) - substring */
if (argc >= 3) {
int from, to;
JSString *p = JS_VALUE_GET_STRING(arg);
int len = p->len;
JSString *p = JS_VALUE_GET_STRING(arg);
int len = p->len;
/* text(string, from) - substring from index to end */
/* text(string, from, to) - substring */
if (argc >= 2 && (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT ||
JS_VALUE_GET_TAG(argv[1]) == JS_TAG_FLOAT64)) {
int from;
if (JS_ToInt32(ctx, &from, argv[1]))
return JS_NULL;
if (JS_ToInt32(ctx, &to, argv[2]))
return JS_NULL;
/* Adjust negative indices */
/* Adjust negative index */
if (from < 0) from += len;
if (to < 0) to += len;
if (from < 0) from = 0;
if (from > len) from = len;
if (from < 0 || from > to || to > len)
int to = len; /* Default: to end of string */
if (argc >= 3) {
if (JS_ToInt32(ctx, &to, argv[2]))
return JS_NULL;
/* Adjust negative index */
if (to < 0) to += len;
if (to < 0) to = 0;
if (to > len) to = len;
}
if (from > to)
return JS_NULL;
return js_sub_string(ctx, p, from, to);
@@ -36644,15 +36470,6 @@ fail:
return JS_EXCEPTION;
}
static const JSCFunctionListEntry js_cell_text_funcs[] = {
JS_CFUNC_DEF("lower", 1, js_cell_text_lower),
JS_CFUNC_DEF("upper", 1, js_cell_text_upper),
JS_CFUNC_DEF("trim", 2, js_cell_text_trim),
JS_CFUNC_DEF("codepoint", 1, js_cell_text_codepoint),
JS_CFUNC_DEF("search", 3, js_cell_text_search),
JS_CFUNC_DEF("replace", 4, js_cell_text_replace),
};
/* ----------------------------------------------------------------------------
* array function and sub-functions
* ---------------------------------------------------------------------------- */
@@ -37407,14 +37224,6 @@ static JSValue js_cell_array_sort(JSContext *ctx, JSValueConst this_val,
return result;
}
static const JSCFunctionListEntry js_cell_array_funcs[] = {
JS_CFUNC_DEF("reduce", 4, js_cell_array_reduce),
JS_CFUNC_DEF("for", 4, js_cell_array_for),
JS_CFUNC_DEF("find", 4, js_cell_array_find),
JS_CFUNC_DEF("filter", 2, js_cell_array_filter),
JS_CFUNC_DEF("sort", 2, js_cell_array_sort),
};
/* ----------------------------------------------------------------------------
* object function and sub-functions
* ---------------------------------------------------------------------------- */
@@ -37680,10 +37489,6 @@ static JSValue js_cell_fn_apply(JSContext *ctx, JSValueConst this_val,
return result;
}
static const JSCFunctionListEntry js_cell_fn_funcs[] = {
JS_CFUNC_DEF("apply", 2, js_cell_fn_apply),
};
/* ============================================================================
* Blob Intrinsic Type
* ============================================================================ */
@@ -39008,8 +38813,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
ctx->class_proto[JS_CLASS_ARRAY]);
ctx->array_ctor = JS_DupValue(ctx, obj);
JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
countof(js_array_funcs));
/* XXX: create auto_initializer */
{
@@ -39090,17 +38893,14 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
/* Cell Script global functions: text, number, array, object, fn */
{
JSValue text_func = JS_NewCFunction(ctx, js_cell_text, "text", 2);
JS_SetPropertyFunctionList(ctx, text_func, js_cell_text_funcs, countof(js_cell_text_funcs));
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "text", text_func,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JSValue number_func = JS_NewCFunction(ctx, js_cell_number, "number", 2);
JS_SetPropertyFunctionList(ctx, number_func, js_cell_number_funcs, countof(js_cell_number_funcs));
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "number", number_func,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JSValue array_func = JS_NewCFunction(ctx, js_cell_array, "array", 4);
JS_SetPropertyFunctionList(ctx, array_func, js_cell_array_funcs, countof(js_cell_array_funcs));
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "array", array_func,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
@@ -39110,7 +38910,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JSValue fn_obj = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, fn_obj, js_cell_fn_funcs, countof(js_cell_fn_funcs));
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "fn", fn_obj,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
@@ -39186,6 +38985,78 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
JS_NewCFunction(ctx, js_cell_is_proto, "is_proto", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "apply",
JS_NewCFunction(ctx, js_cell_fn_apply, "apply", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "replace",
JS_NewCFunction(ctx, js_cell_text_replace, "replace", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "lower",
JS_NewCFunction(ctx, js_cell_text_lower, "lower", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "upper",
JS_NewCFunction(ctx, js_cell_text_upper, "upper", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "trim",
JS_NewCFunction(ctx, js_cell_text_trim, "trim", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "codepoint",
JS_NewCFunction(ctx, js_cell_text_codepoint, "codepoint", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "search",
JS_NewCFunction(ctx, js_cell_text_search, "search", 3),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "reduce",
JS_NewCFunction(ctx, js_cell_array_reduce, "reduce", 4),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "for",
JS_NewCFunction(ctx, js_cell_array_for, "for", 4),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "find",
JS_NewCFunction(ctx, js_cell_array_find, "find", 4),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "filter",
JS_NewCFunction(ctx, js_cell_array_filter, "filter", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "sort",
JS_NewCFunction(ctx, js_cell_array_sort, "sort", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
/* Number utility functions as globals */
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "whole",
JS_NewCFunction(ctx, js_cell_number_whole, "whole", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "fraction",
JS_NewCFunction(ctx, js_cell_number_fraction, "fraction", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "floor",
JS_NewCFunction(ctx, js_cell_number_floor, "floor", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "ceiling",
JS_NewCFunction(ctx, js_cell_number_ceiling, "ceiling", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "abs",
JS_NewCFunction(ctx, js_cell_number_abs, "abs", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "round",
JS_NewCFunction(ctx, js_cell_number_round, "round", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "sign",
JS_NewCFunction(ctx, js_cell_number_sign, "sign", 1),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "trunc",
JS_NewCFunction(ctx, js_cell_number_trunc, "trunc", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "min",
JS_NewCFunction(ctx, js_cell_number_min, "min", 0),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "max",
JS_NewCFunction(ctx, js_cell_number_max, "max", 0),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "remainder",
JS_NewCFunction(ctx, js_cell_number_remainder, "remainder", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
/* reverse() - reverse an array */
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "reverse",
JS_NewCFunction(ctx, js_cell_reverse, "reverse", 1),

10
test.ce
View File

@@ -336,14 +336,14 @@ function run_tests(package_name, specific_test) {
log.console(` FAIL ${t.name} ${test_entry.error.message}`)
if (test_entry.error.stack) {
log.console(` ${test_entry.error.stack.split('\n').join('\n ')}`)
log.console(` ${text(test_entry.error.stack.split('\n'), '\n ')}`)
}
pkg_result.failed++
file_result.failed++
}
var end_time = time.number()
test_entry.duration_ns = number.round((end_time - start_time) * 1000000000)
test_entry.duration_ns = round((end_time - start_time) * 1000000000)
file_result.tests.push(test_entry)
pkg_result.total++
@@ -415,7 +415,7 @@ function handle_actor_message(msg) {
pending_actor_tests.splice(found_idx, 1)
var end_time = time.number()
var duration_ns = number.round((end_time - base_entry.start_time) * 1000000000)
var duration_ns = round((end_time - base_entry.start_time) * 1000000000)
var results = []
if (is_array(msg)) {
@@ -567,7 +567,7 @@ if (all_actor_tests.length == 0) {
// Generate Reports function
function generate_reports(totals) {
var timestamp = text(number.floor(time.number()))
var timestamp = text(floor(time.number()))
var report_dir = shop.get_reports_dir() + '/test_' + timestamp
ensure_dir(report_dir)
@@ -602,7 +602,7 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
if (t.error) {
txt_report += ` Message: ${t.error.message}\n`
if (t.error.stack) {
txt_report += ` Stack:\n${t.error.stack.split('\n').map(function(l){return ` ${l}`}).join('\n')}\n`
txt_report += ` Stack:\n${text(t.error.stack.split('\n').map(function(l){return ` ${l}`}), '\n')}\n`
}
}
txt_report += `\n`

View File

@@ -24,7 +24,7 @@ function deepCompare(expected, actual, path) {
if (isNaN(expected) && isNaN(actual))
return { passed: true, messages: [] };
var diff = number.abs(expected - actual);
var diff = abs(expected - actual);
if (diff <= EPSILON)
return { passed: true, messages: [] };
@@ -104,7 +104,7 @@ function makeTest(test) {
var compareResult = deepCompare(expected, decoded);
if (!compareResult.passed) {
throw compareResult.messages.join('; ');
throw text(compareResult.messages, '; ');
}
};
}

View File

@@ -1556,7 +1556,7 @@ return {
test_array_slice: function() {
var arr = [1, 2, 3, 4, 5]
var sliced = arr.slice(1, 3)
var sliced = array(arr, 1, 3)
if (length(sliced) != 2) throw "array slice length failed"
if (sliced[0] != 2) throw "array slice first failed"
if (sliced[1] != 3) throw "array slice second failed"
@@ -1573,12 +1573,8 @@ return {
test_array_join: function() {
var arr = [1, 2, 3]
var caught = false
try {
var str = arr.join(",")
} catch (e) {
caught = true
}
var str = text(arr, ",")
if (str != "1,2,3") throw "array join with text() failed"
},
test_array_indexOf: function() {
@@ -1620,8 +1616,8 @@ return {
test_string_slice: function() {
var str = "hello"
if (str.slice(1, 4) != "ell") throw "string slice failed"
if (str.slice(-2) != "lo") throw "string slice negative failed"
if (text(str, 1, 4) != "ell") throw "string slice failed"
if (text(str, -2) != "lo") throw "string slice negative failed: " + text(str, -2)
},
test_string_indexOf: function() {
@@ -2043,8 +2039,8 @@ return {
test_builtin_function_properties_still_work: function() {
// Built-in functions like number, text, array should still have properties
var min_result = number.min(5, 3)
if (min_result != 3) throw "number.min should work"
var min_result = min(5, 3)
if (min_result != 3) throw "min should work"
},
// ============================================================================
@@ -2108,7 +2104,7 @@ return {
var proxy = function(name, args) {
if (is_function(my_record[name])) {
return fn.apply(my_record[name], args)
return apply(my_record[name], args)
}
throw `unknown method: ${name}`
}

View File

@@ -14,7 +14,7 @@ function deep_compare(expected, actual, path) {
if (is_number(expected) && is_number(actual)) {
if (isNaN(expected) && isNaN(actual)) return { passed: true, messages: [] }
var diff = number.abs(expected - actual)
var diff = abs(expected - actual)
if (diff <= EPSILON) return { passed: true, messages: [] }
return { passed: false, messages: [`Value mismatch at ${path}: ${expected} vs ${actual} (diff ${diff})`] }
}
@@ -115,7 +115,7 @@ function make_test(t) {
var dec = wota.decode(enc)
var cmp = deep_compare(t.input, dec)
if (!cmp.passed) throw cmp.messages.join('; ')
if (!cmp.passed) throw text(cmp.messages, '; ')
}
}

18
time.cm
View File

@@ -47,9 +47,9 @@ time.isleap = function(y) { return time.yearsize(y) == 366; };
/* timecode utility */
time.timecode = function(t, fps = 24)
{
var s = number.whole(t);
var s = whole(t);
var frac = t - s;
return `${s}:${number.whole(frac * fps)}`;
return `${s}:${whole(frac * fps)}`;
};
/* per-month day counts (non-leap) */
@@ -62,7 +62,7 @@ function time_record(num = now(),
{
if (is_object(num)) return num;
var monthdays = time.monthdays.slice();
var monthdays = array(time.monthdays);
var rec = {
second : 0, minute : 0, hour : 0,
yday : 0, year : 0,
@@ -77,13 +77,13 @@ function time_record(num = now(),
/* split into day + seconds-of-day */
var hms = num % time.day;
var day = number.floor(num / time.day);
var day = floor(num / time.day);
if (hms < 0) { hms += time.day; day--; }
rec.second = hms % time.minute;
var tmp = number.floor(hms / time.minute);
var tmp = floor(hms / time.minute);
rec.minute = tmp % time.minute;
rec.hour = number.floor(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 */
@@ -171,7 +171,7 @@ function time_text(num = now(),
/* BCE/CE */
var year = rec.year > 0 ? rec.year : rec.year - 1;
if (fmt.includes("c")) {
if (year < 0) { year = number.abs(year); fmt = fmt.replaceAll("c", "BC"); }
if (year < 0) { year = abs(year); fmt = fmt.replaceAll("c", "BC"); }
else fmt = fmt.replaceAll("c", "AD");
}
@@ -193,10 +193,10 @@ function time_text(num = now(),
fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/m[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/v[^bB]/g, rec.weekday);
fmt = fmt.replaceAll("mb", time.monthstr[rec.month].slice(0, 3));
fmt = fmt.replaceAll("mb", text(time.monthstr[rec.month], 0, 3));
fmt = fmt.replaceAll("mB", time.monthstr[rec.month]);
fmt = fmt.replaceAll("vB", time.weekdays[rec.weekday]);
fmt = fmt.replaceAll("vb", time.weekdays[rec.weekday].slice(0, 3));
fmt = fmt.replaceAll("vb", text(time.weekdays[rec.weekday], 0, 3));
return fmt;
}

233
toml.cm
View File

@@ -1,58 +1,88 @@
// Simple TOML parser for cell modules
// Supports basic TOML features needed for the module system
//
// Avoids regex .replace(/.../g, ...) so no global-regex state issues.
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
}
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
}
function parse_toml(toml_text) {
if (!is_text(toml_text)) return null
// Prefer Misty split if present; fall back to JS split.
var lines = array(toml_text, '\n')
if (lines == null) lines = toml_text.split('\n')
function parse_toml(text) {
if (!is_text(text)) return null
var lines = text.split('\n')
var result = {}
var current_section = result
var current_section_name = ''
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim()
var line = trim(lines[i])
if (line == null) line = lines[i]
// Skip empty lines and comments
if (!line || line.startsWith('#')) continue
// Section header
if (line.startsWith('[') && line.endsWith(']')) {
var section_path = parse_key_path(line.slice(1, -1))
var section_path = parse_key_path(text(line, 1, -1))
if (section_path == null) return null
current_section = result
// Reconstruct name for debugging/legacy (not strictly needed for object construction)
current_section_name = section_path.join('.')
current_section_name = text(section_path, '.')
for (var j = 0; j < section_path.length; j++) {
var key = section_path[j]
if (!current_section[key]) {
// Only treat null as "missing"; do not clobber false/0/""
if (current_section[key] == null) {
current_section[key] = {}
} else if (!is_object(current_section[key])) {
// Scalar/table collision like: a = 1 then [a.b]
return null
}
current_section = current_section[key]
}
continue
}
// Key-value pair
var eq_index = line.indexOf('=')
if (eq_index > 0) {
var key_part = line.substring(0, eq_index).trim()
var value = line.substring(eq_index + 1).trim()
// Handle quoted keys in key-value pairs too if needed?
// For now assuming simple keys or quoted keys
var key_part = trim(line.substring(0, eq_index))
var value = trim(line.substring(eq_index + 1))
if (key_part == null) key_part = line.substring(0, eq_index).trim()
if (value == null) value = line.substring(eq_index + 1).trim()
var key = parse_key(key_part)
// Parse value
if (key == null) return null
if (value.startsWith('"') && value.endsWith('"')) {
// String - unescape quotes
current_section[key] = value.slice(1, -1).replace(/\\"/g, '"')
current_section[key] = toml_unescape(text(value, 1, -1))
if (current_section[key] == null) return null
} else if (value.startsWith('[') && value.endsWith(']')) {
// Array
current_section[key] = parse_array(value)
if (current_section[key] == null) return null
} else if (value == 'true' || value == 'false') {
// Boolean
current_section[key] = value == 'true'
} else if (is_number(value)) {
// Number
current_section[key] = Number(value)
} else {
// Unquoted string
@@ -60,157 +90,160 @@ function parse_toml(text) {
}
}
}
return result
}
function parse_key(str) {
if (str.startsWith('"') && str.endsWith('"')) {
return str.slice(1, -1).replace(/\\"/g, '"')
}
return str
if (!is_text(str)) return null
if (str.startsWith('"') && str.endsWith('"')) {
var inner = text(str, 1, -1)
return toml_unescape(inner)
}
return str
}
// Split a key path by dots, respecting quotes
function parse_key_path(str) {
if (!is_text(str)) return null
var parts = []
var current = ''
var in_quote = false
for (var i = 0; i < str.length; i++) {
var c = str[i]
if (c == '"' && (i==0 || str[i-1] != '\\')) {
if (c == '"' && (i == 0 || str[i - 1] != '\\')) {
in_quote = !in_quote
// We don't verify if it's strictly correct TOML quote usage, just rudimentary
} else if (c == '.' && !in_quote) {
parts.push(parse_key(current.trim()))
var piece = trim(current)
if (piece == null) piece = current.trim()
parts.push(parse_key(piece))
current = ''
continue
}
current += c
}
if (current.trim().length > 0)
parts.push(parse_key(current.trim()))
var tail = trim(current)
if (tail == null) tail = current.trim()
if (tail.length > 0) parts.push(parse_key(tail))
return parts
}
function parse_array(str) {
// Remove brackets
str = str.slice(1, -1).trim()
if (!is_text(str)) return null
// Remove brackets and trim
str = text(str, 1, -1)
str = trim(str)
if (str == null) str = text(str, 1, -1).trim()
if (!str) return []
var items = []
var current = ''
var in_quotes = false
for (var i = 0; i < str.length; i++) {
var char = str[i]
if (char == '"' && (i == 0 || str[i-1] != '\\')) {
var ch = str[i]
if (ch == '"' && (i == 0 || str[i - 1] != '\\')) {
in_quotes = !in_quotes
current += char
} else if (char == ',' && !in_quotes) {
items.push(parse_value(current.trim()))
current += ch
} else if (ch == ',' && !in_quotes) {
var piece = trim(current)
if (piece == null) piece = current.trim()
items.push(parse_value(piece))
current = ''
} else {
current += char
current += ch
}
}
if (current.trim()) {
items.push(parse_value(current.trim()))
}
var last = trim(current)
if (last == null) last = current.trim()
if (last) items.push(parse_value(last))
return items
}
function parse_value(str) {
if (!is_text(str)) return null
if (str.startsWith('"') && str.endsWith('"')) {
return str.slice(1, -1).replace(/\\"/g, '"')
} else if (str == 'true' || str == 'false') {
return str == 'true'
} else if (!isNaN(Number(str))) {
return Number(str)
} else {
return str
return toml_unescape(text(str, 1, -1))
}
if (str == 'true' || str == 'false') return str == 'true'
// Use your existing numeric test; TOML numeric formats are richer, but this keeps your "module TOML" scope.
if (!isNaN(Number(str))) return Number(str)
return str
}
function encode_toml(obj) {
var result = []
function encode_value(value) {
if (is_text(value)) {
return '"' + value.replace(/"/g, '\\"') + '"'
} else if (is_logical(value)) {
return value ? 'true' : 'false'
} else if (is_number(value)) {
return text(value)
} else if (is_array(value)) {
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 < value.length; i++) {
items.push(encode_value(value[i]))
}
return '[' + items.join(', ') + ']'
for (var i = 0; i < value.length; i++) items.push(encode_value(value[i]))
return '[' + text(items, ', ') + ']'
}
return text(value)
}
function quote_key(k) {
if (k.includes('.') || k.includes('"') || k.includes(' ')) {
return '"' + k.replace(/"/g, '\\"') + '"'
}
return k
if (k.includes('.') || k.includes('"') || k.includes(' ')) {
return '"' + toml_escape(k) + '"'
}
return k
}
// First pass: encode top-level simple values
var keys = array(obj)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var value = obj[key]
if (!is_object(value)) {
result.push(quote_key(key) + ' = ' + encode_value(value))
}
if (!is_object(value)) result.push(quote_key(key) + ' = ' + encode_value(value))
}
// Second pass: encode nested objects
function encode_section(obj, path) {
var keys = array(obj)
function encode_section(o, path) {
var keys = array(o)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var value = obj[key]
var value = o[key]
if (is_object(value)) {
// Nested object - create section
// We MUST quote the key segment if it has dots, otherwise it becomes a nested table path
var quoted = quote_key(key)
var section_path = path ? path + '.' + quoted : quoted
result.push('[' + section_path + ']')
// First encode direct properties of this section
// Direct properties
var section_keys = array(value)
for (var j = 0; j < section_keys.length; j++) {
var sk = section_keys[j]
var sv = value[sk]
if (!is_object(sv)) {
result.push(quote_key(sk) + ' = ' + encode_value(sv))
}
if (!is_object(sv)) result.push(quote_key(sk) + ' = ' + encode_value(sv))
}
// Then encode nested sections
// Nested sections
encode_section(value, section_path)
}
}
}
encode_section(obj, '')
return result.join('\n')
return text(result, '\n')
}
return {
decode: parse_toml,
encode: encode_toml
}
}