/** * Validate an `if="…"` expression: compile it with jsep and walk the AST, * flagging any operator or construct outside TDC's supported subset. Shared by * every `if`-bearing tag (, , conditional ). */ import '../expr/operators.js'; import { type Diagnostic } from '../errors/index.js'; import type { AttrContext } from '../generated/TDCParser.js'; /** * The `&&` trap, shared so every attribute holding an expression can * explain it the same way. A config is XML-SHAPED but is not XML, so an entity * is the characters it is written with — and a parser error about "&" sends the * reader looking for a syntax mistake that is not there. */ export declare function xmlEntity(expr: string): { found: string; means: string; } | undefined; /** * What to call the site in a message. `if=` is the oldest home and its wording is * quoted in the docs, so it stays the default and nothing about it moves. */ export interface ExprSite { /** The noun phrase — "if expression", "expr= expression", "mean= parameter". */ readonly label: string; /** Its article, for the two messages that need one. */ readonly article: string; } /** The site's noun phrase, for an attribute that holds an expression. */ export declare function exprSite(attribute: string, kind?: string): ExprSite; export declare function checkIfExpression(attr: AttrContext, expr: string, sink: { diagnostics: Diagnostic[]; }, site?: ExprSite): void; /** * The names an `if=` expression uses, checked against what exists. * * An identifier that names no sequence is not an error by itself — it is how a * bare word works: `if="Gender == Male"` compares against the literal `Male`, * and the documentation is written that way throughout. What decides is WHERE * the identifier sits: * * - the whole condition (`if="Ready"`, `if="!Ready"`) — a name. An unknown one * is its own name as a string, which is never empty, so the branch fires on * every row. * - the left of a comparison, and anything arithmetic — a name. An unknown one * equals nothing, so the branch fires on no row. * - the right of a comparison — left alone. `A == B` is a value comparison * when B is declared and a bare word when it is not, and both are meant. * * A dot is read the same two ways the engine reads it: `Person.FirstName` is a * field of a compound, `Gender.Male` asks whether Gender came out `Male`. So the * root must always exist, and the tail is checked only where the root is a * compound — on a plain sequence the tail is a value, and a value cannot be * known from the config alone. */ export declare function checkExpressionNames(attr: AttrContext, expr: string, sink: { diagnostics: Diagnostic[]; }, known: readonly string[], compounds: readonly string[], eachBuiltins?: readonly string[], finiteValues?: ReadonlyMap, declaredAbove?: readonly string[]): void; /** One `if=` put aside, and where its complaint belongs in the report. */ export interface PendingExpression { readonly at: number; readonly attr: AttrContext; readonly expr: string; readonly eachBuiltins: readonly string[]; /** * The names visible where this expression was WRITTEN, when that is narrower * than the run's. * * A `` member sees the pool's own fields and nothing else — the pool is * built before any row exists, so an env column has no value to read. Deferring * the check to the end and resolving against the run's names got this wrong in * both directions at once: a sibling field was refused with TDC215 though the * engine resolves it correctly, and an env column was ACCEPTED though the * condition is then constant-false on every member. Measured, six rows: * * if="role == surgeon" TDC215 engine: yes/yes/yes/no/no/no * if="Age >= 18" is valid engine: badge=[] on every row */ readonly scope?: readonly string[] | undefined; /** * The sequences declared ABOVE this expression, when it is a condition that * decides a column while that column is being BUILT — `` and * `missing_when=`. * * Naming a column declared below it is answered differently by the two * engines, and both answers are defensible: the in-memory engine has not * built that column yet, so the condition is constant-false; the lazy * registry builds it on demand, so it resolves for real. Measured, five rows * from one seed, with `A` conditional on `B` declared below it: * * mode="memory" A is empty on every row * mode="disk" A resolves wherever B is `p` * * So the config is refused instead — TDC308 — which is what `` * has always done, and what TDC240 does for `running`/`stat`. A condition * used where the whole row already exists (``, ``, * ``) leaves this undefined: both engines agree there, because * the table is finished before any of them is read. */ readonly declaredAbove?: readonly string[] | undefined; } /** * The put-aside expressions, checked now that every name is known. * * Held back rather than checked as the walk passed: an expression may name a * sequence declared BELOW it, and the run resolves that happily, so checking * mid-walk would invent errors on configs that work. Each complaint is spliced * back at the position its attribute was found, so the report still reads top to * bottom. */ /** * Narrow the expressions put aside since `from` to the names they could actually * see. Used for a ``, whose members read each other and nothing from the * run — see `PendingExpression.scope` for what went wrong without it. */ export declare function scopePending(pending: PendingExpression[], from: number, names: readonly string[]): void; export declare function runPendingExpressions(pending: readonly PendingExpression[], diagnostics: Diagnostic[], known: readonly string[], compounds: readonly string[], finiteValues: ReadonlyMap): void; //# sourceMappingURL=expr-check.d.ts.map