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:
2026-03-16 10:43:08 +00:00
parent f7e4e3d762
commit 4c54843542
3 changed files with 46 additions and 612 deletions

View File

@@ -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_("&", "&amp;"), replace_("<", "&lt;"), replace_(">", "&gt;"), replace_("\"", "&quot;"), replace_("'", "&#x27;")); };
var escape = function(s) { return (function() {
var r = (String(s));
r = replace_(r, "&", "&amp;");
r = replace_(r, "<", "&lt;");
r = replace_(r, ">", "&gt;");
r = replace_(r, "\"", "&quot;");
r = replace_(r, "'", "&#x27;");
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_("&", "&amp;"), replace_("<", "&lt;"), replace_(">", "&gt;"), replace_("\"", "&quot;")); };
var escapeHtml = function(s) { return (function() {
var r = (String(s));
r = replace_(r, "&", "&amp;");
r = replace_(r, "<", "&lt;");
r = replace_(r, ">", "&gt;");
r = replace_(r, "\"", "&quot;");
return r;
})(); };
PRIMITIVES["escape-html"] = escapeHtml;
// escape-attr