Merge branch 'optimize_mcode' into fix_aot

This commit is contained in:
2026-02-21 14:13:51 -06:00
15 changed files with 1419 additions and 25 deletions

86
bench_arith.ce Normal file
View File

@@ -0,0 +1,86 @@
// bench_arith.ce — arithmetic and number crunching benchmark
// Tests: integer add/mul, float ops, loop counter overhead, conditionals
var time = use('time')
def iterations = 2000000
// 1. Integer sum in tight loop
function bench_int_sum() {
var i = 0
var s = 0
for (i = 0; i < iterations; i++) {
s = s + i
}
return s
}
// 2. Integer multiply + mod (sieve-like)
function bench_int_mul_mod() {
var i = 0
var s = 0
for (i = 1; i < iterations; i++) {
s = s + (i * 7 % 1000)
}
return s
}
// 3. Float math — accumulate with division
function bench_float_arith() {
var i = 0
var s = 0.5
for (i = 1; i < iterations; i++) {
s = s + 1.0 / i
}
return s
}
// 4. Nested loop with branch (fizzbuzz-like counter)
function bench_branch() {
var i = 0
var fizz = 0
var buzz = 0
var fizzbuzz = 0
for (i = 1; i <= iterations; i++) {
if (i % 15 == 0) {
fizzbuzz = fizzbuzz + 1
} else if (i % 3 == 0) {
fizz = fizz + 1
} else if (i % 5 == 0) {
buzz = buzz + 1
}
}
return fizz + buzz + fizzbuzz
}
// 5. Nested loop (small inner)
function bench_nested() {
var i = 0
var j = 0
var s = 0
def outer = 5000
def inner = 5000
for (i = 0; i < outer; i++) {
for (j = 0; j < inner; j++) {
s = s + 1
}
}
return s
}
// Run each and print timing
function run(name, fn) {
var start = time.number()
var result = fn()
var elapsed = time.number() - start
var ms = whole(elapsed * 100000) / 100
log.console(` ${name}: ${ms} ms (result: ${result})`)
}
log.console("=== Arithmetic Benchmark ===")
log.console(` iterations: ${iterations}`)
run("int_sum ", bench_int_sum)
run("int_mul_mod ", bench_int_mul_mod)
run("float_arith ", bench_float_arith)
run("branch ", bench_branch)
run("nested_loop ", bench_nested)

67
bench_arith.js Normal file
View File

@@ -0,0 +1,67 @@
// bench_arith.js — arithmetic and number crunching benchmark (QuickJS)
const iterations = 2000000;
function bench_int_sum() {
let s = 0;
for (let i = 0; i < iterations; i++) {
s = s + i;
}
return s;
}
function bench_int_mul_mod() {
let s = 0;
for (let i = 1; i < iterations; i++) {
s = s + (i * 7 % 1000);
}
return s;
}
function bench_float_arith() {
let s = 0.5;
for (let i = 1; i < iterations; i++) {
s = s + 1.0 / i;
}
return s;
}
function bench_branch() {
let fizz = 0, buzz = 0, fizzbuzz = 0;
for (let i = 1; i <= iterations; i++) {
if (i % 15 === 0) {
fizzbuzz = fizzbuzz + 1;
} else if (i % 3 === 0) {
fizz = fizz + 1;
} else if (i % 5 === 0) {
buzz = buzz + 1;
}
}
return fizz + buzz + fizzbuzz;
}
function bench_nested() {
let s = 0;
const outer = 5000, inner = 5000;
for (let i = 0; i < outer; i++) {
for (let j = 0; j < inner; j++) {
s = s + 1;
}
}
return s;
}
function run(name, fn) {
const start = performance.now();
const result = fn();
const elapsed = performance.now() - start;
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
}
console.log("=== Arithmetic Benchmark ===");
console.log(` iterations: ${iterations}`);
run("int_sum ", bench_int_sum);
run("int_mul_mod ", bench_int_mul_mod);
run("float_arith ", bench_float_arith);
run("branch ", bench_branch);
run("nested_loop ", bench_nested);

68
bench_arith.lua Normal file
View File

@@ -0,0 +1,68 @@
-- bench_arith.lua — arithmetic and number crunching benchmark (Lua)
local iterations = 2000000
local clock = os.clock
local function bench_int_sum()
local s = 0
for i = 0, iterations - 1 do
s = s + i
end
return s
end
local function bench_int_mul_mod()
local s = 0
for i = 1, iterations - 1 do
s = s + (i * 7 % 1000)
end
return s
end
local function bench_float_arith()
local s = 0.5
for i = 1, iterations - 1 do
s = s + 1.0 / i
end
return s
end
local function bench_branch()
local fizz, buzz, fizzbuzz = 0, 0, 0
for i = 1, iterations do
if i % 15 == 0 then
fizzbuzz = fizzbuzz + 1
elseif i % 3 == 0 then
fizz = fizz + 1
elseif i % 5 == 0 then
buzz = buzz + 1
end
end
return fizz + buzz + fizzbuzz
end
local function bench_nested()
local s = 0
local outer, inner = 5000, 5000
for i = 0, outer - 1 do
for j = 0, inner - 1 do
s = s + 1
end
end
return s
end
local function run(name, fn)
local start = clock()
local result = fn()
local elapsed = (clock() - start) * 1000
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
end
print("=== Arithmetic Benchmark ===")
print(string.format(" iterations: %d", iterations))
run("int_sum ", bench_int_sum)
run("int_mul_mod ", bench_int_mul_mod)
run("float_arith ", bench_float_arith)
run("branch ", bench_branch)
run("nested_loop ", bench_nested)

113
bench_array.ce Normal file
View File

