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:
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -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()),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod ast;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod source_registry;
|
||||
pub mod tokens;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user