Lox Interpreter done (Chapter 13)

This commit is contained in:
Moritz Gmeiner 2023-01-28 14:19:12 +01:00
commit 719a014977
16 changed files with 707 additions and 196 deletions

View file

@ -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 },
}

View file

@ -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}"),
}
}
}

View file

@ -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()?;

View file

@ -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 } => {