@@ -0,0 +1,113 @@
// bench_array.ce — array operation benchmark
// Tests: sequential access, push/build, index write, sum reduction, sort
var time = use('time')
def size = 100000
// 1. Build array with push
function bench_push() {
var a = []
var i = 0
for (i = 0; i < size; i++) {
a[] = i
}
return length(a)
}
// 2. Index write into preallocated array
function bench_index_write() {
var a = array(size, 0)
var i = 0
for (i = 0; i < size; i++) {
a[i] = i
}
return a[size - 1]
}
// 3. Sequential read and sum
function bench_seq_read() {
var a = array(size, 0)
var i = 0
for (i = 0; i < size; i++) {
a[i] = i
}
var s = 0
for (i = 0; i < size; i++) {
s = s + a[i]
}
return s
}
// 4. Reverse array in-place
function bench_reverse() {
var a = array(size, 0)
var i = 0
for (i = 0; i < size; i++) {
a[i] = i
}
var lo = 0
var hi = size - 1
var tmp = 0
while (lo < hi) {
tmp = a[lo]
a[lo] = a[hi]
a[hi] = tmp
lo = lo + 1
hi = hi - 1
}
return a[0]
}
// 5. Nested array access (matrix-like, 300x300)
function bench_matrix() {
def n = 300
var mat = array(n, null)
var i = 0
var j = 0
for (i = 0; i < n; i++) {
mat[i] = array(n, 0)
for (j = 0; j < n; j++) {
mat[i][j] = i * n + j
}
}
// sum diagonal
var s = 0
for (i = 0; i < n; i++) {
s = s + mat[i][i]
}
return s
}
// 6. filter-like: count evens
function bench_filter_count() {
var a = array(size, 0)
var i = 0
for (i = 0; i < size; i++) {
a[i] = i
}
var count = 0
for (i = 0; i < size; i++) {
if (a[i] % 2 == 0) {
count = count + 1
}
}
return count
}
function run(name, fn) {
var start = time.number()
var result = fn()
var elapsed = time.number() - start
var ms = whole(elapsed * 100000) / 100
log.console(` ${name}: ${ms} ms (result: ${result})`)
}
log.console("=== Array Benchmark ===")
log.console(` size: ${size}`)
run("push ", bench_push)
run("index_write ", bench_index_write)
run("seq_read_sum ", bench_seq_read)
run("reverse ", bench_reverse)
run("matrix_300 ", bench_matrix)
run("filter_count ", bench_filter_count)

93
bench_array.js Normal file
View File

@@ -0,0 +1,93 @@
// bench_array.js — array operation benchmark (QuickJS)
const size = 100000;
function bench_push() {
let a = [];
for (let i = 0; i < size; i++) {
a.push(i);
}
return a.length;
}
function bench_index_write() {
let a = new Array(size).fill(0);
for (let i = 0; i < size; i++) {
a[i] = i;
}
return a[size - 1];
}
function bench_seq_read() {
let a = new Array(size).fill(0);
for (let i = 0; i < size; i++) {
a[i] = i;
}
let s = 0;
for (let i = 0; i < size; i++) {
s = s + a[i];
}
return s;
}
function bench_reverse() {
let a = new Array(size).fill(0);
for (let i = 0; i < size; i++) {
a[i] = i;
}
let lo = 0, hi = size - 1, tmp;
while (lo < hi) {
tmp = a[lo];
a[lo] = a[hi];
a[hi] = tmp;
lo = lo + 1;
hi = hi - 1;
}
return a[0];
}
function bench_matrix() {
const n = 300;
let mat = new Array(n);
for (let i = 0; i < n; i++) {
mat[i] = new Array(n).fill(0);
for (let j = 0; j < n; j++) {
mat[i][j] = i * n + j;
}
}
let s = 0;
for (let i = 0; i < n; i++) {
s = s + mat[i][i];
}
return s;
}
function bench_filter_count() {
let a = new Array(size).fill(0);
for (let i = 0; i < size; i++) {
a[i] = i;
}
let count = 0;
for (let i = 0; i < size; i++) {
if (a[i] % 2 === 0) {
count = count + 1;
}
}
return count;
}
function run(name, fn) {
const start = performance.now();
const result = fn();
const elapsed = performance.now() - start;
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
}
console.log("=== Array Benchmark ===");
console.log(` size: ${size}`);
run("push ", bench_push);
run("index_write ", bench_index_write);
run("seq_read_sum ", bench_seq_read);
run("reverse ", bench_reverse);
run("matrix_300 ", bench_matrix);
run("filter_count ", bench_filter_count);

93
bench_array.lua Normal file
View File

