Master the fundamental concepts of lexical 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 worksWhen a lexer hits an illegal character, quitting leaves the user with one error and no further analysis. Clang, Rust, and TypeScript all recover: skip the bad input, emit an error token, and keep scanning so the parser can report multiple issues in one compile.
On error, record the location and message, advance past the offending character (or run of garbage), and resume normal scanning. Some lexers insert a sentinel ERROR token so the parser can synchronize at a statement boundary.
cLoading…
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 add error recovery to your lexer so invalid characters are skipped after reporting. This exercise asks you to continue producing tokens after errors, enabling multi-error diagnostics in later compiler phases.
Make your lexer recover from bad input: instead of stopping (or flooding the user with one error per junk character), emit a single TOKEN_ERROR for each bad stretch, then carry on producing normal tokens so the parser can still see the rest of the file.
A source file on stdin. Valid tokens are the ones from Basic Lexer Implementation (keywords int return if else while, the operators + - * / = == != < > <= >= && ||, delimiters ( ) { } ; ,, numbers, identifiers, // comments), plus:
TOKEN_STRING: "..." on one line; a backslash escapes the next character./* ... */ block comments, skipped like whitespace.| Bad input | Error token lexeme | Message | Resume at |
|---|---|---|---|
A run of characters that start no token (@, #, $, `, a lone ! & |, …) with nothing valid between them | the whole run, e.g. @@ | invalid characters | the first character that starts a valid token or is whitespace |
A digit run glued to letters/_ | the whole run, e.g. 12ab | malformed number | after the run |
" with no closing quote on that line | from the " to the end of the line (not the newline) | unterminated string | the newline |
/* with no closing */ | /* | unterminated comment | EOF |
So x@#$y is x, one error @#$, then y, and != is still a valid TOKEN_NE, while !@ is one error run.
Every token, one per line, in the format of Basic Lexer Implementation:
cLoading…
Error tokens add their message:
cLoading…
Then LINE:COLUMN TOKEN_EOF and finally errors=N. Lines and columns start at 1; a newline moves to the next line, column 1; every other character is one column.
Input:
cLoading…
Output:
cLoading…
Hidden tests mix all four error kinds, put junk runs directly against identifiers and operators, and leave a block comment open at EOF.