Implement reader macros (#;, #|...|, #', #name) and #z3 demo
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m13s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m13s
Reader macros in parser.sx spec, Python parser.py, and hand-written sx.js: - #; datum comment: read and discard next expression - #|...| raw string: no escape processing - #' quote shorthand: (quote expr) - #name extensible dispatch: registered handler transforms next expression #z3 reader macro demo (reader_z3.py): translates define-primitive declarations from primitives.sx into SMT-LIB verification conditions. Same source, two interpretations — bootstrappers compile to executable code, #z3 extracts proof obligations. 48 parser tests (SX spec + Python), all passing. Rebootstrapped JS+Python. Demo page at /plans/reader-macro-demo with side-by-side examples. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,9 @@
|
||||
function isMacro(x) { return x && x._macro === true; }
|
||||
function isRaw(x) { return x && x._raw === true; }
|
||||
|
||||
// --- Reader macro registry ---
|
||||
var _readerMacros = {};
|
||||
|
||||
// --- Parser ---
|
||||
|
||||
var RE_WS = /\s+/y;
|
||||
@@ -155,6 +158,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Reader macro dispatch: #
|
||||
if (ch === "#") { this._advance(1); return "#"; }
|
||||
|
||||
// Symbol
|
||||
RE_SYMBOL.lastIndex = this.pos;
|
||||
m = RE_SYMBOL.exec(this.text);
|
||||
@@ -171,6 +177,27 @@
|
||||
throw parseErr("Unexpected character: " + ch + " | context: «" + ctx.replace(/\n/g, "\\n") + "»", this);
|
||||
};
|
||||
|
||||
Tokenizer.prototype._readRawString = function () {
|
||||
var buf = [];
|
||||
while (this.pos < this.text.length) {
|
||||
var ch = this.text[this.pos];
|
||||
if (ch === "|") { this._advance(1); return buf.join(""); }
|
||||
buf.push(ch);
|
||||
this._advance(1);
|
||||
}
|
||||
throw parseErr("Unterminated raw string", this);
|
||||
};
|
||||
|
||||
Tokenizer.prototype._readIdent = function () {
|
||||
RE_SYMBOL.lastIndex = this.pos;
|
||||
var m = RE_SYMBOL.exec(this.text);
|
||||
if (m && m.index === this.pos) {
|
||||
this._advance(m[0].length);
|
||||
return m[0];
|
||||
}
|
||||
throw parseErr("Expected identifier after #", this);
|
||||
};
|
||||
|
||||
function isDigit(c) { return c >= "0" && c <= "9"; }
|
||||
|
||||
function parseErr(msg, tok) {
|
||||
@@ -199,6 +226,33 @@
|
||||
}
|
||||
return [new Symbol("unquote"), parseExpr(tok)];
|
||||
}
|
||||
// Reader macro dispatch: #
|
||||
if (raw === "#") {
|
||||
tok._advance(1); // consume #
|
||||
if (tok.pos >= tok.text.length) throw parseErr("Unexpected end of input after #", tok);
|
||||
var dispatch = tok.text[tok.pos];
|
||||
if (dispatch === ";") {
|
||||
tok._advance(1);
|
||||
parseExpr(tok); // read and discard
|
||||
return parseExpr(tok); // return next
|
||||
}
|
||||
if (dispatch === "|") {
|
||||
tok._advance(1);
|
||||
return tok._readRawString();
|
||||
}
|
||||
if (dispatch === "'") {
|
||||
tok._advance(1);
|
||||
return [new Symbol("quote"), parseExpr(tok)];
|
||||
}
|
||||
// Extensible dispatch: #name expr
|
||||
if (/[a-zA-Z_~]/.test(dispatch)) {
|
||||
var macroName = tok._readIdent();
|
||||
var handler = _readerMacros[macroName];
|
||||
if (!handler) throw parseErr("Unknown reader macro: #" + macroName, tok);
|
||||
return handler(parseExpr(tok));
|
||||
}
|
||||
throw parseErr("Unknown reader macro: #" + dispatch, tok);
|
||||
}
|
||||
return tok.next();
|
||||
}
|
||||
|
||||
@@ -1500,6 +1554,9 @@
|
||||
}
|
||||
},
|
||||
|
||||
/** Register a reader macro: Sx.registerReaderMacro("z3", fn) */
|
||||
registerReaderMacro: function (name, handler) { _readerMacros[name] = handler; },
|
||||
|
||||
// For testing / sx-test.js
|
||||
_types: { NIL: NIL, Symbol: Symbol, Keyword: Keyword, Lambda: Lambda, Component: Component, RawHTML: RawHTML },
|
||||
_eval: sxEval,
|
||||
|
||||
Reference in New Issue
Block a user