@@ -0,0 +1,93 @@
-- bench_array.lua — array operation benchmark (Lua)
local size = 100000
local clock = os.clock
local function bench_push()
local a = {}
for i = 0, size - 1 do
a[#a + 1] = i
end
return #a
end
local function bench_index_write()
local a = {}
for i = 1, size do a[i] = 0 end
for i = 1, size do
a[i] = i - 1
end
return a[size]
end
local function bench_seq_read()
local a = {}
for i = 1, size do
a[i] = i - 1
end
local s = 0
for i = 1, size do
s = s + a[i]
end
return s
end
local function bench_reverse()
local a = {}
for i = 1, size do
a[i] = i - 1
end
local lo, hi = 1, size
while lo < hi do
a[lo], a[hi] = a[hi], a[lo]
lo = lo + 1
hi = hi - 1
end
return a[1]
end
local function bench_matrix()
local n = 300
local mat = {}
for i = 1, n do
mat[i] = {}
for j = 1, n do
mat[i][j] = (i - 1) * n + (j - 1)
end
end
local s = 0
for i = 1, n do
s = s + mat[i][i]
end
return s
end
local function bench_filter_count()
local a = {}
for i = 1, size do
a[i] = i - 1
end
local count = 0
for i = 1, size do
if a[i] % 2 == 0 then
count = count + 1
end
end
return count
end
local function run(name, fn)
local start = clock()
local result = fn()
local elapsed = (clock() - start) * 1000
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
end
print("=== Array Benchmark ===")
print(string.format(" size: %d", size))
run("push ", bench_push)
run("index_write ", bench_index_write)
run("seq_read_sum ", bench_seq_read)
run("reverse ", bench_reverse)
run("matrix_300 ", bench_matrix)
run("filter_count ", bench_filter_count)

21
bench_fib.ce Normal file
View File

@@ -0,0 +1,21 @@
var time = use('time')
function fib(n) {
if (n < 2) {
return n
}
return fib(n - 1) + fib(n - 2)
}
function run(name, fn) {
var start = time.number()
var result = fn()
var elapsed = time.number() - start
var ms = whole(elapsed * 100000) / 100
log.console(` ${name}: ${ms} ms (result: ${result})`)
}
log.console("=== Cell fib ===")
run("fib(25)", function() { return fib(25) })
run("fib(30)", function() { return fib(30) })
run("fib(35)", function() { return fib(35) })

118
bench_object.ce Normal file
View File

@@ -0,0 +1,118 @@
// bench_object.ce — object/record and string benchmark
// Tests: property read/write, string concat, string interpolation, method-like dispatch
var time = use('time')
def iterations = 200000
// 1. Record create + property write
function bench_record_create() {
var i = 0
var r = null
for (i = 0; i < iterations; i++) {
r = {x: i, y: i + 1, z: i + 2}
}
return r.z
}
// 2. Property read in loop
function bench_prop_read() {
var obj = {x: 10, y: 20, z: 30, w: 40}
var i = 0
var s = 0
for (i = 0; i < iterations; i++) {
s = s + obj.x + obj.y + obj.z + obj.w
}
return s
}
// 3. Dynamic property access (computed keys)
function bench_dynamic_prop() {
var obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
var keys = ["a", "b", "c", "d", "e"]
var i = 0
var j = 0
var s = 0
for (i = 0; i < iterations; i++) {
for (j = 0; j < 5; j++) {
s = s + obj[keys[j]]
}
}
return s
}
// 4. String concatenation
function bench_string_concat() {
var i = 0
var s = ""
def n = 10000
for (i = 0; i < n; i++) {
s = s + "x"
}
return length(s)
}
// 5. String interpolation
function bench_interpolation() {
var i = 0
var s = ""
def n = 50000
for (i = 0; i < n; i++) {
s = `item_${i}`
}
return s
}
// 6. Prototype chain / method-like call
function make_point(x, y) {
return {
x: x,
y: y,
sum: function(self) {
return self.x + self.y
}
}
}
function bench_method_call() {
var p = make_point(3, 4)
var i = 0
var s = 0
for (i = 0; i < iterations; i++) {
s = s + p.sum(p)
}
return s
}
// 7. Function call overhead (simple recursion depth)
function fib(n) {
if (n <= 1) return n
return fib(n - 1) + fib(n - 2)
}
function bench_fncall() {
var i = 0
var s = 0
for (i = 0; i < 20; i++) {
s = s + fib(25)
}
return s
}
function run(name, fn) {
var start = time.number()
var result = fn()
var elapsed = time.number() - start
var ms = whole(elapsed * 100000) / 100
log.console(` ${name}: ${ms} ms (result: ${result})`)
}
log.console("=== Object / String / Call Benchmark ===")
log.console(` iterations: ${iterations}`)
run("record_create ", bench_record_create)
run("prop_read ", bench_prop_read)
run("dynamic_prop ", bench_dynamic_prop)
run("string_concat ", bench_string_concat)
run("interpolation ", bench_interpolation)
run("method_call ", bench_method_call)
run("fncall_fib25 ", bench_fncall)

99
bench_object.js Normal file
View File

@@ -0,0 +1,99 @@
// bench_object.js — object/string/call benchmark (QuickJS)
const iterations = 200000;
function bench_record_create() {
let r;
for (let i = 0; i < iterations; i++) {
r = {x: i, y: i + 1, z: i + 2};
}
return r.z;
}
function bench_prop_read() {
const obj = {x: 10, y: 20, z: 30, w: 40};
let s = 0;
for (let i = 0; i < iterations; i++) {
s = s + obj.x + obj.y + obj.z + obj.w;
}
return s;
}
function bench_dynamic_prop() {
const obj = {a: 1, b: 2, c: 3, d: 4, e: 5};
const keys = ["a", "b", "c", "d", "e"];
let s = 0;
for (let i = 0; i < iterations; i++) {
for (let j = 0; j < 5; j++) {
s = s + obj[keys[j]];
}
}
return s;
}
function bench_string_concat() {
let s = "";
const n = 10000;
for (let i = 0; i < n; i++) {
s = s + "x";
}
return s.length;
}
function bench_interpolation() {
let s = "";
const n = 50000;
for (let i = 0; i < n; i++) {
s = `item_${i}`;
}
return s;
}
function make_point(x, y) {
return {
x: x,
y: y,
sum: function(self) {
return self.x + self.y;
}
};
}
function bench_method_call() {
const p = make_point(3, 4);
let s = 0;
for (let i = 0; i < iterations; i++) {
s = s + p.sum(p);
}
return s;
}
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
function bench_fncall() {
let s = 0;
for (let i = 0; i < 20; i++) {
s = s + fib(25);
}
return s;
}
function run(name, fn) {
const start = performance.now();
const result = fn();
const elapsed = performance.now() - start;
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
}
console.log("=== Object / String / Call Benchmark ===");
console.log(` iterations: ${iterations}`);
run("record_create ", bench_record_create);
run("prop_read ", bench_prop_read);
run("dynamic_prop ", bench_dynamic_prop);
run("string_concat ", bench_string_concat);
run("interpolation ", bench_interpolation);
run("method_call ", bench_method_call);
run("fncall_fib25 ", bench_fncall);

101
bench_object.lua Normal file
View File

@@ -0,0 +1,101 @@
-- bench_object.lua — object/string/call benchmark (Lua)
local iterations = 200000
local clock = os.clock
local function bench_record_create()
local r
for i = 0, iterations - 1 do
r = {x = i, y = i + 1, z = i + 2}
end
return r.z
end
local function bench_prop_read()
local obj = {x = 10, y = 20, z = 30, w = 40}
local s = 0
for i = 0, iterations - 1 do
s = s + obj.x + obj.y + obj.z + obj.w
end
return s
end
local function bench_dynamic_prop()
local obj = {a = 1, b = 2, c = 3, d = 4, e = 5}
local keys = {"a", "b", "c", "d", "e"}
local s = 0
for i = 0, iterations - 1 do
for j = 1, 5 do
s = s + obj[keys[j]]
end
end
return s
end
local function bench_string_concat()
local parts = {}
local n = 10000
for i = 1, n do
parts[i] = "x"
end
local s = table.concat(parts)
return #s
end
local function bench_interpolation()
local s = ""
local n = 50000
for i = 0, n - 1 do
s = string.format("item_%d", i)
end
return s
end
local function make_point(x, y)
return {
x = x,
y = y,
sum = function(self)
return self.x + self.y
end
}
end
local function bench_method_call()
local p = make_point(3, 4)
local s = 0
for i = 0, iterations - 1 do
s = s + p.sum(p)
end
return s
end
local function fib(n)
if n <= 1 then return n end
return fib(n - 1) + fib(n - 2)
end
local function bench_fncall()
local s = 0
for i = 0, 19 do
s = s + fib(25)
end
return s
end
local function run(name, fn)
local start = clock()
local result = fn()
local elapsed = (clock() - start) * 1000
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
end
print("=== Object / String / Call Benchmark ===")
print(string.format(" iterations: %d", iterations))
run("record_create ", bench_record_create)
run("prop_read ", bench_prop_read)
run("dynamic_prop ", bench_dynamic_prop)
run("string_concat ", bench_string_concat)
run("interpolation ", bench_interpolation)
run("method_call ", bench_method_call)
run("fncall_fib25 ", bench_fncall)

184
mcode.cm
View File

@@ -38,6 +38,11 @@ var mcode = function(ast) {
is_array: "is_array", is_function: "is_func", is_object: "is_record",
is_stone: "is_stone", is_integer: "is_int", is_text: "is_text",
is_number: "is_num", is_logical: "is_bool", is_null: "is_null",
is_blob: "is_blob", is_data: "is_data",
is_true: "is_true", is_false: "is_false", is_fit: "is_fit",
is_character: "is_char", is_digit: "is_digit", is_letter: "is_letter",
is_lower: "is_lower", is_upper: "is_upper", is_whitespace: "is_ws",
is_actor: "is_actor",
length: "length"
}
@@ -89,6 +94,8 @@ var mcode = function(ast) {
var s_filename = null
var s_has_disruption = false
var s_slot_types = {}
var s_num_err_label = null
var s_num_err_emitted = false
// Shared closure vars for binop helpers (avoids >4 param functions)
var _bp_dest = 0
@@ -118,7 +125,9 @@ var mcode = function(ast) {
cur_line: s_cur_line,
cur_col: s_cur_col,
has_disruption: s_has_disruption,
slot_types: s_slot_types
slot_types: s_slot_types,
num_err_label: s_num_err_label,
num_err_emitted: s_num_err_emitted
}
}
@@ -141,6 +150,8 @@ var mcode = function(ast) {
s_cur_col = saved.cur_col
s_has_disruption = saved.has_disruption
s_slot_types = saved.slot_types
s_num_err_label = saved.num_err_label
s_num_err_emitted = saved.num_err_emitted
}
// Slot allocation
@@ -413,29 +424,37 @@ var mcode = function(ast) {
var emit_numeric_binop = function(op_str) {
var left_known = is_known_number(_bp_ln) || slot_is_num(_bp_left)
var right_known = is_known_number(_bp_rn) || slot_is_num(_bp_right)
var t0 = null
var done = null
if (left_known && right_known) {
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
mark_slot(_bp_dest, "num")
return null
}
var t0 = alloc_slot()
var err = gen_label("num_err")
var done = gen_label("num_done")
if (s_num_err_label == null) {
s_num_err_label = gen_label("num_err")
}
t0 = alloc_slot()
if (!left_known) {
emit_2("is_num", t0, _bp_left)
emit_jump_cond("jump_false", t0, err)
emit_jump_cond("jump_false", t0, s_num_err_label)
mark_slot(_bp_left, "num")
}
if (!right_known) {
emit_2("is_num", t0, _bp_right)
emit_jump_cond("jump_false", t0, err)
emit_jump_cond("jump_false", t0, s_num_err_label)
mark_slot(_bp_right, "num")
}
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
emit_jump(done)
emit_label(err)
emit_log_error("cannot apply '" + _bp_op_sym + "': operands must be numbers")
emit_0("disrupt")
emit_label(done)
if (!s_num_err_emitted) {
done = gen_label("num_done")
emit_jump(done)
emit_label(s_num_err_label)
emit_log_error("operands must be numbers")
emit_0("disrupt")
emit_label(done)
s_num_err_emitted = true
}
mark_slot(_bp_dest, "num")
return null
}
@@ -460,23 +479,30 @@ var mcode = function(ast) {
// emit_neg_decomposed: emit type-guarded negate
var emit_neg_decomposed = function(dest, src, src_node) {
var t0 = null
var done = null
if (is_known_number(src_node) || slot_is_num(src)) {
emit_2("negate", dest, src)
mark_slot(dest, "num")
return null
}
var t0 = alloc_slot()
var err = gen_label("neg_err")
var done = gen_label("neg_done")
if (s_num_err_label == null) {
s_num_err_label = gen_label("num_err")
}
t0 = alloc_slot()
emit_2("is_num", t0, src)
emit_jump_cond("jump_false", t0, err)
emit_jump_cond("jump_false", t0, s_num_err_label)
mark_slot(src, "num")
emit_2("negate", dest, src)
emit_jump(done)
emit_label(err)
emit_log_error("cannot negate: operand must be a number")
emit_0("disrupt")
emit_label(done)
if (!s_num_err_emitted) {
done = gen_label("num_done")
emit_jump(done)
emit_label(s_num_err_label)
emit_log_error("operands must be numbers")
emit_0("disrupt")
emit_label(done)
s_num_err_emitted = true
}
mark_slot(dest, "num")
return null
}
@@ -852,6 +878,7 @@ var mcode = function(ast) {
var inline_every = true
var inline_some = true
var inline_reduce = true
var inline_map = true
// --- Helper: emit a reduce loop body ---
// r = {acc, i, arr, fn, len, fn_arity}; emits loop updating acc in-place.
@@ -1141,6 +1168,96 @@ var mcode = function(ast) {
return dest
}
// --- Inline expansion: array(arr, fn) → map ---
var expand_inline_map = function(dest, arr_slot, fn_slot) {
var result = alloc_slot()
var len = alloc_slot()
var i = alloc_slot()
var check = alloc_slot()
var item = alloc_slot()
var fn_arity = alloc_slot()
var arity_is_zero = alloc_slot()
var arity_is_one = alloc_slot()
var null_s = alloc_slot()
var zero = alloc_slot()
var one = alloc_slot()
var f = alloc_slot()
var val = alloc_slot()
var loop_label = gen_label("map_loop")
var call_one_label = gen_label("map_call_one")
var call_two_label = gen_label("map_call_two")
var call_done_label = gen_label("map_call_done")
var done_label = gen_label("map_done")
add_instr(["array", result, 0])
emit_2("length", len, arr_slot)
emit_2("int", i, 0)
emit_2("int", zero, 0)
emit_2("int", one, 1)
emit_1("null", null_s)
emit_2("length", fn_arity, fn_slot)
emit_label(loop_label)
emit_3("lt", check, i, len)
emit_jump_cond("jump_false", check, done_label)
emit_3("load_index", item, arr_slot, i)
emit_3("eq", arity_is_zero, fn_arity, zero)
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
emit_3("frame", f, fn_slot, 0)
emit_3("setarg", f, 0, null_s)
emit_2("invoke", f, val)
emit_jump(call_done_label)
emit_label(call_one_label)
emit_3("eq", arity_is_one, fn_arity, one)
emit_jump_cond("jump_false", arity_is_one, call_two_label)
emit_3("frame", f, fn_slot, 1)
emit_3("setarg", f, 0, null_s)
emit_3("setarg", f, 1, item)
emit_2("invoke", f, val)
emit_jump(call_done_label)
emit_label(call_two_label)
emit_3("frame", f, fn_slot, 2)
emit_3("setarg", f, 0, null_s)
emit_3("setarg", f, 1, item)
emit_3("setarg", f, 2, i)
emit_2("invoke", f, val)
emit_label(call_done_label)
emit_2("push", result, val)
emit_3("add", i, i, one)
emit_jump(loop_label)
emit_label(done_label)
emit_2("move", dest, result)
return dest
}
// --- Inline expansion: array(arr, intrinsic_op) → map with direct opcode ---
var expand_inline_map_intrinsic = function(dest, arr_slot, opcode) {
var result = alloc_slot()
var len = alloc_slot()
var i = alloc_slot()
var check = alloc_slot()
var item = alloc_slot()
var val = alloc_slot()
var zero = alloc_slot()
var one = alloc_slot()
var loop_label = gen_label("mapi_loop")
var done_label = gen_label("mapi_done")
add_instr(["array", result, 0])
emit_2("length", len, arr_slot)
emit_2("int", i, 0)
emit_2("int", zero, 0)
emit_2("int", one, 1)
emit_label(loop_label)
emit_3("lt", check, i, len)
emit_jump_cond("jump_false", check, done_label)
emit_3("load_index", item, arr_slot, i)
emit_2(opcode, val, item)
emit_2("push", result, val)
emit_3("add", i, i, one)
emit_jump(loop_label)
emit_label(done_label)
emit_2("move", dest, result)
return dest
}
// --- Inline expansion: reduce(arr, fn[, initial[, reverse]]) ---
var expand_inline_reduce = function(dest, args, nargs) {
var arr_slot = args.arr
@@ -1908,6 +2025,25 @@ var mcode = function(ast) {
d = alloc_slot()
return expand_inline_reduce(d, {arr: a0, fn: a1, init: a2, rev: a3}, nargs)
}
// array(arr, fn) → inline map expansion
// Skip when first arg is a number literal (that's array(N, fn) — creation, not map)
if (nargs == 2 && fname == "array" && inline_map
&& args_list[0].kind != "number") {
// Specialized: array(arr, known_sensory_intrinsic) → direct opcode loop
if (args_list[1].kind == "name" && args_list[1].intrinsic == true
&& sensory_ops[args_list[1].name] != null) {
a0 = gen_expr(args_list[0], -1)
d = alloc_slot()
return expand_inline_map_intrinsic(d, a0, sensory_ops[args_list[1].name])
}
// General: array(arr, fn_literal) → map loop with arity dispatch
if (args_list[1].kind == "function") {
a0 = gen_expr(args_list[0], -1)
a1 = gen_expr(args_list[1], -1)
d = alloc_slot()
return expand_inline_map(d, a0, a1)
}
}
}
// Collect arg slots
@@ -2605,6 +2741,8 @@ var mcode = function(ast) {
s_vars = []
s_intrinsic_cache = []
s_slot_types = {}
s_num_err_label = null
s_num_err_emitted = false
s_loop_break = null
s_loop_continue = null
s_label_map = {}
@@ -2804,6 +2942,8 @@ var mcode = function(ast) {
s_label_counter = 0
s_func_counter = 0
s_slot_types = {}
s_num_err_label = null
s_num_err_emitted = false
s_loop_break = null
s_loop_continue = null
s_label_map = {}

View File

@@ -269,6 +269,18 @@ typedef enum MachOpcode {
MACH_IS_STONE, /* R(A) = is_stone(R(B)) */
MACH_LENGTH, /* R(A) = length(R(B)) — array/text/blob length */
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
MACH_IS_BLOB, /* R(A) = is_blob(R(B)) */
MACH_IS_DATA, /* R(A) = is_data(R(B)) — plain record, not array/func/blob */
MACH_IS_TRUE, /* R(A) = (R(B) === true) */
MACH_IS_FALSE, /* R(A) = (R(B) === false) */
MACH_IS_FIT, /* R(A) = is_fit(R(B)) — safe integer */
MACH_IS_CHAR, /* R(A) = is_character(R(B)) — single char text */
MACH_IS_DIGIT, /* R(A) = is_digit(R(B)) */
MACH_IS_LETTER, /* R(A) = is_letter(R(B)) */
MACH_IS_LOWER, /* R(A) = is_lower(R(B)) */
MACH_IS_UPPER, /* R(A) = is_upper(R(B)) */
MACH_IS_WS, /* R(A) = is_whitespace(R(B)) */
MACH_IS_ACTOR, /* R(A) = is_actor(R(B)) — has actor_sym property */
MACH_OP_COUNT
} MachOpcode;
@@ -381,6 +393,18 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_IS_STONE] = "is_stone",
[MACH_LENGTH] = "length",
[MACH_IS_PROXY] = "is_proxy",
[MACH_IS_BLOB] = "is_blob",
[MACH_IS_DATA] = "is_data",
[MACH_IS_TRUE] = "is_true",
[MACH_IS_FALSE] = "is_false",
[MACH_IS_FIT] = "is_fit",
[MACH_IS_CHAR] = "is_char",
[MACH_IS_DIGIT] = "is_digit",
[MACH_IS_LETTER] = "is_letter",
[MACH_IS_LOWER] = "is_lower",
[MACH_IS_UPPER] = "is_upper",
[MACH_IS_WS] = "is_ws",
[MACH_IS_ACTOR] = "is_actor",
};
/* ---- Compile-time constant pool entry ---- */
@@ -1396,6 +1420,12 @@ vm_dispatch:
DT(MACH_IS_ARRAY), DT(MACH_IS_FUNC),
DT(MACH_IS_RECORD), DT(MACH_IS_STONE),
DT(MACH_LENGTH), DT(MACH_IS_PROXY),
DT(MACH_IS_BLOB), DT(MACH_IS_DATA),
DT(MACH_IS_TRUE), DT(MACH_IS_FALSE),
DT(MACH_IS_FIT), DT(MACH_IS_CHAR),
DT(MACH_IS_DIGIT), DT(MACH_IS_LETTER),
DT(MACH_IS_LOWER), DT(MACH_IS_UPPER),
DT(MACH_IS_WS), DT(MACH_IS_ACTOR),
};
#pragma GCC diagnostic pop
#undef DT
@@ -2386,6 +2416,123 @@ vm_dispatch:
frame->slots[a] = JS_NewBool(ctx, is_proxy);
VM_BREAK();
}
VM_CASE(MACH_IS_BLOB):
frame->slots[a] = JS_NewBool(ctx, mist_is_blob(frame->slots[b]));
VM_BREAK();
VM_CASE(MACH_IS_DATA): {
JSValue v = frame->slots[b];
int result = 0;
if (mist_is_gc_object(v) && !mist_is_array(v)
&& !mist_is_function(v) && !mist_is_blob(v))
result = 1;
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_TRUE):
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_TRUE);
VM_BREAK();
VM_CASE(MACH_IS_FALSE):
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_FALSE);
VM_BREAK();
VM_CASE(MACH_IS_FIT): {
JSValue v = frame->slots[b];
int result = 0;
if (JS_IsInt(v)) {
result = 1;
} else if (JS_IsShortFloat(v)) {
double d = JS_VALUE_GET_FLOAT64(v);
result = (isfinite(d) && trunc(d) == d && fabs(d) <= 9007199254740992.0);
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_CHAR): {
JSValue v = frame->slots[b];
int result = 0;
if (MIST_IsImmediateASCII(v))
result = (MIST_GetImmediateASCIILen(v) == 1);
else if (mist_is_text(v))
result = (js_string_value_len(v) == 1);
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_DIGIT): {
JSValue v = frame->slots[b];
int result = 0;
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
result = (ch >= '0' && ch <= '9');
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
result = (ch >= '0' && ch <= '9');
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_LETTER): {
JSValue v = frame->slots[b];
int result = 0;
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_LOWER): {
JSValue v = frame->slots[b];
int result = 0;
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
result = (ch >= 'a' && ch <= 'z');
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
result = (ch >= 'a' && ch <= 'z');
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_UPPER): {
JSValue v = frame->slots[b];
int result = 0;
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
result = (ch >= 'A' && ch <= 'Z');
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
result = (ch >= 'A' && ch <= 'Z');
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_WS): {
JSValue v = frame->slots[b];
int result = 0;
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
int ch = MIST_GetImmediateASCIIChar(v, 0);
result = (ch == ' ' || ch == '\t' || ch == '\n'
|| ch == '\r' || ch == '\f' || ch == '\v');
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
uint32_t ch = js_string_value_get(v, 0);
result = (ch == ' ' || ch == '\t' || ch == '\n'
|| ch == '\r' || ch == '\f' || ch == '\v');
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_IS_ACTOR): {
JSValue v = frame->slots[b];
int result = 0;
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym)) {
result = JS_HasPropertyKey(ctx, v, ctx->actor_sym) > 0;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
}
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
/* Logical */
VM_CASE(MACH_NOT): {
int bval = JS_ToBool(ctx, frame->slots[b]);
@@ -3031,6 +3178,18 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); }
else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); }
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
else if (strcmp(op, "is_blob") == 0) { AB2(MACH_IS_BLOB); }
else if (strcmp(op, "is_data") == 0) { AB2(MACH_IS_DATA); }
else if (strcmp(op, "is_true") == 0) { AB2(MACH_IS_TRUE); }
else if (strcmp(op, "is_false") == 0) { AB2(MACH_IS_FALSE); }
else if (strcmp(op, "is_fit") == 0) { AB2(MACH_IS_FIT); }
else if (strcmp(op, "is_char") == 0) { AB2(MACH_IS_CHAR); }
else if (strcmp(op, "is_digit") == 0) { AB2(MACH_IS_DIGIT); }
else if (strcmp(op, "is_letter") == 0) { AB2(MACH_IS_LETTER); }
else if (strcmp(op, "is_lower") == 0) { AB2(MACH_IS_LOWER); }
else if (strcmp(op, "is_upper") == 0) { AB2(MACH_IS_UPPER); }
else if (strcmp(op, "is_ws") == 0) { AB2(MACH_IS_WS); }
else if (strcmp(op, "is_actor") == 0) { AB2(MACH_IS_ACTOR); }
/* Logical */
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }

