Setra is a domain-specific language for discrete mathematics, with a full compiler pipeline written from scratch in C++. Lexer, recursive-descent parser, and semantic analyzer — no ANTLR, no Flex/Bison, no dependencies. Every token, every grammar rule, every type check written by hand.
What Setra Looks Like
Setra expressions map directly to discrete math notation. Sets, predicates, quantifiers, and logical connectives are first-class syntax:
// Setra source
let A = {1, 2, 3, 4, 5};
let B = {x ∈ A | x > 2};
assert ∀x ∈ A: x > 0;
assert ∃x ∈ B: x = 5;
The Compiler Pipeline
Lexer
A hand-written scanner that consumes source characters and emits tokens. Special handling for Unicode math symbols (∀, ∃, ∈, ∩, ∪) so they tokenize cleanly alongside ASCII operators. The lexer is a single-pass state machine — no regex, no backtracking.
Parser
A recursive-descent parser that builds an AST from the token stream. Operator precedence is encoded directly in the call hierarchy — expressions with lower precedence call parsers for higher-precedence operators. The grammar is LL(1) by design: one token of lookahead is always enough to decide which production to apply.
// Parser excerpt (C++)
ASTNode* Parser::parseSet() {
expect(Token::LBRACE);
auto node = new SetNode();
while (!peek(Token::RBRACE)) {
node->add(parseExpr());
if (peek(Token::COMMA)) advance();
}
expect(Token::RBRACE);
return node;
}
Semantic Analyzer
A tree-walk over the AST that resolves identifiers, checks set membership types, validates quantifier bindings, and ensures assert statements receive boolean-typed expressions. Type errors are reported with the source line and column.
What I'd Do Differently
The current pipeline stops at semantic analysis — there's no code generation stage. If I were starting Setra today I'd add an interpreter pass (tree-walking evaluator) rather than targeting machine code, since the primary use case is expression evaluation in a discrete math course context. I'd also add better error recovery in the parser — currently a parse error aborts compilation, which makes the experience poor when debugging larger Setra programs.
Read the companion post: Writing a Compiler for Discrete Math in C++ →