Add .sxbc s-expression bytecode format
Bytecode modules are now serialized as s-expressions (.sxbc) in addition to JSON (.sxbc.json). The .sxbc format is the canonical representation — content-addressable, parseable by the SX parser, and suitable for CID referencing. Annotation layers (source maps, variable names, tests, docs) can reference the bytecode CID without polluting the bytecode itself. Format: (sxbc version hash (code :arity N :bytecode (...) :constants (...))) The browser loader tries .sxbc first (via load-sxbc kernel primitive), falls back to .sxbc.json. Caddy needs .sxbc MIME type to serve the new format (currently 404s, JSON fallback works). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -199,6 +199,7 @@
|
||||
|
||||
/**
|
||||
* Deserialize type-tagged JSON constant back to JS value for loadModule.
|
||||
* (Legacy — used for .sxbc.json fallback)
|
||||
*/
|
||||
function deserializeConstant(c) {
|
||||
if (!c || !c.t) return null;
|
||||
@@ -228,33 +229,58 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Try loading a pre-compiled .sxbc.json bytecode module.
|
||||
* Try loading a pre-compiled bytecode module.
|
||||
* Tries .sxbc (s-expression) first, falls back to .sxbc.json (legacy).
|
||||
* Returns true on success, null on failure (caller falls back to .sx source).
|
||||
*/
|
||||
function loadBytecodeFile(path) {
|
||||
var bcPath = path.replace(/\.sx$/, '.sxbc.json');
|
||||
var url = _baseUrl + bcPath + _cacheBust;
|
||||
// Try .sxbc (s-expression format) first
|
||||
var sxbcPath = path.replace(/\.sx$/, '.sxbc');
|
||||
var url = _baseUrl + sxbcPath + _cacheBust;
|
||||
try {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", url, false);
|
||||
xhr.send();
|
||||
if (xhr.status !== 200) return null;
|
||||
if (xhr.status === 200 && xhr.responseText.indexOf("(sxbc") === 0) {
|
||||
// Parse with the SX kernel — returns the (sxbc ...) form
|
||||
var parsed = K.eval('(first (sx-parse ' + JSON.stringify(xhr.responseText) + '))');
|
||||
// (sxbc version hash (code ...)) — third element is the module
|
||||
var module = K.eval('(nth (sx-parse ' + JSON.stringify(xhr.responseText) + ') 0)');
|
||||
// Use evalSxbc which understands the sxbc format
|
||||
var result = K.eval('(load-sxbc (first (sx-parse ' + JSON.stringify(xhr.responseText) + ')))');
|
||||
if (typeof result === 'string' && result.indexOf('Error') === 0) {
|
||||
console.warn("[sx-platform] sxbc FAIL " + path + ":", result);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
// Fall through to JSON
|
||||
}
|
||||
|
||||
var json = JSON.parse(xhr.responseText);
|
||||
// Fallback: .sxbc.json (legacy format)
|
||||
var bcPath = path.replace(/\.sx$/, '.sxbc.json');
|
||||
url = _baseUrl + bcPath + _cacheBust;
|
||||
try {
|
||||
var xhr2 = new XMLHttpRequest();
|
||||
xhr2.open("GET", url, false);
|
||||
xhr2.send();
|
||||
if (xhr2.status !== 200) return null;
|
||||
|
||||
var json = JSON.parse(xhr2.responseText);
|
||||
if (!json.module || json.magic !== 'SXBC') return null;
|
||||
|
||||
var module = {
|
||||
var module2 = {
|
||||
_type: 'dict',
|
||||
bytecode: { _type: 'list', items: json.module.bytecode },
|
||||
constants: { _type: 'list', items: json.module.constants.map(deserializeConstant) },
|
||||
};
|
||||
|
||||
var result = K.loadModule(module);
|
||||
if (typeof result === 'string' && result.indexOf('Error') === 0) {
|
||||
console.warn("[sx-platform] bytecode FAIL " + path + ":", result);
|
||||
var result2 = K.loadModule(module2);
|
||||
if (typeof result2 === 'string' && result2.indexOf('Error') === 0) {
|
||||
console.warn("[sx-platform] bytecode FAIL " + path + ":", result2);
|
||||
return null;
|
||||
}
|
||||
console.log("[sx-platform] ok " + path + " (bytecode)");
|
||||
return true;
|
||||
} catch(e) {
|
||||
console.error("[sx-platform] bytecode EXCEPTION " + path + ":", e);
|
||||
|
||||
Reference in New Issue
Block a user