flanger
A language frontend in Rust, kept small so I can learn how compilers work.
Why, if fshell already has a parser
fshell already has a lexer, a parser, and a syntax tree, but they were built around what a shell needs: bare words, pipelines, POSIX fallback, and aliases that expand as you type.
flanger is a conventional language written from scratch, small enough to understand and test completely. Unlike fshell, it is meant to go past parsing into code generation.
The language today
A program using everything the language supports today:
fn main() -> i64 {
return 1 + 2 * 3;
}
Functions take no parameters and contain a single return statement. If you leave the return type out, it defaults to Unit. Expressions are integer arithmetic with the usual precedence, and parentheses override it. The only types are i64 and i32.
Lexer
Hand-written and byte-based, ASCII only for now (UTF-8 is a TODO). Every token has a span with start and end offsets, so errors point at exact characters.
One byte of lookahead separates - from ->, and the keywords fn and return are matched without splitting identifiers such as returnValue. It reports two errors, unexpected character and integer overflow, both with spans.
Parser and errors
Recursive descent over the tokens. Additive expressions sit above multiplicative ones, which sit above primaries, so 1 + 2 * 3 parses as 1 + (2 * 3). Parenthesized expressions count as primaries.
Errors carry a span, the expected token, and the token found. Unknown type names get their own error instead of a generic mismatch. Lexer errors are wrapped by the parser, so one error type covers both stages.
The expression tree starts with two cases:
pub enum Expression {
Integer(i64),
Binary(Box<BinaryExpression>),
}
Tokens, spans, and errors each have their own module, so I can test every stage on its own. Function names are borrowed slices of the source, not owned strings.
Testing
About one test per behavior: token spans, minus versus arrow, keyword prefixes, overflow, precedence, associativity, parentheses. The tests check spans exactly, not only token kinds, since error messages depend on them.
Status
Only the frontend exists. There is no code generation or execution yet, and the command line entry point prints a placeholder. Next are function parameters and more statement kinds, then a simple backend.