Refactor SX primitives: modular, isomorphic, general-purpose
Spec modularization: - Add (define-module :name) markers to primitives.sx creating 11 modules (7 core, 4 stdlib). Bootstrappers can now selectively include modules. - Add parse_primitives_by_module() to boundary_parser.py. - Remove split-ids primitive; inline at 4 call sites in blog/market queries. Python file split: - primitives.py: slimmed to registry + core primitives only (~350 lines) - primitives_stdlib.py: NEW — stdlib primitives (format, text, style, debug) - primitives_ctx.py: NEW — extracted 12 page context builders from IO - primitives_io.py: add register_io_handler decorator, auto-derive IO_PRIMITIVES from registry, move sync IO bridges here JS parity fixes: - = uses === (strict equality), != uses !== - round supports optional ndigits parameter - concat uses nil-check not falsy-check (preserves 0, "", false) - escape adds single quote entity (') matching Python/markupsafe - assert added (was missing from JS entirely) Bootstrapper modularization: - PRIMITIVES_JS_MODULES / PRIMITIVES_PY_MODULES dicts keyed by module - --modules CLI flag for selective inclusion (core.* always included) - Regenerated sx-ref.js and sx_ref.py with all fixes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -996,13 +996,19 @@ ADAPTER_DEPS = {
|
||||
}
|
||||
|
||||
|
||||
def compile_ref_to_js(adapters: list[str] | None = None) -> str:
|
||||
def compile_ref_to_js(
|
||||
adapters: list[str] | None = None,
|
||||
modules: list[str] | None = None,
|
||||
) -> str:
|
||||
"""Read reference .sx files and emit JavaScript.
|
||||
|
||||
Args:
|
||||
adapters: List of adapter names to include.
|
||||
Valid names: html, sx, dom, engine.
|
||||
None = include all adapters.
|
||||
modules: List of primitive module names to include.
|
||||
core.* are always included. stdlib.* are opt-in.
|
||||
None = include all modules (backward compatible).
|
||||
"""
|
||||
ref_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
emitter = JSEmitter()
|
||||
@@ -1060,9 +1066,25 @@ def compile_ref_to_js(adapters: list[str] | None = None) -> str:
|
||||
has_parser = "parser" in adapter_set
|
||||
adapter_label = "+".join(sorted(adapter_set)) if adapter_set else "core-only"
|
||||
|
||||
# Determine which primitive modules to include
|
||||
prim_modules = None # None = all
|
||||
if modules is not None:
|
||||
prim_modules = [m for m in _ALL_JS_MODULES if m.startswith("core.")]
|
||||
for m in modules:
|
||||
if m not in prim_modules:
|
||||
if m not in PRIMITIVES_JS_MODULES:
|
||||
raise ValueError(f"Unknown module: {m!r}. Valid: {', '.join(PRIMITIVES_JS_MODULES)}")
|
||||
prim_modules.append(m)
|
||||
|
||||
parts = []
|
||||
parts.append(PREAMBLE)
|
||||
parts.append(PLATFORM_JS)
|
||||
parts.append(PLATFORM_JS_PRE)
|
||||
parts.append('\n // =========================================================================')
|
||||
parts.append(' // Primitives')
|
||||
parts.append(' // =========================================================================\n')
|
||||
parts.append(' var PRIMITIVES = {};')
|
||||
parts.append(_assemble_primitives_js(prim_modules))
|
||||
parts.append(PLATFORM_JS_POST)
|
||||
|
||||
# Parser platform must come before compiled parser.sx
|
||||
if has_parser:
|
||||
@@ -1181,7 +1203,235 @@ PREAMBLE = '''\
|
||||
return arguments.length ? arguments[arguments.length - 1] : false;
|
||||
}'''
|
||||
|
||||
PLATFORM_JS = '''
|
||||
# ---------------------------------------------------------------------------
|
||||
# Primitive modules — JS implementations keyed by spec module name.
|
||||
# core.* modules are always included; stdlib.* are opt-in.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
PRIMITIVES_JS_MODULES: dict[str, str] = {
|
||||
"core.arithmetic": '''
|
||||
// core.arithmetic
|
||||
PRIMITIVES["+"] = function() { var s = 0; for (var i = 0; i < arguments.length; i++) s += arguments[i]; return s; };
|
||||
PRIMITIVES["-"] = function(a, b) { return arguments.length === 1 ? -a : a - b; };
|
||||
PRIMITIVES["*"] = function() { var s = 1; for (var i = 0; i < arguments.length; i++) s *= arguments[i]; return s; };
|
||||
PRIMITIVES["/"] = function(a, b) { return a / b; };
|
||||
PRIMITIVES["mod"] = function(a, b) { return a % b; };
|
||||
PRIMITIVES["inc"] = function(n) { return n + 1; };
|
||||
PRIMITIVES["dec"] = function(n) { return n - 1; };
|
||||
PRIMITIVES["abs"] = Math.abs;
|
||||
PRIMITIVES["floor"] = Math.floor;
|
||||
PRIMITIVES["ceil"] = Math.ceil;
|
||||
PRIMITIVES["round"] = function(x, n) {
|
||||
if (n === undefined || n === 0) return Math.round(x);
|
||||
var f = Math.pow(10, n); return Math.round(x * f) / f;
|
||||
};
|
||||
PRIMITIVES["min"] = Math.min;
|
||||
PRIMITIVES["max"] = Math.max;
|
||||
PRIMITIVES["sqrt"] = Math.sqrt;
|
||||
PRIMITIVES["pow"] = Math.pow;
|
||||
PRIMITIVES["clamp"] = function(x, lo, hi) { return Math.max(lo, Math.min(hi, x)); };
|
||||
''',
|
||||
|
||||
"core.comparison": '''
|
||||
// core.comparison
|
||||
PRIMITIVES["="] = function(a, b) { return a === b; };
|
||||
PRIMITIVES["!="] = function(a, b) { return a !== b; };
|
||||
PRIMITIVES["<"] = function(a, b) { return a < b; };
|
||||
PRIMITIVES[">"] = function(a, b) { return a > b; };
|
||||
PRIMITIVES["<="] = function(a, b) { return a <= b; };
|
||||
PRIMITIVES[">="] = function(a, b) { return a >= b; };
|
||||
''',
|
||||
|
||||
"core.logic": '''
|
||||
// core.logic
|
||||
PRIMITIVES["not"] = function(x) { return !isSxTruthy(x); };
|
||||
''',
|
||||
|
||||
"core.predicates": '''
|
||||
// core.predicates
|
||||
PRIMITIVES["nil?"] = isNil;
|
||||
PRIMITIVES["number?"] = function(x) { return typeof x === "number"; };
|
||||
PRIMITIVES["string?"] = function(x) { return typeof x === "string"; };
|
||||
PRIMITIVES["list?"] = Array.isArray;
|
||||
PRIMITIVES["dict?"] = function(x) { return x !== null && typeof x === "object" && !Array.isArray(x) && !x._sym && !x._kw; };
|
||||
PRIMITIVES["empty?"] = function(c) { return isNil(c) || (Array.isArray(c) ? c.length === 0 : typeof c === "string" ? c.length === 0 : Object.keys(c).length === 0); };
|
||||
PRIMITIVES["contains?"] = function(c, k) {
|
||||
if (typeof c === "string") return c.indexOf(String(k)) !== -1;
|
||||
if (Array.isArray(c)) return c.indexOf(k) !== -1;
|
||||
return k in c;
|
||||
};
|
||||
PRIMITIVES["odd?"] = function(n) { return n % 2 !== 0; };
|
||||
PRIMITIVES["even?"] = function(n) { return n % 2 === 0; };
|
||||
PRIMITIVES["zero?"] = function(n) { return n === 0; };
|
||||
''',
|
||||
|
||||
"core.strings": '''
|
||||
// core.strings
|
||||
PRIMITIVES["str"] = function() {
|
||||
var p = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var v = arguments[i]; if (isNil(v)) continue; p.push(String(v));
|
||||
}
|
||||
return p.join("");
|
||||
};
|
||||
PRIMITIVES["upper"] = function(s) { return String(s).toUpperCase(); };
|
||||
PRIMITIVES["lower"] = function(s) { return String(s).toLowerCase(); };
|
||||
PRIMITIVES["trim"] = function(s) { return String(s).trim(); };
|
||||
PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); };
|
||||
PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); };
|
||||
PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); };
|
||||
PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; };
|
||||
PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; };
|
||||
PRIMITIVES["slice"] = function(c, a, b) { return b !== undefined ? c.slice(a, b) : c.slice(a); };
|
||||
PRIMITIVES["concat"] = function() {
|
||||
var out = [];
|
||||
for (var i = 0; i < arguments.length; i++) if (!isNil(arguments[i])) out = out.concat(arguments[i]);
|
||||
return out;
|
||||
};
|
||||
''',
|
||||
|
||||
"core.collections": '''
|
||||
// core.collections
|
||||
PRIMITIVES["list"] = function() { return Array.prototype.slice.call(arguments); };
|
||||
PRIMITIVES["dict"] = function() {
|
||||
var d = {};
|
||||
for (var i = 0; i < arguments.length - 1; i += 2) d[arguments[i]] = arguments[i + 1];
|
||||
return d;
|
||||
};
|
||||
PRIMITIVES["range"] = function(a, b, step) {
|
||||
var r = []; step = step || 1;
|
||||
for (var i = a; step > 0 ? i < b : i > b; i += step) r.push(i);
|
||||
return r;
|
||||
};
|
||||
PRIMITIVES["get"] = function(c, k, def) { var v = (c && c[k]); return v !== undefined ? v : (def !== undefined ? def : NIL); };
|
||||
PRIMITIVES["len"] = function(c) { return Array.isArray(c) ? c.length : typeof c === "string" ? c.length : Object.keys(c).length; };
|
||||
PRIMITIVES["first"] = function(c) { return c && c.length > 0 ? c[0] : NIL; };
|
||||
PRIMITIVES["last"] = function(c) { return c && c.length > 0 ? c[c.length - 1] : NIL; };
|
||||
PRIMITIVES["rest"] = function(c) { return c ? c.slice(1) : []; };
|
||||
PRIMITIVES["nth"] = function(c, n) { return c && n >= 0 && n < c.length ? c[n] : NIL; };
|
||||
PRIMITIVES["cons"] = function(x, c) { return [x].concat(c || []); };
|
||||
PRIMITIVES["append"] = function(c, x) { return (c || []).concat([x]); };
|
||||
PRIMITIVES["chunk-every"] = function(c, n) {
|
||||
var r = []; for (var i = 0; i < c.length; i += n) r.push(c.slice(i, i + n)); return r;
|
||||
};
|
||||
PRIMITIVES["zip-pairs"] = function(c) {
|
||||
var r = []; for (var i = 0; i < c.length - 1; i++) r.push([c[i], c[i + 1]]); return r;
|
||||
};
|
||||
''',
|
||||
|
||||
"core.dict": '''
|
||||
// core.dict
|
||||
PRIMITIVES["keys"] = function(d) { return Object.keys(d || {}); };
|
||||
PRIMITIVES["vals"] = function(d) { var r = []; for (var k in d) r.push(d[k]); return r; };
|
||||
PRIMITIVES["merge"] = function() {
|
||||
var out = {};
|
||||
for (var i = 0; i < arguments.length; i++) { var d = arguments[i]; if (d && !isNil(d)) for (var k in d) out[k] = d[k]; }
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["assoc"] = function(d) {
|
||||
var out = {}; if (d && !isNil(d)) for (var k in d) out[k] = d[k];
|
||||
for (var i = 1; i < arguments.length - 1; i += 2) out[arguments[i]] = arguments[i + 1];
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["dissoc"] = function(d) {
|
||||
var out = {}; for (var k in d) out[k] = d[k];
|
||||
for (var i = 1; i < arguments.length; i++) delete out[arguments[i]];
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["into"] = function(target, coll) {
|
||||
if (Array.isArray(target)) return Array.isArray(coll) ? coll.slice() : Object.entries(coll);
|
||||
var r = {}; for (var i = 0; i < coll.length; i++) { var p = coll[i]; if (Array.isArray(p) && p.length >= 2) r[p[0]] = p[1]; }
|
||||
return r;
|
||||
};
|
||||
''',
|
||||
|
||||
"stdlib.format": '''
|
||||
// stdlib.format
|
||||
PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); };
|
||||
PRIMITIVES["parse-int"] = function(v, d) { var n = parseInt(v, 10); return isNaN(n) ? (d || 0) : n; };
|
||||
PRIMITIVES["format-date"] = function(s, fmt) {
|
||||
if (!s) return "";
|
||||
try {
|
||||
var d = new Date(s);
|
||||
if (isNaN(d.getTime())) return String(s);
|
||||
var months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
||||
var short_months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
||||
return fmt.replace(/%-d/g, d.getDate()).replace(/%d/g, ("0"+d.getDate()).slice(-2))
|
||||
.replace(/%B/g, months[d.getMonth()]).replace(/%b/g, short_months[d.getMonth()])
|
||||
.replace(/%Y/g, d.getFullYear()).replace(/%m/g, ("0"+(d.getMonth()+1)).slice(-2))
|
||||
.replace(/%H/g, ("0"+d.getHours()).slice(-2)).replace(/%M/g, ("0"+d.getMinutes()).slice(-2));
|
||||
} catch (e) { return String(s); }
|
||||
};
|
||||
PRIMITIVES["parse-datetime"] = function(s) { return s ? String(s) : NIL; };
|
||||
''',
|
||||
|
||||
"stdlib.text": '''
|
||||
// stdlib.text
|
||||
PRIMITIVES["pluralize"] = function(n, s, p) {
|
||||
if (s || (p && p !== "s")) return n == 1 ? (s || "") : (p || "s");
|
||||
return n == 1 ? "" : "s";
|
||||
};
|
||||
PRIMITIVES["escape"] = function(s) {
|
||||
return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");
|
||||
};
|
||||
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
||||
''',
|
||||
|
||||
"stdlib.style": '''
|
||||
// stdlib.style
|
||||
PRIMITIVES["css"] = function() {
|
||||
var atoms = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var a = arguments[i];
|
||||
if (isNil(a) || a === false) continue;
|
||||
atoms.push(isKw(a) ? a.name : String(a));
|
||||
}
|
||||
if (!atoms.length) return NIL;
|
||||
return new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []);
|
||||
};
|
||||
PRIMITIVES["merge-styles"] = function() {
|
||||
var valid = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
||||
}
|
||||
if (!valid.length) return NIL;
|
||||
if (valid.length === 1) return valid[0];
|
||||
var allDecls = valid.map(function(v) { return v.declarations; }).join(";");
|
||||
return new StyleValue("sx-merged", allDecls, [], [], []);
|
||||
};
|
||||
''',
|
||||
|
||||
"stdlib.debug": '''
|
||||
// stdlib.debug
|
||||
PRIMITIVES["assert"] = function(cond, msg) {
|
||||
if (!isSxTruthy(cond)) throw new Error("Assertion error: " + (msg || "Assertion failed"));
|
||||
return true;
|
||||
};
|
||||
''',
|
||||
}
|
||||
|
||||
# Modules to include by default (all)
|
||||
_ALL_JS_MODULES = list(PRIMITIVES_JS_MODULES.keys())
|
||||
|
||||
# Selected primitive modules for current compilation (None = all)
|
||||
|
||||
|
||||
def _assemble_primitives_js(modules: list[str] | None = None) -> str:
|
||||
"""Assemble JS primitive code from selected modules.
|
||||
|
||||
If modules is None, all modules are included.
|
||||
Core modules are always included regardless of the list.
|
||||
"""
|
||||
if modules is None:
|
||||
modules = _ALL_JS_MODULES
|
||||
parts = []
|
||||
for mod in modules:
|
||||
if mod in PRIMITIVES_JS_MODULES:
|
||||
parts.append(PRIMITIVES_JS_MODULES[mod])
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
PLATFORM_JS_PRE = '''
|
||||
// =========================================================================
|
||||
// Platform interface — JS implementation
|
||||
// =========================================================================
|
||||
@@ -1305,180 +1555,9 @@ PLATFORM_JS = '''
|
||||
function error(msg) { throw new Error(msg); }
|
||||
function inspect(x) { return JSON.stringify(x); }
|
||||
|
||||
// =========================================================================
|
||||
// Primitives
|
||||
// =========================================================================
|
||||
|
||||
var PRIMITIVES = {};
|
||||
|
||||
// Arithmetic
|
||||
PRIMITIVES["+"] = function() { var s = 0; for (var i = 0; i < arguments.length; i++) s += arguments[i]; return s; };
|
||||
PRIMITIVES["-"] = function(a, b) { return arguments.length === 1 ? -a : a - b; };
|
||||
PRIMITIVES["*"] = function() { var s = 1; for (var i = 0; i < arguments.length; i++) s *= arguments[i]; return s; };
|
||||
PRIMITIVES["/"] = function(a, b) { return a / b; };
|
||||
PRIMITIVES["mod"] = function(a, b) { return a % b; };
|
||||
PRIMITIVES["inc"] = function(n) { return n + 1; };
|
||||
PRIMITIVES["dec"] = function(n) { return n - 1; };
|
||||
PRIMITIVES["abs"] = Math.abs;
|
||||
PRIMITIVES["floor"] = Math.floor;
|
||||
PRIMITIVES["ceil"] = Math.ceil;
|
||||
PRIMITIVES["round"] = Math.round;
|
||||
PRIMITIVES["min"] = Math.min;
|
||||
PRIMITIVES["max"] = Math.max;
|
||||
PRIMITIVES["sqrt"] = Math.sqrt;
|
||||
PRIMITIVES["pow"] = Math.pow;
|
||||
PRIMITIVES["clamp"] = function(x, lo, hi) { return Math.max(lo, Math.min(hi, x)); };
|
||||
|
||||
// Comparison
|
||||
PRIMITIVES["="] = function(a, b) { return a == b; };
|
||||
PRIMITIVES["!="] = function(a, b) { return a != b; };
|
||||
PRIMITIVES["<"] = function(a, b) { return a < b; };
|
||||
PRIMITIVES[">"] = function(a, b) { return a > b; };
|
||||
PRIMITIVES["<="] = function(a, b) { return a <= b; };
|
||||
PRIMITIVES[">="] = function(a, b) { return a >= b; };
|
||||
|
||||
// Logic
|
||||
PRIMITIVES["not"] = function(x) { return !isSxTruthy(x); };
|
||||
|
||||
// String
|
||||
PRIMITIVES["str"] = function() {
|
||||
var p = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var v = arguments[i]; if (isNil(v)) continue; p.push(String(v));
|
||||
}
|
||||
return p.join("");
|
||||
};
|
||||
PRIMITIVES["upper"] = function(s) { return String(s).toUpperCase(); };
|
||||
PRIMITIVES["lower"] = function(s) { return String(s).toLowerCase(); };
|
||||
PRIMITIVES["trim"] = function(s) { return String(s).trim(); };
|
||||
PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); };
|
||||
PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); };
|
||||
PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); };
|
||||
PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; };
|
||||
PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; };
|
||||
PRIMITIVES["slice"] = function(c, a, b) { return b !== undefined ? c.slice(a, b) : c.slice(a); };
|
||||
PRIMITIVES["concat"] = function() {
|
||||
var out = [];
|
||||
for (var i = 0; i < arguments.length; i++) if (arguments[i]) out = out.concat(arguments[i]);
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
||||
|
||||
// Predicates
|
||||
PRIMITIVES["nil?"] = isNil;
|
||||
PRIMITIVES["number?"] = function(x) { return typeof x === "number"; };
|
||||
PRIMITIVES["string?"] = function(x) { return typeof x === "string"; };
|
||||
PRIMITIVES["list?"] = Array.isArray;
|
||||
PRIMITIVES["dict?"] = function(x) { return x !== null && typeof x === "object" && !Array.isArray(x) && !x._sym && !x._kw; };
|
||||
PRIMITIVES["empty?"] = function(c) { return isNil(c) || (Array.isArray(c) ? c.length === 0 : typeof c === "string" ? c.length === 0 : Object.keys(c).length === 0); };
|
||||
PRIMITIVES["contains?"] = function(c, k) {
|
||||
if (typeof c === "string") return c.indexOf(String(k)) !== -1;
|
||||
if (Array.isArray(c)) return c.indexOf(k) !== -1;
|
||||
return k in c;
|
||||
};
|
||||
PRIMITIVES["odd?"] = function(n) { return n % 2 !== 0; };
|
||||
PRIMITIVES["even?"] = function(n) { return n % 2 === 0; };
|
||||
PRIMITIVES["zero?"] = function(n) { return n === 0; };
|
||||
|
||||
// Collections
|
||||
PRIMITIVES["list"] = function() { return Array.prototype.slice.call(arguments); };
|
||||
PRIMITIVES["dict"] = function() {
|
||||
var d = {};
|
||||
for (var i = 0; i < arguments.length - 1; i += 2) d[arguments[i]] = arguments[i + 1];
|
||||
return d;
|
||||
};
|
||||
PRIMITIVES["range"] = function(a, b, step) {
|
||||
var r = []; step = step || 1;
|
||||
for (var i = a; step > 0 ? i < b : i > b; i += step) r.push(i);
|
||||
return r;
|
||||
};
|
||||
PRIMITIVES["get"] = function(c, k, def) { var v = (c && c[k]); return v !== undefined ? v : (def !== undefined ? def : NIL); };
|
||||
PRIMITIVES["len"] = function(c) { return Array.isArray(c) ? c.length : typeof c === "string" ? c.length : Object.keys(c).length; };
|
||||
PRIMITIVES["first"] = function(c) { return c && c.length > 0 ? c[0] : NIL; };
|
||||
PRIMITIVES["last"] = function(c) { return c && c.length > 0 ? c[c.length - 1] : NIL; };
|
||||
PRIMITIVES["rest"] = function(c) { return c ? c.slice(1) : []; };
|
||||
PRIMITIVES["nth"] = function(c, n) { return c && n >= 0 && n < c.length ? c[n] : NIL; };
|
||||
PRIMITIVES["cons"] = function(x, c) { return [x].concat(c || []); };
|
||||
PRIMITIVES["append"] = function(c, x) { return (c || []).concat([x]); };
|
||||
PRIMITIVES["keys"] = function(d) { return Object.keys(d || {}); };
|
||||
PRIMITIVES["vals"] = function(d) { var r = []; for (var k in d) r.push(d[k]); return r; };
|
||||
PRIMITIVES["merge"] = function() {
|
||||
var out = {};
|
||||
for (var i = 0; i < arguments.length; i++) { var d = arguments[i]; if (d && !isNil(d)) for (var k in d) out[k] = d[k]; }
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["assoc"] = function(d) {
|
||||
var out = {}; if (d && !isNil(d)) for (var k in d) out[k] = d[k];
|
||||
for (var i = 1; i < arguments.length - 1; i += 2) out[arguments[i]] = arguments[i + 1];
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["dissoc"] = function(d) {
|
||||
var out = {}; for (var k in d) out[k] = d[k];
|
||||
for (var i = 1; i < arguments.length; i++) delete out[arguments[i]];
|
||||
return out;
|
||||
};
|
||||
PRIMITIVES["chunk-every"] = function(c, n) {
|
||||
var r = []; for (var i = 0; i < c.length; i += n) r.push(c.slice(i, i + n)); return r;
|
||||
};
|
||||
PRIMITIVES["zip-pairs"] = function(c) {
|
||||
var r = []; for (var i = 0; i < c.length - 1; i++) r.push([c[i], c[i + 1]]); return r;
|
||||
};
|
||||
PRIMITIVES["into"] = function(target, coll) {
|
||||
if (Array.isArray(target)) return Array.isArray(coll) ? coll.slice() : Object.entries(coll);
|
||||
var r = {}; for (var i = 0; i < coll.length; i++) { var p = coll[i]; if (Array.isArray(p) && p.length >= 2) r[p[0]] = p[1]; }
|
||||
return r;
|
||||
};
|
||||
|
||||
// Format
|
||||
PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); };
|
||||
PRIMITIVES["parse-int"] = function(v, d) { var n = parseInt(v, 10); return isNaN(n) ? (d || 0) : n; };
|
||||
PRIMITIVES["pluralize"] = function(n, s, p) {
|
||||
if (s || (p && p !== "s")) return n == 1 ? (s || "") : (p || "s");
|
||||
return n == 1 ? "" : "s";
|
||||
};
|
||||
PRIMITIVES["escape"] = function(s) {
|
||||
return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");
|
||||
};
|
||||
PRIMITIVES["format-date"] = function(s, fmt) {
|
||||
if (!s) return "";
|
||||
try {
|
||||
var d = new Date(s);
|
||||
if (isNaN(d.getTime())) return String(s);
|
||||
var months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
||||
var short_months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
||||
return fmt.replace(/%-d/g, d.getDate()).replace(/%d/g, ("0"+d.getDate()).slice(-2))
|
||||
.replace(/%B/g, months[d.getMonth()]).replace(/%b/g, short_months[d.getMonth()])
|
||||
.replace(/%Y/g, d.getFullYear()).replace(/%m/g, ("0"+(d.getMonth()+1)).slice(-2))
|
||||
.replace(/%H/g, ("0"+d.getHours()).slice(-2)).replace(/%M/g, ("0"+d.getMinutes()).slice(-2));
|
||||
} catch (e) { return String(s); }
|
||||
};
|
||||
PRIMITIVES["parse-datetime"] = function(s) { return s ? String(s) : NIL; };
|
||||
PRIMITIVES["split-ids"] = function(s) {
|
||||
if (!s) return [];
|
||||
return String(s).split(",").map(function(x) { return x.trim(); }).filter(function(x) { return x; });
|
||||
};
|
||||
PRIMITIVES["css"] = function() {
|
||||
// Stub — CSSX requires style dictionary which is browser-only
|
||||
var atoms = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var a = arguments[i];
|
||||
if (isNil(a) || a === false) continue;
|
||||
atoms.push(isKw(a) ? a.name : String(a));
|
||||
}
|
||||
if (!atoms.length) return NIL;
|
||||
return new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []);
|
||||
};
|
||||
PRIMITIVES["merge-styles"] = function() {
|
||||
var valid = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
||||
}
|
||||
if (!valid.length) return NIL;
|
||||
if (valid.length === 1) return valid[0];
|
||||
var allDecls = valid.map(function(v) { return v.declarations; }).join(";");
|
||||
return new StyleValue("sx-merged", allDecls, [], [], []);
|
||||
};
|
||||
'''
|
||||
|
||||
PLATFORM_JS_POST = '''
|
||||
function isPrimitive(name) { return name in PRIMITIVES; }
|
||||
function getPrimitive(name) { return PRIMITIVES[name]; }
|
||||
|
||||
@@ -2825,18 +2904,22 @@ if __name__ == "__main__":
|
||||
p = argparse.ArgumentParser(description="Bootstrap-compile SX reference spec to JavaScript")
|
||||
p.add_argument("--adapters", "-a",
|
||||
help="Comma-separated adapter list (html,sx,dom,engine). Default: all")
|
||||
p.add_argument("--modules", "-m",
|
||||
help="Comma-separated primitive modules (core.* always included). Default: all")
|
||||
p.add_argument("--output", "-o",
|
||||
help="Output file (default: stdout)")
|
||||
args = p.parse_args()
|
||||
|
||||
adapters = args.adapters.split(",") if args.adapters else None
|
||||
js = compile_ref_to_js(adapters)
|
||||
modules = args.modules.split(",") if args.modules else None
|
||||
js = compile_ref_to_js(adapters, modules)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
f.write(js)
|
||||
included = ", ".join(adapters) if adapters else "all"
|
||||
print(f"Wrote {args.output} ({len(js)} bytes, adapters: {included})",
|
||||
mods = ", ".join(modules) if modules else "all"
|
||||
print(f"Wrote {args.output} ({len(js)} bytes, adapters: {included}, modules: {mods})",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
print(js)
|
||||
|
||||
Reference in New Issue
Block a user