A clean, educational implementation of a mathematical expression compiler in C. This project demonstrates compiler frontend architecture with lexer, parser, AST, and evaluator.
- Floating-point arithmetic: Full support for double-precision numbers
- Operators:
+,-,*,/,^(power) - Operator precedence: Following standard mathematical conventions
- Right-associative exponentiation:
2^3^2= 512 (not 64) - Unary minus: Support for negative numbers and chained negation
- Parentheses: For grouping expressions
- Mathematical functions:
- Trigonometric:
sin,cos,tan,asin,acos,atan - Logarithmic:
log(natural),log10 - Exponential:
exp - Other:
sqrt,abs,ceil,floor
- Trigonometric:
The project follows a classic compiler frontend design:
Input String → Lexer → Tokens → Parser → AST → Evaluator → Result
- Lexer (
lexer.c): Tokenizes input into numbers, operators, identifiers, parentheses - Parser (
parser.c): Recursive-descent parser implementing the grammar - AST (
ast.c): Abstract Syntax Tree node creation and memory management - Evaluator (
eval.c): Tree-walk interpreter that computes results - Main (
main.c): REPL-style interactive loop
expr → add
add → mul (("+" | "-") mul)*
mul → power (("*" | "/") power)*
power → unary ("^" power)?
unary → "-" unary | primary
primary → NUMBER
| IDENT "(" expr ")"
| "(" expr ")"
- GCC (or compatible C compiler)
- Make
- Standard C library with math support
cd math_engine
makeThis produces the math_engine executable.
make clean./math_engineThen enter expressions:
> 3 + 4 * 2
= 11
> sin(0.5) + cos(0.5)
= 1.356
> 2 ^ 3 ^ 2
= 512
> sqrt(16) * log(10)
= 9.21
> -(-5)
= 5
> (1 + 2) * (3 + 4)
= 21
Press Ctrl+D (Unix/Mac) or Ctrl+Z (Windows) to exit.
3 + 4 * (2 - 1) → 7
sin(0.5) + log(10) → 2.78305
2 ^ 3 ^ 2 → 512 (right-associative)
sqrt(2) * sqrt(2) → 2
-5 + 3 → -2
exp(log(5)) → 5
floor(3.7) + ceil(2.3) → 6
math_engine/
├── include/
│ ├── token.h # Token definitions
│ ├── lexer.h # Lexer interface
│ ├── ast.h # AST node definitions
│ ├── parser.h # Parser interface
│ └── eval.h # Evaluator interface
├── src/
│ ├── lexer.c # Tokenizer implementation
│ ├── ast.c # AST node management
│ ├── parser.c # Recursive-descent parser
│ ├── eval.c # Tree-walk evaluator
│ └── main.c # REPL main program
├── Makefile # Build configuration
└── README.md # This file
- Edit
eval.cin theeval_function()function - Add a new
else ifbranch for your function name - Implement the logic using
math.hor custom code
Example:
else if (strcmp(name, "square") == 0) {
return arg * arg;
}- Add token type in
token.h(e.g.,TOKEN_PERCENT) - Add lexer recognition in
lexer.c - Add parsing logic in
parser.cat appropriate precedence level - Add evaluation in
eval.cineval_binary()
Would require:
- Symbol table for storing variable values
- Assignment operator parsing
- Environment passing through evaluator
- Language: C99
- Compiler Flags:
-Wall -Wextra -std=c99 - Memory Management: Manual with
malloc/free - Error Handling: Reports to stderr, returns
NANon errors - Number Parsing: Uses
strtod()for IEEE 754 compliance
This project demonstrates:
- Lexical analysis with state machines
- Recursive-descent parsing
- AST construction and traversal
- Operator precedence and associativity
- Memory management in C
- Separation of compilation phases
- Error handling strategies
- Functions take only one argument
- No user-defined functions
- No complex numbers
- Input limited to single-line expressions
Educational/Public Domain - Use freely for learning purposes.