Compiler Design and ConstructionUnit 1010 min read
Error Handling in Compilers: Detection, Recovery, and Reporting
Unit 10 of Compiler Design and Construction explores how compilers detect, diagnose, and recover from errors in source code, covering error types, recovery strategies, and their integration across compilation phases. Learn how real-world systems like eSewa and Ncell handle validation errors, and trace error recovery in
TAKEAWAYS:
- Errors in compilers are classified into syntax errors (grammar violations), semantic errors (logical violations), and runtime errors (execution failures), each requiring distinct handling strategies.
- Error recovery techniques include panic-mode recovery, phrase-level recovery, and global correction, with trade-offs between simplicity and correctness.
- The symbol table and error handler interact dynamically: errors may corrupt symbol table entries, while symbol table checks (e.g., type mismatches) trigger semantic errors.
- Phases of compilation (lexical, syntax, semantic) each have unique error-handling challenges, from token misclassification to undefined variable references.
- Real-world applications: eSewa validates transaction syntax before processing, while Ncell’s billing system uses semantic checks to flag invalid SIM numbers.
- Exam focus: Expect questions on error recovery techniques, interaction between symbol tables and error handlers, and phase-specific error examples (e.g., LR parsing errors).
1. Introduction to Error Handling in Compilers
Compilers must not only translate correct programs but also detect, diagnose, and recover from errors gracefully. Errors can arise from:
- User mistakes (typos, syntax violations).
- Language ambiguities (e.g.,
int x = 5; x = "hello";in C). - Environment constraints (e.g., insufficient memory for a declared array).
Key Goal: Provide meaningful error messages while minimizing disruption to compilation.
2. Types of Errors in Compilation
Errors are categorized based on when they are detected and their nature:
| Error Type | Phase Detected | Example | Recovery Challenge |
|---|---|---|---|
| Lexical Errors | Lexical Analysis | if(x==5 (missing )) |
Token stream corruption; may cause cascading errors. |
| Syntax Errors | Syntax Analysis | int x = 5 +; (invalid statement) |
Parsing table conflicts; handle pruning needed. |
| Semantic Errors | Semantic Analysis | x = y + "5"; (type mismatch) |
Symbol table inconsistencies; requires context. |
| Runtime Errors | Execution (not compilation) | Division by zero | Beyond compiler scope; handled by runtime system. |
Phases of compilation with error detection points highlighted (Image: Sharmila chandrakanth, CC BY-SA 4.0, via Wikimedia Commons)
3. Error Detection Mechanisms
A. Lexical Errors
- Cause: Invalid tokens (e.g.,
if(x==5missing)). - Detection: Lexical analyzer flags tokens that don’t match the language’s lexicon (e.g.,
$$$is invalid). - Recovery:
- Skip invalid tokens until a valid one is found (e.g., treat
$$$as a no-op). - Insert sentinels (e.g.,
;or}) to resume parsing.
- Skip invalid tokens until a valid one is found (e.g., treat
Example: In C, int x = 5$$$; → Lexer skips $$$ and treats it as a comment or error.
B. Syntax Errors
- Cause: Violations of grammar rules (e.g., missing
;in Java). - Detection: Parsing algorithms (LL/LR) fail to reduce the input string.
- Recovery Techniques:
- Panic-Mode Recovery:
- Delete input symbols until a sync point (e.g.,
;,},else) is found. - Pros: Simple, fast.
- Cons: May discard valid code.
- Example: In
if(x==5$$$ y=10;, delete$$$to reachy=10;.
- Delete input symbols until a sync point (e.g.,
- Phrase-Level Recovery:
- Replace erroneous substring with a correct one (e.g.,
$$$→;). - Pros: Preserves more code.
- Cons: Requires grammar knowledge.
- Replace erroneous substring with a correct one (e.g.,
- Global Correction:
- Use AI/heuristics to suggest fixes (e.g., IntelliJ’s "Quick Fix").
- Example:
int x = 5$$$;→ Suggestint x = 5;orint x = 5; y = 10;.
- Panic-Mode Recovery:
MERMAID FLOWCHART:
flowchart TD
A["Start"] --> B["Lexical Analysis"]
B --> C{"Valid Token?"}
C -->|"No"| D["Error: Skip Token"]
C -->|"Yes"| E["Syntax Analysis"]
E --> F{"Grammar Match?"}
F -->|"No"| G["Error: Panic Mode<br/>Delete Until Sync Point"]
F -->|"Yes"| H["Semantic Analysis"]
H --> I{"Type Check?"}
I -->|"No"| J["Error: Semantic Fix"]
I -->|"Yes"| K["Code Generation"]C. Semantic Errors
- Cause: Logical violations (e.g., type mismatches, undefined variables).
- Detection: Symbol table checks (e.g.,
xnot declared before use). - Recovery:
- Insert default values (e.g., treat undefined
xas0). - Warn instead of fail (e.g., Python’s
NameErrorvs. C’s compile-time error).
- Insert default values (e.g., treat undefined
Example: In Python, x = y + 5 where y is undefined → Runtime error. In C, this is a compile-time error.
4. Interaction with Symbol Tables
The symbol table stores identifiers, types, and scopes. Errors can corrupt it:
- Example:
int x = 5; x = "hello";→ Type mismatch detected during semantic analysis. - Recovery:
- Rollback: Undo symbol table updates if an error is detected.
- Partial Recovery: Mark
xas "tainted" and continue with warnings.
FIGURE: Symbol Table Corruption
Step 1: Declare `x` as `int`
| Name | Type | Scope |
|------|-------|-------|
| x | int | global|
Step 2: Assign `x = "hello"` (Error!)
| Name | Type | Scope | Status |
|------|-----------|-------|----------|
| x | int | global| Tainted |
Error Message: "Type mismatch: 'int' assigned 'str'"
5. Error Recovery in Parsing (LR Parsing Example)
Grammar: E → E + T | T, T → T * F | F, F → (E) | id
Input: id + id * id (correct) vs. id + id $$$ * id (error with $$$).
Handle Pruning
- Handle: The rightmost substring derivable from the start symbol (e.g.,
+ TinE + T). - Pruning: Remove handles to recover from errors.
MERMAID LR PARSING TRACE:
stateDiagram-v2
[*] --> State0: Stack = [0], Input = id + id * id
State0 --> State2: Shift 'id' → Stack = [0, id, 3]
State2 --> State4: Shift '+' → Stack = [0, id, 3, +, 5]
State4 --> State6: Shift 'id' → Stack = [0, id, 3, +, 5, id, 7]
State6 --> State8: Shift '*' → Stack = [0, id, 3, +, 5, id, 7, *, 9]
State8 --> State10: Shift 'id' → Stack = [0, id, 3, +, 5, id, 7, *, 9, id, 11]
State10 --> State12: Reduce F → T → E → E + T
State12 --> State14: Reduce T → E
State14 --> State16: AcceptWith Error ($$$):
- After
id + id $$, parser detects invalid token. - Panic Mode: Delete `` until sync point (
*). - Resume parsing:
id + id * id.
6. Real-World Applications
A. eSewa (Nepal)
- Error Handling: Validates transaction syntax (e.g.,
amount,phone) before processing.- Lexical Check: Ensures
amountis numeric. - Semantic Check: Verifies
phoneexists in the database. - Recovery: If
phoneis invalid, shows"Phone not registered"instead of crashing.
- Lexical Check: Ensures
B. Ncell Billing System
- Error Handling: Detects invalid SIM numbers during recharge.
- Syntax Error:
Recharge 1234567890$$→ Skips$$. - Semantic Error:
Recharge 9843012345(invalid format) →"Invalid SIM format. Use 10 digits."
- Syntax Error:
C. Daraz Order Processing
- Queue Error Handling: If an order has
item_id = $$$, the system:- Skips the invalid item.
- Processes valid items.
- Logs:
"Item $$$ not found. Order processed for remaining items."
7. Error Handling Across Compilation Phases
| Phase | Error Type | Detection Method | Recovery Strategy |
|---|---|---|---|
| Lexical Analysis | Invalid tokens | Finite automaton rejection | Skip or insert sentinels |
| Syntax Analysis | Grammar violations | LR/LL parsing failure | Panic-mode or phrase-level recovery |
| Semantic Analysis | Type/scope violations | Symbol table checks | Warn or rollback |
| Code Generation | Invalid intermediate code | Type/control flow checks | Generate default code or halt |
8. Worked Example: Error Recovery in a Simple Compiler
Input: int x = 5; x = y + 3;
Error: y is undefined.
Steps:
- Lexical Phase: Tokens =
[int, x, =, 5, ;, x, =, y, +, 3, ;]. - Syntax Phase: Parses correctly (grammar allows assignments).
- Semantic Phase:
- Checks
xis declared (int). - Checks
yis not declared → Error. - Recovery: Insert
int y = 0;beforex = y + 3;. - Output:
int x = 5; int y = 0; // Inserted default x = y + 3;
- Checks
Symbol Table After Recovery:
| Name | Type | Scope |
|---|---|---|
| x | int | global |
| y | int | global |
9. Advanced Techniques
A. Error Correction via Parsing Tables
- Modify LR parsing tables to include error productions (e.g.,
error → ε). - Example: For
E → E + T | error, if+is missing, reduceEtoerror.
B. Machine Learning for Error Prediction
- Tools like GitHub Copilot or CLion use ML to suggest fixes for:
- Missing semicolons.
- Incorrect variable names.
- Unclosed braces.
Exam Tip
Differentiate Error Types:
- Lexical: Invalid tokens (e.g.,
if(x==5). - Syntax: Grammar violations (e.g.,
int x = 5;missing;). - Semantic: Logical errors (e.g.,
x = "hello"forint x).
- Lexical: Invalid tokens (e.g.,
Recovery Strategies:
- Panic Mode: Delete until sync point (e.g.,
;). - Phrase-Level: Replace erroneous substring (e.g.,
$→;). - Global: Use heuristics (e.g., IntelliJ’s "Quick Fix").
- Panic Mode: Delete until sync point (e.g.,
Symbol Table Interaction:
- Errors can corrupt the symbol table (e.g., undefined variable).
- Recovery may involve rollback or partial updates.
Real-World Links:
- eSewa: Validates transaction syntax/semantics.
- Ncell: Handles invalid SIM formats.
- Daraz: Skips invalid items in orders.
Common Exam Questions:
- "How does panic-mode recovery work?" → Delete until sync point.
- "Explain handle pruning in LR parsing." → Remove rightmost handles to recover.
- "How does the symbol table help in error detection?" → Tracks declarations to catch undefined variables.
Final Note: Error handling is not just about failing fast—it’s about preserving as much valid code as possible while guiding the user to fix issues. Master the trade-offs between recovery techniques and their impact on compilation.
Based on the TU BSc CSIT syllabus for Compiler Design and Construction (CSC365), unit 10.
Discussion
Loading…