nfl/synforms

Shared slot-layout helpers for proc/template/iterator/method/converter/type forms, used identically by both the lowering pass (lower.nim) and the emission pass (backend.nim). Kept in one place so the two passes cannot silently disagree on where generic params, pragmas, and bodies sit in the form (see #43).

Types

StaticWhenClause = object
  isElse*: bool              ## True for the trailing `(else body…)` clause; `test` is unused then.
  test*: Syntax
  body*: seq[Syntax]
One clause of a static-when form, as parsed by parseStaticWhenClauses.

Procs

proc bodyStartAfterParams(sx: Syntax; paramsIdx: int; formName = "proc"): int {.
    ...raises: [CompilerError], tags: [], forbids: [].}
Returns the index of the first body item after paramsIdx, skipping an optional (: return-type) clause immediately following the params. Shared by procBodyStart (name-bearing forms) and lambdaBodyStart (do, which has no name slot) so both agree on how a return-type clause is recognised and diagnosed.
proc declaredNames(sx: Syntax): seq[string] {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
The top-level name(s) a decl form (isDeclForm) binds — used by the REPL (#14) to detect when a new transcript entry redefines an earlier one. Every returned name has its export marker (trailing *) already stripped, so (proc f* …) and a later (proc f …) are recognized as the same binding. Best-effort: an unrecognized or malformed shape (already destined to be rejected by lower.nim at compile time) simply contributes no names rather than raising here.
proc formName(sx: Syntax): string {....raises: [], tags: [], forbids: [].}
The symbol name of a form's head, for use in diagnostics; falls back to the generic "form" when sx is not itself a symbol.
proc isDeclForm(sx: Syntax): bool {....raises: [], tags: [], forbids: [].}
True for a top-level form the REPL (#14) should treat as a declaration or void statement — never wrapped for value printing — rather than a printable expression. Covers every head lowerStmt/emitStmt special- case (declFormHeads), plus a block when any of its direct children is itself a decl form: defclass (preamble.nfl) expands to (block (type …) (proc …) …), which must stay a declaration, while a progn- style (block expr) wrapping a single printable value should not.
proc isDefvarForm(sx: Syntax): bool {....raises: [], tags: [], forbids: [].}
var is overloaded: (var name value) / (var (name type) value) is a module/statement-level declaration, while (var ((name value) …) body…) is the local mutable-binding form (like let, but mutable). The two are distinguished by shape: a local binding list is always a list of lists (each entry a (name value) pair or (name {.pragma.} value) triple), while a declaration's name slot is never further nested that way — it is a bare symbol, or a flat (name type) pair whose first element is not itself a list. Anything not clearly a bindings list is treated as an (possibly malformed) declaration, so bad declarations still get declaration-shaped diagnostics instead of confusing "bad binding" errors.
proc isKeywordSym(sx: Syntax): bool {....raises: [], tags: [], forbids: [].}
True for a :field-style keyword symbol — used both by macro &key parameter parsing (expand.nim) and by destructuring object patterns ([:name n], #47).
proc isObjectPattern(pattern: Syntax): bool {....raises: [], tags: [], forbids: [].}
A destructuring vector pattern (#12) is an object pattern (matches by field name, #47) rather than a positional one when its first element is a :field keyword; anything else — including an empty vector — is positional.
proc isPragmaClause(sx: Syntax): bool {....raises: [], tags: [], forbids: [].}
True for a {.pragma.} clause form, i.e. (pragma ...).
proc isVarSectionForm(sx: Syntax): bool {....raises: [], tags: [], forbids: [].}
A var/const section form declares multiple bindings at statement/module scope using the same binding-list grammar as the local mutable-binding form, but with no body: (var ((x 1) (y 2))). This is distinguished from the local form (which requires a body) purely by arity — (var (bindings…) body…) has 3+ items, a section has exactly 2.
proc lambdaBodyStart(sx: Syntax): int {....raises: [CompilerError], tags: [],
                                        forbids: [].}
do has no name slot: (do (params) (: T)? body…), so params sit at slot 1 and the optional return-type clause (if any) at slot 2.
proc objectFieldParts(field: Syntax): tuple[ok: bool, pragma: Syntax,
    typeIdx, defaultIdx: int] {....raises: [], tags: [], forbids: [].}
Resolves the shape of an object field spec — (name Type), (name {.pragma.} Type), (name Type default), or (name {.pragma.} Type default) — so lower.nim and backend.nim agree on where the pragma, type, and default sit. ok is false for anything else (wrong arity, or a 4-element form whose slot 1 is not a pragma clause); pragma is nil and defaultIdx is -1 when absent.
proc parseStaticWhenClauses(sx: Syntax; requireElse: bool): seq[StaticWhenClause] {.
    ...raises: [CompilerError], tags: [], forbids: [].}
Parses (static-when (test body…)… [(else body…)]) into its clauses, shared by lower.nim and backend.nim so the two passes can't disagree on shape (#32). requireElse is true in expression position — a static-when whose tests are all false has no value there, so an expression-position use without else is rejected here rather than letting Nim fail obscurely on the resulting when with no matching branch.
proc procBodyStart(sx: Syntax): int {....raises: [CompilerError], tags: [],
                                      forbids: [].}
Returns the index of the first body item in a name-bearing form (proc, template, iterator, method, func, converter).
proc procGenericIdx(sx: Syntax): int {....raises: [], tags: [], forbids: [].}
Returns the index of the optional generic-params vector in a proc or type form, or -1 if none is present. Generic params appear as a sxVector immediately after the name (slot 2).
proc procParamsIdx(sx: Syntax): int {....raises: [], tags: [], forbids: [].}
Returns the index of the parameter list in a proc form, skipping an optional generic-params vector and/or pragma clause that may appear between the name and params.
proc validatePattern(pattern: Syntax; names: var seq[Syntax];
                     rejectObjectIn: string = "") {.
    ...raises: [CompilerError, Exception], tags: [RootEffect], forbids: [].}