# Project architecture and evolution notes ## Runtime flow ```text .eftl document -> VS Code language client (`src/extension.ts`) -> LSP server (`src/server.ts`) -> tokenizer (`src/tokenizer.ts`) -> stack parser (`src/parser.ts`) -> diagnostics and per-document variable definitions ``` Separately, VS Code loads the TextMate grammar, language configuration, and snippets directly from `package.json` contributions. ## Current responsibilities - `src/extension.ts`: starts `out/server.js` over IPC for file-backed EFTL documents. - `src/tokenizer.ts`: recognizes a fixed, mostly uppercase set of tags, expressions, comments, and plain text; stores one-based line and column plus a character length. - `src/parser.ts`: checks stack-balanced open and close token kinds. It declares an `AstNode` interface but does not currently build an AST or enforce IF/WHILE child ordering. - `src/server.ts`: tokenizes and parses on document changes, validates VAR `type`, collects variable declarations, publishes diagnostics, and implements same-document go-to-definition by word matching. - `syntaxes/eftl.tmLanguage.json`: highlights a broader syntax independently of the TypeScript parser. - `language-configuration.json`: defines comment, bracket, auto-close, surrounding-pair, and folding behavior. - `snippets/eftl.json` and `examples/sample.eftl`: provide authoring examples and useful regression inputs. ## Baseline gaps to re-check These observations describe the repository when this skill was created and are not permanent requirements: - Documented `LOG`, `FOR`, `IS_EMPTY`, and `IS_NOT_EMPTY` constructs are not tokenized or structurally parsed. - The specification describes case-insensitive tags; tokenization currently uses exact uppercase matches. - An unrecognized `[` currently makes `scanText()` return without consuming input, so lowercase, unknown, or incomplete tags can trap tokenization in an infinite loop. - Prefix checks such as `[VAR` need a tag-name boundary; otherwise longer unknown names can be misclassified. - WHILE attributes and the attribute form of TRIM are not recognized by the current exact scanners. - Parser validation is balance-only; it does not enforce root scope, IF branch ordering/cardinality, WHILE shape, or allowed bodies. - Attribute parsing uses regular expressions in the server and validates only VAR type. - Source positions combine one-based line/column with token string length. Multiline diagnostic end ranges and variable-definition selections require care. - `eftl.maxNumberOfProblems` is declared but not consumed by the server. - No automated test script is declared. - `npm run compile` passes at the baseline. `npm run lint` fails before linting because ESLint 9 cannot find a flat `eslint.config.*` file. ## Preferred direction Evolve incrementally toward: 1. a source-span model based on offsets with reliable LSP conversion; 2. a context-aware scanner that can report malformed input and recover; 3. structured attribute parsing with ranges; 4. an AST or equivalent parse structure that represents documented relationships; 5. separate semantic passes for declarations, references, types, and TAG payloads; 6. table-driven language metadata shared where doing so reduces drift; 7. core unit tests plus a small number of LSP integration tests. Do not perform this redesign wholesale for an unrelated small fix. Introduce seams that make the next supported construct easier and safer. ## Completion criteria for a language construct A construct is complete only when applicable layers agree on: - accepted spellings and delimiters; - attribute names and body form; - nesting and ordering; - malformed-input recovery; - diagnostic message, severity, and exact range; - highlighting and folding; - snippet/example syntax; - focused positive, negative, boundary, and regression tests.