move internals to pure C
This commit is contained in:
@@ -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
|
||||
@@ -32,7 +32,7 @@ globalThis.meme = function(obj, ...mixins) {
|
||||
var result = _ObjectCreate(obj)
|
||||
|
||||
array.for(mixins, mix => {
|
||||
if (isa(mix, object)) {
|
||||
if (is_object(mix)) {
|
||||
for (var key in mix)
|
||||
result[key] = mix[key]
|
||||
}
|
||||
@@ -102,100 +102,34 @@ 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 = []
|
||||
@@ -229,14 +163,14 @@ globalThis.isa = function(value, master) {
|
||||
// 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) {
|
||||
@@ -413,8 +347,6 @@ globalThis.parallel = pronto.parallel
|
||||
globalThis.race = pronto.race
|
||||
globalThis.sequence = pronto.sequence
|
||||
|
||||
|
||||
|
||||
$_.time_limit = function(requestor, seconds)
|
||||
{
|
||||
if (!pronto.is_requestor(requestor))
|
||||
@@ -983,12 +915,9 @@ if (!locator)
|
||||
// Store references we need internally before deleting
|
||||
var _Array = Array
|
||||
var _String = String
|
||||
var _Number = Number
|
||||
var _Boolean = Boolean
|
||||
var _Math = Math
|
||||
var _Function = Function
|
||||
|
||||
var _Error = Error
|
||||
var _JSON = JSON
|
||||
|
||||
// juicing these before Math is gone
|
||||
@@ -1043,7 +972,7 @@ delete globalThis.unescape
|
||||
delete globalThis.Intl
|
||||
delete globalThis.RegExp
|
||||
|
||||
_ObjectFreeze(globalThis)
|
||||
stone(globalThis)
|
||||
|
||||
$_.clock(_ => {
|
||||
// Get capabilities for the main program
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
306
internal/text.c
306
internal/text.c
@@ -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;
|
||||
// we’ll 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 URL‐safe, 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;
|
||||
}
|
||||
602
internal/text.cm
602
internal/text.cm
@@ -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
|
||||
@@ -38,7 +38,6 @@ endif
|
||||
link_args = link
|
||||
sources = []
|
||||
src += [ # core
|
||||
'qjs_blob.c',
|
||||
'monocypher.c',
|
||||
'cell.c',
|
||||
'wildmatch.c',
|
||||
@@ -58,7 +57,6 @@ scripts = [
|
||||
'wildstar.c',
|
||||
'fit.c',
|
||||
'crypto.c',
|
||||
'internal/text.c',
|
||||
'utf8.c',
|
||||
'internal/kim.c',
|
||||
'time.c',
|
||||
|
||||
3315
source/quickjs.c
3315
source/quickjs.c
File diff suppressed because it is too large
Load Diff
2
test.ce
2
test.ce
@@ -567,7 +567,7 @@ if (all_actor_tests.length == 0) {
|
||||
|
||||
// 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)
|
||||
|
||||
|
||||
10
time.cm
10
time.cm
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user