20 Commits

Author SHA1 Message Date
John Alanbrook
828a1ddcf7 merge trampoline and call_ic 2025-12-31 16:58:39 -06:00
John Alanbrook
8abee37622 persistent current frame 2025-12-31 14:28:43 -06:00
John Alanbrook
7c3cce1ce2 works 2025-12-31 12:45:32 -06:00
John Alanbrook
334f3a789b runs 2025-12-31 11:09:18 -06:00
John Alanbrook
e21cd4e70b tail call 2025-12-31 09:19:39 -06:00
John Alanbrook
41eb4bf6f7 bench 2025-12-30 22:56:31 -06:00
John Alanbrook
5f761cc7af wire callinternal to use trampoline 2025-12-30 16:58:06 -06:00
John Alanbrook
7ae5a0c06b null + anything = null 2025-12-30 14:40:54 -06:00
John Alanbrook
0664c11af6 set up quickening for adds 2025-12-30 00:39:43 -06:00
John Alanbrook
058ad89c96 op switch 2025-12-29 20:46:12 -06:00
John Alanbrook
a0038a7ab2 blob 2025-12-29 17:53:21 -06:00
John Alanbrook
d0674e7921 remove internal scripts 2025-12-29 15:45:21 -06:00
John Alanbrook
b586df63ad move internals to pure C 2025-12-29 14:28:54 -06:00
John Alanbrook
05b57550da vm trampoline setup 2025-12-29 13:01:05 -06:00
John Alanbrook
fa616ee444 trampoline 2025-12-29 01:35:57 -06:00
John Alanbrook
0720368c48 go 2025-12-29 01:26:25 -06:00
John Alanbrook
4f3e2819fe var behaves like let 2025-12-29 00:48:28 -06:00
John Alanbrook
3b53a9dcc3 prop ic 2025-12-28 23:55:57 -06:00
John Alanbrook
6d7581eff8 IC structure 2025-12-28 23:38:10 -06:00
John Alanbrook
dca1963b5d null access 2025-12-28 22:57:53 -06:00
28 changed files with 6471 additions and 2523 deletions

View File

@@ -3,7 +3,7 @@ var pkg = use('package')
var fd = use('fd')
var time = use('time')
var json = use('json')
var utf8 = use('utf8')
var blob = use('blob')
var os = use('os')
var testlib = use('internal/testlib')
var math = use('math/radians')
@@ -524,7 +524,7 @@ log.console(`Benchmarks: ${total_benches} total`)
// Generate reports
function generate_reports() {
var timestamp = number.floor(time.number()).toString()
var timestamp = text(number.floor(time.number()))
var report_dir = shop.get_reports_dir() + '/bench_' + timestamp
testlib.ensure_dir(report_dir)
@@ -578,7 +578,7 @@ Total benchmarks: ${total_benches}
}
testlib.ensure_dir(report_dir)
fd.slurpwrite(`${report_dir}/bench.txt`, utf8.encode(txt_report))
fd.slurpwrite(`${report_dir}/bench.txt`, stone(new blob(txt_report)))
log.console(`Report written to ${report_dir}/bench.txt`)
// Generate JSON per package
@@ -595,7 +595,7 @@ Total benchmarks: ${total_benches}
}
var json_path = `${report_dir}/${pkg_res.package.replace(/\//g, '_')}.json`
fd.slurpwrite(json_path, utf8.encode(json.encode(pkg_benches)))
fd.slurpwrite(json_path, stone(new blob(json.encode(pkg_benches))))
}
}

View File

@@ -257,5 +257,6 @@ return {
x = (x + o.x) | 0
}
return blackhole(sink, x)
}
},
}

View File

@@ -158,31 +158,4 @@ for (var i = 0; i < n; i++)
advance(0.01);
log.console(energy().toFixed(9))
var js = use('js')
// Get function metadata
var fn_info = js.fn_info(advance)
log.console(`${fn_info.filename}:${fn_info.line}:${fn_info.column}: function: ${fn_info.name}`)
// Display arguments
if (fn_info.args && fn_info.args.length > 0) {
log.console(` args: ${fn_info.args.join(' ')}`)
}
// Display local variables
if (fn_info.locals && fn_info.locals.length > 0) {
log.console(' locals:')
for (var i = 0; i < fn_info.locals.length; i++) {
var local = fn_info.locals[i]
log.console(` ${local.index}: ${local.type} ${local.name}`)
}
}
// Display stack size
log.console(` stack_size: ${fn_info.stack_size}`)
// Display disassembly
log.console(js.disassemble(advance))
log.console(js.disassemble(advance).length)
$stop()

View File

