Separate core spec from web framework
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m49s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m49s
Three-layer architecture:
spec/ — Core language (19 files): evaluator, parser, primitives,
CEK machine, types, continuations. Host-independent.
web/ — Web framework (20 files): signals, adapters, engine,
orchestration, boot, router, CSSX. Built on core spec.
sx/ — Application (sx-docs website). Built on web framework.
Split boundary.sx into boundary-core.sx (type-of, make-env, identical?)
and boundary-web.sx (IO primitives, signals, spreads, page helpers).
Bootstrappers search spec/ → web/ → shared/sx/ref/ for .sx files.
Original files remain in shared/sx/ref/ as fallback during transition.
All 63 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1325,9 +1325,17 @@ except ImportError:
|
||||
)
|
||||
|
||||
|
||||
def _parse_special_forms_spec(ref_dir: str) -> set[str]:
|
||||
def _parse_special_forms_spec(ref_dir: str, source_dirs=None) -> set[str]:
|
||||
"""Parse special-forms.sx to extract declared form names."""
|
||||
filepath = os.path.join(ref_dir, "special-forms.sx")
|
||||
filepath = None
|
||||
if source_dirs:
|
||||
for d in source_dirs:
|
||||
p = os.path.join(d, "special-forms.sx")
|
||||
if os.path.exists(p):
|
||||
filepath = p
|
||||
break
|
||||
if not filepath:
|
||||
filepath = os.path.join(ref_dir, "special-forms.sx")
|
||||
if not os.path.exists(filepath):
|
||||
return set()
|
||||
with open(filepath) as f:
|
||||
@@ -1359,9 +1367,9 @@ def _extract_eval_dispatch_names(all_sections: list) -> set[str]:
|
||||
|
||||
|
||||
def _validate_special_forms(ref_dir: str, all_sections: list,
|
||||
has_continuations: bool) -> None:
|
||||
has_continuations: bool, source_dirs=None) -> None:
|
||||
"""Cross-check special-forms.sx against eval.sx dispatch. Warn on mismatches."""
|
||||
spec_names = _parse_special_forms_spec(ref_dir)
|
||||
spec_names = _parse_special_forms_spec(ref_dir, source_dirs=source_dirs)
|
||||
if not spec_names:
|
||||
return
|
||||
|
||||
@@ -1432,6 +1440,20 @@ def compile_ref_to_py(
|
||||
prim_modules.append(m)
|
||||
|
||||
ref_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
_project = os.path.abspath(os.path.join(ref_dir, "..", "..", ".."))
|
||||
_source_dirs = [
|
||||
os.path.join(_project, "spec"),
|
||||
os.path.join(_project, "web"),
|
||||
ref_dir,
|
||||
]
|
||||
|
||||
def _find_sx(filename):
|
||||
for d in _source_dirs:
|
||||
p = os.path.join(d, filename)
|
||||
if os.path.exists(p):
|
||||
return p
|
||||
return None
|
||||
|
||||
emitter = PyEmitter()
|
||||
|
||||
# Resolve adapter set
|
||||
@@ -1494,7 +1516,7 @@ def compile_ref_to_py(
|
||||
has_async = "async" in adapter_set
|
||||
if has_async:
|
||||
async_filename = ADAPTER_FILES["async"][0]
|
||||
async_filepath = os.path.join(ref_dir, async_filename)
|
||||
async_filepath = _find_sx(async_filename) or os.path.join(ref_dir, async_filename)
|
||||
if os.path.exists(async_filepath):
|
||||
with open(async_filepath) as f:
|
||||
async_src = f.read()
|
||||
@@ -1513,7 +1535,7 @@ def compile_ref_to_py(
|
||||
|
||||
all_sections = []
|
||||
for filename, label in sx_files:
|
||||
filepath = os.path.join(ref_dir, filename)
|
||||
filepath = _find_sx(filename) or os.path.join(ref_dir, filename)
|
||||
if not os.path.exists(filepath):
|
||||
continue
|
||||
with open(filepath) as f:
|
||||
@@ -1531,7 +1553,7 @@ def compile_ref_to_py(
|
||||
has_continuations = "continuations" in ext_set
|
||||
|
||||
# Validate special forms
|
||||
_validate_special_forms(ref_dir, all_sections, has_continuations)
|
||||
_validate_special_forms(ref_dir, all_sections, has_continuations, source_dirs=_source_dirs)
|
||||
|
||||
# Build output
|
||||
has_html = "html" in adapter_set
|
||||
|
||||
@@ -78,6 +78,13 @@ def compile_ref_to_js(
|
||||
from shared.sx.ref.sx_ref import evaluate
|
||||
|
||||
ref_dir = _HERE
|
||||
_PROJECT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
|
||||
# Source directories: core spec, web framework, and legacy ref (for bootstrapper tools)
|
||||
_source_dirs = [
|
||||
os.path.join(_PROJECT, "spec"), # Core spec
|
||||
os.path.join(_PROJECT, "web"), # Web framework
|
||||
ref_dir, # Legacy location (fallback)
|
||||
]
|
||||
env = load_js_sx()
|
||||
|
||||
# Resolve adapter set
|
||||
@@ -195,9 +202,16 @@ def compile_ref_to_js(
|
||||
parts.append(PLATFORM_CEK_JS)
|
||||
|
||||
# Translate each spec file using js.sx
|
||||
def _find_sx(filename):
|
||||
for d in _source_dirs:
|
||||
p = os.path.join(d, filename)
|
||||
if os.path.exists(p):
|
||||
return p
|
||||
return None
|
||||
|
||||
for filename, label in sx_files:
|
||||
filepath = os.path.join(ref_dir, filename)
|
||||
if not os.path.exists(filepath):
|
||||
filepath = _find_sx(filename)
|
||||
if not filepath:
|
||||
continue
|
||||
with open(filepath) as f:
|
||||
src = f.read()
|
||||
|
||||
Reference in New Issue
Block a user