Parser
The notation front end has a clean three-stage shape: tokenize the source, apply the Shunting Yard algorithm to produce reverse Polish notation, then evaluate that RPN into a parser entity.
Parser.parse() runs the full pathA concrete example
The parser tests use x+3*y as a simple RPN example. Multiplication has higher precedence, so the infix source becomes x 3 y * + before evaluation.
x + 3*yHuman-facing notation.
x 3 y * +Operator precedence is now encoded by order rather than parentheses.
ExpressionThe RPN stack is evaluated by parser operations and function dispatch.
Stage 1: tokenization
tokenize() recognizes numbers, variables, operators, functions, brackets, collections, and other parser syntax. Bracketed regions become nested scopes, so later stages can work with structure instead of repeatedly rescanning raw characters.
Stage 2: Shunting Yard and RPN
toRPN() uses operator precedence and associativity to reorder a token scope. Parentheses and nested scopes no longer need to drive precedence once the sequence has been converted.
Parser.toRPN(Parser.tokenize('x+3*y'))
// x 3 y * +
This exact transformation is protected by the tokenizer/parser specs.
Stage 3: evaluate the RPN stack
parseRPN() consumes the RPN representation and routes operations to the core arithmetic handlers or registered functions. The stack can produce scalar expressions as well as equations and structured parser values.
Function dispatch is data-driven
src/core/dispatch.ts maps parser-visible names to implementations and metadata. That metadata lets the parser know argument counts, whether equations are accepted in particular positions, whether structured values distribute element-wise, whether argument evaluation is deferred, and whether the entry belongs to ordinary notation or Nerdamer Scripting.
| Example | Arguments | Element-wise | Deferred args | Use |
|---|---|---|---|---|
| factor | 1 | yes | no | Nerdamer Notation |
| diff | 1–3 | yes | no | Nerdamer Notation |
| matrix | 1+ | no | no | Nerdamer Notation |
| if | 2–3 | no | yes | Nerdamer Scripting |
Why deferred arguments exist
Some scripting constructs must decide whether or when to evaluate their arguments. An if cannot eagerly evaluate both branches, and loops need to retain executable parser work rather than only final values. The dispatch metadata marks those constructs so the parser can preserve the argument RPN for later evaluation.
Parser state is shared more widely than one object
Parser.create() returns another parser object with its own known-value table, but it is not a fully isolated parser environment. Settings, constants, operators, precision, and the imaginary-unit symbol are shared at package level. That distinction matters when debugging tests that mutate parser configuration.
Useful places to look
Parser.ts
Tokenization, RPN conversion, parsing, values, operators, and parser configuration.
open sourceoperations/
Core parser arithmetic and comparison routes such as addition, multiplication, division, and powers.
open sourcedispatch.ts
The registry connecting parser names to the actual algebra, calculus, math, solver, and scripting implementations.
open source