Math Expression Evaluator — Implementation Plan

Phase: Implement

Steps are ordered. Each step includes writing the code and its unit tests (TDD).

Step 1: Project Skeleton

  • go mod init matheval
  • Create directory structure: cmd/matheval/, token/, lexer/, ast/, parser/, evaluator/, repl/
  • Create placeholder main.go

Step 2: Token Package

  • Define Type enum constants
  • Define Token struct
  • Add String() method on Type for debugging

Step 3: Lexer

  • Implement Tokenize(input string) ([]Token, error)
  • Handle: whitespace skipping, number literals (integers and decimals), operators +-*/, parentheses (), EOF, invalid characters
  • Tests: valid expressions, decimal numbers, invalid chars, empty input, whitespace-only

Step 4: AST Package

  • Define Node interface with sealed marker
  • Define NumberLit struct
  • Define BinaryExpr struct

Step 5: Parser

  • Implement recursive-descent parser following grammar:
    • expr → term (('+' | '-') term)*
    • term → factor (('*' | '/') factor)*
    • factor → NUMBER | '(' expr ')'
  • Internal parser struct to track position in token slice
  • Return error on: unexpected token, mismatched parens, trailing tokens
  • Tests: single number, simple binary, precedence, parentheses, nested parens, error cases

Step 6: Evaluator

  • Implement Eval(node ast.Node) (float64, error)
  • Recursively walk AST
  • Return error on division by zero
  • Tests: literals, all 4 operators, nested expressions, division by zero

Step 7: REPL

  • Implement Run(r io.Reader, w io.Writer)
  • Read line, tokenize, parse, evaluate, print result or error
  • Loop until EOF
  • Tests: successful expression, error expression, multi-line session

Step 8: main.go

  • Wire repl.Run(os.Stdin, os.Stdout)

Step 9: Integration Test

  • End-to-end test: feed expression string through all stages, verify result
  • Test edge cases: deeply nested parens, long expressions

Step 10: Final Commit & README

  • Write README.md with usage instructions
  • Final commit