Refactor union resolution to explicitly try reference members before non-reference members. This replaces the previous error-message-based fallback with a deterministic, structural approach. - Update `parseValueWithReferences` and `resolveNestedReferences` to partition members by reference presence. - Update documentation in `README.md`, `AGENTS.md`, and `syntax-rework-plan.md` to reflect this behavior.
5.8 KiB
Implementation Plan: Syntax Clarity & Parser Robustness
Status: In progress — Phases 1, 2 & 3 applied (Phases 4–6 not yet done)
Goals
- Eliminate the backtracking heuristics in the value parser by making the value grammar context-free.
- Remove redundant/ambiguous syntax forms.
- Make union resolution deterministic and structural (not error-message-driven).
- Address the
csv-parsequote conflict. - Keep the
;separator (forced by CSV constraints — not the source of ambiguity).
All changes are breaking to the DSL → bump to 2.0.0, update README + csv-loader.md, add a migration note.
Phase 1 — Mandatory brackets for composite values ✅ Applied
Files: src/value-parser.ts, src/index.test.ts, src/csv-loader/reference-resolver.ts
- Removed the
allowOmitBracketsparameter fromparseValue,parseTupleValue,parseArrayValue. - Deleted the
elementIsTupleOrArraydisambiguation block and allsavedPosrestore logic inparseArrayValue. - Dropped the
allowOmitBrackets = schema.type === "tuple" || "array"special case in top-levelparseValue— brackets are always required. - Array references (
@table[]values) now also require brackets, for consistency. - Values must now be fully bracketed:
[a; 1]; [b; 2](no more[a; 1]; [b; 2]without outer brackets).
Decision: Full mandatory brackets (no carve-out).
Phase 2 — Single array form Type[] ✅ Applied
Files: src/parser.ts, src/index.test.ts
- Removed the
[Type][]array syntax fromparseSchemaInternal. Kept onlyType[]. [string]is now a 1-tuple (previously it collapsed to an array). The tuple branch always returns{ type: "tuple", elements }.- Updated
schemaToTypeStringinsrc/type-utils.ts— thearraycase no longer special-cases tuple elements; arrays are alwayselementType[](kept the(union)[]paren wrapping).
Tests updated: index.test.ts bracket-optional tests, encounter.csv / enemy_intents.csv fixtures, and parseCsv-typeDeclarations.test.ts array-of-tuple values.
Phase 3 — Deterministic union resolution ✅ Applied
Files: src/csv-loader/reference-resolver.ts, src/csv-loader/module-gen.ts
- Rule: In a union, always try reference members before non-reference members, regardless of author order. This makes fallback structural instead of error-message-driven.
- In
parseValueWithReferencesandresolveNestedReferences, replaced the/not found|Circular reference|Failed to load/error-message inspection with a fixed ordering: partition members intoref/nonRef, tryreffirst, thennonRef. module-gen.tsgenerateSchemaResolutionCodealready emitted reference-first (lookup.get(...) ?? value), so runtime now matches generated code.- Documented the rule in README and AGENTS.md.
Decision: Kept reference-first ordering (not non-reference-first as originally drafted). Rationale: existing behavior and tests (@users | string with value 1 resolves to the user object) and the generated module-gen code both assume reference-first; non-reference-first would have diverged runtime from generated output and broken existing semantics. The plan's real goal — removing the error-message regex — is achieved.
Phase 4 — Quote conflict resolution
Files: src/parser.ts, src/csv-loader/loader.ts, README, csv-loader.md
Options (pick one):
- (a) Standardize on single-quoted literals only — minimal change, but leaves a footgun.
- (b) Quote the entire schema cell so
csv-parsetreats it as one field — requires loader-side handling to strip the outer quotes before parsing. - (c) Drop quotes entirely — use bare-token literals (e.g.
on/offas identifiers). Most robust, largest change.
Recommendation: (b) — it's the only option that fixes the root cause (schema cells containing " break csv-parse) without redesigning the literal syntax. Requires: in loader.ts, detect schema-row cells that are fully quoted and unwrap before parseSchema.
Phase 5 — Cleanup & consistency
Files: src/csv-loader/reference-resolver.ts, src/csv-loader/loader.ts, src/csv-loader/module-gen.ts
- Reverse-reference performance:
resolveReverseReferencedoesrefTable.filter(...)per row. Build aMap<fk, rows[]>lookup once per referenced table (mirroring whatmodule-gen.tsalready generates) and reuse it. Cache the lookup alongside the parsed table inreferenceTableCache. - Remove the
,stop-character inparseReferenceValue(value parser has a,stop char the schema parser doesn't — drift). Make reference ID parsing consistent with the rest of the value grammar. int/float/number→number: document the collapse in README (type-level collapse is intentional; parse-time distinction remains).
Phase 6 — Docs & migration
Files: README.md, csv-loader.md, AGENTS.md
- Update all syntax tables and examples for mandatory brackets + single array form.
- Add a Migration section listing the breaking changes:
- Composite values must be fully bracketed.
[Type][]array form removed — useType[].[single]is now a 1-tuple, not an array.- Union resolution now prefers non-reference members.
- (If Phase 4b) schema cells may be fully quoted.
- Update
AGENTS.mdgotchas: union ordering rule, quote handling.
Validation
npm run typechecknpm run test(updatesrc/index.test.ts,src/csv-loader/*.test.tsfirst)- Manually verify the integration fixture (
user_rev.csv/order_rev.csv) still resolves.
Suggested execution order
Phases 1 → 2 are tightly coupled (both touch bracket parsing) — do them together. Phase 3 is independent. Phase 4 is independent. Phases 5–6 are cleanup/docs and can go last.