CD Notes
Build a comprehensive syntax and semantic analyzer with error reporting
Introduction and Motivation
A syntax checker is like a spell-checker for code — it reads your program and reports every error it can find, with helpful messages pointing to exactly where the problem is and what might fix it. Unlike a full compiler that stops at the first error, a good syntax checker continues analyzing after each error, potentially finding dozens of issues in a single pass. This project teaches you how to build robust error handling into a compiler and how static analysis tools like ESLint, pylint, and clang-tidy work under the hood.
The key insight is that useful error reporting requires more than just detecting errors — it requires recovering from them gracefully so the checker can find additional errors, and providing context-aware suggestions that help the programmer fix the problem quickly.
Project Architecture
Our syntax checker operates as a multi-pass analysis pipeline:
Error Categories and Detection
Lexical Errors (Detected by Scanner)
// Unterminated string literal
char *msg = "hello world; // Error: string never closed
// Invalid character in source
int x = 5 § 3; // Error: '§' not recognized
// Invalid numeric literal
int y = 0x3G5; // Error: 'G' invalid in hexSyntax Errors (Detected by Parser)
// Missing semicolon
int x = 5 // Error: expected ';' after expression
// Unmatched parentheses
if (x > 5 { // Error: expected ')' before '{'
// Invalid expression
int z = * + 3; // Error: unexpected '*' in expression
// Missing function body
void func() // Error: expected '{' after ')'
int y = 10;Semantic Errors (Detected by Analyzer)
// Undeclared variable
int y = x + 5; // Error: 'x' not declared in this scope
// Type mismatch
int count = "hello"; // Error: cannot assign string to int
// Duplicate declaration
int x = 5;
int x = 10; // Error: 'x' already declared at line 1
// Function argument mismatch
int add(int a, int b);
add(1, 2, 3); // Error: too many arguments (expected 2)Error Recovery Strategies
The most important aspect of a syntax checker is continuing after errors. Here are the strategies implemented:
Panic Mode Recovery
When an error is detected, skip tokens until a synchronizing token (like ;, }, or a keyword) is found:
void recover_to_synchronize(Parser *p) {
// Skip tokens until we find a safe restart point
while (p->current.type != TOKEN_SEMICOLON &&
p->current.type != TOKEN_RBRACE &&
p->current.type != TOKEN_EOF) {
advance(p);
}
if (p->current.type == TOKEN_SEMICOLON)
advance(p); // Consume the synchronizing token
}Error Productions in Grammar
Add grammar rules that explicitly handle common mistakes:
statement : expression ';'
| expression error { report("Missing semicolon"); yyerrok; }
| error ';' { report("Invalid statement"); yyerrok; }
;
if_stmt : IF '(' expression ')' block
| IF expression block { report("Missing parentheses around condition"); }
| IF '(' expression block { report("Missing ')' after condition"); }
;Brace Matching with Stack
Track opening braces on a stack to detect mismatches and provide helpful messages:
The Error Object
Each detected error is stored in a structured format for later reporting:
Pretty Error Output
The error reporter formats errors with source context, inspired by modern compilers like Rust's error messages:
| 12 | int sum = count + value; |
|---|---|
| 18 | int result = x + y |
| 19 | return result; |
| 7 | int temp = 0; |
Implementing "Did You Mean?" Suggestions
For undeclared variables, compute edit distance to all declared variables and suggest the closest match:
Testing Strategy
Create test files for each error category:
test_clean.c — Valid program, zero errors expected
test_semicolons.c — Missing semicolons at various positions
test_braces.c — Mismatched and missing braces
test_undeclared.c — Variables used before declaration
test_types.c — Type mismatch errors
test_duplicates.c — Duplicate variable/function declarations
test_args.c — Wrong number of function arguments
test_combined.c — Multiple error types in one file
test_recovery.c — Verify checker continues after errorsFor each test file, maintain an expected output file and write an automated test that compares actual output against expected output. This regression testing catches any changes that break existing error detection.
Key Takeaways
Building a syntax checker teaches you that error handling is not an afterthought — it is a fundamental design consideration that affects every phase of the compiler. Good error recovery allows finding multiple errors per compilation, saving developers time. Helpful error messages with source context, suggestions, and related notes transform a frustrating debugging experience into a guided correction process. These techniques are used by every modern IDE and linting tool, making this knowledge immediately practical.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Syntax Checker Project.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Compiler Design topic.
Search Terms
compiler-design, compiler design, compiler, design, projects, syntax, checker, project
Related Compiler Design Topics