6.2 KiB
Implementation Plan: Syntax Clarity & Parser Robustness
Status: Draft — not yet applied
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
Files: src/value-parser.ts, src/index.test.ts, src/csv-loader/reference-resolver.ts
- Remove the
allowOmitBracketsparameter fromparseValue,parseTupleValue,parseArrayValue. - Delete the
elementIsTupleOrArraydisambiguation block and allsavedPosrestore logic inparseArrayValue. - In
parseValue(top-level), drop theallowOmitBrackets = schema.type === "tuple" || "array"special case — brackets are always required. - Values now must be fully bracketed:
[a; 1]; [b; 2](no more[a; 1]; [b; 2]without outer brackets).
Tests to update (currently assert bracket-optional behavior):
index.test.ts: "should parse tuple without brackets" [L177], "should parse array without brackets" [L215], "should parse array of tuples without outer brackets" [L249].reference-resolver.ts/index.test.tsreference tests: "should parse array reference IDs without brackets" [L685].
Decision point: Full mandatory brackets, or a top-level-only carve-out (brackets optional only when the value is the whole cell and the element is non-composite)? Recommend full mandatory for a clean grammar; note the carve-out as a fallback if ergonomics matter more.
Phase 2 — Single array form Type[]
Files: src/parser.ts, src/index.test.ts
- Remove the
[Type][]array syntax fromparseSchemaInternal. Keep onlyType[]. - This makes
[string]a 1-tuple (currently it collapses to an array). Update the tuple branch:[t]→{ type: "tuple", elements: [t] }; the special caseelements.length === 1 && !elements[0].namebecomes a real tuple. - Update
schemaToTypeStringinsrc/type-utils.ts— thearraycase currently special-cases tuple elements; with[Type][]gone, arrays are alwayselementType[](keep the(union)[]paren wrapping).
Tests to update:
index.test.ts: "should parse array of tuples" [L239] uses[string; number][]→ becomes[string; number][](unchanged) but "array of tuples without outer brackets" [L249] changes per Phase 1.- Any test using
[number][],[string][]schema strings (e.g. README examples,csv-loader.md).
Phase 3 — Deterministic union resolution
Files: src/csv-loader/reference-resolver.ts, src/validator.ts (via type-utils.ts), src/csv-loader/module-gen.ts
- Rule: In a union, always try non-reference members before reference members, regardless of author order. This makes fallback structural instead of error-message-driven.
- In
parseValueWithReferencesandresolveNestedReferences, replace the/not found|Circular reference|Failed to load/error-message inspection with a fixed ordering: partition members intononRef/ref, trynonReffirst, thenref. - In
module-gen.tsgenerateSchemaResolutionCodeunion case, apply the same ordering so generated code matches runtime behavior. - Document the rule in README (union member ordering is currently "first match wins" per AGENTS.md).
Note: This changes behavior for @users[] | string — string would now win for plain strings. Flag as a deliberate semantic change.
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.