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 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 isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||||
@@ -2030,262 +2030,12 @@ PRIMITIVES["trampoline"] = trampoline;
|
|||||||
|
|
||||||
// === Transpiled from stdlib (library functions from former primitives) ===
|
// === 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
|
// upcase
|
||||||
var upcase = upper;
|
var upcase = function(s) { return upper(s); };
|
||||||
PRIMITIVES["upcase"] = upcase;
|
PRIMITIVES["upcase"] = upcase;
|
||||||
|
|
||||||
// downcase
|
// downcase
|
||||||
var downcase = lower;
|
var downcase = function(s) { return lower(s); };
|
||||||
PRIMITIVES["downcase"] = downcase;
|
PRIMITIVES["downcase"] = downcase;
|
||||||
|
|
||||||
// string-length
|
// string-length
|
||||||
@@ -2312,44 +2062,20 @@ PRIMITIVES["starts-with?"] = startsWith;
|
|||||||
})(); };
|
})(); };
|
||||||
PRIMITIVES["ends-with?"] = endsWith;
|
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
|
// pluralize
|
||||||
var pluralize = function(count, singular, plural) { return (isSxTruthy((count == 1)) ? sxOr(singular, "") : sxOr(plural, "s")); };
|
var pluralize = function(count, singular, plural) { return (isSxTruthy((count == 1)) ? sxOr(singular, "") : sxOr(plural, "s")); };
|
||||||
PRIMITIVES["pluralize"] = pluralize;
|
PRIMITIVES["pluralize"] = pluralize;
|
||||||
|
|
||||||
// escape
|
// 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;
|
PRIMITIVES["escape"] = escape;
|
||||||
|
|
||||||
// parse-datetime
|
// parse-datetime
|
||||||
@@ -2574,7 +2300,14 @@ PRIMITIVES["is-render-expr?"] = isRenderExpr;
|
|||||||
PRIMITIVES["merge-spread-attrs"] = mergeSpreadAttrs;
|
PRIMITIVES["merge-spread-attrs"] = mergeSpreadAttrs;
|
||||||
|
|
||||||
// escape-html
|
// 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;
|
PRIMITIVES["escape-html"] = escapeHtml;
|
||||||
|
|
||||||
// escape-attr
|
// escape-attr
|
||||||
|
|||||||
@@ -249,11 +249,12 @@
|
|||||||
|
|
||||||
(define escape-html
|
(define escape-html
|
||||||
(fn (s)
|
(fn (s)
|
||||||
(-> (str s)
|
(let ((r (str s)))
|
||||||
(replace "&" "&")
|
(set! r (replace r "&" "&"))
|
||||||
(replace "<" "<")
|
(set! r (replace r "<" "<"))
|
||||||
(replace ">" ">")
|
(set! r (replace r ">" ">"))
|
||||||
(replace "\"" """))))
|
(set! r (replace r "\"" """))
|
||||||
|
r)))
|
||||||
|
|
||||||
(define escape-attr
|
(define escape-attr
|
||||||
(fn (s)
|
(fn (s)
|
||||||
|
|||||||
340
spec/stdlib.sx
340
spec/stdlib.sx
@@ -1,292 +1,27 @@
|
|||||||
;; ==========================================================================
|
;; ==========================================================================
|
||||||
;; stdlib.sx — Standard library functions
|
;; stdlib.sx — Standard library functions
|
||||||
;;
|
;;
|
||||||
;; Every function here CAN be expressed in SX using the irreducible
|
;; Functions expressed in SX using the irreducible primitive set.
|
||||||
;; primitive set. They are library functions, not primitives.
|
|
||||||
;;
|
;;
|
||||||
;; These were previously platform-provided primitives. Moving them to
|
;; CONSTRAINT: Replacing a native callable (PRIMITIVES entry) with a
|
||||||
;; SX tightens the strange loop — less out-of-band, more auditable,
|
;; transpiled SX function can break the transpiled evaluator when:
|
||||||
;; portable, and verifiable.
|
;; 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)
|
;; Only functions safe from all three constraints are moved here.
|
||||||
;; Must load before: render.sx, freeze.sx, types.sx, user code
|
;; The rest remain as host-provided PRIMITIVES for now.
|
||||||
;;
|
|
||||||
;; 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
|
|
||||||
;; ==========================================================================
|
;; ==========================================================================
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Logic
|
;; String predicates and aliases
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(define not
|
(define upcase (fn (s) (upper s)))
|
||||||
(fn (x) (if x false true)))
|
(define downcase (fn (s) (lower s)))
|
||||||
|
(define string-length (fn (s) (len s)))
|
||||||
|
(define substring (fn (s start end) (slice s start end)))
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; 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 string-contains?
|
(define string-contains?
|
||||||
(fn (s needle) (!= (index-of s needle) -1)))
|
(fn (s needle) (!= (index-of s needle) -1)))
|
||||||
@@ -301,42 +36,6 @@
|
|||||||
(if (< slen plen) false
|
(if (< slen plen) false
|
||||||
(= (slice s (- slen plen)) suffix)))))
|
(= (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
|
;; Text utilities
|
||||||
@@ -350,12 +49,13 @@
|
|||||||
|
|
||||||
(define escape
|
(define escape
|
||||||
(fn (s)
|
(fn (s)
|
||||||
(-> (str s)
|
(let ((r (str s)))
|
||||||
(replace "&" "&")
|
(set! r (replace r "&" "&"))
|
||||||
(replace "<" "<")
|
(set! r (replace r "<" "<"))
|
||||||
(replace ">" ">")
|
(set! r (replace r ">" ">"))
|
||||||
(replace "\"" """)
|
(set! r (replace r "\"" """))
|
||||||
(replace "'" "'"))))
|
(set! r (replace r "'" "'"))
|
||||||
|
r)))
|
||||||
|
|
||||||
(define parse-datetime
|
(define parse-datetime
|
||||||
(fn (s) (if s (str s) nil)))
|
(fn (s) (if s (str s) nil)))
|
||||||
|
|||||||
Reference in New Issue
Block a user