Implement reader macros (#;, #|...|, #', #name) and #z3 demo
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:
2026-03-08 20:21:40 +00:00
parent 56589a81b2
commit 03ba8e58e5
12 changed files with 7625 additions and 26 deletions

View File

@@ -235,3 +235,57 @@ class TestSerialize:
def test_roundtrip(self):
original = '(div :class "main" (p "hello") (span 42))'
assert serialize(parse(original)) == original
# ---------------------------------------------------------------------------
# Reader macros
# ---------------------------------------------------------------------------
class TestReaderMacros:
"""Test #; datum comment, #|...| raw string, and #' quote shorthand."""
def test_datum_comment_discards(self):
assert parse_all("#;(ignored) 42") == [42]
def test_datum_comment_in_list(self):
assert parse("(1 #;2 3)") == [1, 3]
def test_datum_comment_nested(self):
assert parse_all("#;(a (b c) d) 99") == [99]
def test_raw_string_basic(self):
assert parse('#|hello|') == "hello"
def test_raw_string_with_quotes(self):
assert parse('#|say "hi"|') == 'say "hi"'
def test_raw_string_with_backslashes(self):
assert parse('#|a\\nb|') == 'a\\nb'
def test_raw_string_empty(self):
assert parse('#||') == ""
def test_quote_shorthand_symbol(self):
assert parse("#'foo") == [Symbol("quote"), Symbol("foo")]
def test_quote_shorthand_list(self):
assert parse("#'(1 2 3)") == [Symbol("quote"), [1, 2, 3]]
def test_hash_at_eof_errors(self):
with pytest.raises(ParseError):
parse("#")
def test_unknown_reader_macro_errors(self):
with pytest.raises(ParseError, match="Unknown reader macro"):
parse("#x foo")
def test_extensible_reader_macro(self):
"""Registered reader macros transform the next expression."""
from shared.sx.parser import register_reader_macro
register_reader_macro("upper", lambda expr: str(expr).upper())
try:
result = parse('#upper "hello"')
assert result == "HELLO"
finally:
from shared.sx.parser import _READER_MACROS
del _READER_MACROS["upper"]