Initialize Rust implementation of Lox interpreter

This initial commit sets up the basic structure for a Rust
implementation of the Lox interpreter, including:

- Lexer for tokenizing source code - Parser for building AST - Basic
interpreter functionality - Environment for variable storage

The core components include source tracking, error handling, and basic
expression evaluation.
This commit is contained in:
Giulio Agostini
2025-10-03 19:07:12 +02:00
commit a7df45dc72
18 changed files with 2710 additions and 0 deletions
+304
View File
@@ -0,0 +1,304 @@
use crate::frontend::{
source_registry::{SourceId, SourcePosition, SourceSlice},
tokens::{LiteralValue, TokenType},
};
use std::fmt::{Debug, Display};
/*
* grammar:
* program -> statement* EOF
* statement -> expression_statement
* | print_statement
* | var_statement
* | block_statement
* | assignment_statement
* | if_statement
*
* expression_statement -> expression ";"
* print_statement -> "print" expression ";"
* var_statement -> "var" IDENTIFIER ("=" expression)? ";"
* assignment_statement -> IDENTIFIER "=" expression ";"
* block_statement -> "do" statement* "end"
* if_statement -> "if" expression "then" statement ("elif" expression "then" statement)* ("else" statement)? "end"
*
* expression -> assignment
* assignment -> IDENTIFIER "=" assignment | logical_or
* logical_or -> logical_and (("or" logical_and)*
* logical_and -> equality (("and" equality)*
* equality -> comparison (("==" | "!=") comparison)*
* comparison -> term ((">" | ">=" | "<" | "<=") term)*
* term -> factor (("+" | "-") factor)*
* factor -> unary (("*" | "/") unary)*
* unary -> ("!" | "-") unary | primary
* primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
*/
#[derive(Clone)]
pub enum Expr {
Literal {
value: LiteralValue,
},
Binary {
left: Box<Expr>,
operator: TokenType,
right: Box<Expr>,
},
Unary {
operator: TokenType,
operand: Box<Expr>,
},
Grouping {
expression: Box<Expr>,
},
Variable {
name: String,
},
}
pub trait ExprPrint {
fn print(&self) -> String;
}
impl ExprPrint for Expr {
fn print(&self) -> String {
match self {
Expr::Literal { value } => format!("Literal {}", value),
Expr::Binary {
left,
operator,
right,
} => {
format!("Binary ({} {} {})", left, operator, right)
}
Expr::Unary { operator, operand } => {
format!("Unary ({} {})", operator, operand)
}
Expr::Grouping { expression } => {
format!("Grouping (group {})", expression)
}
Expr::Variable { name } => {
format!("Variable (variable {})", name)
}
}
}
}
// Implementazione Display per Expr (per debugging)
impl std::fmt::Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Literal { value } => write!(f, "Literal {}", value),
Expr::Binary {
left,
operator,
right,
} => write!(f, "Binary ({} {} {})", left, operator, right),
Expr::Unary { operator, operand } => write!(f, "Unary ({} {})", operator, operand),
Expr::Grouping { expression } => write!(f, "Grouping (group {})", expression),
Expr::Variable { name } => write!(f, "Variable (variable {})", name),
}
}
}
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Literal { value } => write!(f, "{:?}", value),
Expr::Binary {
left,
operator,
right,
} => write!(f, "({:?} {:?} {:?})", operator, left, right),
Expr::Unary { operator, operand } => write!(f, "({:?} {:?})", operator, operand),
Expr::Grouping { expression } => write!(f, "(group {:?})", expression),
Expr::Variable { name } => write!(f, "(variable {:?})", name),
}
}
}
#[derive(Clone)]
pub enum Stmt {
Expression {
expression: Box<Expr>,
},
Print {
expression: Box<Expr>,
},
Stmt {
expression: Box<Expr>,
},
Var {
name: String,
initializer: Option<Box<Expr>>,
},
Assign {
name: String,
value: Box<Expr>,
},
Return {
expression: Box<Expr>,
},
Block {
statements: Box<Vec<Stmt>>,
},
If {
condition: Box<Expr>,
then_branch: Box<Stmt>,
elif_branch: Vec<(Box<Expr>, Box<Stmt>)>,
else_branch: Option<Box<Stmt>>,
},
}
impl Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Stmt::Expression { expression } => write!(f, "Expression ({})", expression),
Stmt::If {
condition,
then_branch,
elif_branch,
else_branch,
} => {
let mut result = format!("IF ({}) {{\n{}\n}}", condition, then_branch);
for (condition, branch) in elif_branch {
result.push_str(&format!(" ELIF ({}) {{\n{}\n}}", condition, branch));
}
if let Some(else_branch) = else_branch {
result.push_str(&format!(" ELSE {{\n{}\n}}", else_branch));
}
write!(f, "IfStmt {}", result)
}
Stmt::Print { expression } => write!(f, "Print({});", expression),
Stmt::Stmt { expression } => write!(f, "Stmt({});", expression),
Stmt::Var { name, initializer } => {
write!(f, "Var({} = {:?});", name, initializer)
}
Stmt::Assign { name, value } => write!(f, "Assign({} = {});", name, value),
Stmt::Return { expression } => write!(f, "Return({});", expression),
Stmt::Block { statements } => write!(
f,
"Block([\n{}\n])",
statements
.iter()
.map(|stmt| format!("\t \t{}", stmt))
.collect::<Vec<_>>()
.join("\n")
),
}
}
}
impl Debug for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Stmt::Expression { expression } => write!(f, " Expression ({:?})", expression),
Stmt::If {
condition,
then_branch,
elif_branch,
else_branch,
} => {
let mut result = format!("IF ({:?}) {{\n{:?}\n}}", condition, then_branch);
for (condition, branch) in elif_branch {
result.push_str(&format!(" ELIF ({:?}) {{\n{:?}\n}}", condition, branch));
}
if let Some(else_branch) = else_branch {
result.push_str(&format!(" ELSE {{\n{:?}\n}}", else_branch));
}
write!(f, "IfStmt {:?}", result)
}
Stmt::Print { expression } => write!(f, "Print({:?});", expression),
Stmt::Stmt { expression } => write!(f, "Stmt({:?});", expression),
Stmt::Var { name, initializer } => {
write!(f, "Var({:?} = {:?});", name, initializer)
}
Stmt::Assign { name, value } => write!(f, "Assign({:?} = {:?});", name, value),
Stmt::Return { expression } => write!(f, "Return({:?});", expression),
Stmt::Block { statements } => write!(
f,
"Block([\n{:?}\n])",
statements
.iter()
.map(|stmt| format!("{:?}", stmt))
.collect::<Vec<_>>()
.join("\t\t\n")
),
}
}
}
pub trait AstNodeKind {
fn kind(&self) -> &'static str;
}
impl AstNodeKind for Expr {
fn kind(&self) -> &'static str {
match self {
Expr::Literal { value: _ } => "literal",
Expr::Binary {
left: _,
operator: _,
right: _,
} => "binary expression",
Expr::Unary {
operator: _,
operand: _,
} => "unary expression",
Expr::Grouping { expression: _ } => "grouping expression",
Expr::Variable { name: _ } => "variable expression",
}
}
}
impl AstNodeKind for Stmt {
fn kind(&self) -> &'static str {
match self {
Stmt::Expression { expression: _ } => "expression",
Stmt::Var {
name: _,
initializer: _,
} => "variable declaration",
Stmt::Assign { name: _, value: _ } => "assignment",
Stmt::Return { expression: _ } => "return statement",
Stmt::Block { statements: _ } => "block statement",
Stmt::If {
condition: _,
then_branch: _,
elif_branch: _,
else_branch: _,
} => "if statement",
Stmt::Print { expression: _ } => "print statement",
Stmt::Stmt { expression: _ } => "statement",
}
}
}
#[derive(Clone, PartialEq)]
pub struct AstNode<T: AstNodeKind + Debug + Display> {
pub node: T,
pub source_slice: SourceSlice,
}
impl<T: AstNodeKind + Debug + Display> Display for AstNode<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AstNode \n{{ \n\tnode: {},\n\tsource_slice: {}, \n}}",
self.node, self.source_slice
)
}
}
impl<T: AstNodeKind + Debug + Display> Debug for AstNode<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AstNode \n{{ \n\tnode: {:?},\n\tsource_slice: {:?}, \n}}",
self.node, self.source_slice
)
}
}
impl<T: AstNodeKind + Debug + Display> AstNode<T> {
pub fn new(node: T, source_slice: SourceSlice) -> Self {
AstNode { node, source_slice }
}
}
+282
View File
@@ -0,0 +1,282 @@
use crate::frontend::source_registry::{SourceId, SourcePosition, SourceSlice};
use crate::frontend::tokens::{LiteralValue, Token, TokenType};
use crate::result::{LoxError, LoxResult};
pub struct Lexer {
input: String,
start_char: usize,
current_char: usize,
start_pos: SourcePosition,
end_pos: SourcePosition,
source_id: SourceId,
}
fn get_keyword_token(word: &str) -> Option<TokenType> {
match word {
"and" => Some(TokenType::And),
"class" => Some(TokenType::Class),
"do" => Some(TokenType::StartBlock),
"end" => Some(TokenType::EndBlock),
"false" => Some(TokenType::False),
"for" => Some(TokenType::For),
"fun" => Some(TokenType::Fun),
"if" => Some(TokenType::If),
"elif" => Some(TokenType::Elif),
"else" => Some(TokenType::Else),
"or" => Some(TokenType::Or),
"print" => Some(TokenType::Print),
"return" => Some(TokenType::Return),
"super" => Some(TokenType::Super),
"this" => Some(TokenType::This),
"true" => Some(TokenType::True),
"var" => Some(TokenType::Var),
"val" => Some(TokenType::Val),
"while" => Some(TokenType::While),
"True" => Some(TokenType::True),
"False" => Some(TokenType::False),
"Nil" => Some(TokenType::Nil),
_ => None,
}
}
impl Lexer {
pub fn new(input: String, source_id: SourceId) -> Lexer {
Lexer {
input,
start_char: 0,
current_char: 0,
start_pos: SourcePosition::default(),
end_pos: SourcePosition::default(),
source_id,
}
}
fn advance_column(&mut self) {
self.current_char += 1;
self.end_pos.column += 1;
}
fn advance_line(&mut self) {
self.end_pos.line += 1;
self.end_pos.column = 0;
}
pub fn scans_tokens(&mut self) -> LoxResult<Vec<Token>> {
let mut tokens = Vec::new();
while !self.is_at_end() {
self.start_char = self.current_char;
self.start_pos = self.end_pos.clone();
match self.scan_token() {
Ok(Some(token)) => tokens.push(token),
Ok(None) => {}
Err(err) => return Err(err),
}
}
tokens.push(self.make_token(TokenType::Eof));
println!("Tokens:");
tokens.iter().for_each(|val| println!("{}", val));
Ok(tokens)
}
fn is_at_end(&self) -> bool {
self.current_char >= self.input.len()
}
fn advance(&mut self) -> char {
self.advance_column();
self.input.chars().nth(self.current_char - 1).unwrap()
}
fn peek(&self) -> char {
if self.is_at_end() {
'\0'
} else {
self.input.chars().nth(self.current_char).unwrap()
}
}
fn peek_next(&self) -> char {
if self.current_char + 1 >= self.input.len() {
'\0'
} else {
self.input.chars().nth(self.current_char + 1).unwrap()
}
}
fn make_token(&self, token_type: TokenType) -> Token {
let text = self.input[self.start_char..self.current_char].to_string();
Token::new(
token_type,
text,
SourceSlice::from_positions(
self.source_id.clone(),
self.start_pos.clone(),
self.end_pos.clone(),
),
)
}
fn make_token_with_literal(&self, token_type: TokenType, literal: LiteralValue) -> Token {
let text = self.input[self.start_char..self.current_char].to_string();
Token::new_complete(
token_type,
text,
Some(literal),
SourceSlice::from_positions(
self.source_id.clone(),
self.start_pos.clone(),
self.end_pos.clone(),
),
)
}
fn scan_token(&mut self) -> LoxResult<Option<Token>> {
let c = self.advance();
match (c, self.peek()) {
('(', _) => Ok(Some(self.make_token(TokenType::LeftParen))),
(')', _) => Ok(Some(self.make_token(TokenType::RightParen))),
('{', _) => Ok(Some(self.make_token(TokenType::LeftBrace))),
('}', _) => Ok(Some(self.make_token(TokenType::RightBrace))),
('[', _) => Ok(Some(self.make_token(TokenType::LeftBracket))),
(']', _) => Ok(Some(self.make_token(TokenType::RightBracket))),
(',', _) => Ok(Some(self.make_token(TokenType::Comma))),
('.', _) => Ok(Some(self.make_token(TokenType::Dot))),
('-', _) => Ok(Some(self.make_token(TokenType::Minus))),
('+', _) => Ok(Some(self.make_token(TokenType::Plus))),
(';', _) => Ok(Some(self.make_token(TokenType::Semicolon))),
('*', _) => Ok(Some(self.make_token(TokenType::Star))),
('!', '=') => {
self.advance(); // consuma il '='
Ok(Some(self.make_token(TokenType::BangEqual)))
}
('!', _) => Ok(Some(self.make_token(TokenType::Bang))),
('=', '=') => {
self.advance(); // consuma il '='
Ok(Some(self.make_token(TokenType::EqualEqual)))
}
('=', _) => Ok(Some(self.make_token(TokenType::Equal))),
('<', '=') => {
self.advance(); // consuma il '='
Ok(Some(self.make_token(TokenType::LessEqual)))
}
('<', _) => Ok(Some(self.make_token(TokenType::Less))),
('>', '=') => {
self.advance(); // consuma il '='
Ok(Some(self.make_token(TokenType::GreaterEqual)))
}
('>', _) => Ok(Some(self.make_token(TokenType::Greater))),
('/', '/') => {
// Commento single-line
while self.peek() != '\n' && !self.is_at_end() {
self.advance();
}
Ok(None)
}
('/', '*') => {
// Commento multi-line
while (self.peek() != '*' || self.peek_next() != '/') && !self.is_at_end() {
if self.peek() == '\n' {
self.advance_line();
}
self.advance();
}
if self.is_at_end() {
return Err(LoxError::LexicalError {
source_slice: SourceSlice::from_positions(
self.source_id.clone(),
self.start_pos.clone(),
self.end_pos.clone(),
),
message: "Unterminated comment".to_string(),
});
} else {
self.advance(); // consuma '*'
self.advance(); // consuma '/'
}
Ok(None)
}
('/', _) => Ok(Some(self.make_token(TokenType::Slash))),
(' ', _) | ('\r', _) | ('\t', _) => Ok(None),
('\n', _) => {
self.advance_line();
Ok(None)
}
('"', _) => self.string(),
(c, _) if c.is_digit(10) => self.number(),
(c, _) if c.is_alphanumeric() || c == '_' => self.identifier(),
_ => Err(LoxError::LexicalError {
source_slice: SourceSlice::from_positions(
self.source_id.clone(),
self.start_pos.clone(),
self.end_pos.clone(),
),
message: format!("Unexpected character: {}", c),
}),
}
}
fn string(&mut self) -> LoxResult<Option<Token>> {
while self.peek() != '"' && !self.is_at_end() {
self.advance();
}
if self.is_at_end() {
return Err(LoxError::LexicalError {
source_slice: SourceSlice::from_positions(
self.source_id.clone(),
self.start_pos.clone(),
self.end_pos.clone(),
),
message: "Unterminated string".to_string(),
});
}
self.advance();
Ok(Some(self.make_token_with_literal(
TokenType::String,
LiteralValue::String(self.input[self.start_char..self.current_char].to_string()),
)))
}
fn number(&mut self) -> LoxResult<Option<Token>> {
while self.peek().is_digit(10) {
self.advance();
}
if self.peek() == '.' && self.peek_next().is_digit(10) {
self.advance();
while self.peek().is_digit(10) {
self.advance();
}
}
Ok(Some(
self.make_token_with_literal(
TokenType::Number,
LiteralValue::Number(
self.input[self.start_char..self.current_char]
.parse()
.unwrap(),
),
),
))
}
fn identifier(&mut self) -> LoxResult<Option<Token>> {
while self.peek().is_alphanumeric() {
self.advance();
}
let text = self.input[self.start_char..self.current_char].to_string();
match get_keyword_token(&text) {
Some(TokenType::True) => Ok(Some(
self.make_token_with_literal(TokenType::True, LiteralValue::Boolean(true)),
)),
Some(TokenType::False) => Ok(Some(
self.make_token_with_literal(TokenType::False, LiteralValue::Boolean(false)),
)),
Some(TokenType::Nil) => Ok(Some(
self.make_token_with_literal(TokenType::Nil, LiteralValue::Nil),
)),
Some(token_type) => Ok(Some(self.make_token(token_type))),
None => Ok(Some(self.make_token_with_literal(
TokenType::Identifier,
LiteralValue::String(text.clone()),
))),
}
}
}
+5
View File
@@ -0,0 +1,5 @@
pub mod ast;
pub mod lexer;
pub mod parser;
pub mod source_registry;
pub mod tokens;
+437
View File
@@ -0,0 +1,437 @@
use crate::{
frontend::{
ast::{AstNode, Expr, Stmt},
tokens::{LiteralValue, Token, TokenType},
},
logging::display_ast::{pretty_print_with_config, PrettyConfig, PrettyPrint},
result::{LoxError, LoxResult},
};
pub struct Parser {
tokens: Vec<Token>,
current: usize,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Self { tokens, current: 0 }
}
pub fn parse(&mut self) -> LoxResult<Vec<AstNode<Stmt>>> {
let mut statements = Vec::new();
let mut errors = Vec::new();
println!("\n \n ++++++++++++++++++++++++++++++++++++\nStarting parsing");
// Con configurazione personalizzata
let config = PrettyConfig {
indent: " ".to_string(),
max_depth: None,
show_positions: false,
..Default::default()
};
while !self.is_at_end() {
match self.statement() {
Ok(stmt) => {
println!(
"Parsed Successfully: {}",
pretty_print_with_config(&stmt.node, &config)
);
statements.push(stmt.clone());
}
Err(err) => {
println!("Error: {:?}", err);
errors.push(err);
let old_current = self.current;
self.synchronize();
println!("Synchronized from {} to {}", old_current, self.current);
}
}
}
if !errors.is_empty() {
return Err(errors.into_iter().next().unwrap());
}
Ok(statements)
}
fn statement(&mut self) -> LoxResult<AstNode<Stmt>> {
match (&self.peek().token_type, &self.peek_next().token_type) {
(TokenType::Var, _) => Ok(AstNode::new(
self.var_statement()?,
self.peek().source_slice.clone(),
)),
(TokenType::Print, _) => Ok(AstNode::new(
self.print_statement()?,
self.peek().source_slice.clone(),
)),
(TokenType::Return, _) => Ok(AstNode::new(
self.return_statement()?,
self.peek().source_slice.clone(),
)),
(TokenType::StartBlock, _) => Ok(AstNode::new(
self.block_statement()?,
self.peek().source_slice.clone(),
)),
(TokenType::Identifier, TokenType::Equal) => Ok(AstNode::new(
self.assignment_statement()?,
self.peek().source_slice.clone(),
)),
(TokenType::If, _) => Ok(AstNode::new(
self.if_statement()?,
self.peek().source_slice.clone(),
)),
_ => Ok(AstNode::new(
self.expression_statement()?,
self.peek().source_slice.clone(),
)),
}
}
fn if_statement(&mut self) -> LoxResult<Stmt> {
self.advance();
let condition = self.expression()?;
let then_branch = self.statement()?;
let mut elif_branches = Vec::new();
while self.peek().token_type == TokenType::Elif {
self.advance();
let condition = self.expression()?;
let then_branch = self.statement()?;
elif_branches.push((Box::new(condition), Box::new(then_branch.node)));
}
let else_branch = if self.peek().token_type == TokenType::Else {
self.advance();
Some(Box::new(self.statement()?.node))
} else {
None
};
Ok(Stmt::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch.node),
elif_branch: elif_branches,
else_branch: else_branch,
})
}
fn var_statement(&mut self) -> LoxResult<Stmt> {
self.advance();
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
let name_lexeme = name.lexeme.clone();
self.consume(TokenType::Equal, "Expect '=' after variable name.")?;
let value = self.expression()?;
self.consume(
TokenType::Semicolon,
"Expect ';' after variable declaration.",
)?;
Ok(Stmt::Var {
name: name_lexeme,
initializer: Some(Box::new(value)),
})
}
fn assignment_statement(&mut self) -> LoxResult<Stmt> {
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
let name_lexeme = name.lexeme.clone();
self.consume(TokenType::Equal, "Expect '=' after variable name.")?;
let value = self.expression()?;
self.consume(
TokenType::Semicolon,
"Expect ';' after variable declaration.",
)?;
Ok(Stmt::Assign {
name: name_lexeme,
value: Box::new(value),
})
}
fn print_statement(&mut self) -> LoxResult<Stmt> {
// consume the print keyword
self.advance();
let expr = self.expression()?;
self.consume(TokenType::Semicolon, "Expect ';' after value.")?;
Ok(Stmt::Print {
expression: Box::new(expr),
})
}
fn block_statement(&mut self) -> LoxResult<Stmt> {
self.advance();
let mut statements = Vec::new();
while self.peek().token_type != TokenType::EndBlock && !self.is_at_end() {
let stmt = self.statement()?;
statements.push(stmt.node.clone());
if let Stmt::Expression { .. } = stmt.node {
break;
}
}
self.consume(TokenType::EndBlock, "Expect 'end' after block.")?;
Ok(Stmt::Block {
statements: Box::new(statements),
})
}
fn expression_statement(&mut self) -> LoxResult<Stmt> {
let expr = self.expression()?;
if self.peek().token_type == TokenType::Semicolon {
self.advance();
return Ok(Stmt::Stmt {
expression: Box::new(expr),
});
}
Ok(Stmt::Expression {
expression: Box::new(expr),
})
}
fn return_statement(&mut self) -> LoxResult<Stmt> {
self.advance();
let expr = self.expression()?;
self.consume(TokenType::Semicolon, "Expect ';' after return value.")?;
Ok(Stmt::Return {
expression: Box::new(expr),
})
}
fn expression(&mut self) -> LoxResult<Expr> {
self.or_and()
}
fn or_and(&mut self) -> LoxResult<Expr> {
let mut expr = self.equality()?;
while [TokenType::Or, TokenType::And].contains(&self.peek().token_type) {
let operator = self.peek().token_type.clone();
self.advance();
let right = self.equality()?;
expr = Expr::Binary {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
fn equality(&mut self) -> LoxResult<Expr> {
let mut expr = self.comparison()?;
while [TokenType::BangEqual, TokenType::EqualEqual].contains(&self.peek().token_type) {
let operator = self.peek().token_type.clone();
self.advance();
let right = self.comparison()?;
expr = Expr::Binary {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
fn comparison(&mut self) -> LoxResult<Expr> {
let mut expr = self.term()?;
while [
TokenType::Greater,
TokenType::GreaterEqual,
TokenType::Less,
TokenType::LessEqual,
]
.contains(&self.peek().token_type)
{
let operator = self.peek().token_type.clone();
self.advance();
let right = self.term()?;
expr = Expr::Binary {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
fn term(&mut self) -> LoxResult<Expr> {
let mut expr = self.factor()?;
while [TokenType::Plus, TokenType::Minus].contains(&self.peek().token_type) {
let operator = self.peek().token_type.clone();
self.advance();
let right = self.factor()?;
expr = Expr::Binary {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
fn factor(&mut self) -> LoxResult<Expr> {
let mut expr = self.unary()?;
while [TokenType::Slash, TokenType::Star].contains(&self.peek().token_type) {
let operator = self.peek().token_type.clone();
self.advance();
let right = self.unary()?;
expr = Expr::Binary {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
fn unary(&mut self) -> LoxResult<Expr> {
if [TokenType::Bang, TokenType::Minus].contains(&self.peek().token_type) {
let operator = self.peek().token_type.clone();
self.advance();
let right = self.unary()?;
return Ok(Expr::Unary {
operator,
operand: Box::new(right),
});
}
self.primary()
}
fn primary(&mut self) -> LoxResult<Expr> {
match self.peek().token_type {
TokenType::False => {
self.advance();
Ok(Expr::Literal {
value: LiteralValue::Boolean(false),
})
}
TokenType::True => {
self.advance();
Ok(Expr::Literal {
value: LiteralValue::Boolean(true),
})
}
TokenType::Nil => {
self.advance();
Ok(Expr::Literal {
value: LiteralValue::Nil,
})
}
TokenType::Number => {
self.advance();
let token = self.previous();
if let Some(literal) = &token.literal {
Ok(Expr::Literal {
value: literal.clone(),
})
} else {
Err(self.error(token, "Expected number literal").into())
}
}
TokenType::String => {
self.advance();
let token = self.previous();
if let Some(literal) = &token.literal {
Ok(Expr::Literal {
value: literal.clone(),
})
} else {
Err(self.error(token, "Expected string literal").into())
}
}
TokenType::LeftParen => {
self.advance();
let expr = self.expression()?;
self.consume(TokenType::RightParen, "Expect ')' after expression.")?;
Ok(Expr::Grouping {
expression: Box::new(expr),
})
}
TokenType::Identifier => {
let name = self.peek().lexeme.clone();
self.advance();
Ok(Expr::Variable { name: name })
}
_ => Err(self.error(self.peek(), "Expect expression.").into()),
}
}
fn is_at_end(&self) -> bool {
if self.current >= self.tokens.len() {
true
} else {
self.tokens[self.current].token_type == TokenType::Eof
}
}
fn advance(&mut self) -> &Token {
if !self.is_at_end() {
self.current += 1;
}
self.peek()
}
fn peek(&self) -> &Token {
if self.is_at_end() {
&self.tokens.last().unwrap()
} else {
&self.tokens[self.current]
}
}
fn peek_next(&self) -> &Token {
if self.is_at_end() {
&self.peek()
} else {
&self.tokens[self.current + 1]
}
}
fn previous(&self) -> &Token {
&self.tokens[self.current - 1]
}
fn consume(&mut self, token_type: TokenType, message: &str) -> LoxResult<&Token> {
if self.peek().token_type == token_type {
self.advance();
Ok(self.previous())
} else {
Err(self.error(self.peek(), message).into())
}
}
fn error(&self, token: &Token, message: &str) -> LoxError {
LoxError::ParseError {
source_slice: token.source_slice.clone(),
message: message.to_string(),
}
}
fn synchronize(&mut self) {
self.advance();
while !self.is_at_end() {
if self.previous().token_type == TokenType::Semicolon {
return;
}
match self.peek().token_type {
TokenType::Class
| TokenType::Fun
| TokenType::Var
| TokenType::For
| TokenType::If
| TokenType::While
| TokenType::Print
| TokenType::Return => return,
_ => {}
}
self.advance();
}
}
}
+201
View File
@@ -0,0 +1,201 @@
use std::{
fmt::{Debug, Display},
fs,
io::ErrorKind,
};
use crate::result::{LoxError, LoxResult};
pub struct SourceRegistry {
pub sources: Vec<SourceFile>,
}
pub type SourceId = usize;
pub enum SourceType {
File,
String,
}
pub struct SourceFile {
pub id: SourceId,
pub source_type: SourceType,
pub file_name: String,
pub content: String,
}
impl SourceFile {
pub fn new(id: SourceId, source_type: SourceType, file_name: String, content: String) -> Self {
Self {
id,
source_type,
file_name,
content,
}
}
pub fn new_source_file(id: SourceId, file_name: String, content: String) -> Self {
Self {
id,
source_type: SourceType::File,
file_name,
content,
}
}
pub fn new_source_string(id: SourceId, content: String) -> Self {
Self {
id,
source_type: SourceType::String,
file_name: String::new(),
content,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SourcePosition {
pub line: usize,
pub column: usize,
}
impl Display for SourcePosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
#[derive(Clone, PartialEq, Default)]
pub struct SourceSlice {
pub source_id: SourceId,
pub start_position: SourcePosition,
pub end_position: SourcePosition,
}
impl Debug for SourceSlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SourceSlice \n{{ \n\tsource_id: {:?},\n\tstart_position: {:?},\n\tend_position: {:?}, \n\tline: {:?} \n}}",
self.source_id, self.start_position, self.end_position, self.source_id
)
}
}
impl Display for SourceSlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SourceSlice \n{{ \n\tsource_id: {},\n\tstart_position: {},\n\tend_position: {}, \n\tline: {} \n}}",
self.source_id, self.start_position, self.end_position, self.source_id
)
}
}
impl SourceSlice {
pub fn from_positions(
source_id: SourceId,
start_position: SourcePosition,
end_position: SourcePosition,
) -> Self {
Self {
source_id,
start_position,
end_position,
}
}
pub fn tree_point(
source_id: SourceId,
column_start: usize,
column_end: usize,
line: usize,
) -> Self {
Self {
source_id,
start_position: SourcePosition {
line,
column: column_start,
},
end_position: SourcePosition {
line,
column: column_end,
},
}
}
}
impl SourceRegistry {
pub fn new() -> Self {
Self {
sources: Vec::new(),
}
}
fn read_file_detailed(path: &str) -> LoxResult<String> {
match fs::read_to_string(path) {
Ok(content) => Ok(content),
Err(error) => {
let error_msg = match error.kind() {
ErrorKind::NotFound => {
format!("File '{}' not found", path)
}
ErrorKind::PermissionDenied => {
format!("Permission denied reading '{}'", path)
}
ErrorKind::InvalidData => {
format!("File '{}' contains invalid UTF-8", path)
}
_ => {
format!("Failed to read '{}': {}", path, error)
}
};
Err(LoxError::IoError { message: error_msg })
}
}
}
pub fn add_source_file(&mut self, filename: String) -> LoxResult<SourceId> {
let content = Self::read_file_detailed(&filename)?;
let source_id = self.sources.len();
self.sources.push(SourceFile {
id: source_id,
source_type: SourceType::File,
file_name: filename,
content,
});
Ok(source_id)
}
pub fn add_source_string(&mut self, content: String) -> LoxResult<SourceId> {
let source_id = self.sources.len();
self.sources.push(SourceFile {
id: source_id,
source_type: SourceType::String,
file_name: String::new(),
content,
});
Ok(source_id)
}
pub fn get_by_id(&self, id: SourceId) -> &SourceFile {
self.sources.get(id).unwrap()
}
pub fn get_source_code(&self, source: &SourceSlice) -> String {
let lines = self
.sources
.get(source.source_id)
.unwrap()
.content
.split('\n')
.collect::<Vec<&str>>();
let mut line_of_code = String::new();
lines
.get(source.start_position.line..source.end_position.line + 1)
.unwrap()
.iter()
.for_each(|line| {
line_of_code.push_str(line);
line_of_code.push('\n');
});
line_of_code
}
}
+184
View File
@@ -0,0 +1,184 @@
use std::fmt;
use crate::frontend::source_registry::SourceSlice;
#[derive(Debug, Clone, PartialEq)]
pub enum LiteralValue {
Identifier(String),
String(String),
Number(f64),
Boolean(bool),
Nil,
}
impl fmt::Display for LiteralValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LiteralValue::Identifier(id) => write!(f, "{}", id),
LiteralValue::String(s) => write!(f, "{}", s),
LiteralValue::Number(n) => write!(f, "{}", n),
LiteralValue::Boolean(b) => write!(f, "{}", b),
LiteralValue::Nil => write!(f, "nil"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenType {
// Single character tokens
LeftParen,
RightParen,
LeftBrace,
RightBrace,
LeftBracket,
RightBracket,
Comma,
Dot,
Minus,
Plus,
Semicolon,
Slash,
Star,
Percent,
// One or two character tokens
Bang,
BangEqual,
Equal,
EqualEqual,
Greater,
GreaterEqual,
Less,
LessEqual,
// Literals
Identifier,
String,
Number,
// Keywords
And,
Class,
StartBlock,
EndBlock,
False,
True,
Fun,
For,
While,
If,
Elif,
Else,
Nil,
Or,
Print,
Return,
Super,
This,
Var,
Val,
Eof,
}
impl fmt::Display for TokenType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenType::Bang => write!(f, "!"),
TokenType::BangEqual => write!(f, "!="),
TokenType::Equal => write!(f, "="),
TokenType::EqualEqual => write!(f, "=="),
TokenType::Greater => write!(f, ">"),
TokenType::GreaterEqual => write!(f, ">="),
TokenType::Less => write!(f, "<"),
TokenType::LessEqual => write!(f, "<="),
TokenType::Identifier => write!(f, "IDENTIFIER"),
TokenType::String => write!(f, "STRING"),
TokenType::Number => write!(f, "NUMBER"),
TokenType::And => write!(f, "and"),
TokenType::Class => write!(f, "class"),
TokenType::Else => write!(f, "else"),
TokenType::False => write!(f, "false"),
TokenType::True => write!(f, "true"),
TokenType::Fun => write!(f, "fun"),
TokenType::For => write!(f, "for"),
TokenType::If => write!(f, "if"),
TokenType::Nil => write!(f, "nil"),
TokenType::Or => write!(f, "or"),
TokenType::Print => write!(f, "print"),
TokenType::Return => write!(f, "return"),
TokenType::Super => write!(f, "super"),
TokenType::This => write!(f, "this"),
TokenType::Var => write!(f, "var"),
TokenType::While => write!(f, "while"),
TokenType::Eof => write!(f, "EOF"),
TokenType::LeftParen => write!(f, "("),
TokenType::RightParen => write!(f, ")"),
TokenType::LeftBrace => write!(f, "{{"),
TokenType::RightBrace => write!(f, "}}"),
TokenType::LeftBracket => write!(f, "["),
TokenType::RightBracket => write!(f, "]"),
TokenType::Comma => write!(f, ","),
TokenType::Dot => write!(f, "."),
TokenType::Minus => write!(f, "-"),
TokenType::Plus => write!(f, "+"),
TokenType::Semicolon => write!(f, ";"),
TokenType::Slash => write!(f, "/"),
TokenType::Star => write!(f, "*"),
TokenType::StartBlock => write!(f, "do"),
TokenType::EndBlock => write!(f, "end"),
TokenType::Elif => write!(f, "elif"),
TokenType::Val => write!(f, "val"),
TokenType::Percent => write!(f, "%"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub token_type: TokenType,
pub lexeme: String,
pub literal: Option<LiteralValue>,
pub source_slice: SourceSlice,
}
impl Token {
pub fn new(token_type: TokenType, lexeme: String, source_slice: SourceSlice) -> Self {
Token {
token_type,
lexeme,
literal: None,
source_slice,
}
}
pub fn new_complete(
token_type: TokenType,
lexeme: String,
literal: Option<LiteralValue>,
source_slice: SourceSlice,
) -> Self {
Token {
token_type,
lexeme,
literal,
source_slice,
}
}
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.literal {
Some(literal) => write!(
f,
"{:?} {} {:?} ref: {:?}",
self.token_type, self.lexeme, literal, self.source_slice
),
None => write!(
f,
"{:?} {} None ref: {:?}",
self.token_type, self.lexeme, self.source_slice
),
}
}
}