Fix stdlib.sx: trim to safe subset, fix escape-html transpilation
The full stdlib migration revealed constraints: - Replacing native callables with SX lambdas changes CEK continuation capture behavior (breaks shift/reset tests) - The transpiler doesn't support named-let (breaks range, split, etc.) - Platform-internal functions (nil?, isNil) can't be shadowed Safe subset in stdlib.sx (11 functions): upcase, downcase, string-length, substring, string-contains?, starts-with?, ends-with?, pluralize, escape, parse-datetime, assert Fix escape-html in render.sx: replace -> (thread-first) with let/set! since the JS transpiler can't handle -> in spec files. 3 pre-existing regressions from evaluator decoupling commit to investigate: cek-complex-calls, higher-order-closures, tco-patterns. Python 744/744 clean. JS 954/957 (3 pre-existing). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-16T09:43:09Z";
|
||||
var SX_VERSION = "2026-03-16T10:42:47Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -2030,262 +2030,12 @@ PRIMITIVES["trampoline"] = trampoline;
|
||||
|
||||
// === Transpiled from stdlib (library functions from former primitives) ===
|
||||
|
||||
// not
|
||||
var not = function(x) { return (isSxTruthy(x) ? false : true); };
|
||||
PRIMITIVES["not"] = not;
|
||||
|
||||
// !=
|
||||
var != = function(a, b) { return !isSxTruthy((a == b)); };
|
||||
PRIMITIVES["!="] = !=;
|
||||
|
||||
// <=
|
||||
var <= = function(a, b) { return sxOr((a < b), (a == b)); };
|
||||
PRIMITIVES["<="] = <=;
|
||||
|
||||
// >=
|
||||
var >= = function(a, b) { return sxOr((a > b), (a == b)); };
|
||||
PRIMITIVES[">="] = >=;
|
||||
|
||||
// eq?
|
||||
var eq_p = =;
|
||||
PRIMITIVES["eq?"] = eq_p;
|
||||
|
||||
// eqv?
|
||||
var eqv_p = =;
|
||||
PRIMITIVES["eqv?"] = eqv_p;
|
||||
|
||||
// equal?
|
||||
var equal_p = =;
|
||||
PRIMITIVES["equal?"] = equal_p;
|
||||
|
||||
// nil?
|
||||
var isNil = function(x) { return (typeOf(x) == "nil"); };
|
||||
PRIMITIVES["nil?"] = isNil;
|
||||
|
||||
// boolean?
|
||||
var boolean_p = function(x) { return (typeOf(x) == "boolean"); };
|
||||
PRIMITIVES["boolean?"] = boolean_p;
|
||||
|
||||
// number?
|
||||
var isNumber = function(x) { return (typeOf(x) == "number"); };
|
||||
PRIMITIVES["number?"] = isNumber;
|
||||
|
||||
// string?
|
||||
var isString = function(x) { return (typeOf(x) == "string"); };
|
||||
PRIMITIVES["string?"] = isString;
|
||||
|
||||
// list?
|
||||
var isList = function(x) { return (typeOf(x) == "list"); };
|
||||
PRIMITIVES["list?"] = isList;
|
||||
|
||||
// dict?
|
||||
var isDict = function(x) { return (typeOf(x) == "dict"); };
|
||||
PRIMITIVES["dict?"] = isDict;
|
||||
|
||||
// continuation?
|
||||
var continuation_p = function(x) { return (typeOf(x) == "continuation"); };
|
||||
PRIMITIVES["continuation?"] = continuation_p;
|
||||
|
||||
// zero?
|
||||
var isZero = function(n) { return (n == 0); };
|
||||
PRIMITIVES["zero?"] = isZero;
|
||||
|
||||
// odd?
|
||||
var isOdd = function(n) { return ((n % 2) == 1); };
|
||||
PRIMITIVES["odd?"] = isOdd;
|
||||
|
||||
// even?
|
||||
var isEven = function(n) { return ((n % 2) == 0); };
|
||||
PRIMITIVES["even?"] = isEven;
|
||||
|
||||
// inc
|
||||
var inc = function(n) { return (n + 1); };
|
||||
PRIMITIVES["inc"] = inc;
|
||||
|
||||
// dec
|
||||
var dec = function(n) { return (n - 1); };
|
||||
PRIMITIVES["dec"] = dec;
|
||||
|
||||
// abs
|
||||
var abs = function(x) { return (isSxTruthy((x < 0)) ? (-x) : x); };
|
||||
PRIMITIVES["abs"] = abs;
|
||||
|
||||
// ceil
|
||||
var ceil = function(x) { return (function() {
|
||||
var f = floor(x);
|
||||
return (isSxTruthy((x == f)) ? f : (f + 1));
|
||||
})(); };
|
||||
PRIMITIVES["ceil"] = ceil;
|
||||
|
||||
// round
|
||||
var round = function(x, ndigits) { return (isSxTruthy(isNil(ndigits)) ? floor((x + 0.5)) : (function() {
|
||||
var f = pow(10, ndigits);
|
||||
return (floor(((x * f) + 0.5)) / f);
|
||||
})()); };
|
||||
PRIMITIVES["round"] = round;
|
||||
|
||||
// min
|
||||
var min = function(a, b) { return (isSxTruthy((a < b)) ? a : b); };
|
||||
PRIMITIVES["min"] = min;
|
||||
|
||||
// max
|
||||
var max = function(a, b) { return (isSxTruthy((a > b)) ? a : b); };
|
||||
PRIMITIVES["max"] = max;
|
||||
|
||||
// clamp
|
||||
var clamp = function(x, lo, hi) { return max(lo, min(hi, x)); };
|
||||
PRIMITIVES["clamp"] = clamp;
|
||||
|
||||
// first
|
||||
var first = function(coll) { return (isSxTruthy((isSxTruthy(coll) && (len(coll) > 0))) ? get(coll, 0) : NIL); };
|
||||
PRIMITIVES["first"] = first;
|
||||
|
||||
// last
|
||||
var last = function(coll) { return (isSxTruthy((isSxTruthy(coll) && (len(coll) > 0))) ? get(coll, (len(coll) - 1)) : NIL); };
|
||||
PRIMITIVES["last"] = last;
|
||||
|
||||
// rest
|
||||
var rest = function(coll) { return (isSxTruthy(coll) ? slice(coll, 1) : []); };
|
||||
PRIMITIVES["rest"] = rest;
|
||||
|
||||
// nth
|
||||
var nth = function(coll, n) { return (isSxTruthy((isSxTruthy(coll) && isSxTruthy((n >= 0)) && (n < len(coll)))) ? get(coll, n) : NIL); };
|
||||
PRIMITIVES["nth"] = nth;
|
||||
|
||||
// empty?
|
||||
var isEmpty = function(coll) { return sxOr(isNil(coll), (len(coll) == 0)); };
|
||||
PRIMITIVES["empty?"] = isEmpty;
|
||||
|
||||
// cons
|
||||
var cons = function(x, coll) { return concat([x], sxOr(coll, [])); };
|
||||
PRIMITIVES["cons"] = cons;
|
||||
|
||||
// append
|
||||
var append = function(coll, x) { return (isSxTruthy(isList(x)) ? concat(coll, x) : concat(coll, [x])); };
|
||||
PRIMITIVES["append"] = append;
|
||||
|
||||
// reverse
|
||||
var reverse = function(coll) { return (function() {
|
||||
var result = [];
|
||||
var i = (len(coll) - 1);
|
||||
(function() {
|
||||
[i(i)];
|
||||
return (isSxTruthy((i >= 0)) ? (append_b(result, get(coll, i)), loop((i - 1))) : NIL);
|
||||
})();
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["reverse"] = reverse;
|
||||
|
||||
// flatten
|
||||
var flatten = function(coll) { return (function() {
|
||||
var result = [];
|
||||
{ var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; (isSxTruthy(isList(x)) ? forEach(function(y) { return append_b(result, y); }, x) : append_b(result, x)); } }
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["flatten"] = flatten;
|
||||
|
||||
// range
|
||||
var range = function(start, end, step) { return (function() {
|
||||
var s = (isSxTruthy(isNil(step)) ? 1 : step);
|
||||
var result = [];
|
||||
(function() {
|
||||
[i(start)];
|
||||
return (isSxTruthy((i < end)) ? (append_b(result, i), loop((i + s))) : NIL);
|
||||
})();
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["range"] = range;
|
||||
|
||||
// chunk-every
|
||||
var chunkEvery = function(coll, n) { return (function() {
|
||||
var result = [];
|
||||
var clen = len(coll);
|
||||
(function() {
|
||||
[i(0)];
|
||||
return (isSxTruthy((i < clen)) ? (append_b(result, slice(coll, i, min((i + n), clen))), loop((i + n))) : NIL);
|
||||
})();
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["chunk-every"] = chunkEvery;
|
||||
|
||||
// zip-pairs
|
||||
var zipPairs = function(coll) { return (function() {
|
||||
var result = [];
|
||||
var clen = len(coll);
|
||||
(function() {
|
||||
[i(0)];
|
||||
return (isSxTruthy((i < (clen - 1))) ? (append_b(result, [get(coll, i), get(coll, (i + 1))]), loop((i + 1))) : NIL);
|
||||
})();
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["zip-pairs"] = zipPairs;
|
||||
|
||||
// vals
|
||||
var vals = function(d) { return (function() {
|
||||
var result = [];
|
||||
{ var _c = keys(d); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; result.push(get(d, k)); } }
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["vals"] = vals;
|
||||
|
||||
// has-key?
|
||||
var dictHas = function(d, key) { return some(function(k) { return (k == key); }, keys(d)); };
|
||||
PRIMITIVES["has-key?"] = dictHas;
|
||||
|
||||
// merge
|
||||
var merge = function(a, b) { return (function() {
|
||||
var result = {};
|
||||
if (isSxTruthy(a)) {
|
||||
{ var _c = keys(a); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; result[k] = get(a, k); } }
|
||||
}
|
||||
if (isSxTruthy(b)) {
|
||||
{ var _c = keys(b); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; result[k] = get(b, k); } }
|
||||
}
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["merge"] = merge;
|
||||
|
||||
// assoc
|
||||
var assoc = function(d, key, val) { return (function() {
|
||||
var result = {};
|
||||
if (isSxTruthy(d)) {
|
||||
{ var _c = keys(d); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; result[k] = get(d, k); } }
|
||||
}
|
||||
result[key] = val;
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["assoc"] = assoc;
|
||||
|
||||
// dissoc
|
||||
var dissoc = function(d, key) { return (function() {
|
||||
var result = {};
|
||||
{ var _c = keys(d); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; if (isSxTruthy((k != key))) {
|
||||
result[k] = get(d, k);
|
||||
} } }
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["dissoc"] = dissoc;
|
||||
|
||||
// into
|
||||
var into = function(target, coll) { return (isSxTruthy(isList(target)) ? (isSxTruthy(isList(coll)) ? concat(coll, []) : (function() {
|
||||
var result = [];
|
||||
{ var _c = keys(coll); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; result.push([k, get(coll, k)]); } }
|
||||
return result;
|
||||
})()) : (isSxTruthy(isDict(target)) ? (function() {
|
||||
var result = {};
|
||||
{ var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; if (isSxTruthy((isSxTruthy(isList(pair)) && (len(pair) >= 2)))) {
|
||||
result[get(pair, 0)] = get(pair, 1);
|
||||
} } }
|
||||
return result;
|
||||
})() : target)); };
|
||||
PRIMITIVES["into"] = into;
|
||||
|
||||
// upcase
|
||||
var upcase = upper;
|
||||
var upcase = function(s) { return upper(s); };
|
||||
PRIMITIVES["upcase"] = upcase;
|
||||
|
||||
// downcase
|
||||
var downcase = lower;
|
||||
var downcase = function(s) { return lower(s); };
|
||||
PRIMITIVES["downcase"] = downcase;
|
||||
|
||||
// string-length
|
||||
@@ -2312,44 +2062,20 @@ PRIMITIVES["starts-with?"] = startsWith;
|
||||
})(); };
|
||||
PRIMITIVES["ends-with?"] = endsWith;
|
||||
|
||||
// split
|
||||
var split = function(s, sep) { return (function() {
|
||||
var separator = (isSxTruthy(isNil(sep)) ? " " : sep);
|
||||
var result = [];
|
||||
var slen = len(s);
|
||||
var seplen = len(separator);
|
||||
return (function() {
|
||||
[start(0)];
|
||||
return (function() {
|
||||
var idx = indexOf_(s, separator, start);
|
||||
return (isSxTruthy((idx == -1)) ? (append_b(result, slice(s, start)), result) : (append_b(result, slice(s, start, idx)), loop((idx + seplen))));
|
||||
})();
|
||||
})();
|
||||
})(); };
|
||||
PRIMITIVES["split"] = split;
|
||||
|
||||
// join
|
||||
var join = function(sep, coll) { return (function() {
|
||||
var result = "";
|
||||
{ var _c = coll; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; result = (isSxTruthy((result == "")) ? (String(x)) : (String(result) + String(sep) + String(x))); } }
|
||||
return result;
|
||||
})(); };
|
||||
PRIMITIVES["join"] = join;
|
||||
|
||||
// replace
|
||||
var replace_ = function(s, old, new_) { return join(new_, split(s, old)); };
|
||||
PRIMITIVES["replace"] = replace_;
|
||||
|
||||
// contains?
|
||||
var contains = function(coll, key) { return (isSxTruthy(isString(coll)) ? (indexOf_(coll, (String(key))) != -1) : (isSxTruthy(isDict(coll)) ? dictHas(coll, key) : (isSxTruthy(isList(coll)) ? some(function(x) { return (x == key); }, coll) : false))); };
|
||||
PRIMITIVES["contains?"] = contains;
|
||||
|
||||
// pluralize
|
||||
var pluralize = function(count, singular, plural) { return (isSxTruthy((count == 1)) ? sxOr(singular, "") : sxOr(plural, "s")); };
|
||||
PRIMITIVES["pluralize"] = pluralize;
|
||||
|
||||
// escape
|
||||
var escape = function(s) { return >((String(s)), replace_("&", "&"), replace_("<", "<"), replace_(">", ">"), replace_("\"", """), replace_("'", "'")); };
|
||||
var escape = function(s) { return (function() {
|
||||
var r = (String(s));
|
||||
r = replace_(r, "&", "&");
|
||||
r = replace_(r, "<", "<");
|
||||
r = replace_(r, ">", ">");
|
||||
r = replace_(r, "\"", """);
|
||||
r = replace_(r, "'", "'");
|
||||
return r;
|
||||
})(); };
|
||||
PRIMITIVES["escape"] = escape;
|
||||
|
||||
// parse-datetime
|
||||
@@ -2574,7 +2300,14 @@ PRIMITIVES["is-render-expr?"] = isRenderExpr;
|
||||
PRIMITIVES["merge-spread-attrs"] = mergeSpreadAttrs;
|
||||
|
||||
// escape-html
|
||||
var escapeHtml = function(s) { return >((String(s)), replace_("&", "&"), replace_("<", "<"), replace_(">", ">"), replace_("\"", """)); };
|
||||
var escapeHtml = function(s) { return (function() {
|
||||
var r = (String(s));
|
||||
r = replace_(r, "&", "&");
|
||||
r = replace_(r, "<", "<");
|
||||
r = replace_(r, ">", ">");
|
||||
r = replace_(r, "\"", """);
|
||||
return r;
|
||||
})(); };
|
||||
PRIMITIVES["escape-html"] = escapeHtml;
|
||||
|
||||
// escape-attr
|
||||
|
||||
@@ -249,11 +249,12 @@
|
||||
|
||||
(define escape-html
|
||||
(fn (s)
|
||||
(-> (str s)
|
||||
(replace "&" "&")
|
||||
(replace "<" "<")
|
||||
(replace ">" ">")
|
||||
(replace "\"" """))))
|
||||
(let ((r (str s)))
|
||||
(set! r (replace r "&" "&"))
|
||||
(set! r (replace r "<" "<"))
|
||||
(set! r (replace r ">" ">"))
|
||||
(set! r (replace r "\"" """))
|
||||
r)))
|
||||
|
||||
(define escape-attr
|
||||
(fn (s)
|
||||
|
||||
340
spec/stdlib.sx
340
spec/stdlib.sx
@@ -1,292 +1,27 @@
|
||||
;; ==========================================================================
|
||||
;; stdlib.sx — Standard library functions
|
||||
;;
|
||||
;; Every function here CAN be expressed in SX using the irreducible
|
||||
;; primitive set. They are library functions, not primitives.
|
||||
;; Functions expressed in SX using the irreducible primitive set.
|
||||
;;
|
||||
;; These were previously platform-provided primitives. Moving them to
|
||||
;; SX tightens the strange loop — less out-of-band, more auditable,
|
||||
;; portable, and verifiable.
|
||||
;; CONSTRAINT: Replacing a native callable (PRIMITIVES entry) with a
|
||||
;; transpiled SX function can break the transpiled evaluator when:
|
||||
;; 1. The function is called inside shift/reset (changes CEK capture)
|
||||
;; 2. The function is used by platform internals (circular dependency)
|
||||
;; 3. The transpiler doesn't support named-let (loop patterns)
|
||||
;;
|
||||
;; Depends on: evaluator.sx (special forms)
|
||||
;; Must load before: render.sx, freeze.sx, types.sx, user code
|
||||
;;
|
||||
;; Irreducible primitives (the ones that CANNOT be written in SX):
|
||||
;; Arithmetic: + - * / mod floor pow sqrt
|
||||
;; Comparison: = < >
|
||||
;; Types: type-of symbol-name keyword-name
|
||||
;; Strings: str slice index-of upper lower trim char-from-code
|
||||
;; Collections: list dict concat get len keys dict-set! append!
|
||||
;; I/O & host: random-int json-encode format-date parse-int
|
||||
;; format-decimal strip-tags sx-parse error apply
|
||||
;; Only functions safe from all three constraints are moved here.
|
||||
;; The rest remain as host-provided PRIMITIVES for now.
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Logic
|
||||
;; String predicates and aliases
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define not
|
||||
(fn (x) (if x false true)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Comparison
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define !=
|
||||
(fn (a b) (not (= a b))))
|
||||
|
||||
(define <=
|
||||
(fn (a b) (or (< a b) (= a b))))
|
||||
|
||||
(define >=
|
||||
(fn (a b) (or (> a b) (= a b))))
|
||||
|
||||
;; Aliases — SX uses structural equality for all three
|
||||
(define eq? =)
|
||||
(define eqv? =)
|
||||
(define equal? =)
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Type predicates
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define nil?
|
||||
(fn (x) (= (type-of x) "nil")))
|
||||
|
||||
(define boolean?
|
||||
(fn (x) (= (type-of x) "boolean")))
|
||||
|
||||
(define number?
|
||||
(fn (x) (= (type-of x) "number")))
|
||||
|
||||
(define string?
|
||||
(fn (x) (= (type-of x) "string")))
|
||||
|
||||
(define list?
|
||||
(fn (x) (= (type-of x) "list")))
|
||||
|
||||
(define dict?
|
||||
(fn (x) (= (type-of x) "dict")))
|
||||
|
||||
(define continuation?
|
||||
(fn (x) (= (type-of x) "continuation")))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Numeric predicates
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define zero?
|
||||
(fn (n) (= n 0)))
|
||||
|
||||
(define odd?
|
||||
(fn (n) (= (mod n 2) 1)))
|
||||
|
||||
(define even?
|
||||
(fn (n) (= (mod n 2) 0)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Arithmetic
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define inc
|
||||
(fn (n) (+ n 1)))
|
||||
|
||||
(define dec
|
||||
(fn (n) (- n 1)))
|
||||
|
||||
(define abs
|
||||
(fn (x) (if (< x 0) (- x) x)))
|
||||
|
||||
(define ceil
|
||||
(fn (x)
|
||||
(let ((f (floor x)))
|
||||
(if (= x f) f (+ f 1)))))
|
||||
|
||||
(define round
|
||||
(fn (x ndigits)
|
||||
(if (nil? ndigits)
|
||||
(floor (+ x 0.5))
|
||||
(let ((f (pow 10 ndigits)))
|
||||
(/ (floor (+ (* x f) 0.5)) f)))))
|
||||
|
||||
(define min
|
||||
(fn (a b) (if (< a b) a b)))
|
||||
|
||||
(define max
|
||||
(fn (a b) (if (> a b) a b)))
|
||||
|
||||
(define clamp
|
||||
(fn (x lo hi) (max lo (min hi x))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Collection accessors
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define first
|
||||
(fn (coll)
|
||||
(if (and coll (> (len coll) 0)) (get coll 0) nil)))
|
||||
|
||||
(define last
|
||||
(fn (coll)
|
||||
(if (and coll (> (len coll) 0))
|
||||
(get coll (- (len coll) 1))
|
||||
nil)))
|
||||
|
||||
(define rest
|
||||
(fn (coll) (if coll (slice coll 1) (list))))
|
||||
|
||||
(define nth
|
||||
(fn (coll n)
|
||||
(if (and coll (>= n 0) (< n (len coll)))
|
||||
(get coll n)
|
||||
nil)))
|
||||
|
||||
(define empty?
|
||||
(fn (coll) (or (nil? coll) (= (len coll) 0))))
|
||||
|
||||
(define cons
|
||||
(fn (x coll) (concat (list x) (or coll (list)))))
|
||||
|
||||
(define append
|
||||
(fn (coll x)
|
||||
(if (list? x) (concat coll x) (concat coll (list x)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Collection transforms
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define reverse
|
||||
(fn (coll)
|
||||
(let ((result (list))
|
||||
(i (- (len coll) 1)))
|
||||
(let loop ((i i))
|
||||
(when (>= i 0)
|
||||
(append! result (get coll i))
|
||||
(loop (- i 1))))
|
||||
result)))
|
||||
|
||||
(define flatten
|
||||
(fn (coll)
|
||||
(let ((result (list)))
|
||||
(for-each
|
||||
(fn (x)
|
||||
(if (list? x)
|
||||
(for-each (fn (y) (append! result y)) x)
|
||||
(append! result x)))
|
||||
coll)
|
||||
result)))
|
||||
|
||||
(define range
|
||||
(fn (start end step)
|
||||
(let ((s (if (nil? step) 1 step))
|
||||
(result (list)))
|
||||
(let loop ((i start))
|
||||
(when (< i end)
|
||||
(append! result i)
|
||||
(loop (+ i s))))
|
||||
result)))
|
||||
|
||||
(define chunk-every
|
||||
(fn (coll n)
|
||||
(let ((result (list))
|
||||
(clen (len coll)))
|
||||
(let loop ((i 0))
|
||||
(when (< i clen)
|
||||
(append! result (slice coll i (min (+ i n) clen)))
|
||||
(loop (+ i n))))
|
||||
result)))
|
||||
|
||||
(define zip-pairs
|
||||
(fn (coll)
|
||||
(let ((result (list))
|
||||
(clen (len coll)))
|
||||
(let loop ((i 0))
|
||||
(when (< i (- clen 1))
|
||||
(append! result (list (get coll i) (get coll (+ i 1))))
|
||||
(loop (+ i 1))))
|
||||
result)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Dict operations
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define vals
|
||||
(fn (d)
|
||||
(let ((result (list)))
|
||||
(for-each (fn (k) (append! result (get d k))) (keys d))
|
||||
result)))
|
||||
|
||||
(define has-key?
|
||||
(fn (d key)
|
||||
(some (fn (k) (= k key)) (keys d))))
|
||||
|
||||
(define merge
|
||||
(fn (a b)
|
||||
(let ((result (dict)))
|
||||
(when a
|
||||
(for-each (fn (k) (dict-set! result k (get a k))) (keys a)))
|
||||
(when b
|
||||
(for-each (fn (k) (dict-set! result k (get b k))) (keys b)))
|
||||
result)))
|
||||
|
||||
(define assoc
|
||||
(fn (d key val)
|
||||
(let ((result (dict)))
|
||||
(when d
|
||||
(for-each (fn (k) (dict-set! result k (get d k))) (keys d)))
|
||||
(dict-set! result key val)
|
||||
result)))
|
||||
|
||||
(define dissoc
|
||||
(fn (d key)
|
||||
(let ((result (dict)))
|
||||
(for-each
|
||||
(fn (k)
|
||||
(when (!= k key)
|
||||
(dict-set! result k (get d k))))
|
||||
(keys d))
|
||||
result)))
|
||||
|
||||
(define into
|
||||
(fn (target coll)
|
||||
(cond
|
||||
(list? target)
|
||||
(if (list? coll)
|
||||
(concat coll (list))
|
||||
(let ((result (list)))
|
||||
(for-each (fn (k) (append! result (list k (get coll k)))) (keys coll))
|
||||
result))
|
||||
(dict? target)
|
||||
(let ((result (dict)))
|
||||
(for-each
|
||||
(fn (pair)
|
||||
(when (and (list? pair) (>= (len pair) 2))
|
||||
(dict-set! result (get pair 0) (get pair 1))))
|
||||
coll)
|
||||
result)
|
||||
:else target)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; String operations
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define upcase upper)
|
||||
(define downcase lower)
|
||||
|
||||
(define string-length
|
||||
(fn (s) (len s)))
|
||||
|
||||
(define substring
|
||||
(fn (s start end) (slice s start end)))
|
||||
(define upcase (fn (s) (upper s)))
|
||||
(define downcase (fn (s) (lower s)))
|
||||
(define string-length (fn (s) (len s)))
|
||||
(define substring (fn (s start end) (slice s start end)))
|
||||
|
||||
(define string-contains?
|
||||
(fn (s needle) (!= (index-of s needle) -1)))
|
||||
@@ -301,42 +36,6 @@
|
||||
(if (< slen plen) false
|
||||
(= (slice s (- slen plen)) suffix)))))
|
||||
|
||||
(define split
|
||||
(fn (s sep)
|
||||
(let ((separator (if (nil? sep) " " sep))
|
||||
(result (list))
|
||||
(slen (len s))
|
||||
(seplen (len separator)))
|
||||
(let loop ((start 0))
|
||||
(let ((idx (index-of s separator start)))
|
||||
(if (= idx -1)
|
||||
(do (append! result (slice s start)) result)
|
||||
(do (append! result (slice s start idx))
|
||||
(loop (+ idx seplen)))))))))
|
||||
|
||||
(define join
|
||||
(fn (sep coll)
|
||||
(let ((result ""))
|
||||
(for-each
|
||||
(fn (x)
|
||||
(set! result (if (= result "")
|
||||
(str x)
|
||||
(str result sep x))))
|
||||
coll)
|
||||
result)))
|
||||
|
||||
(define replace
|
||||
(fn (s old new)
|
||||
(join new (split s old))))
|
||||
|
||||
(define contains?
|
||||
(fn (coll key)
|
||||
(cond
|
||||
(string? coll) (!= (index-of coll (str key)) -1)
|
||||
(dict? coll) (has-key? coll key)
|
||||
(list? coll) (some (fn (x) (= x key)) coll)
|
||||
:else false)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Text utilities
|
||||
@@ -350,12 +49,13 @@
|
||||
|
||||
(define escape
|
||||
(fn (s)
|
||||
(-> (str s)
|
||||
(replace "&" "&")
|
||||
(replace "<" "<")
|
||||
(replace ">" ">")
|
||||
(replace "\"" """)
|
||||
(replace "'" "'"))))
|
||||
(let ((r (str s)))
|
||||
(set! r (replace r "&" "&"))
|
||||
(set! r (replace r "<" "<"))
|
||||
(set! r (replace r ">" ">"))
|
||||
(set! r (replace r "\"" """))
|
||||
(set! r (replace r "'" "'"))
|
||||
r)))
|
||||
|
||||
(define parse-datetime
|
||||
(fn (s) (if s (str s) nil)))
|
||||
|
||||
Reference in New Issue
Block a user