Math Expression Parser
A math expression parser and evaluator. Takes a string like "2 + 3 * (4 - 1)"
and returns the numeric result (11).
When to use this skill
When you need to evaluate mathematical expressions from strings with correct operator precedence, parentheses, and error handling — without adding a dependency.
Node Graph
token-types ─────────────┬──→ tokenizer ──┐
│ │
ast-types ──┬────────────┤──→ parser ─────┤──→ evaluate (root: public API)
│ │ │
└────────────┴──→ evaluator ──┘
Nodes
| Node | Type | Depends On | Description |
|---|---|---|---|
token-types | leaf | — | Token kind enum and Token data type |
ast-types | leaf | — | AST node types: NumberLiteral, UnaryExpr, BinaryExpr |
tokenizer | internal | token-types | Lexer: string → Token[] |
parser | internal | token-types, ast-types | Recursive descent: Token[] → AstNode |
evaluator | internal | ast-types | Tree walker: AstNode → number |
evaluate | root | tokenizer, parser, evaluator | Pipeline: string → number (calc()) |
Subset Extraction
You can translate any subset by following the depends-on edges:
- Just the evaluator (AST in, number out):
ast-types+evaluator - Parser only (tokens in, AST out):
token-types+ast-types+parser - Full pipeline: all 6 nodes
Supported Operations
| Operator | Symbol | Precedence | Associativity |
|---|---|---|---|
| Addition | + | 1 (lowest) | Left |
| Subtraction | - | 1 | Left |
| Multiplication | * | 2 | Left |
| Division | / | 2 | Left |
| Modulo | % | 2 | Left |
| Exponentiation | ** | 3 | Right |
| Unary minus | - (prefix) | 4 | Right (prefix) |
Important: Unary minus binds tighter than exponentiation.
-2 ** 2 = (-2)² = 4, NOT -(2²) = -4.
$ARGUMENTS
Format: <nodes> [--lang <language>]
- nodes: One or more node names from the table above, or
allfor the full pipeline. When specifying individual nodes, all transitive dependencies are included automatically. - --lang: Target language for the translation. Defaults to
typescriptif omitted. Supported values includepython,rust,go, and others for which translation hints exist innodes/<name>/to-<lang>.md.
Examples:
evaluate --lang python— full pipeline translated to Pythonevaluator --lang rust— evaluator + ast-types translated to Rusttokenizer parser --lang go— tokenizer + parser + leaf dependencies translated to Goall— all 6 nodes in TypeScript (identity, useful for validation)
How to Use This Skill
- Parse
$ARGUMENTSto determine which nodes and target language are requested - Read this file for overview, the node graph, and subset extraction rules
- For each node you need (respecting dependency order), read
nodes/<name>/spec.mdfor behavior and test vectors - Read
nodes/<name>/to-<lang>.mdfor language-specific translation guidance - Generate implementation + tests for each node
- If the spec is ambiguous or insufficient, consult the TypeScript reference at
reference/src/<name>.tsfor the canonical implementation
Generated Code Documentation
Every public function, class, type, and interface in generated code must have idiomatic doc comments in the target language's standard format:
| Language | Format |
|---|---|
| TypeScript | JSDoc (/** */) with @param, @returns |
| Python | Google-style docstrings with Args, Returns, Raises |
| Kotlin/Java | KDoc/JavaDoc (/** */) with @param, @return, @throws |
| C# | XML doc comments (///) with <summary>, <param>, <returns> |
| Go | GoDoc comments (starting with the function/type name) |
| Rust | /// doc comments with # Arguments, # Returns, # Errors |
| C++ | Doxygen (/** or ///) with @brief, @param, @return |
| Swift | DocC (///) with - Parameters:, - Returns:, - Throws: |
Doc comments should describe what the function does, its parameters, return value, and error conditions. Derive content from the node spec — do not invent behavior not in the spec.
Each generated file must include a provenance header as the first comment, in the file's idiomatic comment style:
Generated by {agent} using {model}
From special:math-expression-parser (https://github.com/caryden/special)
Node: {node-name}
Replace {agent}, {model}, and {node-name} with actual values. The
provenance trace makes generated code traceable back to the skill and model
that produced it.
The per-node specs are self-contained — you can build nodes in parallel as long as their dependencies are satisfied.
Reference Implementation
The TypeScript reference lives at reference/src/<name>.ts with corresponding tests
at reference/src/<name>.test.ts. The reference has 100% line and function coverage —
every code path is tested and verified. Tests serve as the behavioral contract: if the
spec and translation hints leave something ambiguous, the reference + its tests are
the source of truth.
To run the reference tests:
cd reference && bun test --coverage
Error Handling
The parser and evaluator define several error cases that all translations must handle:
| Error Condition | Stage | Expected Behavior |
|---|---|---|
| Empty or whitespace-only input | tokenizer/evaluate | Return an error indicating empty expression |
Unrecognized characters (@, #, etc.) | tokenizer | Error with the character and its position |
| Unmatched parentheses | parser | Error indicating mismatched or missing parenthesis |
| Trailing tokens after valid expression | parser | Error indicating unexpected tokens remain |
| Division by zero | evaluator | Error indicating division by zero |
| Modulo by zero | evaluator | Error indicating modulo by zero |
All errors should include enough context (position, character, or operator) for the caller to produce a useful diagnostic message. See individual node specs for exact error semantics and test vectors.
