Self-hosted z3.sx translator, prove.sx prover, parser unicode, auto reader macros
- z3.sx: SX-to-SMT-LIB translator written in SX (359 lines), replaces Python translation logic - prove.sx: SMT-LIB satisfiability checker in SX — proves all 91 primitives sat by construction - Parser: support unicode characters (em-dash, accented letters) in symbols - Auto-resolve reader macros: #name finds name-translate in component env, no Python registration - Platform primitives: type-of, symbol-name, keyword-name, sx-parse registered in primitives.py - Cond heuristic: predicates ending in ? recognized as Clojure-style tests - Library loading: z3.sx loaded at startup with reload callbacks for hot-reload ordering - reader_z3.py: rewritten as thin shell delegating to z3.sx - Split monolithic .sx files: essays (22), plans (13), reactive-islands (6) into separate files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,29 @@ def register_reader_macro(name: str, handler: Any) -> None:
|
||||
_READER_MACROS[name] = handler
|
||||
|
||||
|
||||
def _resolve_sx_reader_macro(name: str):
|
||||
"""Auto-resolve a reader macro from the component env.
|
||||
|
||||
If a file like z3.sx defines (define z3-translate ...), then #z3 is
|
||||
automatically available as a reader macro without any Python registration.
|
||||
Looks for {name}-translate as a Lambda in the component env.
|
||||
"""
|
||||
try:
|
||||
from .jinja_bridge import get_component_env
|
||||
from .evaluator import _trampoline, _call_lambda
|
||||
from .types import Lambda
|
||||
except ImportError:
|
||||
return None
|
||||
env = get_component_env()
|
||||
fn = env.get(f"{name}-translate")
|
||||
if fn is None or not isinstance(fn, Lambda):
|
||||
return None
|
||||
# Return a Python callable that invokes the SX lambda
|
||||
def _sx_handler(expr):
|
||||
return _trampoline(_call_lambda(fn, [expr], env))
|
||||
return _sx_handler
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SxExpr — pre-built sx source marker
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -114,8 +137,8 @@ class Tokenizer:
|
||||
NUMBER = re.compile(r"-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?")
|
||||
KEYWORD = re.compile(r":[a-zA-Z_~*+\-><=/!?&\[]{1}[a-zA-Z0-9_~*+\-><=/!?.:&/\[\]#,]*")
|
||||
# Symbols may start with alpha, _, or common operator chars, plus ~ for components,
|
||||
# <> for the fragment symbol, and & for &key/&rest.
|
||||
SYMBOL = re.compile(r"[a-zA-Z_~*+\-><=/!?&][a-zA-Z0-9_~*+\-><=/!?.:&]*")
|
||||
# <> for the fragment symbol, & for &key/&rest, and unicode letters (é, ñ, em-dash…).
|
||||
SYMBOL = re.compile(r"[a-zA-Z_~*+\-><=/!?&\u0080-\uFFFF][a-zA-Z0-9_~*+\-><=/!?.:&\u0080-\uFFFF]*")
|
||||
|
||||
def __init__(self, text: str):
|
||||
self.text = text
|
||||
@@ -321,6 +344,9 @@ def _parse_expr(tok: Tokenizer) -> Any:
|
||||
if dispatch.isalpha() or dispatch in "_~":
|
||||
macro_name = tok._read_ident()
|
||||
handler = _READER_MACROS.get(macro_name)
|
||||
if handler is None:
|
||||
# Auto-resolve: look for {name}-translate in component env
|
||||
handler = _resolve_sx_reader_macro(macro_name)
|
||||
if handler is None:
|
||||
raise ParseError(f"Unknown reader macro: #{macro_name}",
|
||||
tok.pos, tok.line, tok.col)
|
||||
|
||||
Reference in New Issue
Block a user