Repository navigation
Description
Audited on master at 5630d74ec9cd912d4e2f325349f1943cbc15bcc6. This is a scoped implementation item under #147, based on the typed control/dataflow/effect audit. Proposed syntax below is illustrative unless explicitly marked as a current reproducer.
Problem and current evidence
Python and TypeScript implement semantics separately. TypeScript even derives its types through Python type strings (nlsc/emitter_typescript.py:16–44). The shared semantic suite currently has only a PythonRunner in tests/semantic/runners.py and tests/semantic/conftest.py; CLI output validation only invokes Python's compiler in nlsc/cli.py:224–232.
Reproduced against current emitters:
| Input/operation | Python | TypeScript under Node |
|---|---|---|
Return "True and False" |
original literal | "true && false" |
Distinct lists [1] and [1], compare a == b |
true | false |
1 if items else 0, with items=[] |
0 | 1 |
1 / 0 |
ZeroDivisionError | non-finite number rather than a typed failure |
| Branch-local result used after IF | path-dependent unbound error | out-of-scope reference even on true path |
The equality, truthiness, and division behavior contradict the existing core document. Separately, nlsc/emitter_typescript.py:173–191 emits invariant descriptions as comments, while Python generates constructor checks. Python @literal implementations also need explicit target support; silently ignoring them is not semantic equivalence.
Implementation hints
- Add a TypeScriptRunner using strict compilation plus Node execution and parameterize the existing semantic tests over both runners. Start this harness immediately; phase in checked-IR adapters as the dependency items land.
- Both backends consume the same checked operations/TypeRefs and stop parsing LOGIC descriptions or converting Python type strings to infer meaning.
- Implement small specified runtime helpers for structural equality, NLS truthiness, numeric/error behavior, construction constraints, and normalized error payloads where native target behavior differs.
- Define construction/import boundaries for structural records so constraints/invariants are actually checked. Document the limits of construction-time checks when mutable aliases exist.
- Maintain a target capability matrix. Unsupported IR nodes, foreign/literal implementations, contracts, or control policies must produce diagnostics, not silently incomplete output.
- Validate generated TypeScript with
tsc --strictin the required CI job. Runtime tests are essential: the corrupted string fixture already passes strict compilation. - Compare values, normalized failures, and ordered effect traces. Normalize only permitted representation differences.
Acceptance criteria
- All table fixtures match the documented semantic contract on both targets.
- Guard error identities/codes and field constraints/invariants are executable on both supported targets.
- Core semantic tests run against both backends in CI, with missing required toolchains treated as a gate failure.
- Invalid source is rejected by shared semantic validation before target-specific output.
- Unsupported target capabilities fail explicitly; Python-only literal blocks cannot silently disappear.
- The supported emitter paths consume checked IR and have no independent prose interpretation.
- Deterministic output/lock identity includes the backend semantics/capability version.
This is a scoped execution and migration plan under #147 and the already-open #20; Rust stays tracked in #24 rather than being required for the first conformance milestone.
Implementation ordering
Integration depends on #194, #195, #196, #197, #198. Add the behavior/diagnostic fixtures before implementation. The TypeScript runner and current regression cases can begin immediately; migration to fully checked IR follows the listed passes.
Osprey-informed implementation refinements
Updated foundation
The initial IR is now implemented by #194 / #203. #191 / #204, #190 / #205, and #193 / #206 are also closed with merged PRs. The original table above records the earlier audit snapshot; it should remain a regression inventory, not be presented as a re-test of current master. Extend the existing IR, lowering, and shared pipeline.
Shared frontend and checked phases
- Establish the same boundary for NLS's existing regex/Tree-sitter and localization paths that Osprey establishes for its two syntaxes: spelling/parsing differences end before shared semantic analysis. Checkers and emitters do not branch on which frontend produced a module.
- Compare a defined semantic projection of equivalent IRs: exclude source positions and diagnostic-only raw spelling, normalize incidental IDs when necessary, and retain every type/control/effect/failure distinction. Do not confuse byte-identical serialized source metadata with semantic equivalence.
- Preserve source spans for diagnostics while erasing spelling-only differences. Lowering must reject or explicitly mark constructs the canonical IR cannot represent.
- Represent completed validation phases explicitly (for example a CheckedModule wrapper with versioned pass evidence). Parsed/lowered successfully, contains no Foreign nodes, type-checked, control-checked, and effect/failure-checked are different facts. Never let a legacy
checkedflag imply passes that have not run. - Route every executable-producing CLI path through the same required passes. Diagnostic/raw IR inspection can remain available, but must label incomplete validation and cannot advertise the module as executable checked IR.
Target capability gate and conformance
- Validate resolved IR against a versioned TargetCapabilities contract before writing executable output or invoking the target toolchain. Exercise compile/run/watch/test and target-IR emission paths; target-independent verification should not invent a backend requirement.
- Capability checks use resolved symbol identities, including aliases/shadowing, and include required runtime/foreign adapters. Parsing an unsupported node is not permission to emit it.
- Add direct-handler cases from feat(effects): add typed lexical handlers and injectable operation implementations #207 and, on supporting targets, single-shot cases from feat(control): enforce affine single-shot handler resumption #208. Test values, typed failures, ordered effects, and cleanup. Unsupported resumption modes fail explicitly before output.
- Keep the Python/TypeScript conformance milestone independent of the optional Osprey target study research(backends): evaluate Osprey as an optional checked-IR target #209 and existing Rust issue feat: Rust target #24.
Additional acceptance cases:
- Equivalent supported source through each available NLS frontend has the same semantic projection and shared diagnostics.
- An incomplete/failed semantic phase cannot be bypassed by any executable-producing command; explicit scaffold/inspection modes remain visibly distinct.
- An unsupported capability produces a source diagnostic with no partial executable output or toolchain launch.
- Builtin aliasing/shadowing cannot bypass target validation.
- Handler and resumption fixtures join the common value/failure/event-trace matrix as those features land.
Osprey references: canonical frontend law, target capability validator, capability regression tests, and multiplicity implementation. The pinned Osprey checker explicitly rejects unsupported many and declaration-level abort; NLS must likewise gate semantic support rather than infer it from accepted syntax.
Activity
Merge pull request #236 from Mnehmos/feat/202-emitter-semantics-version
Merge pull request #237 from Mnehmos/feat/202-no-silent-truncation
Merge pull request #238 from Mnehmos/feat/202-loop-capability
Merge pull request #239 from Mnehmos/feat/202-runner-gating
Merge pull request #240 from Mnehmos/feat/202-guard-error-codes
Merge pull request #241 from Mnehmos/feat/202-ir-body-emission-python
Merge pull request #242 from Mnehmos/feat/202-ir-body-emission-typesc…
Mnehmos commentedon Sep 12, 2026
All acceptance criteria are now implemented and merged. Mapping, with the evidence:
tests/semantic/test_cross_target_divergences.py(string literals, structural list equality, empty-list truthiness, typed division-by-zero, branch-local bindings) on the shared python/typescriptrunnerfixture; TS runtime helpers (__nls_eq,__nls_truthy,__nls_div).codeon both targets andExecutionResult.exception_codecarries it; constraints/invariants via Python__post_init__and TS validatingmake_<Type>factories (tests/semantic/test_type_constraints.py)require_typescript_runner()rule (skip locally, fail underNLSC_REQUIRE_TS=1);typescript-conformanceCI job runstests/semanticwith the gate activeevaluate_semantic_gate) runs before capability gate and emission in compile/run/ci/verify; #237 closed the remaining holes (silently truncated ANLU blocks nowEPARSE001; missing RETURNS isEIR005scaffold/fatal-strict)ETARGET002capability matrix:@literal/@main(fatal),@property(strict-only); #238 addedloop_steps(fatal on TypeScript, which previously emitted the step verbatim as invalid TS)IROperationstatement/expression nodes — guards, bindings, branches, augmented assigns, result — with per-ANLU fallback to the legacy prose path only for explicitly foreign (scaffold) contentsemantics_versionmarkers in.nl.locktargets (py-3,ts-2);--frozen-lockfile/cirefuse stale markers withELOCK004Final state: 1017 tests green (semantic suite parameterized over both runners),
tsc --strictconformance in CI, capability matrix and lock identity versioned. Remaining roadmap items live in their own issues: #197, #200, #201, #207, #208, #209, #147 (spec-against-IR validation and emitter plugin boundary), #24, #146, #90, #27.