Skip to content

Limits And Errors

Diagnostics include source locations when source text is available.

Only failures registered by an operation contract are recoverable as err values. Fatal diagnostics are never implicitly converted. See Error Handling for safe access, try, catch, matching, and runtime rejection.

The current TypeScript implementation rejects more than 64 nested syntax levels in a structural path so malformed or machine-generated input cannot overflow the host JavaScript stack. This covers indentation blocks; parentheses, brackets, and braces; and nested expression forms such as unary operators, conditional expressions, catch, and chained operators. Nilakan reports NLK1006 and suggests splitting the code into functions or intermediate assignments.

This is an implementation resource limit, not a claim that deeper programs are meaningless or a permanent part of the language grammar. The value may change as the implementation evolves.

Runtime limits can be configured through the CLI:

nilakan --max-rounds 100000 program.nlk
nilakan --max-call-depth 100 program.nlk
nilakan --max-loop-depth 100 program.nlk
nilakan --max-prime-depth 5 program.nlk

The interpreter tracks:

  • attempted commit-loop body rounds across the whole run, default 100000
  • function call depth, default 100
  • loop depth, default 100
  • source prime depth, default 5
  • runtime values, limited to 256 container levels and 1,000,000 visited nodes
  • repetition results, limited to 1,000,000 collection slots or JavaScript UTF-16 string units

The default source prime-depth limit is 5. Function calls start again at prime depth zero. Runtime loop depth is different: it counts dynamically active commit loops across function-call boundaries.

Execution tracing has separate capture budgets: by default 5,000 events, 100,000 value nodes, and 2,000,000 UTF-16 text units across the run. Nested speculative trace events consume these budgets before publication. When a budget is exhausted, tracing stops and the program continues normally; the playground marks the retained trace as truncated. API callers can configure maxTraceEvents, maxTraceNodes, and maxTraceCharacters, and receive onTraceTruncated once. These budgets do not apply when no trace callback is supplied.

One round is charged immediately before each commit-loop body execution. Ordinary assignments, expressions, top-level statements, and function calls do not independently consume rounds. A current-state guard that is initially false consumes no round. A guard that reads primed state speculatively executes the body first, so every speculative round—including the final discarded round—counts. A round that later fails also counts. Nested loops and recursive function calls share the same run-wide counter.

Runtime value and repetition limits are fixed safety bounds rather than CLI-configurable limits. A container is itself one visited node, so a list with 1,000,000 scalar items exceeds the node limit even though its item count alone is within the repetition-slot limit. Process large values in smaller pieces when NLK5006 is reported.

| Category | Example | | ------------- | ------------------------------------------------------------------------ | | syntax/parse | malformed literal, tab indentation, unsupported slice assignment | | static | undefined variable, invalid statement placement, duplicate function name | | type | value does not satisfy a type contract | | value | registered list.index(x) absence; invalid ord/chr domain | | key | registered safe dictionary or set operation misses | | index | registered sequence get, pop, or set misses | | panic | runtime rejection of an error through an unknown shape | | round limit | maxRounds exceeded | | call depth | maxCallDepth exceeded | | loop depth | maxLoopDepth exceeded | | prime depth | maxPrimeDepth exceeded | | runtime value | repetition size, value size, or value nesting exceeds NLK5006 limits |

Major static restrictions include:

  • top-level while and init are not supported
  • top-level return is illegal
  • top-level primed reads are illegal
  • primed assignment is legal only inside commit loops
  • constant commit-loop guards are rejected
  • functions must end with a final return
  • non-terminating direct or mutual recursion is stopped by the call-depth limit
  • reserved built-in names cannot be rebound
  • unknown methods are rejected

Only failures registered by a specific operation are recoverable. The current registered failures are selected value, key, and index errors. Type errors—including invalid operation argument types—and known arity errors remain fatal, as do static, syntax, panic, and resource-limit errors.