Contributors · Internals

Expression model

Expression is the library's primary symbolic scalar representation. Rather than thinking of it as a generic AST node, it is better understood as a typed internal value: each expression has a required type and value, with additional fields present only when that form of expression needs them.

Groups describe storage and canonicalization. The categories NUM, VAR, EXP, FUN, GRP, PRD, SUM, and INF reflect how expressions are represented and canonicalized internally. They are implementation details rather than a general classification of mathematical objects.

Anatomy of an Expression

type and value identify the stored node. Other fields are representation-dependent. In particular, multiplier and power can be absent from storage even though getMultiplier() and getPower() still provide the effective values algorithms should use.

Always stored Conditional / optional storage
multiplierOuter exact coefficient when explicitly stored, normally a Rational.Optional storage
type + valueThe internal group code and canonical key identifying the node.Always stored
powerOuter symbolic exponent when explicitly stored.Optional storage
elementsKeyed child expressions used by aggregate groups such as sums and products.By group
args / nameFunction name and arguments for FUN nodes.FUN only
baseExplicit base stored by EXP nodes.EXP only
Representative node relationshipsSolid = always stored · dashed = conditional
flowchart LR E["Expression"] --> T["type / value"] E -.-> M["multiplier: Rational"] E -.-> P["power: Expression"] E -.-> C["elements: child Expressions"] E -.-> A["args: function arguments"] E -.-> B["base: EXP base"] E -.-> N["name: function name"] T --> G["NUM · VAR · EXP · FUN · GRP · PRD · SUM · INF"]

Why the multiplier matters

Numeric coefficients are usually kept outside the symbolic child structure. That is why a contributor should not picture 2*x simply as a product node containing the children 2 and x.

Input2*x

Ordinary mathematical notation.

Core representationmultiplier = 2value = xpower = 1

The numeric coefficient can live on the symbolic node.

Benefitlike terms combine structurally

Arithmetic can reason about the symbolic base separately from its coefficient.

The expression groups

The type field identifies how an Expression is represented internally. These groups are organizational categories used by Nerdamer's canonicalization logic; they are not general mathematical classifications.

NUM

Plain numeric value

Stores plain numeric values.

3/2
VAR

Variable representation

Stores symbols or variables whose powers fit the variable representation.

x^2
EXP

Separate exponential node

Stores bases with powers that require a separate exponential node. It is not the same thing as the function exp(...).

x^y
FUN

Function call

Stores function calls and their argument expressions.

sin(x)
GRP

Common-base sum

Stores sums with a common base but differing powers.

x + x^ycos(x) - 3*cos(x)^2
PRD

Symbolic product

Stores products of non-numeric symbolic factors. Numeric coefficients are normally moved to the outer multiplier during parsing.

x*y
SUM

Other sums

Stores sums that do not fit the common-base grouping. For example, 1 + x + x^2 contains a numeric term and a grouped polynomial-like component.

1 + x + x^2
INF

Infinite value

Stores infinite values.

Infinity

How grouping drives canonicalization

Nerdamer does more than place parsed input into a tree of addition and multiplication nodes. Arithmetic assigns symbolic expressions canonical lookup identities, separates numeric coefficients from symbolic structure where possible, and uses keyed child collections to combine compatible terms and factors as expressions are built.

Canonical grouping during arithmeticConceptual flow
flowchart LR A["parsed Expression"] --> B["separate outer multiplier / power"] B --> C["build canonical key"] C --> D{"compatible key already present?"} D -->|"same term"| E["combine coefficients"] D -->|"same base, different power"| F["GRP: key members by power"] D -->|"different additive structure"| G["SUM"] D -->|"product factor"| H["PRD: combine matching bases / powers"]

Like terms combine without a later collection pass

For a simple variable term, the numeric coefficient normally lives in the outer multiplier. This lets addition recognize equal symbolic structure directly.

2*x + 3*x

x:
  multiplier = 2
+
x:
  multiplier = 3

→ 5*x

The two terms have the same symbolic value and the same power, so addition combines their multipliers. The parser does not need to preserve a binary tree such as Add(Mul(2,x), Mul(3,x)) and then run a separate like-term pass to discover the result.

GRP collects powers of a common symbolic base

The more unusual case occurs when two additive terms have the same canonical base but different powers. The ordinary lookup key matches, but the complete terms do not. Nerdamer represents that relationship explicitly with GRP.

x + x^2

GRP: common base x
  power 1 → x
  power 2 → x^2

Members of a group are keyed by power. A heterogeneous term cannot join that common-base group, so an expression such as 1 + x + x^2 is represented as a SUM containing the numeric term and the grouped polynomial-like component.

1 + x + x^2

SUM
  #   → 1
  x   → GRP
          power 1 → x
          power 2 → x^2

The base does not have to be a polynomial variable

GRP belongs to the general Expression model rather than to the separate polynomial classes. Any compatible symbolic expression can act as the common base. Functions are an important example.

cos(x) - 3*cos(x)^2

GRP: common base cos(x)
  power 1 → cos(x)
  power 2 → -3*cos(x)^2

The same idea also permits groups such as x + x^y. This is why GRP should not be read simply as “polynomial”: it is a generalized common-base collection inside ordinary symbolic expressions.

Products use the same keyed-structure idea

A PRD keeps the overall numeric coefficient outside its symbolic factor collection and uses canonical factor keys to find repeated bases. Matching factors can therefore combine their powers as the product is formed.

2*x*y*x^2

multiplier = 2

PRD
  x → x^3
  y → y

Keys are structural helpers, not serialized identities

Expression.keyValue() supplies the lookup key used by the addition and multiplication machinery. Atomic expressions generally use a multiplier-free identity; aggregate expressions derive their key from canonical children. When a shorter key collides with a different subexpression, the arithmetic code can fall back to a fuller subexpression key before inserting it. These keys exist to support expression combination and should not be treated as a persistent file format.

A node is canonical structure, not source text

Parsing normalizes expressions. Ordering, grouping, multipliers, and powers are chosen to make later symbolic operations predictable, so the tree is not intended to preserve the exact spelling of the input. That is why text() should be read as the canonical representation rather than a source-code round trip.

const expression = Expression.create('2*x + 1');

expression.text();                    // "1+2*x"

expression.evaluate({ x: 3 }).text(); // "7"

Expression is only one parser result type

Most scalar mathematics lands here, but the parser's result union is wider. Algorithms that require an Expression should narrow or convert explicitly rather than assume every parsed value is scalar.

Expressionsymbolic scalar
EquationLHS / RHS structure
Vectorordered values
Matrixtwo-dimensional values
Collectionordered aggregate
ValuesSetfinite set
Dictionarykey/value aggregate
Primary source

The class documentation in Expression.ts describes the group meanings and representation fields directly.

src/core/classes/expression/Expression.ts
Next stop

The parser page explains how source notation is turned into these objects in the first place.

Parser internals