Master the fundamental concepts of semantic analysis through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksRust's mismatched types with expected/found labels, Clang's caret diagnostics, and Elm's friendly errors all come from deliberate semantic error design. A compiler that prints error with no location or context is worse than no compiler at all.
Include: what went wrong, where (file, line, column), and often a hint. Use consistent formatting. Continue after non-fatal errors to report multiple issues. Attach notes for related locations (declaration site vs use site).
cLoading…
foo?)Production compilers embed this step inside a longer pipeline. GCC flows through cpp, cc1, assembly, and ld; Clang uses the driver, Sema, LLVM IR passes, and a target backend. LLVM bitcode, JVM bytecode, and WASM are other familiar IRs at the same layer. The exercise isolates one pass so you can test it alone before chaining it to the next stage.
You will implement semantic error reporting with clear messages and source locations. This exercise asks you to format type and scope errors so users can fix their code without guessing what the compiler meant.
Build the diagnostics engine of a compiler front end: collect errors and warnings from several checks, then print them the way Clang does: location, severity, message, the offending source line, and a caret under the exact column.
A block-structured program, one statement per line. Indentation uses spaces only (no tabs).
| Line | Meaning |
|---|---|
int NAME; / int NAME = EXPR; | declaration (also char, float) |
NAME = EXPR; / return EXPR; / return; | statements that use names |
break; / continue; | loop control |
{ / if (EXPR) { / while (EXPR) { | open a block (only while is a loop) |
} | close the innermost block |
EXPR contains identifiers, numbers, operators and parentheses. Lines outside any block are the global scope.
| Severity | Message | Reported at |
|---|---|---|
| error | use of undeclared identifier 'NAME' | the identifier, for every such use |
| error | redefinition of 'NAME' | the name in the second declaration in the same scope (that declaration is then ignored) |
| error | 'break' statement not in loop statement (or 'continue') | the keyword, when no enclosing block is a while |
| warning | declaration shadows a variable declared on line K | the new name, when an enclosing scope already declares it (K = nearest such declaration) |
| warning | unused variable 'NAME' | the name's declaration, for a block-scoped variable never mentioned again before its block closes (reading or assigning both count as a mention; globals are never reported) |
A declaration's initializer is checked before the name comes into scope. Blocks still open at the end of input are closed then.
Diagnostics sorted by line, then column; at the same position they keep the order they were found in (a declaration's own checks come before the unused-variable check made when its block closes). Each diagnostic is three lines:
cLoading…
The line number is right-aligned in 5 characters, and the caret is preceded by COL - 1 spaces after " | ". Finish with Clang's summary: N warning(s) and M error(s) generated.: omitting a part that is zero, using the singular for 1 (1 warning generated.), or no diagnostics if there were none.
Input:
cLoading…
Output:
cLoading…
Diagnostic record (position, severity, message) collected into an array by the individual checks, then sorted and rendered in one place.Hidden tests cover shadowing combined with unused variables at the same position, continue inside a nested if within a loop, redefinitions, blocks left open at EOF, and a program with no diagnostics.