From 7d0c96f3285ae3f02e5fd9f7b573fa5d5934431f Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 21 Feb 2026 13:44:34 -0600 Subject: [PATCH] inline --- bench_arith.ce | 86 +++++++++++++++++++++++++ bench_arith.js | 67 ++++++++++++++++++++ bench_arith.lua | 68 ++++++++++++++++++++ bench_array.ce | 113 +++++++++++++++++++++++++++++++++ bench_array.js | 93 +++++++++++++++++++++++++++ bench_array.lua | 93 +++++++++++++++++++++++++++ bench_fib.ce | 21 +++++++ bench_object.ce | 118 +++++++++++++++++++++++++++++++++++ bench_object.js | 99 +++++++++++++++++++++++++++++ bench_object.lua | 101 ++++++++++++++++++++++++++++++ mcode.cm | 115 ++++++++++++++++++++++++++++++++++ source/mach.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++ source/runtime.c | 81 ++++++++++++++++++++++++ streamline.cm | 15 ++++- vm_suite.ce | 146 +++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 1372 insertions(+), 3 deletions(-) create mode 100644 bench_arith.ce create mode 100644 bench_arith.js create mode 100644 bench_arith.lua create mode 100644 bench_array.ce create mode 100644 bench_array.js create mode 100644 bench_array.lua create mode 100644 bench_fib.ce create mode 100644 bench_object.ce create mode 100644 bench_object.js create mode 100644 bench_object.lua diff --git a/bench_arith.ce b/bench_arith.ce new file mode 100644 index 00000000..6abae5a5 --- /dev/null +++ b/bench_arith.ce @@ -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) diff --git a/bench_arith.js b/bench_arith.js new file mode 100644 index 00000000..0dda3616 --- /dev/null +++ b/bench_arith.js @@ -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); diff --git a/bench_arith.lua b/bench_arith.lua new file mode 100644 index 00000000..bc6aaa13 --- /dev/null +++ b/bench_arith.lua @@ -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) diff --git a/bench_array.ce b/bench_array.ce new file mode 100644 index 00000000..91f9d6d2 --- /dev/null +++ b/bench_array.ce @@ -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) diff --git a/bench_array.js b/bench_array.js new file mode 100644 index 00000000..ea493be6 --- /dev/null +++ b/bench_array.js @@ -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); diff --git a/bench_array.lua b/bench_array.lua new file mode 100644 index 00000000..033dca1c --- /dev/null +++ b/bench_array.lua @@ -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) diff --git a/bench_fib.ce b/bench_fib.ce new file mode 100644 index 00000000..ad734132 --- /dev/null +++ b/bench_fib.ce @@ -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) }) diff --git a/bench_object.ce b/bench_object.ce new file mode 100644 index 00000000..b4c68efd --- /dev/null +++ b/bench_object.ce @@ -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) diff --git a/bench_object.js b/bench_object.js new file mode 100644 index 00000000..fe4b9595 --- /dev/null +++ b/bench_object.js @@ -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); diff --git a/bench_object.lua b/bench_object.lua new file mode 100644 index 00000000..d24dad0d --- /dev/null +++ b/bench_object.lua @@ -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) diff --git a/mcode.cm b/mcode.cm index c6fae5b3..c80eb7c5 100644 --- a/mcode.cm +++ b/mcode.cm @@ -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" } @@ -873,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. @@ -1162,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 @@ -1929,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 diff --git a/source/mach.c b/source/mach.c index 65c32b7a..d9efe270 100644 --- a/source/mach.c +++ b/source/mach.c @@ -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 ---- */ @@ -1397,6 +1421,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 @@ -2364,6 +2394,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]); @@ -3009,6 +3156,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); } diff --git a/source/runtime.c b/source/runtime.c index acc23613..b749bc0e 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -11389,6 +11389,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; @@ -11556,6 +11629,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); diff --git a/streamline.cm b/streamline.cm index 95c80daa..c1aa2704 100644 --- a/streamline.cm +++ b/streamline.cm @@ -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 @@ -359,7 +363,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. diff --git a/vm_suite.ce b/vm_suite.ce index 1d1f66cf..15ba88c0 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -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 // ============================================================================