@@ -8,7 +8,7 @@
var fd = use('fd')
var crypto = use('crypto')
var utf8 = use('utf8')
var blob = use('blob')
var os = use('os')
var toolchains = use('toolchains')
var shop = use('internal/shop')
@@ -73,7 +73,8 @@ Build.detect_host_target = function() {
// ============================================================================
function content_hash(str) {
return text(crypto.blake2(utf8.encode(str)), 'h')
var bb = stone(new blob(str))
return text(crypto.blake2(bb, 32), 'h')
}
function get_build_dir() {
@@ -379,7 +380,7 @@ Build.build_all_dynamic = function(target, buildtype = 'release') {
var lib = Build.build_dynamic('core', target, buildtype)
results.push({ package: 'core', library: lib })
} catch (e) {
log.error('Failed to build core: ' + e)
log.error('Failed to build core: ' + text(e))
results.push({ package: 'core', error: e })
}
}

View File

@@ -1,303 +0,0 @@
/* array.cm - array creation and manipulation utilities */
var _isArray = Array.isArray
var _slice = Array.prototype.slice
var _push = Array.prototype.push
var _sort = Array.prototype.sort
var _keys = Object.keys
var _from = Array.from
function array(arg, arg2, arg3, arg4) {
// array(number) - create array of size with nulls
// array(number, initial_value) - create array with initial values
if (typeof arg == 'number') {
if (arg < 0) return null
var len = number.floor(arg)
var result = []
if (arg2 == null) {
result.length = 100
} else if (typeof arg2 == 'function') {
var arity = arg2.length
for (var i = 0; i < len; i++) {
result[i] = arity >= 1 ? arg2(i) : arg2()
}
} else {
for (var i = 0; i < len; i++) result[i] = arg2
}
return result
}
// array(array) - copy
// array(array, function, reverse, exit) - map
// array(array, another_array) - concat
// array(array, from, to) - slice
if (_isArray(arg)) {
if (arg2 == null) {
// Copy
return _slice.call(arg)
}
if (typeof arg2 == 'function') {
// Map
var fn = arg2
var reverse = arg3 == true
var exit = arg4
var result = []
if (reverse) {
for (var i = arg.length - 1; i >= 0; i--) {
var val = fn(arg[i], i)
if (exit != null && val == exit) break
result[i] = val
}
} else {
for (var i = 0; i < arg.length; i++) {
var val = fn(arg[i], i)
if (exit != null && val == exit) break
_push.call(result, val)
}
}
return result
}
if (_isArray(arg2)) {
// Concat
var result = _slice.call(arg)
for (var i = 0; i < arg2.length; i++) {
_push.call(result, arg2[i])
}
return result
}
if (typeof arg2 == 'number') {
// Slice
var from = arg2
var to = arg3
var len = arg.length
if (from < 0) from += len
if (to == null) to = len
if (to < 0) to += len
if (from < 0 || from > to || to > len) return null
return _slice.call(arg, from, to)
}
return null
}
// array(object) - keys
if (typeof arg == 'object' && arg != null && !_isArray(arg)) {
if (arg instanceof Set) {
return _from(arg)
}
return _keys(arg)
}
// array(text) - split into grapheme clusters
// array(text, separator) - split by separator
// array(text, length) - dice into chunks
if (typeof arg == 'string') {
if (arg2 == null) {
// Split into grapheme clusters (simplified: split into characters)
var result = []
for (var i = 0; i < arg.length; i++) {
_push.call(result, arg[i])
}
return result
}
if (typeof arg2 == 'string') {
// Split by separator
return arg.split(arg2)
}
if (typeof arg2 == 'number') {
// Dice into chunks
var len = number.floor(arg2)
if (len <= 0) return null
var result = []
for (var i = 0; i < arg.length; i += len) {
_push.call(result, arg.substring(i, i + len))
}
return result
}
return null
}
return null
}
array.reduce = function(arr, fn, initial, reverse) {
if (!_isArray(arr)) return null
if (typeof fn != 'function') return null
var len = arr.length
if (initial == null) {
if (len == 0) return null
if (len == 1) return arr[0]
if (reverse == true) {
var acc = arr[len - 1]
for (var i = len - 2; i >= 0; i--) {
acc = fn(acc, arr[i])
}
return acc
} else {
var acc = arr[0]
for (var i = 1; i < len; i++) {
acc = fn(acc, arr[i])
}
return acc
}
} else {
if (len == 0) return initial
if (reverse == true) {
var acc = initial
for (var i = len - 1; i >= 0; i--) {
acc = fn(acc, arr[i])
}
return acc
} else {
var acc = initial
for (var i = 0; i < len; i++) {
acc = fn(acc, arr[i])
}
return acc
}
}
}
array.for = function(arr, fn, reverse, exit) {
if (!_isArray(arr)) return null
if (arr.length == 0) return null
if (typeof fn != 'function') return null
if (reverse == true) {
for (var i = arr.length - 1; i >= 0; i--) {
var result = fn(arr[i], i)
if (exit != null && result == exit) return exit
}
} else {
for (var i = 0; i < arr.length; i++) {
var result = fn(arr[i], i)
if (exit != null && result == exit) return exit
}
}
return null
}
array.find = function(arr, fn, reverse, from) {
if (!_isArray(arr)) return null
var len = arr.length
if (typeof fn != 'function') {
// Compare exactly
var target = fn
if (reverse == true) {
var start = from != null ? from : len - 1
for (var i = start; i >= 0; i--) {
if (arr[i] == target) return i
}
} else {
var start = from != null ? from : 0
for (var i = start; i < len; i++) {
if (arr[i] == target) return i
}
}
return null
}
if (reverse == true) {
var start = from != null ? from : len - 1
for (var i = start; i >= 0; i--) {
if (fn(arr[i], i) == true) return i
}
} else {
var start = from != null ? from : 0
for (var i = start; i < len; i++) {
if (fn(arr[i], i) == true) return i
}
}
return null
}
array.filter = function(arr, fn) {
if (!_isArray(arr)) return null
if (typeof fn != 'function') return null
var result = []
for (var i = 0; i < arr.length; i++) {
var val = fn(arr[i], i)
if (val == true) {
_push.call(result, arr[i])
} else if (val != false) {
return null
}
}
return result
}
array.sort = function(arr, select) {
if (!_isArray(arr)) return null
var result = _slice.call(arr)
var keys = []
// Extract keys
for (var i = 0; i < result.length; i++) {
var key
if (select == null) {
key = result[i]
} else if (typeof select == 'string' || typeof select == 'number') {
key = result[i][select]
} else if (_isArray(select)) {
key = select[i]
} else {
return null
}
if (typeof key != 'number' && typeof key != 'string') return null
keys[i] = key
}
// Check all keys are same type
if (keys.length > 0) {
var keyType = typeof keys[0]
for (var i = 1; i < keys.length; i++) {
if (typeof keys[i] != keyType) return null
}
}
// Create index array and sort
var indices = []
for (var i = 0; i < result.length; i++) indices[i] = i
// Stable sort using indices
_sort.call(indices, function(a, b) {
if (keys[a] < keys[b]) return -1
if (keys[a] > keys[b]) return 1
return a - b // stable
})
var sorted = []
for (var i = 0; i < indices.length; i++) {
sorted[i] = result[indices[i]]
}
return sorted
}
return array

View File

@@ -7,6 +7,7 @@ var SYSYM = '__SYSTEM__'
var hidden = _cell.hidden
var os = hidden.os;
_cell.os = null
var dylib_ext
@@ -27,18 +28,6 @@ function use_embed(name) {
return load_internal(`js_${name}_use`)
}
globalThis.meme = function(obj, ...mixins) {
var result = _ObjectCreate(obj)
array.for(mixins, mix => {
if (isa(mix, object)) {
for (var key in mix)
result[key] = mix[key]
}
})
return result
}
globalThis.logical = function(val1)
{
if (val1 == 0 || val1 == false || val1 == "false" || val1 == null)
@@ -48,7 +37,6 @@ globalThis.logical = function(val1)
return null;
}
var utf8 = use_embed('utf8')
var js = use_embed('js')
var fd = use_embed('fd')
@@ -68,13 +56,6 @@ if (!fd.is_dir(core_path)) {
var use_cache = {}
use_cache['core/os'] = os
var _Symbol = Symbol
globalThis.key = function()
{
return _Symbol()
}
// Load a core module from the file system
function use_core(path) {
var cache_key = 'core/' + path
@@ -88,7 +69,7 @@ function use_core(path) {
if (fd.is_file(file_path)) {
var script_blob = fd.slurp(file_path)
var script = utf8.decode(script_blob)
var script = text(script_blob)
var mod = `(function setup_module(use){${script}})`
var fn = js.eval('core:' + path, mod)
var result = fn.call(sym, use_core);
@@ -101,141 +82,36 @@ function use_core(path) {
}
var blob = use_core('blob')
var blob_stone = blob.prototype.stone
var blob_stonep = blob.prototype.stonep;
delete blob.prototype.stone;
delete blob.prototype.stonep;
// Capture Object and Array methods before they're deleted
var _Object = Object
var _ObjectKeys = Object.keys
var _ObjectFreeze = Object.freeze
var _ObjectIsFrozen = Object.isFrozen
var _ObjectDefineProperty = Object.defineProperty
var _ObjectGetPrototypeOf = Object.getPrototypeOf
var _ObjectCreate = Object.create
var _ArrayIsArray = Array.isArray
Object.prototype.toString = function()
{
return json.encode(this)
}
function deepFreeze(object) {
if (object instanceof blob)
blob_stone.call(object);
var propNames = _ObjectKeys(object);
for (var name of propNames) {
var value = object[name];
if ((value && typeof value == "object") || typeof value == "function")
deepFreeze(value);
}
return _ObjectFreeze(object);
}
globalThis.actor = function()
{
}
globalThis.stone = deepFreeze
stone.p = function(object)
{
if (object instanceof blob)
return blob_stonep.call(object)
return _ObjectIsFrozen(object)
}
var actor_mod = use_core('actor')
var wota = use_core('wota')
var nota = use_core('nota')
// Load internal modules for global functions
globalThis.text = use_core('internal/text')
globalThis.number = use_core('internal/number')
globalThis.array = use_core('internal/array')
globalThis.object = use_core('internal/object')
globalThis.fn = use_core('internal/fn')
// Global utility functions (use already-captured references)
var _isArray = _ArrayIsArray
var _keys = _ObjectKeys
var _getPrototypeOf = _ObjectGetPrototypeOf
var _create = _ObjectCreate
globalThis.length = function(value) {
if (value == null) return null
// For functions, return arity
if (typeof value == 'function') return value.length
// For strings, return codepoint count
if (typeof value == 'string') return value.length
// For arrays, return element count
if (_isArray(value)) return value.length
// For blobs, return bit count
if (value instanceof blob && typeof value.length == 'number') return value.length
// For records with length field
if (typeof value == 'object' && value != null) {
if ('length' in value) {
var len = value.length
if (typeof len == 'function') return len.call(value)
if (typeof len == 'number') return len
}
}
return null
}
globalThis.reverse = function(value) {
if (_isArray(value)) {
var result = []
for (var i = value.length - 1; i >= 0; i--) {
result.push(value[i])
}
return result
}
// For blobs, would need blob module support
if (isa(value, blob)) {
// Simplified: return null for now, would need proper blob reversal
return null
}
return null
}
globalThis.isa = function(value, master) {
if (master == null) return false
// isa(value, array) - check if object has all keys
if (_isArray(master)) {
if (typeof value != 'object' || value == null) return false
for (var i = 0; i < master.length; i++) {
if (!(master[i] in value)) return false
}
return true
}
// isa(value, function) - check if function.prototype is in chain
if (typeof master == 'function') {
// Special type checks
if (master == stone) return _ObjectIsFrozen(value) || typeof value != 'object'
if (master == number) return typeof value == 'number'
if (master == text) return typeof value == 'string'
if (master == logical) return typeof value == 'boolean'
if (master == array) return _isArray(value)
if (master == object) return typeof value == 'object' && value != null && !_isArray(value)
if (master == fn) return typeof value == 'function'
if (master == actor) return isa(value, object) && value[ACTORDATA]
if (master == stone) return is_stone(value)
if (master == number) return is_number(value)
if (master == text) return is_text(value)
if (master == logical) return is_logical(value)
if (master == array) return is_array(value)
if (master == object) return is_object(value)
if (master == fn) return is_function(value)
if (master == actor) return is_object(value) && value[ACTORDATA]
// Check prototype chain
if (master.prototype) {
@@ -261,55 +137,9 @@ globalThis.isa = function(value, master) {
return false
}
globalThis.proto = function(obj) {
if (!isa(obj, object)) return null
var p = _getPrototypeOf(obj)
if (p == _Object.prototype) return null
return p
}
globalThis.splat = function(obj) {
if (typeof obj != 'object' || obj == null) return null
var result = {}
var current = obj
// Walk prototype chain and collect text keys
while (current != null) {
var keys = _keys(current)
for (var i = 0; i < keys.length; i++) {
var k = keys[i]
if (!(k in result)) {
var val = current[k]
// Only include serializable types
if (typeof val == 'object' || typeof val == 'number' ||
typeof val == 'string' || typeof val == 'boolean') {
result[k] = val
}
}
}
current = _getPrototypeOf(current)
}
// Call to_data if present
if (typeof obj.to_data == 'function') {
var extra = obj.to_data(result)
if (typeof extra == 'object' && extra != null) {
var extraKeys = _keys(extra)
for (var i = 0; i < extraKeys.length; i++) {
result[extraKeys[i]] = extra[extraKeys[i]]
}
}
}
return result
}
var ENETSERVICE = 0.1
var REPLYTIMEOUT = 60 // seconds before replies are ignored
globalThis.pi = 3.14159265358979323846264338327950288419716939937510
function caller_data(depth = 0)
{
var file = "nofile"
@@ -486,6 +316,8 @@ _cell.config = config
ENETSERVICE = config.net_service
REPLYTIMEOUT = config.reply_timeout
/*
When handling a message, the message appears like this:
{
@@ -516,9 +348,7 @@ function guid(bits = 256)
return text(guid,'h')
}
var _Symbol = Symbol
var HEADER = _Symbol()
var HEADER = key()
// takes a function input value that will eventually be called with the current time in number form.
$_.clock = function(fn) {
@@ -836,7 +666,6 @@ function turn(msg)
}
//log.console(`FIXME: need to get main from config, not just set to true`)
//log.console(`FIXME: remove global access (ie globalThis.use)`)
//log.console(`FIXME: add freeze/unfreeze at this level, so we can do it (but scripts cannot)`)
actor_mod.register_actor(_cell.id, turn, true, config.ar_timer)
@@ -891,12 +720,13 @@ function handle_actor_disconnect(id) {
function handle_sysym(msg)
{
var from
switch(msg.kind) {
case 'stop':
disrupt("got stop message")
break
case 'underling':
var from = msg.from
from = msg.from
var greeter = greeters[from[ACTORDATA].id]
if (greeter) greeter(msg.message)
if (msg.message.type == 'disrupt')
@@ -911,7 +741,7 @@ function handle_sysym(msg)
} else throw new Error('Got a contact message, but no portal is established.')
break
case 'couple': // from must be notified when we die
var from = msg.from
from = msg.from
underlings.add(from[ACTORDATA].id)
log.system(`actor ${from} is coupled to me`)
break
@@ -975,78 +805,15 @@ if (!locator) {
if (!locator)
throw new Error(`Main program ${_cell.args.program} could not be found`)
// Hide JavaScript built-ins - make them inaccessible
// Store references we need internally before deleting
var _Object = Object
var _Array = Array
var _String = String
var _Number = Number
var _Boolean = Boolean
var _Math = Math
var _Function = Function
stone(globalThis)
var _Error = Error
var _JSON = JSON
// juicing these before Math is gone
use_core('math/radians')
use_core('math/cycles')
use_core('math/degrees')
// Delete from globalThis
delete globalThis.Object
delete globalThis.Math
delete globalThis.Number
delete globalThis.String
delete globalThis.Array
delete globalThis.Boolean
delete globalThis.Date
delete globalThis.Function
delete globalThis.Reflect
delete globalThis.Proxy
delete globalThis.WeakMap
delete globalThis.WeakSet
delete globalThis.WeakRef
delete globalThis.BigInt
delete globalThis.Symbol
//delete globalThis.Map
//delete globalThis.Set
delete globalThis.Promise
delete globalThis.ArrayBuffer
delete globalThis.DataView
delete globalThis.Int8Array
delete globalThis.Uint8Array
delete globalThis.Uint8ClampedArray
delete globalThis.Int16Array
delete globalThis.Uint16Array
delete globalThis.Int32Array
delete globalThis.Uint32Array
delete globalThis.Float32Array
delete globalThis.Float64Array
delete globalThis.BigInt64Array
delete globalThis.BigUint64Array
delete globalThis.eval
delete globalThis.parseInt
delete globalThis.parseFloat
delete globalThis.isNaN
delete globalThis.isFinite
delete globalThis.decodeURI
delete globalThis.decodeURIComponent
delete globalThis.encodeURI
delete globalThis.encodeURIComponent
delete globalThis.escape
delete globalThis.unescape
delete globalThis.Intl
delete globalThis.RegExp
_ObjectFreeze(globalThis)
var rads = use_core("math/radians")
$_.clock(_ => {
// Get capabilities for the main program
var file_info = shop.file_info ? shop.file_info(locator.path) : null
var inject = shop.script_inject_for ? shop.script_inject_for(file_info) : []
// Build values array for injection
var vals = []
for (var i = 0; i < inject.length; i++) {
@@ -1055,15 +822,15 @@ $_.clock(_ => {
if (key == 'fd') vals.push(fd)
else vals.push($_[key])
}
// Create use function bound to the program's package
var pkg = file_info ? file_info.package : null
var use_fn = function(path) { return shop.use(path, pkg) }
// Call with signature: setup_module(args, use, ...capabilities)
// The script wrapper builds $_ from the injected capabilities for backward compatibility
var val = locator.symbol.call(null, _cell.args.arg, use_fn, ...vals)
if (val)
throw new Error('Program must not return anything');
})

View File

@@ -1,22 +0,0 @@
/* fn.cm - function utilities */
var _apply = Function.prototype.apply
var _isArray = Array.isArray
var fn = {}
fn.apply = function(func, args) {
if (typeof func != 'function') return func
if (!_isArray(args)) {
args = [args]
}
if (args.length > func.length) {
throw new Error("fn.apply: too many arguments")
}
return _apply.call(func, null, args)
}
return fn

View File

@@ -1,169 +0,0 @@
/* number.cm - number conversion and math utilities */
var _floor = Math.floor
var _ceil = Math.ceil
var _round = Math.round
var _abs = Math.abs
var _trunc = Math.trunc
var _min = Math.min
var _max = Math.max
var _pow = Math.pow
var _parseInt = parseInt
var _parseFloat = parseFloat
function number(val, format) {
if (val == true) return 1
if (val == false) return 0
if (typeof val == 'number') return val
if (typeof val == 'string') {
if (typeof format == 'number') {
// radix conversion
if (format < 2 || format > 36) return null
var result = _parseInt(val, format)
if (isNaN(result)) return null
return result
}
if (typeof format == 'string') {
return parse_formatted(val, format)
}
// default: parse as decimal
var result = _parseFloat(val)
if (!isa(result, number)) return null
return result
}
return null
}
function parse_formatted(str, format) {
if (!format || format == "" || format == "n") {
var result = _parseFloat(str)
if (isNaN(result)) return null
return result
}
switch (format) {
case "u": // underbar separator
str = str.split('_').join('')
break
case "d": // comma separator
str = str.split(',').join('')
break
case "s": // space separator
str = str.split(' ').join('')
break
case "v": // European style: period separator, comma decimal
str = str.split('.').join('')
str = str.replace(',', '.')
break
case "l": // locale - treat like 'd' for now
str = str.split(',').join('')
break
case "i": // integer with underbar
str = str.split('_').join('')
break
case "b": // binary
return _parseInt(str, 2)
case "o": // octal
return _parseInt(str, 8)
case "h": // hex
return _parseInt(str, 16)
case "t": // base32
return _parseInt(str, 32)
case "j": // JavaScript style prefix
if (str.startsWith('0x') || str.startsWith('0X'))
return _parseInt(str.slice(2), 16)
if (str.startsWith('0o') || str.startsWith('0O'))
return _parseInt(str.slice(2), 8)
if (str.startsWith('0b') || str.startsWith('0B'))
return _parseInt(str.slice(2), 2)
return _parseFloat(str)
default:
return null
}
var result = _parseFloat(str)
if (isNaN(result)) return null
return result
}
number.whole = function(n) {
if (typeof n != 'number') return null
return _trunc(n)
}
number.fraction = function(n) {
if (typeof n != 'number') return null
return n - _trunc(n)
}
number.floor = function(n, place) {
if (typeof n != 'number') return null
if (place == null || place == 0) return _floor(n)
var mult = _pow(10, place)
return _floor(n * mult) / mult
}
number.ceiling = function(n, place) {
if (typeof n != 'number') return null
if (place == null || place == 0) return _ceil(n)
var mult = _pow(10, place)
return _ceil(n * mult) / mult
}
number.abs = function(n) {
if (typeof n != 'number') return null
return _abs(n)
}
number.round = function(n, place) {
if (typeof n != 'number') return null
if (place == null || place == 0) return _round(n)
var mult = _pow(10, place)
return _round(n * mult) / mult
}
number.sign = function(n) {
if (typeof n != 'number') return null
if (n < 0) return -1
if (n > 0) return 1
return 0
}
number.trunc = function(n, place) {
if (typeof n != 'number') return null
if (place == null || place == 0) return _trunc(n)
var mult = _pow(10, place)
return _trunc(n * mult) / mult
}
number.min = function(...vals) {
if (vals.length == 0) return null
var result = vals[0]
for (var i = 1; i < vals.length; i++) {
if (typeof vals[i] != 'number') return null
if (vals[i] < result) result = vals[i]
}
return result
}
number.max = function(...vals) {
if (vals.length == 0) return null
var result = vals[0]
for (var i = 1; i < vals.length; i++) {
if (typeof vals[i] != 'number') return null
if (vals[i] > result) result = vals[i]
}
return result
}
number.remainder = function(dividend, divisor) {
if (typeof dividend != 'number' || typeof divisor != 'number') return null
if (divisor == 0) return null
return dividend - (_trunc(dividend / divisor) * divisor)
}
return number

View File

@@ -1,93 +0,0 @@
/* object.cm - object creation and manipulation utilities */
var _keys = array
var _create = meme
var _assign = Object.assign
var _isArray = function(val) { return isa(val, array) }
var _values = Object.values
function object(arg, arg2) {
// object(object) - shallow mutable copy
if (isa(arg, object) && arg2 == null) {
var result = {}
var keys = _keys(arg)
for (var i = 0; i < keys.length; i++) {
result[keys[i]] = arg[keys[i]]
}
return result
}
// object(object, another_object) - combine
if (isa(arg, object) && isa(arg2, object)) {
var result = {}
var keys = _keys(arg)
for (var i = 0; i < keys.length; i++) {
result[keys[i]] = arg[keys[i]]
}
keys = _keys(arg2)
for (var i = 0; i < keys.length; i++) {
result[keys[i]] = arg2[keys[i]]
}
return result
}
// object(object, array_of_keys) - select
if (isa(arg, object) && _isArray(arg2)) {
var result = {}
for (var i = 0; i < arg2.length; i++) {
var key = arg2[i]
if (typeof key == 'string' && key in arg) {
result[key] = arg[key]
}
}
return result
}
// object(array_of_keys) - set with true values
if (_isArray(arg) && arg2 == null) {
var result = {}
for (var i = 0; i < arg.length; i++) {
var key = arg[i]
if (typeof key == 'string') {
result[key] = true
}
}
return result
}
// object(array_of_keys, value) - value set
// object(array_of_keys, function) - functional value set
if (_isArray(arg) && arg2 != null) {
var result = {}
if (typeof arg2 == 'function') {
for (var i = 0; i < arg.length; i++) {
var key = arg[i]
if (typeof key == 'string') {
result[key] = arg2(key)
}
}
} else {
for (var i = 0; i < arg.length; i++) {
var key = arg[i]
if (typeof key == 'string') {
result[key] = arg2
}
}
}
return result
}
return null
}
object.values = function(obj)
{
return _values(obj)
}
object.assign = function(obj, ...args)
{
return _assign(obj, ...args)
}
return object

View File

@@ -6,7 +6,6 @@ var miniz = use('miniz')
var time = use('time')
var js = use('js')
var crypto = use('crypto')
var utf8 = use('utf8')
var blob = use('blob')
var pkg_tools = use('package')
var os = use('os')
@@ -63,7 +62,7 @@ var dylib_ext = '.dylib' // Default extension
var use_cache = os.use_cache
var global_shop_path = os.global_shop_path
var $_ = os.$_
var my$_ = os.$_
Shop.get_package_dir = function(name) {
return global_shop_path + '/packages/' + name
@@ -277,7 +276,7 @@ Shop.load_lock = function() {
// Save lock.toml configuration (to global shop)
Shop.save_lock = function(lock) {
var path = global_shop_path + '/lock.toml'
fd.slurpwrite(path, utf8.encode(toml.encode(lock)));
fd.slurpwrite(path, stone(new blob(toml.encode(lock))));
}
@@ -385,7 +384,7 @@ function inject_values(inject) {
for (var i = 0; i < inject.length; i++) {
var key = strip_dollar(inject[i])
if (key == 'fd') vals.push(fd)
else vals.push($_[key])
else vals.push(my$_[key])
}
return vals
}
@@ -400,23 +399,7 @@ var script_form = function(path, script, pkg, inject) {
var pkg_arg = pkg ? `'${pkg}'` : 'null'
var params = inject_params(inject)
// Build $_ object from injected capabilities for backward compatibility
var build_compat = ''
if (inject && inject.length) {
var compat_props = []
for (var i = 0; i < inject.length; i++) {
var name = inject[i]
var key = name
if (key && key[0] == '$') key = key.substring(1)
compat_props.push(key + ': ' + name)
}
build_compat = 'var $_ = {' + compat_props.join(', ') + '};'
}
// use is passed as a parameter, not on globalThis for the script
// $fd is injected as a capability, but we still allow use('fd') for now
// $_ is built from injected capabilities for backward compatibility
var fn = `(function setup_module(args, use${params}){ def arg = args; def PACKAGE = ${pkg_arg}; ${build_compat} ${script}})`
var fn = `(function setup_module(args, use${params}){ def arg = args; def PACKAGE = ${pkg_arg}; ${script}})`
return fn
}
@@ -431,7 +414,7 @@ function resolve_mod_fn(path, pkg) {
var content = text(fd.slurp(path))
var script = script_form(path, content, file_pkg, inject);
var obj = pull_from_cache(utf8.encode(script))
var obj = pull_from_cache(stone(new blob(script)))
if (obj) {
var fn = js.compile_unblob(obj)
return js.eval_compile(fn)
@@ -443,7 +426,7 @@ function resolve_mod_fn(path, pkg) {
var fn = js.compile(compile_name, script)
put_into_cache(utf8.encode(script), js.compile_blob(fn))
put_into_cache(stone(new blob(script)), js.compile_blob(fn))
return js.eval_compile(fn)
}
@@ -624,7 +607,8 @@ Shop.open_package_dylib = function(pkg) {
}
// If no package context, only check core internal symbols
if (!package_context) {
if (!package_context || package_context == 'core') {
path = path.replace('/', '_')
var core_sym = `js_${path}_use`
if (os.internal_exists(core_sym)) {
return {
@@ -808,7 +792,7 @@ function execute_module(info)
used = mod_resolve.symbol.call(context, null, use_fn, ...vals)
} else if (c_resolve.scope < 900) {
// C only
used = c_resolve.symbol(null, $_)
used = c_resolve.symbol(null, my$_)
} else {
throw new Error(`Module ${info.path} could not be found`)
} if (!used)

View File

@@ -1,306 +0,0 @@
#include "cell.h"
#include <string.h>
#include <stdlib.h>
JSC_CCALL(text_blob_to_hex,
size_t blob_len;
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
uint8_t *bytes = (uint8_t *)blob_data;
size_t hex_len = blob_len * 2;
char *hex_str = malloc(hex_len + 1);
if (!hex_str) return JS_ThrowOutOfMemory(js);
static const char hex_digits[] = "0123456789ABCDEF";
for (size_t i = 0; i < blob_len; ++i) {
hex_str[i * 2] = hex_digits[(bytes[i] >> 4) & 0xF];
hex_str[i * 2 + 1] = hex_digits[bytes[i] & 0xF];
}
hex_str[hex_len] = '\0';
JSValue val = JS_NewString(js, hex_str);
free(hex_str);
return val;
)
JSC_CCALL(text_blob_to_base32,
size_t blob_len;
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
uint8_t *bytes = (uint8_t *)blob_data;
static const char b32_digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
// Calculate exact output length needed
size_t groups = (blob_len + 4) / 5; // Round up to next group of 5
size_t b32_len = groups * 8;
char *b32_str = malloc(b32_len + 1);
if (!b32_str) return JS_ThrowOutOfMemory(js);
size_t in_idx = 0;
size_t out_idx = 0;
while (in_idx < blob_len) {
// Read up to 5 bytes into a 40-bit buffer
uint64_t buf = 0;
int bytes_to_read = (blob_len - in_idx < 5) ? (blob_len - in_idx) : 5;
for (int i = 0; i < bytes_to_read; ++i) {
buf = (buf << 8) | bytes[in_idx++];
}
// Pad buffer to 40 bits if we read fewer than 5 bytes
buf <<= 8 * (5 - bytes_to_read);
// Extract 8 groups of 5 bits each
for (int i = 0; i < 8; ++i) {
b32_str[out_idx++] = b32_digits[(buf >> (35 - i * 5)) & 0x1F];
}
}
// Add padding if necessary
if (blob_len % 5 != 0) {
static const int pad_count[] = {0, 6, 4, 3, 1}; // padding for 0,1,2,3,4 bytes
int padding = pad_count[blob_len % 5];
for (int i = 0; i < padding; ++i) {
b32_str[b32_len - 1 - i] = '=';
}
}
b32_str[b32_len] = '\0';
JSValue val = JS_NewString(js, b32_str);
free(b32_str);
return val;
)
static int base32_char_to_val(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a';
if (c >= '2' && c <= '7') return c - '2' + 26;
return -1;
}
JSC_CCALL(text_base32_to_blob,
const char *str = JS_ToCString(js, argv[0]);
if (!str) return JS_ThrowTypeError(js, "Expected string");
size_t str_len = strlen(str);
if (str_len == 0) {
JS_FreeCString(js, str);
return JS_ThrowTypeError(js, "Empty base32 string");
}
// Remove padding to get effective length
size_t effective_len = str_len;
while (effective_len > 0 && str[effective_len - 1] == '=') {
effective_len--;
}
// Calculate output length: each group of 8 base32 chars -> 5 bytes
size_t output_len = (effective_len * 5) / 8;
uint8_t *output = malloc(output_len);
if (!output) {
JS_FreeCString(js, str);
return JS_ThrowOutOfMemory(js);
}
size_t in_idx = 0;
size_t out_idx = 0;
// Process in groups of 8 characters (40 bits -> 5 bytes)
while (in_idx < effective_len) {
uint64_t buf = 0;
int chars_to_read = (effective_len - in_idx < 8) ? (effective_len - in_idx) : 8;
// Read up to 8 base32 characters into buffer
for (int i = 0; i < chars_to_read; ++i) {
int val = base32_char_to_val(str[in_idx++]);
if (val < 0) {
free(output);
JS_FreeCString(js, str);
return JS_ThrowTypeError(js, "Invalid base32 character");
}
buf = (buf << 5) | val;
}
// Calculate how many bytes we can extract
int bytes_to_extract = (chars_to_read * 5) / 8;
// Shift buffer to align the most significant bits
buf <<= (40 - chars_to_read * 5);
// Extract bytes from most significant to least significant
for (int i = 0; i < bytes_to_extract && out_idx < output_len; ++i) {
output[out_idx++] = (buf >> (32 - i * 8)) & 0xFF;
}
}
JSValue val = js_new_blob_stoned_copy(js, output, output_len);
free(output);
JS_FreeCString(js, str);
return val;
)
static int base64_char_to_val_standard(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
return -1;
}
static int base64_char_to_val_url(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '-') return 62;
if (c == '_') return 63;
return -1;
}
/*─── blob → Base64 (standard, with + and /, padded) ───────────────────*/
JSC_CCALL(text_blob_to_base64,
size_t blob_len;
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
const uint8_t *bytes = blob_data;
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
size_t out_len = ((blob_len + 2) / 3) * 4;
char *out = malloc(out_len + 1);
if (!out) return JS_ThrowOutOfMemory(js);
size_t in_i = 0, out_i = 0;
while (in_i < blob_len) {
uint32_t buf = 0;
int to_read = (blob_len - in_i < 3 ? blob_len - in_i : 3);
for (int j = 0; j < to_read; ++j) {
buf = (buf << 8) | bytes[in_i++];
}
buf <<= 8 * (3 - to_read);
out[out_i++] = b64[(buf >> 18) & 0x3F];
out[out_i++] = b64[(buf >> 12) & 0x3F];
out[out_i++] = (to_read > 1 ? b64[(buf >> 6) & 0x3F] : '=');
out[out_i++] = (to_read > 2 ? b64[ buf & 0x3F] : '=');
}
out[out_len] = '\0';
JSValue v = JS_NewString(js, out);
free(out);
return v;
)
/*─── Base64 → blob (standard, expects + and /, pads allowed) ────────────*/
JSC_CCALL(text_base64_to_blob,
const char *str = JS_ToCString(js, argv[0]);
if (!str) return JS_ThrowTypeError(js, "Expected string");
size_t len = strlen(str);
// strip padding for length calculation
size_t eff = len;
while (eff > 0 && str[eff-1] == '=') eff--;
size_t out_len = (eff * 6) / 8;
uint8_t *out = malloc(out_len);
if (!out) { JS_FreeCString(js, str); return JS_ThrowOutOfMemory(js); }
size_t in_i = 0, out_i = 0;
while (in_i < eff) {
uint32_t buf = 0;
int to_read = (eff - in_i < 4 ? eff - in_i : 4);
for (int j = 0; j < to_read; ++j) {
int v = base64_char_to_val_standard(str[in_i++]);
if (v < 0) { free(out); JS_FreeCString(js, str);
return JS_ThrowTypeError(js, "Invalid base64 character"); }
buf = (buf << 6) | v;
}
buf <<= 6 * (4 - to_read);
int bytes_out = (to_read * 6) / 8;
for (int j = 0; j < bytes_out && out_i < out_len; ++j) {
out[out_i++] = (buf >> (16 - 8*j)) & 0xFF;
}
}
JSValue v = js_new_blob_stoned_copy(js, out, out_len);
free(out);
JS_FreeCString(js, str);
return v;
)
/*─── blob → Base64URL (no padding, - and _) ─────────────────────────────*/
JSC_CCALL(text_blob_to_base64url,
size_t blob_len;
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
const uint8_t *bytes = blob_data;
static const char b64url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
size_t raw_len = ((blob_len + 2) / 3) * 4;
// well drop any trailing '='
char *out = malloc(raw_len + 1);
if (!out) return JS_ThrowOutOfMemory(js);
size_t in_i = 0, out_i = 0;
while (in_i < blob_len) {
uint32_t buf = 0;
int to_read = (blob_len - in_i < 3 ? blob_len - in_i : 3);
for (int j = 0; j < to_read; ++j) {
buf = (buf << 8) | bytes[in_i++];
}
buf <<= 8 * (3 - to_read);
out[out_i++] = b64url[(buf >> 18) & 0x3F];
out[out_i++] = b64url[(buf >> 12) & 0x3F];
if (to_read > 1) out[out_i++] = b64url[(buf >> 6) & 0x3F];
if (to_read > 2) out[out_i++] = b64url[ buf & 0x3F];
}
out[out_i] = '\0';
JSValue v = JS_NewString(js, out);
free(out);
return v;
)
/*─── Base64URL → blob (accepts - / _, no padding needed) ─────────────────*/
JSC_CCALL(text_base64url_to_blob,
const char *str = JS_ToCString(js, argv[0]);
if (!str) return JS_ThrowTypeError(js, "Expected string");
size_t len = strlen(str);
size_t eff = len; // no '=' in URLsafe, but strip if present
while (eff > 0 && str[eff-1] == '=') eff--;
size_t out_len = (eff * 6) / 8;
uint8_t *out = malloc(out_len);
if (!out) { JS_FreeCString(js, str); return JS_ThrowOutOfMemory(js); }
size_t in_i = 0, out_i = 0;
while (in_i < eff) {
uint32_t buf = 0;
int to_read = (eff - in_i < 4 ? eff - in_i : 4);
for (int j = 0; j < to_read; ++j) {
int v = base64_char_to_val_url(str[in_i++]);
if (v < 0) { free(out); JS_FreeCString(js, str);
return JS_ThrowTypeError(js, "Invalid base64url character"); }
buf = (buf << 6) | v;
}
buf <<= 6 * (4 - to_read);
int bytes_out = (to_read * 6) / 8;
for (int j = 0; j < bytes_out && out_i < out_len; ++j) {
out[out_i++] = (buf >> (16 - 8*j)) & 0xFF;
}
}
JSValue v = js_new_blob_stoned_copy(js, out, out_len);
free(out);
JS_FreeCString(js, str);
return v;
)
static const JSCFunctionListEntry js_text_funcs[] = {
MIST_FUNC_DEF(text, blob_to_hex, 1),
MIST_FUNC_DEF(text, blob_to_base32, 1),
MIST_FUNC_DEF(text, base32_to_blob, 1),
MIST_FUNC_DEF(text, blob_to_base64, 1),
MIST_FUNC_DEF(text, base64_to_blob, 1),
MIST_FUNC_DEF(text, blob_to_base64url, 1),
MIST_FUNC_DEF(text, base64url_to_blob, 1),
};
JSValue js_internal_text_use(JSContext *js)
{
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_text_funcs, countof(js_text_funcs));
return mod;
}

View File

@@ -1,602 +0,0 @@
/* text.cm - text conversion and formatting utilities */
var blob = use('blob')
var utf8 = use('utf8')
var _toLowerCase = String.prototype.toLowerCase
var _toUpperCase = String.prototype.toUpperCase
var _trim = String.prototype.trim
var _indexOf = String.prototype.indexOf
var _lastIndexOf = String.prototype.lastIndexOf
var _replace = String.prototype.replace
var _normalize = String.prototype.normalize
var _substring = String.prototype.substring
var _charCodeAt = String.prototype.charCodeAt
var _codePointAt = String.prototype.codePointAt
var _String = String
var that = this
// Convert number to string with given radix
function to_radix(num, radix) {
if (radix < 2 || radix > 36) return null;
var digits = "0123456789abcdefghijklmnopqrstuvwxyz";
var result = "";
var n = number.whole(num);
var negative = n < 0;
n = number.abs(n);
if (n == 0) return "0";
while (n > 0) {
result = digits[n % radix] + result;
n = number.floor(n / radix);
}
return negative ? "-" + result : result;
}
// Insert separator every n digits from right
function add_separator(str, sep, n) {
if (!n || n == 0) return str;
var negative = str[0] == '-';
if (negative) str = str.substring(1);
var parts = str.split('.');
var integer = parts[0];
var decimal = parts[1] || '';
// Add separators to integer part
var result = "";
for (var i = integer.length - 1, count = 0; i >= 0; i--) {
if (count == n && i != integer.length - 1) {
result = sep + result;
count = 0;
}
result = integer[i] + result;
count++;
}
if (decimal) result += '.' + decimal;
return negative ? '-' + result : result;
}
// Format number with separator from left
function add_separator_left(str, sep, n) {
if (!n || n == 0) return str;
var negative = str[0] == '-';
if (negative) str = str.substring(1);
var result = "";
for (var i = 0, count = 0; i < str.length; i++) {
if (count == n && i != 0) {
result += sep;
count = 0;
}
result += str[i];
count++;
}
return negative ? '-' + result : result;
}
/* -------- main text function --------------------------------------- */
function text(...arguments) {
var arg = arguments[0];
// Handle blob conversion
if (arg instanceof blob) {
if (!stone.p(arg))
throw new Error("text: blob must be stone for reading");
var format = arguments[1];
var bit_length = arg.length;
var result = "";
if (typeof format == 'string') {
// Extract style from format
var style = '';
for (var i = 0; i < format.length; i++) {
if ((format[i] >= 'a' && format[i] <= 'z') || (format[i] >= 'A' && format[i] <= 'Z')) {
style = format[i];
break;
}
}
// Handle blob encoding styles
switch (style) {
case 'h': // hexadecimal
return that.blob_to_hex(arg);
case 't': // base32
return that.blob_to_base32(arg);
case 'b': // binary
for (var i = 0; i < bit_length; i++) {
result += arg.read_logical(i) ? '1' : '0';
}
return result;
case 'o': // octal
var bits = 0;
var value = 0;
for (var i = 0; i < bit_length; i++) {
var bit = arg.read_logical(i);
value = (value << 1) | (bit ? 1 : 0);
bits++;
if (bits == 3) {
result += value.toString();
bits = 0;
value = 0;
}
}
// Handle remaining bits
if (bits > 0) {
value = value << (3 - bits);
result += value.toString();
}
return result;
}
}
// Default: interpret as UTF-8 text
// Use the utf8 module to decode the blob
if (arg.length == 0) return ""
return utf8.decode(arg);
}
// Handle array conversion
if (isa(arg, array)) {
var separator = arguments[1] || "";
// Check if all items are valid codepoints
var all_codepoints = true;
for (var i = 0; i < arg.length; i++) {
var item = arg[i];
if (!(typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == number.floor(item))) {
all_codepoints = false;
break;
}
}
if (all_codepoints && separator == "") {
// Use utf8 module to convert codepoints to string
return utf8.from_codepoints(arg);
} else {
// General array to string conversion
var result = "";
for (var i = 0; i < arg.length; i++) {
if (i > 0) result += separator;
var item = arg[i];
if (typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == number.floor(item)) {
// Single codepoint - use utf8 module
result += utf8.from_codepoints([item]);
} else {
result += String(item);
}
}
return result;
}
}
// Handle number conversion
if (typeof arg == 'number') {
var format = arguments[1];
// Simple radix conversion
if (typeof format == 'number') {
return to_radix(arg, format);
}
// Format string conversion
if (typeof format == 'string') {
return format_number(arg, format);
}
// Default conversion
return _String(arg);
}
// Handle text operations
if (typeof arg == 'string') {
if (arguments.length == 1) return arg;
var from = arguments[1];
var to = arguments[2];
if (typeof from != 'number' || typeof to != 'number') return arg;
var len = arg.length;
// Adjust negative indices
if (from < 0) from += len;
if (to < 0) to += len;
// Default values
if (from == null) from = 0;
if (to == null) to = len;
// Validate range
if (from < 0 || from > to || to > len) return null;
return arg.substring(from, to);
}
return null;
}
/* -------- number formatting ---------------------------------------- */
function format_number(num, format) {
// Parse format string
var separation = 0;
var style = '';
var places = 0;
var i = 0;
// Parse separation digit
if (i < format.length && format[i] >= '0' && format[i] <= '9') {
separation = number(format[i]);
i++;
}
// Parse style letter
if (i < format.length) {
style = format[i];
i++;
} else {
return null;
}
// Parse places digits
if (i < format.length && format[i] >= '0' && format[i] <= '9') {
places = number(format[i]);
i++;
if (i < format.length && format[i] >= '0' && format[i] <= '9') {
places = places * 10 + number(format[i]);
i++;
}
}
// Invalid format if there's more
if (i < format.length) return null;
// Real number styles
if (style == 'e' || style == 'n' || style == 's' ||
style == 'u' || style == 'd' || style == 'v' || style == 'l') {
var decimal_point = '.';
var separator = '';
var default_separation = 0;
var default_places = 0;
switch (style) {
case 'e': // exponential
decimal_point = '.';
separator = '';
default_separation = 0;
default_places = 0;
break;
case 'n': // number
decimal_point = '.';
separator = '';
default_separation = 0;
default_places = 0;
break;
case 's': // space
decimal_point = '.';
separator = ' ';
default_separation = 3;
default_places = 0;
break;
case 'u': // underbar
decimal_point = '.';
separator = '_';
default_separation = 0;
default_places = 0;
break;
case 'd': // decimal
decimal_point = '.';
separator = ',';
default_separation = 3;
default_places = 2;
break;
case 'v': // comma (European style)
decimal_point = ',';
separator = '.';
default_separation = 0;
default_places = 0;
break;
case 'l': // locale (default to 'd' style for now)
decimal_point = '.';
separator = ',';
default_separation = 3;
default_places = 2;
break;
}
if (separation == 0) separation = default_separation;
if (places == 0 && style != 'e' && style != 'n') places = default_places;
// Format the number
if (style == 'e') {
// Scientific notation
var str = places > 0 ? num.toExponential(places) : num.toExponential();
return str;
} else if (style == 'n' && (number.abs(num) >= 1e21 || (number.abs(num) < 1e-6 && num != 0))) {
// Use scientific notation for extreme values
return num.toExponential();
} else {
// Regular decimal formatting
var str;
if (places > 0) {
str = num.toFixed(places);
} else {
str = num.toString();
}
// Replace decimal point if needed
if (decimal_point != '.') {
str = str.replace('.', decimal_point);
}
// Add separators
if (separation > 0 && separator) {
str = add_separator(str, separator, separation);
}
return str;
}
}
// Integer styles
if (style == 'i' || style == 'b' || style == 'o' ||
style == 'h' || style == 't') {
var radix = 10;
var default_separation = 0;
var default_places = 1;
switch (style) {
case 'i': // integer
radix = 10;
default_separation = 0;
default_places = 1;
break;
case 'b': // binary
radix = 2;
default_separation = 0;
default_places = 1;
break;
case 'o': // octal
radix = 8;
default_separation = 0;
default_places = 1;
break;
case 'h': // hexadecimal
radix = 16;
default_separation = 0;
default_places = 1;
break;
case 't': // base32
radix = 32;
default_separation = 0;
default_places = 1;
break;
}
if (separation == 0) separation = default_separation;
if (places == 0) places = default_places;
// Convert to integer
var n = number.whole(num);
var str = to_radix(n, radix).toUpperCase();
// Pad with zeros if needed
var negative = str[0] == '-';
if (negative) str = str.substring(1);
while (str.length < places) {
str = '0' + str;
}
// Add separators
if (separation > 0) {
str = add_separator_left(str, '_', separation);
}
return negative ? '-' + str : str;
}
return null;
}
/* -------- text sub-functions --------------------------------------- */
text.lower = function(str) {
if (typeof str != 'string') return null
return _toLowerCase.call(str)
}
text.upper = function(str) {
if (typeof str != 'string') return null
return _toUpperCase.call(str)
}
text.trim = function(str, reject) {
if (typeof str != 'string') return null
if (reject == null) return _trim.call(str)
// Custom trim with reject characters
var start = 0
var end = str.length
while (start < end && reject.indexOf(str[start]) >= 0) start++
while (end > start && reject.indexOf(str[end - 1]) >= 0) end--
return _substring.call(str, start, end)
}
text.normalize = function(str) {
if (typeof str != 'string') return null
return _normalize.call(str, 'NFC')
}
text.codepoint = function(str) {
if (typeof str != 'string' || str.length == 0) return null
return _codePointAt.call(str, 0)
}
text.search = function(str, target, from) {
if (typeof str != 'string') return null
if (typeof target != 'string') return null
if (from == null) from = 0
if (from < 0) from += str.length
if (from < 0) from = 0
var result = _indexOf.call(str, target, from)
if (result == -1) return null
return result
}
text.replace = function(str, target, replacement, limit) {
if (typeof str != 'string') return null
if (typeof target != 'string') return null
if (limit == null) {
// Replace all
var result = str
var pos = 0
while (true) {
var idx = _indexOf.call(result, target, pos)
if (idx == -1) break
var rep = replacement
if (typeof replacement == 'function') {
rep = replacement(target, idx)
if (rep == null) {
pos = idx + target.length
continue
}
}
result = _substring.call(result, 0, idx) + rep + _substring.call(result, idx + target.length)
pos = idx + rep.length
}
return result
}
// Replace with limit
var result = str
var pos = 0
var count = 0
while (count < limit) {
var idx = _indexOf.call(result, target, pos)
if (idx == -1) break
var rep = replacement
if (typeof replacement == 'function') {
rep = replacement(target, idx)
if (rep == null) {
pos = idx + target.length
count++
continue
}
}
result = _substring.call(result, 0, idx) + rep + _substring.call(result, idx + target.length)
pos = idx + rep.length
count++
}
return result
}
text.format = function(str, collection, transformer) {
if (typeof str != 'string') return null
var result = ""
var i = 0
while (i < str.length) {
if (str[i] == '{') {
var end = _indexOf.call(str, '}', i)
if (end == -1) {
result += str[i]
i++
continue
}
var middle = _substring.call(str, i + 1, end)
var colonIdx = _indexOf.call(middle, ':')
var key = colonIdx >= 0 ? _substring.call(middle, 0, colonIdx) : middle
var formatSpec = colonIdx >= 0 ? _substring.call(middle, colonIdx + 1) : ""
var value = null
if (isa(collection, array)) {
var idx = number(key)
if (!isNaN(idx) && idx >= 0 && idx < collection.length) {
value = collection[idx]
}
} else if (isa(collection, object)) {
value = collection[key]
}
var substitution = null
if (transformer != null) {
if (typeof transformer == 'function') {
substitution = transformer(value, formatSpec)
} else if (typeof transformer == 'object') {
var fn = transformer[formatSpec]
if (typeof fn == 'function') {
substitution = fn(value)
}
}
}
if (substitution == null && typeof value == 'number' && formatSpec) {
// Try number formatting
substitution = String(value) // simplified
}
if (substitution == null && value != null) {
substitution = String(value)
}
if (substitution != null) {
result += substitution
} else {
result += _substring.call(str, i, end + 1)
}
i = end + 1
} else {
result += str[i]
i++
}
}
return result
}
text.extract = function(str, pattern, from, to) {
// Simplified pattern matching - returns null for now
// Full implementation would require regex or custom pattern language
if (typeof str != 'string') return null
return null
}
return text

View File

@@ -3,7 +3,7 @@
var toml = use('toml')
var fd = use('fd')
var utf8 = use('utf8')
var blob = use('blob')
var os = use('os')
var global_shop_path = os.global_shop_path
@@ -81,7 +81,7 @@ Link.save = function(links) {
link_cache = links
var cfg = { links: links }
var path = get_links_path()
fd.slurpwrite(path, utf8.encode(toml.encode(cfg)))
fd.slurpwrite(path, new blob(toml.encode(cfg)))
}
Link.add = function(canonical, target, shop) {

View File

@@ -1,18 +0,0 @@
var cycles = {}
var Math_obj = Math
cycles.arc_cosine = function(x) { return Math_obj.acos(x) / (2 * pi) }
cycles.arc_sine = function(x) { return Math_obj.asin(x) / (2 * pi) }
cycles.arc_tangent = function(x) { return Math_obj.atan(x) / (2 * pi) }
cycles.cosine = function(x) { return Math_obj.cos(x * 2 * pi) }
cycles.e = Math_obj.E
cycles.ln = function(x) { return Math_obj.log(x) }
cycles.log = function(x) { return Math_obj.log10(x) }
cycles.log2 = function(x) { return Math_obj.log2(x) }
cycles.power = function(x, y) { return Math_obj.pow(x, y) }
cycles.root = function(x, y) { return Math_obj.pow(x, 1 / y) }
cycles.sine = function(x) { return Math_obj.sin(x * 2 * pi) }
cycles.sqrt = function(x) { return Math_obj.sqrt(x) }
cycles.tangent = function(x) { return Math_obj.tan(x * 2 * pi) }
return cycles

View File

@@ -1,21 +0,0 @@
var degrees = {}
var Math_obj = Math
var deg2rad = pi / 180
var rad2deg = 180 / pi
return {
arc_cosine: function(x) { return Math_obj.acos(x) * rad2deg },
arc_sine: function(x) { return Math_obj.asin(x) * rad2deg },
arc_tangent: function(x) { return Math_obj.atan(x) * rad2deg },
cosine: function(x) { return Math_obj.cos(x * deg2rad) },
e: Math_obj.E,
ln: function(x) { return Math_obj.log(x) },
log: function(x) { return Math_obj.log10(x) },
log2: function(x) { return Math_obj.log2(x) },
power: function(x, y) { return Math_obj.pow(x, y) },
root: function(x, y) { return Math_obj.pow(x, 1/y) },
sine: function(x) { return Math_obj.sin(x * deg2rad) },
sqrt: function(x) { return Math_obj.sqrt(x) },
tangent: function(x) { return Math_obj.tan(x * deg2rad) }
}

View File

@@ -1,17 +0,0 @@
var Math = globalThis.Math
return {
arc_cosine: Math.acos,
arc_sine: Math.asin,
arc_tangent: Math.atan,
cosine: Math.cos,
e: Math.E,
ln: Math.log,
log: Math.log10,
log2: Math.log2,
power: Math.pow,
root: function(x, n) { return Math.pow(x, 1/n) },
sine: Math.sin,
sqrt: Math.sqrt,
tangent: Math.tan
}

View File

@@ -38,7 +38,6 @@ endif
link_args = link
sources = []
src += [ # core
'qjs_blob.c',
'monocypher.c',
'cell.c',
'wildmatch.c',
@@ -58,8 +57,6 @@ scripts = [
'wildstar.c',
'fit.c',
'crypto.c',
'internal/text.c',
'utf8.c',
'internal/kim.c',
'time.c',
'internal/nota.c',
@@ -70,7 +67,6 @@ scripts = [
'net/enet.c',
'wildstar.c',
'archive/miniz.c',
'internal/json.c',
]
foreach file: scripts
@@ -78,7 +74,7 @@ foreach file: scripts
endforeach
srceng = 'source'
includes = [srceng, 'internal', 'debug', 'net', 'archive', 'math']
includes = [srceng, 'internal', 'debug', 'net', 'archive']
foreach file : src
full_path = join_paths(srceng, file)

View File

@@ -33,9 +33,9 @@ function fallback(requestor_array) {
return function fallback_requestor(callback, value) {
check_callback(callback, factory)
let index = 0
let current_cancel = null
let cancelled = false
var index = 0
var current_cancel = null
var cancelled = false
function cancel(reason) {
cancelled = true
@@ -98,10 +98,10 @@ function parallel(requestor_array, throttle, need) {
check_callback(callback, factory)
def results = new Array(length)
def cancel_list = new Array(length)
let next_index = 0
let successes = 0
let failures = 0
let finished = false
var next_index = 0
var successes = 0
var failures = 0
var finished = false
function cancel(reason) {
if (finished) return
@@ -154,7 +154,7 @@ function parallel(requestor_array, throttle, need) {
}
def concurrent = throttle ? number.min(throttle, length) : length
for (let i = 0; i < concurrent; i++) start_one()
for (var i = 0; i < concurrent; i++) start_one()
return cancel
}
@@ -180,10 +180,10 @@ function race(requestor_array, throttle, need) {
check_callback(callback, factory)
def results = new Array(length)
def cancel_list = new Array(length)
let next_index = 0
let successes = 0
let failures = 0
let finished = false
var next_index = 0
var successes = 0
var failures = 0
var finished = false
function cancel(reason) {
if (finished) return
@@ -239,7 +239,7 @@ function race(requestor_array, throttle, need) {
}
def concurrent = throttle ? number.min(throttle, length) : length
for (let i = 0; i < concurrent; i++) start_one()
for (var i = 0; i < concurrent; i++) start_one()
return cancel
}
@@ -258,9 +258,9 @@ function sequence(requestor_array) {
return function sequence_requestor(callback, value) {
check_callback(callback, factory)
let index = 0
let current_cancel = null
let cancelled = false
var index = 0
var current_cancel = null
var cancelled = false
function cancel(reason) {
cancelled = true

View File

@@ -138,72 +138,81 @@ void bitcpy(unsigned char *dst, size_t dst_bit_offset,
}
}
// Fast bit-copy for arbitrary bit ranges (inclusive) from src → dest
void copy_bits_fast(const void *src, void *dest,
size_t n, /* start bit in src (inclusive) */
size_t m, /* end bit in src (inclusive) */
size_t x) /* start bit in dest */
static inline uint16_t load16_window(const uint8_t *s, size_t i, size_t last_byte)
{
const uint8_t *s = (const uint8_t*)src;
uint8_t *d = (uint8_t*)dest;
uint16_t lo = s[i];
uint16_t hi = 0;
if (i + 1 <= last_byte) hi = (uint16_t)s[i + 1] << 8;
return lo | hi;
}
size_t total_bits = m - n + 1;
void copy_bits_fast(const void *src, void *dest, size_t n, size_t m, size_t x)
{
if (m < n) return;
size_t src_bit = n;
size_t dst_bit = x;
const uint8_t *s = (const uint8_t *)src;
uint8_t *d = (uint8_t *)dest;
size_t total_bits = m - n + 1;
size_t src_bit = n;
size_t dst_bit = x;
size_t src_byte = src_bit >> 3;
size_t dst_byte = dst_bit >> 3;
int src_off = src_bit & 7;
int dst_off = dst_bit & 7;
int src_off = src_bit & 7;
int dst_off = dst_bit & 7;
// 1) Leading partial byte to align dest
size_t last_src_byte = m >> 3;
/* Fast path: whole bytes, aligned */
if (src_off == 0 && dst_off == 0 && (total_bits & 7) == 0) {
memcpy(d + dst_byte, s + src_byte, total_bits >> 3);
return;
}
/* 1) Leading partial byte to align dest */
if (dst_off != 0) {
size_t chunk = 8 - dst_off;
if (chunk > total_bits) chunk = total_bits;
uint8_t dst_mask = (((1u << chunk) - 1u) << dst_off);
uint16_t wb = (uint16_t)s[src_byte] | ((uint16_t)s[src_byte + 1] << 8);
uint8_t dst_mask = (uint8_t)(((1u << chunk) - 1u) << dst_off);
uint16_t wb = load16_window(s, src_byte, last_src_byte);
uint8_t bits = (uint8_t)((wb >> src_off) & ((1u << chunk) - 1u));
bits <<= dst_off;
d[dst_byte] = (d[dst_byte] & ~dst_mask) | (bits & dst_mask);
d[dst_byte] = (d[dst_byte] & (uint8_t)~dst_mask) | (bits & dst_mask);
total_bits -= chunk;
src_bit += chunk;
dst_bit += chunk;
src_byte = src_bit >> 3;
dst_byte = dst_bit >> 3;
src_off = src_bit & 7;
dst_off = dst_bit & 7; // now zero
src_bit += chunk;
dst_bit += chunk;
src_byte = src_bit >> 3;
dst_byte = dst_bit >> 3;
src_off = src_bit & 7;
dst_off = dst_bit & 7;
}
// 2) Copy full bytes
/* 2) Copy full bytes */
if (total_bits >= 8) {
size_t num_bytes = total_bits >> 3;
if (src_off == 0) {
size_t num_bytes = total_bits >> 3;
memcpy(&d[dst_byte], &s[src_byte], num_bytes);
total_bits -= num_bytes << 3;
src_byte += num_bytes;
dst_byte += num_bytes;
memcpy(d + dst_byte, s + src_byte, num_bytes);
} else {
size_t num_bytes = total_bits >> 3;
for (size_t i = 0; i < num_bytes; i++) {
uint16_t wb = (uint16_t)s[src_byte + i] |
((uint16_t)s[src_byte + i + 1] << 8);
uint16_t wb = load16_window(s, src_byte + i, last_src_byte);
d[dst_byte + i] = (uint8_t)((wb >> src_off) & 0xFFu);
}
total_bits -= num_bytes << 3;
src_byte += num_bytes;
dst_byte += num_bytes;
}
total_bits -= num_bytes << 3;
src_byte += num_bytes;
dst_byte += num_bytes;
}
// 3) Trailing bits (< 8)
/* 3) Trailing bits (< 8), dest is byte-aligned here */
if (total_bits > 0) {
uint8_t dst_mask = (1u << total_bits) - 1u;
uint16_t wb = (uint16_t)s[src_byte] | ((uint16_t)s[src_byte + 1] << 8);
uint8_t dst_mask = (uint8_t)((1u << total_bits) - 1u);
uint16_t wb = load16_window(s, src_byte, last_src_byte);
uint8_t bits = (uint8_t)((wb >> src_off) & dst_mask);
d[dst_byte] = (d[dst_byte] & ~dst_mask) | (bits & dst_mask);
d[dst_byte] = (d[dst_byte] & (uint8_t)~dst_mask) | (bits & dst_mask);
}
}
@@ -378,16 +387,30 @@ int blob_write_text(blob *b, const char *text) {
return 0;
}
int blob_write_bytes(blob *b, const void *data, size_t len_bytes) {
int blob_write_bytes(blob *b, const void *data, size_t len_bytes)
{
if (!b || b->is_stone) return -1;
if (len_bytes == 0) return 0;
size_t bits = len_bytes * 8;
if (blob_ensure_capacity(b, bits) < 0) return -1;
copy_bits_fast(data, b->data, 0, bits - 1, b->length);
b->length += bits;
if (!len_bytes) return 0;
size_t bit_len = len_bytes << 3;
size_t bit_off = b->length;
size_t new_len = bit_off + bit_len;
if (new_len < bit_off) return -1;
if (blob_ensure_capacity(b, new_len) < 0) return -1;
if ((bit_off & 7) == 0) {
uint8_t *dst = (uint8_t *)b->data + (bit_off >> 3);
memcpy(dst, data, len_bytes);
} else {
copy_bits_fast(data, b->data, 0, bit_len - 1, bit_off);
}
b->length = new_len;
return 0;
}
int blob_read_bit(const blob *b, size_t pos, int *out_bit) {
if (!b || !b->is_stone || !out_bit) return -1;
if (pos >= b->length) return -1;

View File

@@ -28,6 +28,10 @@
#include <stddef.h>
#endif
#ifndef NDEBUG
#include <assert.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
@@ -82,6 +86,29 @@ static inline int list_empty(struct list_head *el)
return el->next == el;
}
/* Move all elements from 'src' to 'dst', leaving 'src' empty.
'dst' must be empty before this call. */
static inline void list_splice(struct list_head *dst, struct list_head *src)
{
#ifndef NDEBUG
assert(dst != src);
assert(list_empty(dst));
#endif
if (!list_empty(src)) {
struct list_head *first = src->next;
struct list_head *last = src->prev;
/* Link dst to src's elements */
dst->next = first;
dst->prev = last;
first->prev = dst;
last->next = dst;
/* Reinitialize src as empty */
init_list_head(src);
}
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)

View File

@@ -32,6 +32,7 @@ DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(go, "go")
DEF(var, "var")
DEF(def, "def")
DEF(this, "this")

View File

@@ -207,10 +207,14 @@ DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, atom)
DEF( mul, 1, 2, 1, none)
DEF( mul_float, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( div_float, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( add_float, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( sub_float, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
@@ -325,6 +329,39 @@ DEF( call3, 1, 1, 1, npopx)
DEF( is_null, 1, 1, 1, none)
DEF( typeof_is_function, 1, 1, 1, none)
/* Per-site IC opcodes with quickening support
* These opcodes embed an IC index instead of an atom. The atom is stored
* in the IC slot itself. This allows direct IC lookup without hashing.
*
* Quickening: get_field (atom) -> get_field_ic (ic_index)
* put_field (atom) -> put_field_ic (ic_index)
*
* The ic_index is a u16 that indexes into the function's per_site_ic array.
* Format: opcode (1 byte) + ic_index (2 bytes) = 3 bytes total
*/
DEF( get_field_ic, 3, 1, 1, u16) /* quickened from get_field, ic_index operand */
DEF( get_field2_ic, 3, 1, 2, u16) /* quickened from get_field2, ic_index operand */
DEF( put_field_ic, 3, 2, 0, u16) /* quickened from put_field, ic_index operand */
/* Fused opcodes for get_loc + get_field pattern
* These eliminate a dup/free pair by reading the local without pushing it,
* then doing the field lookup directly.
*
* Format: opcode (1 byte) + loc_idx (2 bytes) + ic_index (2 bytes) = 5 bytes
* Stack: -> value (reads local, does field lookup, pushes result)
*
* The local is NOT consumed (no refcount change to the local variable).
*/
DEF(get_field_loc_ic, 5, 0, 1, u32) /* fused: get_loc(idx) + get_field_ic, operand is loc_idx:u16 + ic_idx:u16 */
DEF(get_field_arg_ic, 5, 0, 1, u32) /* fused: get_arg(idx) + get_field_ic, operand is arg_idx:u16 + ic_idx:u16 */
/* Fused opcodes for put_field pattern with local source
* Format: opcode (1 byte) + loc_idx (2 bytes) + ic_index (2 bytes) = 5 bytes
* Stack: value -> (reads local as object, sets field to value from stack)
*/
DEF(put_field_loc_ic, 5, 1, 0, u32) /* fused: get_loc(idx) + swap + put_field_ic, operand is loc_idx:u16 + ic_idx:u16 */
DEF(put_field_arg_ic, 5, 1, 0, u32) /* fused: get_arg(idx) + swap + put_field_ic, operand is arg_idx:u16 + ic_idx:u16 */
#endif
#undef DEF

File diff suppressed because it is too large Load Diff

12
test.ce
View File

@@ -3,7 +3,7 @@ var pkg = use('package')
var fd = use('fd')
var time = use('time')
var json = use('json')
var utf8 = use('utf8')
var blob = use('blob')
if (!args) args = []
@@ -550,8 +550,9 @@ function finalize_results() {
}
// If no actor tests, finalize immediately
var totals
if (all_actor_tests.length == 0) {
var totals = { total: 0, passed: 0, failed: 0 }
totals = { total: 0, passed: 0, failed: 0 }
for (var i = 0; i < all_results.length; i++) {
totals.total += all_results[i].total
totals.passed += all_results[i].passed
@@ -564,10 +565,9 @@ if (all_actor_tests.length == 0) {
$delay(check_timeouts, 1000)
}
// Generate Reports function
function generate_reports(totals) {
var timestamp = number.floor(time.number()).toString()
var timestamp = text(number.floor(time.number()))
var report_dir = shop.get_reports_dir() + '/test_' + timestamp
ensure_dir(report_dir)
@@ -628,7 +628,7 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
}
}
ensure_dir(report_dir)
fd.slurpwrite(`${report_dir}/test.txt`, utf8.encode(txt_report))
fd.slurpwrite(`${report_dir}/test.txt`, stone(new blob(txt_report)))
log.console(`Report written to ${report_dir}/test.txt`)
// Generate JSON per package
@@ -645,7 +645,7 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
}
var json_path = `${report_dir}/${pkg_res.package.replace(/\//g, '_')}.json`
fd.slurpwrite(json_path, utf8.encode(json.encode(pkg_tests)))
fd.slurpwrite(json_path, stone(new blob(json.encode(pkg_tests))))
}
}

View File

@@ -1,7 +1,7 @@
// Comprehensive test suite for cell runtime stability
// Tests all core features before implementing performance optimizations
// (bytecode passes, ICs, quickening, tail call optimization)
//
return {
// ============================================================================
// ARITHMETIC OPERATORS - Numbers
@@ -145,26 +145,6 @@ return {
if (!caught) throw "string + boolean should throw"
},
test_null_plus_string_throws: function() {
var caught = false
try {
var x = null + "hello"
} catch (e) {
caught = true
}
if (!caught) throw "null + string should throw"
},
test_string_plus_null_throws: function() {
var caught = false
try {
var x = "hello" + null
} catch (e) {
caught = true
}
if (!caught) throw "string + null should throw"
},
// ============================================================================
// COMPARISON OPERATORS
// ============================================================================
@@ -314,10 +294,100 @@ return {
if (x != 10) throw "var reassignment failed"
},
test_var_hoisting: function() {
var result = x
var x = 5
if (result != null) throw "var hoisting should initialize to null"
// ============================================================================
// VAR BLOCK SCOPING (var now behaves like let)
// ============================================================================
test_var_block_scope_basic: function() {
var x = 1
{
var x = 2
if (x != 2) throw "var should be block scoped - inner scope failed"
}
if (x != 1) throw "var should be block scoped - outer scope affected"
},
test_var_block_scope_if: function() {
var x = 1
if (true) {
var x = 2
if (x != 2) throw "var in if block should be scoped"
}
if (x != 1) throw "var in if block should not affect outer scope"
},
test_var_block_scope_for: function() {
var x = 1
for (var i = 0; i < 1; i = i + 1) {
var x = 2
if (x != 2) throw "var in for block should be scoped"
}
if (x != 1) throw "var in for block should not affect outer scope"
},
test_var_for_loop_iterator_scope: function() {
var sum = 0
for (var i = 0; i < 3; i = i + 1) {
sum = sum + i
}
if (sum != 3) throw "for loop should work with block scoped var"
var caught = false
try {
var y = i
} catch (e) {
caught = true
}
if (!caught) throw "for loop iterator should not leak to outer scope"
},
test_var_nested_blocks: function() {
var x = 1
{
var x = 2
{
var x = 3
if (x != 3) throw "var in nested block level 2 failed"
}
if (x != 2) throw "var in nested block level 1 failed"
}
if (x != 1) throw "var in nested blocks outer scope failed"
},
test_var_redeclaration_different_scope: function() {
var x = 1
{
var x = 2
}
if (x != 1) throw "var in different scope should not affect outer"
},
test_var_switch_scope: function() {
var x = 1
switch (1) {
case 1:
var x = 2
if (x != 2) throw "var in switch should be block scoped"
break
}
if (x != 1) throw "var in switch should not affect outer scope"
},
test_var_while_scope: function() {
var x = 1
var count = 0
while (count < 1) {
var x = 2
if (x != 2) throw "var in while should be block scoped"
count = count + 1
}
if (x != 1) throw "var in while should not affect outer scope"
},
test_var_no_initialization: function() {
{
var x
if (x != null) throw "uninitialized var should be null"
}
},
test_multiple_var_declaration: function() {
@@ -856,7 +926,6 @@ return {
test_typeof_object: function() {
if (typeof {} != "object") throw "typeof object failed"
if (typeof [] != "object") throw "typeof array failed"
if (typeof null != "object") throw "typeof null failed"
},
test_typeof_function: function() {
@@ -896,11 +965,6 @@ return {
if (isa(null, object)) throw "isa null not object failed"
},
test_isa_fn: function() {
if (!isa(function(){}, fn)) throw "isa function failed"
if (isa({}, fn)) throw "isa object not function failed"
},
test_isa_null: function() {
if (isa(null, number)) throw "null not number"
if (isa(null, text)) throw "null not text"
@@ -1222,10 +1286,6 @@ return {
if (!(1 == 1 || 2 == 3)) throw "equality before logical precedence failed"
},
test_precedence_bitwise_comparison: function() {
if (!(5 & 3 == 1)) throw "bitwise before comparison precedence failed"
},
test_precedence_unary_multiplication: function() {
if (-2 * 3 != -6) throw "unary before multiplication precedence failed"
},
@@ -1290,16 +1350,6 @@ return {
// NULL AND UNDEFINED BEHAVIOR
// ============================================================================
test_null_property_access_throws: function() {
var caught = false
try {
var x = null.property
} catch (e) {
caught = true
}
if (!caught) throw "null property access should throw"
},
test_undefined_variable_is_null: function() {
var x
if (x != null) throw "undefined variable should be null"
@@ -1529,5 +1579,14 @@ return {
if (!hasNumbers) throw "string match with regex failed"
},
test_string_plus_string_works: function() {
var x = "hello" + " world"
if (x != "hello world") throw "string + string should work"
},
null_access: function() {
var val = {}
var nn = val.a
if (nn != null) throw "val.a should return null"
},
}

View File

@@ -1,34 +0,0 @@
var blob = use('blob');
var utf8 = use('utf8');
return {
test_blob_to_text: function() {
// Test blob to text conversion
var test_string = "Hello, 世界! 🌍";
var encoded_blob = utf8.encode(test_string);
var decoded_text = text(encoded_blob);
if (test_string != decoded_text) throw "Blob to text failed"
},
test_codepoints_to_text: function() {
// Test array of codepoints conversion
var test_string = "Hello, 世界! 🌍";
var codepoints = [72, 101, 108, 108, 111, 44, 32, 19990, 30028, 33, 32, 127757];
var from_codepoints = text(codepoints);
if (from_codepoints != test_string) throw "Codepoints to text failed"
},
test_array_separator: function() {
// Test array with separator
var words = ["Hello", "world", "from", "text"];
var joined = text(words, " ");
if (joined != "Hello world from text") throw "Array with separator failed"
},
test_mixed_array: function() {
// Test mixed array with codepoints
var mixed = [72, "ello", 32, "world"];
var mixed_result = text(mixed, "");
if (mixed_result != "Hello world") throw "Mixed array test failed"
},
}

10
time.cm
View File

@@ -178,16 +178,16 @@ function time_text(num = now(),
/* substitutions */
var full_offset = zone + (dst ? 1 : 0);
fmt = fmt.replaceAll("yyyy", year.toString().padStart(4, "0"));
fmt = fmt.replaceAll("yyyy", text(year, "i4"))
fmt = fmt.replaceAll("y", year);
fmt = fmt.replaceAll("eee", rec.yday + 1);
fmt = fmt.replaceAll("dd", rec.day.toString().padStart(2, "0"));
fmt = fmt.replaceAll("dd", text(rec.day, "i2"))
fmt = fmt.replaceAll("d", rec.day);
fmt = fmt.replaceAll("hh", rec.hour.toString().padStart(2, "0"));
fmt = fmt.replaceAll("hh", text(rec.hour, "i2"));
fmt = fmt.replaceAll("h", rec.hour);
fmt = fmt.replaceAll("nn", rec.minute.toString().padStart(2, "0"));
fmt = fmt.replaceAll("nn", text(rec.minute, "i2"));
fmt = fmt.replaceAll("n", rec.minute);
fmt = fmt.replaceAll("ss", rec.second.toFixed(2).padStart(2, "0"));
fmt = fmt.replaceAll("ss", text(rec.second, "i2"));
fmt = fmt.replaceAll("s", rec.second);
fmt = fmt.replaceAll("x", dst ? "DST" : ""); /* new */
fmt = fmt.replaceAll("z", (full_offset >= 0 ? "+" : "") + text(full_offset));

210
utf8.c
View File

@@ -1,210 +0,0 @@
#include "cell.h"
#include <string.h>
#include <stdlib.h>
#include "kim.h"
// Get codepoints from a UTF-8 string
JSC_CCALL(utf8_codepoints,
const char *str = JS_ToCString(js, argv[0]);
if (!str) return JS_EXCEPTION;
JSValue arr = JS_NewArray(js);
int idx = 0;
char *ptr = (char*)str;
while (*ptr) {
int codepoint = decode_utf8(&ptr);
JS_SetPropertyUint32(js, arr, idx++, JS_NewInt32(js, codepoint));
}
JS_FreeCString(js, str);
ret = arr;
)
// Create UTF-8 string from codepoints
JSC_CCALL(utf8_from_codepoints,
int len = JS_ArrayLength(js, argv[0]);
// Allocate buffer (worst case: 4 bytes per codepoint + null)
char *buffer = malloc(len * 4 + 1);
char *ptr = buffer;
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
int codepoint;
JS_ToInt32(js, &codepoint, val);
JS_FreeValue(js, val);
encode_utf8(&ptr, codepoint);
}
*ptr = '\0';
ret = JS_NewString(js, buffer);
free(buffer);
)
// Count UTF-8 characters (runes) in a string
JSC_SCALL(utf8_length,
int count = utf8_count(str);
ret = JS_NewInt32(js, count);
)
// Validate UTF-8 string
JSC_SCALL(utf8_validate,
char *ptr = (char*)str;
int valid = 1;
while (*ptr) {
int start_pos = ptr - str;
int codepoint = decode_utf8(&ptr);
// Check for invalid sequences
if (codepoint < 0 || codepoint > 0x10FFFF ||
(codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
valid = 0;
break;
}
// Check for overlong encodings
int bytes_used = ptr - (str + start_pos);
if ((codepoint <= 0x7F && bytes_used != 1) ||
(codepoint <= 0x7FF && bytes_used != 2) ||
(codepoint <= 0xFFFF && bytes_used != 3) ||
(codepoint <= 0x10FFFF && bytes_used != 4)) {
valid = 0;
break;
}
}
ret = JS_NewBool(js, valid);
)
// Get byte length of UTF-8 string
JSC_SCALL(utf8_byte_length,
ret = JS_NewInt32(js, strlen(str));
)
// Encode string to UTF-8 bytes
JSC_SCALL(utf8_encode,
size_t len = strlen(str);
ret = js_new_blob_stoned_copy(js, str, len);
)
// Decode UTF-8 bytes to string
JSC_CCALL(utf8_decode,
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (data == (void*)-1) return JS_EXCEPTION;
if (!data || len == 0) return JS_ThrowTypeError(js, "No data present in blob");
// Create null-terminated string
char *str = malloc(len + 1);
memcpy(str, data, len);
str[len] = '\0';
ret = JS_NewString(js, str);
free(str);
)
// Slice UTF-8 string by character indices (not byte indices)
JSC_CCALL(utf8_slice,
const char *str = JS_ToCString(js, argv[0]);
if (!str) return JS_EXCEPTION;
int start = 0;
int end = utf8_count(str);
if (argc > 1) JS_ToInt32(js, &start, argv[1]);
if (argc > 2) JS_ToInt32(js, &end, argv[2]);
// Handle negative indices
int total = end;
if (start < 0) start = total + start;
if (end < 0) end = total + end;
// Clamp values
if (start < 0) start = 0;
if (end > total) end = total;
if (start >= end) {
JS_FreeCString(js, str);
return JS_NewString(js, "");
}
// Find start position
char *ptr = (char*)str;
for (int i = 0; i < start && *ptr; i++) {
decode_utf8(&ptr);
}
char *start_ptr = ptr;
// Find end position
for (int i = start; i < end && *ptr; i++) {
decode_utf8(&ptr);
}
// Create substring
size_t slice_len = ptr - start_ptr;
char *slice = malloc(slice_len + 1);
memcpy(slice, start_ptr, slice_len);
slice[slice_len] = '\0';
ret = JS_NewString(js, slice);
free(slice);
JS_FreeCString(js, str);
)
// Get character at index
JSC_CCALL(utf8_char_at,
const char *str = JS_ToCString(js, argv[0]);
if (!str) return JS_EXCEPTION;
int index;
JS_ToInt32(js, &index, argv[1]);
char *ptr = (char*)str;
int count = 0;
// Skip to index
while (*ptr && count < index) {
decode_utf8(&ptr);
count++;
}
if (!*ptr || count != index) {
JS_FreeCString(js, str);
return JS_NULL;
}
// Get the character
char *char_start = ptr;
decode_utf8(&ptr);
size_t char_len = ptr - char_start;
char *result = malloc(char_len + 1);
memcpy(result, char_start, char_len);
result[char_len] = '\0';
ret = JS_NewString(js, result);
free(result);
JS_FreeCString(js, str);
)
static const JSCFunctionListEntry js_utf8_funcs[] = {
MIST_FUNC_DEF(utf8, codepoints, 1),
MIST_FUNC_DEF(utf8, from_codepoints, 1),
MIST_FUNC_DEF(utf8, length, 1),
MIST_FUNC_DEF(utf8, validate, 1),
MIST_FUNC_DEF(utf8, byte_length, 1),
MIST_FUNC_DEF(utf8, encode, 1),
MIST_FUNC_DEF(utf8, decode, 1),
MIST_FUNC_DEF(utf8, slice, 3),
MIST_FUNC_DEF(utf8, char_at, 2),
};
JSValue js_utf8_use(JSContext *js)
{
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_utf8_funcs, countof(js_utf8_funcs));
return mod;
}