mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 12:22:42 +00:00
Lox Interpreter done (Chapter 13)
This commit is contained in:
parent
10540708d4
commit
719a014977
16 changed files with 707 additions and 196 deletions
|
|
@ -37,9 +37,13 @@ pub enum ParserError {
|
|||
#[error("Function declaration at {code_pos} has too many parameters")]
|
||||
TooManyParams { code_pos: CodePos },
|
||||
#[error("Return statement outside of function definition")]
|
||||
InvalidReturn { code_pos: CodePos },
|
||||
ReturnOutsideFunction { code_pos: CodePos },
|
||||
#[error("Break statement outside of loop")]
|
||||
InvalidBreak { code_pos: CodePos },
|
||||
#[error("Missing class body at {code_pos}")]
|
||||
MissingClassBody { code_pos: CodePos },
|
||||
#[error("Return statement in init")]
|
||||
ReturnInInit { code_pos: CodePos },
|
||||
#[error("Missing method name after super")]
|
||||
MissingMethodAfterSuper { code_pos: CodePos },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,12 +45,32 @@ pub enum Expr {
|
|||
callee: Box<Expr>,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
Function {
|
||||
Get {
|
||||
target: Box<Expr>,
|
||||
name: String,
|
||||
param_names: Vec<String>,
|
||||
closure_vars: Vec<(String, usize)>,
|
||||
},
|
||||
Set {
|
||||
target: Box<Expr>,
|
||||
name: String,
|
||||
value: Box<Expr>,
|
||||
},
|
||||
Function {
|
||||
name: Box<String>,
|
||||
param_names: Box<Vec<String>>,
|
||||
closure_vars: Box<Vec<(String, usize)>>,
|
||||
body: Box<Stmt>,
|
||||
},
|
||||
Class {
|
||||
superclass: Option<Box<Expr>>,
|
||||
name: String,
|
||||
methods: Box<Vec<Expr>>,
|
||||
},
|
||||
This,
|
||||
Super {
|
||||
super_var: Box<Expr>,
|
||||
this_var: Box<Expr>,
|
||||
method: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
|
|
@ -110,15 +130,36 @@ impl Expr {
|
|||
Expr::Call { callee, args }
|
||||
}
|
||||
|
||||
pub fn get(target: Expr, name: impl Into<String>) -> Self {
|
||||
let target = Box::new(target);
|
||||
let name = name.into();
|
||||
Expr::Get { target, name }
|
||||
}
|
||||
|
||||
pub fn function(name: String, param_names: Vec<String>, body: Stmt) -> Self {
|
||||
let name = Box::new(name);
|
||||
let param_names = Box::new(param_names);
|
||||
#[allow(clippy::box_default)]
|
||||
let closure_vars = Box::new(Vec::new());
|
||||
let body = Box::new(body);
|
||||
Self::Function {
|
||||
name,
|
||||
param_names,
|
||||
closure_vars: Vec::new(),
|
||||
closure_vars,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class(name: impl Into<String>, methods: Vec<Expr>, superclass: Option<Expr>) -> Self {
|
||||
let superclass = superclass.map(Box::new);
|
||||
let name = name.into();
|
||||
let methods = Box::new(methods);
|
||||
Expr::Class {
|
||||
superclass,
|
||||
name,
|
||||
methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
|
|
@ -136,6 +177,8 @@ impl Display for Expr {
|
|||
Expr::GlobalVariable { name } => write!(f, "(var {name} global)"),
|
||||
Expr::Assignment { target, value } => write!(f, "{target} = {value}"),
|
||||
Expr::Call { callee, args } => write!(f, "({callee} {})", args.iter().map(|arg| arg.to_string()).join(" ")),
|
||||
Expr::Get { target, name } => write!(f, "(get {name} {target})"),
|
||||
Expr::Set { target, name, value } => write!(f, "(set {name} {target} {value})"),
|
||||
Expr::Function {
|
||||
name,
|
||||
param_names,
|
||||
|
|
@ -145,11 +188,32 @@ impl Display for Expr {
|
|||
if !closure_vars.is_empty() {
|
||||
let closure_fmt = closure_vars.iter().map(|(name, _level)| name).join(", ");
|
||||
|
||||
write!(f, "fun [{closure_fmt}] {name}({}) {body}", param_names.join(", "))
|
||||
write!(f, "fun [{closure_fmt}] {name}({}) => {body}", param_names.join(", "))
|
||||
} else {
|
||||
write!(f, "fun {name}({}) {body}", param_names.join(", "))
|
||||
write!(f, "fun {name}({}) => {body}", param_names.join(", "))
|
||||
}
|
||||
}
|
||||
Expr::Class {
|
||||
superclass,
|
||||
name,
|
||||
methods,
|
||||
} => {
|
||||
if let Some(superclass) = superclass {
|
||||
writeln!(f, "class {name} < {superclass} {{")?;
|
||||
} else {
|
||||
writeln!(f, "class {name} {{")?;
|
||||
}
|
||||
for method in methods.iter() {
|
||||
writeln!(f, "{method}")?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
Expr::This => write!(f, "this"),
|
||||
Expr::Super {
|
||||
super_var: _,
|
||||
this_var: _,
|
||||
method,
|
||||
} => write!(f, "super.{method}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,10 @@ struct Parser {
|
|||
|
||||
parse_errors: Vec<ParserError>,
|
||||
|
||||
is_in_function: bool,
|
||||
is_in_loop: bool,
|
||||
is_in_class: bool,
|
||||
is_in_function: bool,
|
||||
is_in_init: bool,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
|
|
@ -71,8 +73,10 @@ impl Parser {
|
|||
Parser {
|
||||
token_iter: TokenIter::new(tokens),
|
||||
parse_errors: Vec::new(),
|
||||
is_in_function: false,
|
||||
is_in_loop: false,
|
||||
is_in_class: false,
|
||||
is_in_function: false,
|
||||
is_in_init: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +116,12 @@ impl Parser {
|
|||
return;
|
||||
}
|
||||
|
||||
// when synchronising: assume all false
|
||||
self.is_in_loop = false;
|
||||
self.is_in_class = false;
|
||||
self.is_in_function = false;
|
||||
self.is_in_init = false;
|
||||
|
||||
let peek_token = self.peek_token();
|
||||
|
||||
// if we match a synchronisation point: return
|
||||
|
|
@ -158,23 +168,43 @@ impl Parser {
|
|||
}
|
||||
Ok(Stmt::Break)
|
||||
}
|
||||
TokenType::Return => {
|
||||
let code_pos = self.peek_token().code_pos;
|
||||
assert_eq!(self.next_token().token_type, TokenType::Return);
|
||||
let expr = match self.peek_token().token_type {
|
||||
TokenType::Semicolon => Expr::nil(),
|
||||
_ => self.expression()?,
|
||||
};
|
||||
self.semicolon()?;
|
||||
if !self.is_in_function {
|
||||
return Err(ParserError::InvalidReturn { code_pos });
|
||||
}
|
||||
Ok(Stmt::return_stmt(expr))
|
||||
}
|
||||
TokenType::Return => self.return_statement(),
|
||||
_ => self.expression_statement(),
|
||||
}
|
||||
}
|
||||
|
||||
fn return_statement(&mut self) -> ParserResult<Stmt> {
|
||||
let code_pos = self.peek_token().code_pos;
|
||||
assert_eq!(self.next_token().token_type, TokenType::Return);
|
||||
|
||||
let expr = match self.peek_token().token_type {
|
||||
TokenType::Semicolon => {
|
||||
if !self.is_in_init {
|
||||
Expr::nil()
|
||||
} else {
|
||||
Expr::Variable {
|
||||
name: "this".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if !self.is_in_init {
|
||||
self.expression()?
|
||||
} else {
|
||||
return Err(ParserError::ReturnInInit { code_pos });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.semicolon()?;
|
||||
|
||||
if !self.is_in_function {
|
||||
return Err(ParserError::ReturnOutsideFunction { code_pos });
|
||||
}
|
||||
|
||||
Ok(Stmt::return_stmt(expr))
|
||||
}
|
||||
|
||||
fn if_statement(&mut self) -> ParserResult<Stmt> {
|
||||
assert_eq!(self.next_token().token_type, TokenType::If);
|
||||
|
||||
|
|
@ -217,13 +247,7 @@ impl Parser {
|
|||
|
||||
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
|
||||
|
||||
let body = match self.statement() {
|
||||
Ok(body) => body,
|
||||
Err(err) => {
|
||||
self.is_in_loop = is_in_loop;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let body = self.statement()?;
|
||||
|
||||
self.is_in_loop = is_in_loop;
|
||||
|
||||
|
|
@ -264,13 +288,7 @@ impl Parser {
|
|||
|
||||
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
|
||||
|
||||
let mut body = match self.statement() {
|
||||
Ok(body) => body,
|
||||
Err(err) => {
|
||||
self.is_in_loop = is_in_loop;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let mut body = self.statement()?;
|
||||
|
||||
self.is_in_loop = is_in_loop;
|
||||
|
||||
|
|
@ -333,10 +351,24 @@ impl Parser {
|
|||
|
||||
let name = self.identifier("Missing class name")?;
|
||||
|
||||
let superclass = if self.peek_token().token_type == TokenType::Less {
|
||||
assert_eq!(self.next_token().token_type, TokenType::Less);
|
||||
|
||||
let superclass_name = self.identifier("Expected superclass")?;
|
||||
|
||||
Some(Expr::Variable { name: superclass_name })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.consume_token(TokenType::LeftBrace, |token| ParserError::MissingClassBody {
|
||||
code_pos: token.code_pos,
|
||||
})?;
|
||||
|
||||
let is_in_loop = std::mem::replace(&mut self.is_in_loop, false);
|
||||
let is_in_class = std::mem::replace(&mut self.is_in_class, true);
|
||||
let is_in_function = std::mem::replace(&mut self.is_in_function, false);
|
||||
|
||||
let mut methods = Vec::new();
|
||||
|
||||
while self.peek_token().token_type != TokenType::RightBrace {
|
||||
|
|
@ -350,14 +382,27 @@ impl Parser {
|
|||
}
|
||||
})?;
|
||||
|
||||
let is_in_init = self.is_in_init;
|
||||
if method_name == "init" {
|
||||
self.is_in_init = true;
|
||||
}
|
||||
|
||||
let method = self.fun_params_and_body(method_name)?;
|
||||
|
||||
self.is_in_init = is_in_init;
|
||||
|
||||
methods.push(method);
|
||||
}
|
||||
|
||||
assert_eq!(self.next_token().token_type, TokenType::RightBrace);
|
||||
|
||||
Ok(Stmt::Class { name, methods })
|
||||
self.is_in_loop = is_in_loop;
|
||||
self.is_in_class = is_in_class;
|
||||
self.is_in_function = is_in_function;
|
||||
|
||||
let class = Expr::class(&name, methods, superclass);
|
||||
|
||||
Ok(Stmt::var_decl(name, class))
|
||||
}
|
||||
|
||||
fn fun_declaration(&mut self) -> ParserResult<Stmt> {
|
||||
|
|
@ -399,14 +444,7 @@ impl Parser {
|
|||
let is_in_function = std::mem::replace(&mut self.is_in_function, true);
|
||||
let is_in_loop = std::mem::replace(&mut self.is_in_loop, false);
|
||||
|
||||
let body = match self.block() {
|
||||
Ok(body) => body,
|
||||
Err(err) => {
|
||||
self.is_in_function = is_in_function;
|
||||
self.is_in_loop = is_in_loop;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let body = self.block()?;
|
||||
|
||||
self.is_in_function = is_in_function;
|
||||
self.is_in_loop = is_in_loop;
|
||||
|
|
@ -494,6 +532,10 @@ impl Parser {
|
|||
|
||||
match expr {
|
||||
Expr::Variable { name } => Ok(Expr::assignment(Expr::Variable { name }, value)),
|
||||
Expr::Get { target, name } => {
|
||||
let value = Box::new(value);
|
||||
Ok(Expr::Set { target, name, value })
|
||||
}
|
||||
_ => Err(ParserError::InvalidAssignment { expr, code_pos }),
|
||||
}
|
||||
}
|
||||
|
|
@ -625,11 +667,11 @@ impl Parser {
|
|||
let _ = self.next_token();
|
||||
Ok(Expr::unary(UnaryOp::Negate, self.unary()?))
|
||||
}
|
||||
_ => self.call(),
|
||||
_ => self.call_or_get(),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self) -> ParserResult<Expr> {
|
||||
fn call_or_get(&mut self) -> ParserResult<Expr> {
|
||||
let mut expr = self.primary()?;
|
||||
|
||||
loop {
|
||||
|
|
@ -647,7 +689,13 @@ impl Parser {
|
|||
|
||||
expr = Expr::call(expr, args);
|
||||
}
|
||||
TokenType::Dot => todo!(),
|
||||
TokenType::Dot => {
|
||||
assert_eq!(self.next_token().token_type, TokenType::Dot);
|
||||
|
||||
let name = self.identifier("Expected property name after dot")?;
|
||||
|
||||
expr = Expr::get(expr, name);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
|
@ -695,6 +743,22 @@ impl Parser {
|
|||
TokenType::False => Ok(Expr::bool(false)),
|
||||
TokenType::True => Ok(Expr::bool(true)),
|
||||
TokenType::Nil => Ok(Expr::nil()),
|
||||
TokenType::This => Ok(Expr::This),
|
||||
TokenType::Super => {
|
||||
self.consume_token(TokenType::Dot, |token| ParserError::MissingMethodAfterSuper {
|
||||
code_pos: token.code_pos,
|
||||
})?;
|
||||
let method = self.identifier("Expected method name after super")?;
|
||||
let super_var = Box::new(Expr::Variable {
|
||||
name: "super".to_owned(),
|
||||
});
|
||||
let this_var = Box::new(Expr::This);
|
||||
Ok(Expr::Super {
|
||||
super_var,
|
||||
this_var,
|
||||
method,
|
||||
})
|
||||
}
|
||||
TokenType::LeftParen => {
|
||||
let expr = self.expression()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,6 @@ pub enum Stmt {
|
|||
Block {
|
||||
statements: Vec<Stmt>,
|
||||
},
|
||||
Class {
|
||||
name: String,
|
||||
methods: Vec<Expr>,
|
||||
},
|
||||
ExprStmt {
|
||||
expr: Box<Expr>,
|
||||
},
|
||||
|
|
@ -122,13 +118,6 @@ impl Display for Stmt {
|
|||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
Stmt::Class { name, methods } => {
|
||||
writeln!(f, "class {name} {{")?;
|
||||
for method in methods {
|
||||
writeln!(f, "{method}")?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
Stmt::ExprStmt { expr } => write!(f, "{expr};"),
|
||||
Stmt::Break => write!(f, "break;"),
|
||||
Stmt::Return { expr } => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue