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:
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user