View File

@@ -11405,6 +11405,79 @@ static JSValue js_cell_is_letter (JSContext *ctx, JSValue this_val, int argc, JS
return JS_NewBool (ctx, (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
/* is_true(val) - check if value is exactly true */
static JSValue js_cell_is_true (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
return JS_NewBool (ctx, argv[0] == JS_TRUE);
}
/* is_false(val) - check if value is exactly false */
static JSValue js_cell_is_false (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
return JS_NewBool (ctx, argv[0] == JS_FALSE);
}
/* is_fit(val) - check if value is a safe integer (int32 or float with integer value <= 2^53) */
static JSValue js_cell_is_fit (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
JSValue val = argv[0];
if (JS_IsInt (val)) return JS_TRUE;
if (JS_IsShortFloat (val)) {
double d = JS_VALUE_GET_FLOAT64 (val);
return JS_NewBool (ctx, isfinite (d) && trunc (d) == d && fabs (d) <= 9007199254740992.0);
}
return JS_FALSE;
}
/* is_character(val) - check if value is a single character text */
static JSValue js_cell_is_character (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
JSValue val = argv[0];
if (!JS_IsText (val)) return JS_FALSE;
return JS_NewBool (ctx, js_string_value_len (val) == 1);
}
/* is_digit(val) - check if value is a single digit character */
static JSValue js_cell_is_digit (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
JSValue val = argv[0];
if (!JS_IsText (val)) return JS_FALSE;
if (js_string_value_len (val) != 1) return JS_FALSE;
uint32_t c = js_string_value_get (val, 0);
return JS_NewBool (ctx, c >= '0' && c <= '9');
}
/* is_lower(val) - check if value is a single lowercase letter */
static JSValue js_cell_is_lower (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
JSValue val = argv[0];
if (!JS_IsText (val)) return JS_FALSE;
if (js_string_value_len (val) != 1) return JS_FALSE;
uint32_t c = js_string_value_get (val, 0);
return JS_NewBool (ctx, c >= 'a' && c <= 'z');
}
/* is_upper(val) - check if value is a single uppercase letter */
static JSValue js_cell_is_upper (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
JSValue val = argv[0];
if (!JS_IsText (val)) return JS_FALSE;
if (js_string_value_len (val) != 1) return JS_FALSE;
uint32_t c = js_string_value_get (val, 0);
return JS_NewBool (ctx, c >= 'A' && c <= 'Z');
}
/* is_whitespace(val) - check if value is a single whitespace character */
static JSValue js_cell_is_whitespace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE;
JSValue val = argv[0];
if (!JS_IsText (val)) return JS_FALSE;
if (js_string_value_len (val) != 1) return JS_FALSE;
uint32_t c = js_string_value_get (val, 0);
return JS_NewBool (ctx, c == ' ' || c == '\t' || c == '\n'
|| c == '\r' || c == '\f' || c == '\v');
}
/* is_proto(val, master) - check if val has master in prototype chain */
static JSValue js_cell_is_proto (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 2) return JS_FALSE;
@@ -11572,6 +11645,14 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
js_set_global_cfunc(ctx, "is_text", js_cell_is_text, 1);
js_set_global_cfunc(ctx, "is_proto", js_cell_is_proto, 2);
js_set_global_cfunc(ctx, "is_letter", js_cell_is_letter, 1);
js_set_global_cfunc(ctx, "is_true", js_cell_is_true, 1);
js_set_global_cfunc(ctx, "is_false", js_cell_is_false, 1);
js_set_global_cfunc(ctx, "is_fit", js_cell_is_fit, 1);
js_set_global_cfunc(ctx, "is_character", js_cell_is_character, 1);
js_set_global_cfunc(ctx, "is_digit", js_cell_is_digit, 1);
js_set_global_cfunc(ctx, "is_lower", js_cell_is_lower, 1);
js_set_global_cfunc(ctx, "is_upper", js_cell_is_upper, 1);
js_set_global_cfunc(ctx, "is_whitespace", js_cell_is_whitespace, 1);
/* Utility functions */
js_set_global_cfunc(ctx, "apply", js_cell_fn_apply, 2);

View File

@@ -46,13 +46,17 @@ var streamline = function(ir, log) {
not: true, and: true, or: true,
is_int: true, is_text: true, is_num: true,
is_bool: true, is_null: true, is_identical: true,
is_array: true, is_func: true, is_record: true, is_stone: true
is_array: true, is_func: true, is_record: true, is_stone: true,
is_blob: true, is_data: true,
is_true: true, is_false: true, is_fit: true,
is_char: true, is_digit: true, is_letter: true,
is_lower: true, is_upper: true, is_ws: true, is_actor: true
}
var type_check_map = {
is_int: T_INT, is_text: T_TEXT, is_num: T_NUM,
is_bool: T_BOOL, is_null: T_NULL,
is_array: T_ARRAY, is_func: T_FUNCTION,
is_record: T_RECORD
is_record: T_RECORD, is_blob: T_BLOB
}
// simplify_algebra dispatch tables
@@ -374,7 +378,12 @@ var streamline = function(ir, log) {
is_int: [1, T_BOOL], is_text: [1, T_BOOL], is_num: [1, T_BOOL],
is_bool: [1, T_BOOL], is_null: [1, T_BOOL], is_identical: [1, T_BOOL],
is_array: [1, T_BOOL], is_func: [1, T_BOOL],
is_record: [1, T_BOOL], is_stone: [1, T_BOOL]
is_record: [1, T_BOOL], is_stone: [1, T_BOOL],
is_blob: [1, T_BOOL], is_data: [1, T_BOOL],
is_true: [1, T_BOOL], is_false: [1, T_BOOL], is_fit: [1, T_BOOL],
is_char: [1, T_BOOL], is_digit: [1, T_BOOL], is_letter: [1, T_BOOL],
is_lower: [1, T_BOOL], is_upper: [1, T_BOOL], is_ws: [1, T_BOOL],
is_actor: [1, T_BOOL]
}
// Known intrinsic return types for invoke result inference.

View File

@@ -981,6 +981,99 @@ run("is_proto", function() {
if (!is_proto(b, a)) fail("is_proto failed on meme")
})
run("is_data", function() {
if (!is_data({})) fail("is_data {} should be true")
if (is_data([])) fail("is_data [] should be false")
if (is_data(42)) fail("is_data number should be false")
if (is_data("hello")) fail("is_data string should be false")
if (is_data(null)) fail("is_data null should be false")
if (is_data(true)) fail("is_data bool should be false")
if (is_data(function(){})) fail("is_data function should be false")
})
run("is_true", function() {
if (!is_true(true)) fail("is_true true should be true")
if (is_true(false)) fail("is_true false should be false")
if (is_true(1)) fail("is_true 1 should be false")
if (is_true("true")) fail("is_true string should be false")
if (is_true(null)) fail("is_true null should be false")
})
run("is_false", function() {
if (!is_false(false)) fail("is_false false should be true")
if (is_false(true)) fail("is_false true should be false")
if (is_false(0)) fail("is_false 0 should be false")
if (is_false("")) fail("is_false empty string should be false")
if (is_false(null)) fail("is_false null should be false")
})
run("is_fit", function() {
if (!is_fit(0)) fail("is_fit 0 should be true")
if (!is_fit(42)) fail("is_fit 42 should be true")
if (!is_fit(-100)) fail("is_fit -100 should be true")
if (!is_fit(3.0)) fail("is_fit 3.0 should be true")
if (is_fit(3.5)) fail("is_fit 3.5 should be false")
if (is_fit("42")) fail("is_fit string should be false")
if (is_fit(null)) fail("is_fit null should be false")
})
run("is_character", function() {
if (!is_character("a")) fail("is_character a should be true")
if (!is_character("Z")) fail("is_character Z should be true")
if (!is_character("5")) fail("is_character 5 should be true")
if (!is_character(" ")) fail("is_character space should be true")
if (is_character("ab")) fail("is_character ab should be false")
if (is_character("")) fail("is_character empty should be false")
if (is_character(42)) fail("is_character number should be false")
if (is_character(null)) fail("is_character null should be false")
})
run("is_digit", function() {
if (!is_digit("0")) fail("is_digit 0 should be true")
if (!is_digit("5")) fail("is_digit 5 should be true")
if (!is_digit("9")) fail("is_digit 9 should be true")
if (is_digit("a")) fail("is_digit a should be false")
if (is_digit("55")) fail("is_digit 55 should be false")
if (is_digit(5)) fail("is_digit number should be false")
if (is_digit(null)) fail("is_digit null should be false")
})
run("is_letter", function() {
if (!is_letter("a")) fail("is_letter a should be true")
if (!is_letter("Z")) fail("is_letter Z should be true")
if (is_letter("5")) fail("is_letter 5 should be false")
if (is_letter("ab")) fail("is_letter ab should be false")
if (is_letter(42)) fail("is_letter number should be false")
})
run("is_lower", function() {
if (!is_lower("a")) fail("is_lower a should be true")
if (!is_lower("z")) fail("is_lower z should be true")
if (is_lower("A")) fail("is_lower A should be false")
if (is_lower("5")) fail("is_lower 5 should be false")
if (is_lower("ab")) fail("is_lower ab should be false")
if (is_lower(42)) fail("is_lower number should be false")
})
run("is_upper", function() {
if (!is_upper("A")) fail("is_upper A should be true")
if (!is_upper("Z")) fail("is_upper Z should be true")
if (is_upper("a")) fail("is_upper a should be false")
if (is_upper("5")) fail("is_upper 5 should be false")
if (is_upper("AB")) fail("is_upper AB should be false")
if (is_upper(42)) fail("is_upper number should be false")
})
run("is_whitespace", function() {
if (!is_whitespace(" ")) fail("is_whitespace space should be true")
if (!is_whitespace("\t")) fail("is_whitespace tab should be true")
if (!is_whitespace("\n")) fail("is_whitespace newline should be true")
if (is_whitespace("a")) fail("is_whitespace a should be false")
if (is_whitespace(" ")) fail("is_whitespace two spaces should be false")
if (is_whitespace(42)) fail("is_whitespace number should be false")
if (is_whitespace(null)) fail("is_whitespace null should be false")
})
// ============================================================================
// GLOBAL FUNCTIONS - LENGTH
// ============================================================================
@@ -3409,6 +3502,59 @@ run("array map with exit", function() {
if (length(result) != 5) fail("array map with exit length unexpected")
})
run("inline map intrinsic is_data", function() {
var items = [{}, [], "hello", 42, true]
var result = array(items, is_data)
if (result[0] != true) fail("is_data {} should be true")
if (result[1] != false) fail("is_data [] should be false")
if (result[2] != false) fail("is_data string should be false")
if (result[3] != false) fail("is_data number should be false")
if (result[4] != false) fail("is_data bool should be false")
if (length(result) != 5) fail("result length should be 5")
})
run("inline map intrinsic is_number", function() {
var items = [1, "two", 3.14, null, true]
var result = array(items, is_number)
if (result[0] != true) fail("1 should be number")
if (result[1] != false) fail("'two' should not be number")
if (result[2] != true) fail("3.14 should be number")
if (result[3] != false) fail("null should not be number")
if (result[4] != false) fail("true should not be number")
})
run("inline map intrinsic is_text", function() {
var items = ["hello", 42, "", null]
var result = array(items, is_text)
if (result[0] != true) fail("'hello' should be text")
if (result[1] != false) fail("42 should not be text")
if (result[2] != true) fail("'' should be text")
if (result[3] != false) fail("null should not be text")
})
run("inline map intrinsic is_digit", function() {
var chars = array("a5B2 ")
var result = array(chars, is_digit)
if (result[0] != false) fail("a should not be digit")
if (result[1] != true) fail("5 should be digit")
if (result[2] != false) fail("B should not be digit")
if (result[3] != true) fail("2 should be digit")
if (result[4] != false) fail("space should not be digit")
})
run("inline map lambda", function() {
var arr = [10, 20, 30]
var result = array(arr, function(x) { return x + 1 })
if (result[0] != 11) fail("10+1 should be 11")
if (result[1] != 21) fail("20+1 should be 21")
if (result[2] != 31) fail("30+1 should be 31")
})
run("inline map empty array", function() {
var result = array([], is_number)
if (length(result) != 0) fail("map of empty should be empty")
})
// ============================================================================
// STRING METHOD EDGE CASES
// ============================================================================