nfl/runtime

Search:
Group by:

Runtime support linked into every compiled NFL program: the NflDatum quoted-data representation, seq helpers backing NFL's functional builtins (map, filter, fold, ...), and the throw/catch and defclass/:initarg machinery. Exported (export runtime in compiler.nim) so generated code can call these directly.

Types

NflDatum = ref object
  case kind*: NflDatumKind
  of ndNil:
    nil
  of ndBool:
    boolVal*: bool
  of ndInt:
    intVal*: BiggestInt
  of ndFloat:
    floatVal*: BiggestFloat
  of ndString:
    strVal*: string
  of ndSymbol:
    sym*: string
  of ndList, ndVector:
    items*: seq[NflDatum]
Runtime representation of quoted/quasiquoted data ('expr), as opposed to Syntax which only exists at macro-expansion time.
NflDatumKind = enum
  ndNil, ndBool, ndInt, ndFloat, ndString, ndSymbol, ndList, ndVector
Discriminates NflDatum's variants — the reader's own Syntax kinds, reduced to what a running program needs to represent quoted data.
NflThrow = ref object of CatchableError
  tag*: string
Shared base raised by throw (#55) — every catch/throw pair in a program raises/catches this same type, discriminated at runtime by tag rather than by a distinct Nim exception type per label. tag is the label spelling with its leading : stripped (blockLabelName), matched by string equality — deliberately not hygiene-folded like break-from's lexical label keys, since throw must be able to reach a catch written in ordinary user code from inside a macro expansion.
NflThrowVal[T] = ref object of NflThrow
  value*: T
Carries the value passed to (throw :tag value). Generic so the same exception hierarchy serves every value type; nflCatch downcasts to the branch's own typeof(body) instantiation and re-raises if that downcast wouldn't apply, rather than deferring to Nim's of semantics on a general NflThrow.

Procs

proc newNflThrow[T](tag: string; value: T): NflThrowVal[T]
Builds the exception object raised by (throw :tag value) (#55). The backend wraps the call to this in a raw nnkRaiseStmt (see emitThrow) rather than calling a {.noreturn.} proc directly, purely to follow the same convention emitRaise/emitBreakFrom already use for their own noreturn forms — a raw raise/break statement node is unambiguously noreturn to Nim's typechecker in any expression position, so keeping throw in that same shape avoids relying on {.noreturn.} inference on an ordinary proc call, which is a separate (and less predictable) path through the compiler.
proc nflBoolDatum(value: bool): NflDatum {....raises: [], tags: [], forbids: [].}
Builds an ndBool NflDatum.
proc nflFloatDatum(value: BiggestFloat): NflDatum {....raises: [], tags: [],
    forbids: [].}
Builds an ndFloat NflDatum.
proc nflIntDatum(value: BiggestInt): NflDatum {....raises: [], tags: [],
    forbids: [].}
Builds an ndInt NflDatum.
proc nflListDatum(items: varargs[NflDatum]): NflDatum {....raises: [], tags: [],
    forbids: [].}
Builds an ndList NflDatum from its elements.
proc nflMakeArray[T](n: int; fill: T): seq[T]
Backs NFL's make-array builtin — an n-element seq with every slot set to fill.
proc nflNilDatum(): NflDatum {....raises: [], tags: [], forbids: [].}
Builds an ndNil NflDatum.
proc nflReversed[T](items: openArray[T]): seq[T]
Backs NFL's reverse builtin.
proc nflSeqAny[T](items: openArray[T]; pred: proc (item: T): bool {.closure.}): bool
Closure-pred overload of nflSeqAny.
proc nflSeqAny[T](items: openArray[T]; pred: proc (item: T): bool {.nimcall.}): bool
Backs NFL's any? builtin — true if pred matches at least one element.
proc nflSeqCount[T](items: openArray[T]; pred: proc (item: T): bool {.closure.}): int
Closure-pred overload of nflSeqCount.
proc nflSeqCount[T](items: openArray[T]; pred: proc (item: T): bool {.nimcall.}): int
Backs NFL's count builtin — the number of elements matching pred.
proc nflSeqEvery[T](items: openArray[T]; pred: proc (item: T): bool {.closure.}): bool
Closure-pred overload of nflSeqEvery.
proc nflSeqEvery[T](items: openArray[T]; pred: proc (item: T): bool {.nimcall.}): bool
Backs NFL's every? builtin — true if pred matches every element.
proc nflSeqFilter[T](items: openArray[T]; pred: proc (item: T): bool {.closure.}): seq[
    T]
Closure-pred overload of nflSeqFilter.
proc nflSeqFilter[T](items: openArray[T]; pred: proc (item: T): bool {.nimcall.}): seq[
    T]
Backs NFL's filter builtin — keeps elements where pred is true.
proc nflSeqFoldl[T, U](items: openArray[T]; initial: U;
                       op: proc (acc: U; item: T): U {.closure.}): U
Closure-op overload of nflSeqFoldl.
proc nflSeqFoldl[T, U](items: openArray[T]; initial: U;
                       op: proc (acc: U; item: T): U {.nimcall.}): U
Backs NFL's foldl builtin — left fold, initial as the seed accumulator.
proc nflSeqFoldr[T, U](items: openArray[T]; initial: U;
                       op: proc (item: T; acc: U): U {.closure.}): U
Closure-op overload of nflSeqFoldr.
proc nflSeqFoldr[T, U](items: openArray[T]; initial: U;
                       op: proc (item: T; acc: U): U {.nimcall.}): U
Backs NFL's foldr builtin — right fold, initial as the seed accumulator.
proc nflSeqMap[T, U](items: openArray[T]; op: proc (item: T): U {.closure.}): seq[
    U]
Closure-op overload of nflSeqMap.
proc nflSeqMap[T, U](items: openArray[T]; op: proc (item: T): U {.nimcall.}): seq[
    U]
Backs NFL's map builtin. Overloaded on {.nimcall.}/{.closure.} so both a plain proc and a closure (e.g. a lambda capturing locals) can be passed as op without an explicit cast at the call site.
proc nflSeqPosition[T](items: openArray[T]; value: T): int
Backs NFL's position builtin. -1 when value isn't present — Nim's own find-style sentinel, rather than CL's nil, since a generic T has no nil-like value.
proc nflSeqRemoveIf[T](items: openArray[T];
                       pred: proc (item: T): bool {.closure.}): seq[T]
Closure-pred overload of nflSeqRemoveIf.
proc nflSeqRemoveIf[T](items: openArray[T];
                       pred: proc (item: T): bool {.nimcall.}): seq[T]
Backs NFL's remove-if builtin — the inverse of nflSeqFilter.
proc nflSorted[T](items: openArray[T]): seq[T]
Backs NFL's sort builtin (non-mutating, returns a new seq).
proc nflStringDatum(value: string): NflDatum {....raises: [], tags: [], forbids: [].}
Builds an ndString NflDatum.
proc nflSymbolDatum(value: string): NflDatum {....raises: [], tags: [], forbids: [].}
Builds an ndSymbol NflDatum.
proc nflVectorDatum(items: varargs[NflDatum]): NflDatum {....raises: [], tags: [],
    forbids: [].}
Builds an ndVector NflDatum from its elements.

Macros

macro nflMakeInstance(T: typedesc; args: varargs[untyped]): untyped

Implements make-instance (#85): builds the same nnkObjConstr as new (backend.nim's emitNew), after resolving each initializer's name against T's fields and :initarg-tagged fields across its whole inheritance chain (nflCollectClassShape) — visibility a macro-expand-time defclass/make-instance pair can't have on their own. A slot's own field name and its :initarg are both accepted (aliases, not a rename): (name Type :initarg :nom) can be initialized as either (name ...) or (nom ...).

Each entry of args arrives as nnkCall(ident, valueExpr) — exactly the shape backend.nim's emitCall builds for (name value) — since varargs[untyped] skips Nim's own symbol resolution, so an initializer name like nom need not itself be a callable.

Templates

template nflCatch(tagName: static string; body: untyped): untyped

Implements (catch :tag body…) (#55). body is typeof'd directly (no type slot in the surface syntax to draw the carried type from), unlike emitLabelledBlock's carrier for break-from — that copies the body with same-target break-froms erased before taking typeof, since a lexical, in-place break/return there would otherwise foul the type check. Here typeof(body) is expanded in place at the use site, so an ordinary break/continue/return inside body still semchecks fine.

The e of NflThrowVal[typeof(body)] guard matters: without it, a same-tag throw carrying a value of a different type is an ObjectConversionDefect in debug builds and silently unchecked under -d:danger. With the guard, a tag match with a mismatched value type re-raises instead, so it propagates to (and can be handled by) an outer catch rather than corrupting the result.

template nflInitarg(name: string) {.pragma.}
The field pragma defclass attaches to a slot carrying an :initarg (#85) — (name Type :initarg :nom) emits the object field as name {.nflInitarg: "nom".}: Type. nflMakeInstance reads this pragma back off the field via getImpl (not getTypeImpl, which normalizes a type and has historically dropped field pragmas) to map an external initarg name onto its field, including across an inherited slot — the visibility a macro-expand-time defclass/make-instance pair can't have, since they're separate macro invocations over separate classes.
template nflMatchArity(x: untyped; n: static[int]; exact: static[bool]): bool
Arity test for a match (#13) vector pattern against a tuple, array, or seq scrutinee. Tuples have no .len, so compiles(len(x)) picks between the two: an indexable-but-lenless value (a tuple) always passes — its arity was already fixed by its type, so Nim itself would reject an out-of-range accessor at compile time — while a seq/array is checked for at least (or exactly, for a pattern with no & rest) n elements.
template nflReplShow(body: untyped)
Wraps a value expression the nfl repl (#14) reads at top level so its result gets printed once, consistently. Same compiles(discard body) dispatch as nflStmt above, so a void form (an assignment, a loop, discard, …) simply runs for effect and prints nothing rather than failing to compile — the REPL wraps every non-declaration top-level form in this, not only ones already known to produce a value, and relies on that fallback. body is only ever evaluated once at runtime: the compiles check is purely a compile-time typecheck of a second, never-executed copy of body, exactly as nflStmt relies on already. string/char are repr'd (quoted) rather than $'d so a REPL user can tell the string "1" apart from the int 1 in the output; every other type prefers $ where available, falling back to repr.
template nflStmt(body: untyped)
Wraps a top-level expression form in statement (void) position: discards body's value if it has one, otherwise just runs it. emitStmt's fallback for a form that isn't one of the recognized statement heads (synforms.declFormHeads).