mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
updated error names and Display
This commit is contained in:
parent
b8741ea3cc
commit
f9fe77f1e2
7 changed files with 164 additions and 81 deletions
|
|
@ -4,13 +4,13 @@ use super::CodePos;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LexerError {
|
pub enum LexerError {
|
||||||
#[error("LexerError: Unexpected character '{c}' at {code_pos}.")]
|
#[error("unexpected character '{c}' at {code_pos}.")]
|
||||||
UnexpectedCharacter { c: char, code_pos: CodePos },
|
UnexpectedCharacter { c: char, code_pos: CodePos },
|
||||||
#[error("LexerError: Unterminated string literal starting at {code_pos}.")]
|
#[error("unterminated string literal starting at {code_pos}.")]
|
||||||
UnterminatedStringLiteral { code_pos: CodePos },
|
UnterminatedStringLiteral { code_pos: CodePos },
|
||||||
#[error("LexerError: Unterminated block comment starting at {code_pos}.")]
|
#[error("unterminated block comment starting at {code_pos}.")]
|
||||||
UnterminatedBlockComment { code_pos: CodePos },
|
UnterminatedBlockComment { code_pos: CodePos },
|
||||||
#[error("LexerError: Invalid number literal {lexeme} at {code_pos}: {msg}")]
|
#[error("invalid number literal {lexeme} at {code_pos}: {msg}")]
|
||||||
InvalidNumberLiteral {
|
InvalidNumberLiteral {
|
||||||
lexeme: String,
|
lexeme: String,
|
||||||
msg: String,
|
msg: String,
|
||||||
|
|
|
||||||
|
|
@ -6,46 +6,46 @@ use super::Expr;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
#[error("ParserError: Token stream ended unexpectedly.")]
|
#[error("token stream ended unexpectedly.")]
|
||||||
TokenStreamEnded,
|
TokenStreamEnded,
|
||||||
#[error("ParserError: Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
|
#[error("expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
|
||||||
ExpectedPrimary { token: Token },
|
ExpectedPrimary { token: Token },
|
||||||
#[error("ParserError: Missing semicolon at {code_pos}")]
|
#[error("missing semicolon at {code_pos}")]
|
||||||
MissingSemicolon { code_pos: CodePos },
|
MissingSemicolon { code_pos: CodePos },
|
||||||
#[error("ParserError: Expected variable name at {0}, got {token} instead", token.code_pos)]
|
#[error("expected variable name at {0}, got {token} instead", token.code_pos)]
|
||||||
ExpectedVarName { token: Token },
|
ExpectedVarName { token: Token },
|
||||||
#[error("ParserError: Can't assign to {expr} at {code_pos}")]
|
#[error("can't assign to {expr} at {code_pos}")]
|
||||||
InvalidAssignment { expr: Expr, code_pos: CodePos },
|
InvalidAssignment { expr: Expr, code_pos: CodePos },
|
||||||
#[error("ParserError: Missing closing curly brace at {code_pos}")]
|
#[error("missing closing curly brace at {code_pos}")]
|
||||||
MissingRightBrace { code_pos: CodePos },
|
MissingRightBrace { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing closing parenthesis at {code_pos}")]
|
#[error("missing closing parenthesis at {code_pos}")]
|
||||||
MissingRightParen { code_pos: CodePos },
|
MissingRightParen { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing parenthesis after if at {code_pos}")]
|
#[error("missing parenthesis after if at {code_pos}")]
|
||||||
MissingParenAfterIf { code_pos: CodePos },
|
MissingParenAfterIf { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing parenthesis after while at {code_pos}")]
|
#[error("missing parenthesis after while at {code_pos}")]
|
||||||
MissingParenAfterWhile { code_pos: CodePos },
|
MissingParenAfterWhile { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing parenthesis after for at {code_pos}")]
|
#[error("missing parenthesis after for at {code_pos}")]
|
||||||
MissingParenAfterFor { code_pos: CodePos },
|
MissingParenAfterFor { code_pos: CodePos },
|
||||||
#[error("ParserError: Call at {code_pos} has too many arguments")]
|
#[error("call at {code_pos} has too many arguments")]
|
||||||
TooManyArguments { code_pos: CodePos },
|
TooManyArguments { code_pos: CodePos },
|
||||||
#[error("ParserError: {msg} at {code_pos}")]
|
#[error("{msg} at {code_pos}")]
|
||||||
MissingIdentifier { msg: String, code_pos: CodePos },
|
MissingIdentifier { msg: String, code_pos: CodePos },
|
||||||
#[error("ParserError: Missing arguments to function declaration at {code_pos}")]
|
#[error("missing arguments to function declaration at {code_pos}")]
|
||||||
MissingFunctionArgs { code_pos: CodePos },
|
MissingFunctionArgs { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing body to function declaration at {code_pos}")]
|
#[error("missing body to function declaration at {code_pos}")]
|
||||||
MissingFunctionBody { code_pos: CodePos },
|
MissingFunctionBody { code_pos: CodePos },
|
||||||
#[error("ParserError: Function declaration at {code_pos} has too many parameters")]
|
#[error("function declaration at {code_pos} has too many parameters")]
|
||||||
TooManyParams { code_pos: CodePos },
|
TooManyParams { code_pos: CodePos },
|
||||||
#[error("ParserError: Return statement outside of function definition")]
|
#[error("return statement outside of function definition")]
|
||||||
ReturnOutsideFunction { code_pos: CodePos },
|
ReturnOutsideFunction { code_pos: CodePos },
|
||||||
#[error("ParserError: Break statement outside of loop")]
|
#[error("break statement outside of loop")]
|
||||||
InvalidBreak { code_pos: CodePos },
|
InvalidBreak { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing class body at {code_pos}")]
|
#[error("missing class body at {code_pos}")]
|
||||||
MissingClassBody { code_pos: CodePos },
|
MissingClassBody { code_pos: CodePos },
|
||||||
#[error("ParserError: Return statement in init")]
|
#[error("return statement in init")]
|
||||||
ReturnInInit { code_pos: CodePos },
|
ReturnInInit { code_pos: CodePos },
|
||||||
#[error("ParserError: Missing method name after super")]
|
#[error("missing method name after super")]
|
||||||
MissingMethodAfterSuper { code_pos: CodePos },
|
MissingMethodAfterSuper { code_pos: CodePos },
|
||||||
#[error("ParserError: duplicate parameter name at {code_pos}")]
|
#[error("duplicate parameter name at {code_pos}")]
|
||||||
DuplicateParameterName { code_pos: CodePos },
|
DuplicateParameterName { code_pos: CodePos },
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
use std::fmt::Display;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use rlox2_frontend::lexer::LexerError;
|
use rlox2_frontend::lexer::LexerError;
|
||||||
use rlox2_frontend::parser::{BinaryOp, ParserError, UnaryOp};
|
use rlox2_frontend::parser::{BinaryOp, ParserError, UnaryOp};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -10,89 +10,131 @@ use crate::{LoxClass, ResolverError};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
#[error("RuntimeError: Unary operator {op} had invalid argument {arg}")]
|
#[error("unary operator {op} had invalid argument {arg}")]
|
||||||
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
|
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
|
||||||
#[error("RuntimeError: Binary operator {op} had invalid arguments {left} and {right}")]
|
#[error("binary operator {op} had invalid arguments {left} and {right}")]
|
||||||
BinaryOpInvalidArguments {
|
BinaryOpInvalidArguments {
|
||||||
left: Value,
|
left: Value,
|
||||||
op: BinaryOp,
|
op: BinaryOp,
|
||||||
right: Value,
|
right: Value,
|
||||||
},
|
},
|
||||||
#[error("RuntimeError: Division by zero")]
|
#[error("division by zero")]
|
||||||
DivisionByZero,
|
DivisionByZero,
|
||||||
#[error("RuntimeError: Local {name} is not defined")]
|
#[error("local {name} is not defined")]
|
||||||
NameNotDefined { name: String },
|
NameNotDefined { name: String },
|
||||||
#[error("RuntimeError: Global {name} is not defined")]
|
#[error("global {name} is not defined")]
|
||||||
GlobalNotDefined { name: String },
|
GlobalNotDefined { name: String },
|
||||||
#[error("RuntimeError: {callee} is not callable")]
|
#[error("{callee} is not callable")]
|
||||||
NotCallable { callee: Value },
|
NotCallable { callee: Value },
|
||||||
#[error("RuntimeError: {name}() takes {arity} args, but {given} were given.")]
|
#[error("{name}() takes {arity} args, but {given} were given.")]
|
||||||
WrongArity {
|
WrongArity {
|
||||||
name: String,
|
name: String,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
given: usize,
|
given: usize,
|
||||||
},
|
},
|
||||||
#[error("RuntimeError: Extern function call to {name} failed: {msg}")]
|
#[error("extern function call to {name} failed: {msg}")]
|
||||||
ExtFunCallFailed { name: String, msg: String },
|
ExtFunCallFailed { name: String, msg: String },
|
||||||
#[error("RuntimeError: Uncaught break statement")]
|
#[error("uncaught break statement")]
|
||||||
Break,
|
Break,
|
||||||
#[error("RuntimeError: Uncaught return statement")]
|
#[error("uncaught return statement")]
|
||||||
Return { value: Value },
|
Return { value: Value },
|
||||||
#[error("RuntimeError: Exit with exit code {exit_code}")]
|
#[error("exit with exit code {exit_code}")]
|
||||||
Exit { exit_code: i32 },
|
Exit { exit_code: i32 },
|
||||||
#[error("RuntimeError: Only objects have attributes")]
|
#[error("only objects have attributes")]
|
||||||
InvalidGetTarget,
|
InvalidGetTarget,
|
||||||
#[error("RuntimeError: Only objects have attributes")]
|
#[error("only objects have attributes")]
|
||||||
InvalidSetTarget,
|
InvalidSetTarget,
|
||||||
#[error("RuntimeError: Class {0} has no property {name}", class.name())]
|
#[error("class {0} has no property {name}", class.name())]
|
||||||
UndefinedAttribute { class: Rc<LoxClass>, name: Box<str> },
|
UndefinedAttribute { class: Rc<LoxClass>, name: Box<str> },
|
||||||
#[error("RuntimeError: {value} is not a valid superclass")]
|
#[error("{value} is not a valid superclass")]
|
||||||
InvalidSuperclass { value: Value },
|
InvalidSuperclass { value: Value },
|
||||||
#[error("RuntimeError: stack overflow")]
|
#[error("stack overflow")]
|
||||||
StackOverflow,
|
StackOverflow,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum LoxError {
|
pub enum LoxError {
|
||||||
#[error("{0}", format_multiple_errors(inner))]
|
LexerError { lexer_errors: Vec<LexerError> },
|
||||||
LexerError { inner: Vec<LexerError> },
|
ParserError { parser_errors: Vec<ParserError> },
|
||||||
#[error("{0}", format_multiple_errors(inner))]
|
ResolverError { resolver_error: ResolverError },
|
||||||
ParserError { inner: Vec<ParserError> },
|
RuntimeError { runtime_error: RuntimeError },
|
||||||
#[error("{inner}")]
|
|
||||||
ResolverError { inner: ResolverError },
|
|
||||||
#[error("{inner}")]
|
|
||||||
RuntimeError { inner: RuntimeError },
|
|
||||||
#[error("Called exit() with exit code {exit_code}")]
|
|
||||||
Exit { exit_code: i32 },
|
Exit { exit_code: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_multiple_errors(errs: &[impl std::error::Error]) -> String {
|
impl Display for LoxError {
|
||||||
let msg = if errs.len() == 1 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
errs[0].to_string()
|
let plural = |len: usize| {
|
||||||
} else {
|
if len > 1 {
|
||||||
errs.iter().map(|err| err.to_string()).join("\n")
|
"s"
|
||||||
};
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
msg
|
match self {
|
||||||
|
LoxError::LexerError { lexer_errors } => {
|
||||||
|
let len = lexer_errors.len();
|
||||||
|
|
||||||
|
writeln!(f, "{len} lexer error{}:", plural(len))?;
|
||||||
|
|
||||||
|
if len > 1 {
|
||||||
|
for lexer_error in lexer_errors[..len - 2].iter() {
|
||||||
|
writeln!(f, "{lexer_error}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", lexer_errors[len - 1])
|
||||||
|
}
|
||||||
|
LoxError::ParserError { parser_errors } => {
|
||||||
|
let len = parser_errors.len();
|
||||||
|
|
||||||
|
writeln!(f, "{len} parser error{}:", plural(len))?;
|
||||||
|
|
||||||
|
if len > 1 {
|
||||||
|
for lexer_error in parser_errors[..len - 2].iter() {
|
||||||
|
writeln!(f, "{lexer_error}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", parser_errors[len - 1])
|
||||||
|
}
|
||||||
|
LoxError::ResolverError { resolver_error } => {
|
||||||
|
write!(f, "resolver error: {resolver_error}")
|
||||||
|
}
|
||||||
|
|
||||||
|
LoxError::RuntimeError { runtime_error } => {
|
||||||
|
write!(f, "runtime error: {runtime_error}")
|
||||||
|
}
|
||||||
|
LoxError::Exit { exit_code } => {
|
||||||
|
write!(f, "exited with code {exit_code}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for LoxError {}
|
||||||
|
|
||||||
impl From<Vec<LexerError>> for LoxError {
|
impl From<Vec<LexerError>> for LoxError {
|
||||||
fn from(lexer_errs: Vec<LexerError>) -> Self {
|
fn from(lexer_errs: Vec<LexerError>) -> Self {
|
||||||
LoxError::LexerError { inner: lexer_errs }
|
LoxError::LexerError {
|
||||||
|
lexer_errors: lexer_errs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<ParserError>> for LoxError {
|
impl From<Vec<ParserError>> for LoxError {
|
||||||
fn from(parser_errs: Vec<ParserError>) -> Self {
|
fn from(parser_errs: Vec<ParserError>) -> Self {
|
||||||
LoxError::ParserError { inner: parser_errs }
|
LoxError::ParserError {
|
||||||
|
parser_errors: parser_errs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ResolverError> for LoxError {
|
impl From<ResolverError> for LoxError {
|
||||||
fn from(resolver_err: ResolverError) -> Self {
|
fn from(resolver_err: ResolverError) -> Self {
|
||||||
LoxError::ResolverError {
|
LoxError::ResolverError {
|
||||||
inner: resolver_err,
|
resolver_error: resolver_err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +143,9 @@ impl From<RuntimeError> for LoxError {
|
||||||
fn from(runtime_err: RuntimeError) -> Self {
|
fn from(runtime_err: RuntimeError) -> Self {
|
||||||
match runtime_err {
|
match runtime_err {
|
||||||
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
|
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
|
||||||
_ => LoxError::RuntimeError { inner: runtime_err },
|
_ => LoxError::RuntimeError {
|
||||||
|
runtime_error: runtime_err,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -290,8 +290,6 @@ impl Eval for Stmt {
|
||||||
env.exit_scope();
|
env.exit_scope();
|
||||||
}
|
}
|
||||||
Stmt::ExprStmt { expr } => {
|
Stmt::ExprStmt { expr } => {
|
||||||
// expr.eval(env)?;
|
|
||||||
// Ok(Value::Nil)
|
|
||||||
expr.eval(env)?;
|
expr.eval(env)?;
|
||||||
}
|
}
|
||||||
Stmt::Break => return Err(RuntimeError::Break),
|
Stmt::Break => return Err(RuntimeError::Break),
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ use crate::{LoxError, ResolverError};
|
||||||
type ResolverScope = FxHashMap<Box<str>, ResolveStatus>;
|
type ResolverScope = FxHashMap<Box<str>, ResolveStatus>;
|
||||||
type ResolverFrame = Vec<ResolverScope>;
|
type ResolverFrame = Vec<ResolverScope>;
|
||||||
|
|
||||||
|
type ResolverResult<T> = Result<T, ResolverError>;
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -81,14 +83,21 @@ impl Resolver {
|
||||||
.expect("Tried to pop non-existant ResolverFrame");
|
.expect("Tried to pop non-existant ResolverFrame");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare(&mut self, name: impl Into<Box<str>>) {
|
fn declare(&mut self, name: impl Into<Box<str>>) -> ResolverResult<()> {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
|
||||||
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
|
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
|
||||||
|
if local_scope.get(&name).is_some() {
|
||||||
|
return Err(ResolverError::LocalMulipleDeclarations { name });
|
||||||
|
}
|
||||||
|
|
||||||
local_scope.insert(name, ResolveStatus::Declared);
|
local_scope.insert(name, ResolveStatus::Declared);
|
||||||
} else {
|
} else {
|
||||||
|
// global variables can be declared multiple times
|
||||||
self.global_scope.insert(name, ResolveStatus::Declared);
|
self.global_scope.insert(name, ResolveStatus::Declared);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define(&mut self, name: impl Into<Box<str>>) {
|
fn define(&mut self, name: impl Into<Box<str>>) {
|
||||||
|
|
@ -109,7 +118,7 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_var(&mut self, name: &str) -> Result<Expr, ResolverError> {
|
fn resolve_var(&mut self, name: &str) -> ResolverResult<Expr> {
|
||||||
let mut level = 0;
|
let mut level = 0;
|
||||||
|
|
||||||
for scope in self.local_scopes().iter().rev() {
|
for scope in self.local_scopes().iter().rev() {
|
||||||
|
|
@ -152,7 +161,7 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_stmt(&mut self, stmt: &mut Stmt) -> Result<(), ResolverError> {
|
fn resolve_stmt(&mut self, stmt: &mut Stmt) -> ResolverResult<()> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Print { expr } => self.resolve_expr(expr),
|
Stmt::Print { expr } => self.resolve_expr(expr),
|
||||||
Stmt::IfStmt {
|
Stmt::IfStmt {
|
||||||
|
|
@ -161,29 +170,40 @@ impl Resolver {
|
||||||
else_branch,
|
else_branch,
|
||||||
} => {
|
} => {
|
||||||
self.resolve_expr(condition)?;
|
self.resolve_expr(condition)?;
|
||||||
|
|
||||||
self.resolve_stmt(then_branch)?;
|
self.resolve_stmt(then_branch)?;
|
||||||
|
|
||||||
if let Some(else_branch) = else_branch {
|
if let Some(else_branch) = else_branch {
|
||||||
self.resolve_stmt(else_branch)?;
|
self.resolve_stmt(else_branch)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::While { condition, body } => {
|
Stmt::While { condition, body } => {
|
||||||
self.resolve_expr(condition)?;
|
self.resolve_expr(condition)?;
|
||||||
|
|
||||||
self.resolve_stmt(body)?;
|
self.resolve_stmt(body)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::VarDecl { name, initializer } => {
|
Stmt::VarDecl { name, initializer } => {
|
||||||
self.declare(name.clone());
|
self.declare(name.clone())?;
|
||||||
|
|
||||||
self.resolve_expr(initializer)?;
|
self.resolve_expr(initializer)?;
|
||||||
|
|
||||||
self.define(name.clone());
|
self.define(name.clone());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::Block { statements } => {
|
Stmt::Block { statements } => {
|
||||||
self.enter_scope();
|
self.enter_scope();
|
||||||
|
|
||||||
for statement in statements.iter_mut() {
|
for statement in statements.iter_mut() {
|
||||||
self.resolve_stmt(statement)?;
|
self.resolve_stmt(statement)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.exit_scope();
|
self.exit_scope();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stmt::ExprStmt { expr } => self.resolve_expr(expr),
|
Stmt::ExprStmt { expr } => self.resolve_expr(expr),
|
||||||
|
|
@ -192,18 +212,22 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(&mut self, expr: &mut Expr) -> Result<(), ResolverError> {
|
fn resolve_expr(&mut self, expr: &mut Expr) -> ResolverResult<()> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal { literal: _ } => Ok(()),
|
Expr::Literal { literal: _ } => Ok(()),
|
||||||
Expr::Unary { op: _, expr } => self.resolve_expr(expr),
|
Expr::Unary { op: _, expr } => self.resolve_expr(expr),
|
||||||
Expr::Binary { left, op: _, right } => {
|
Expr::Binary { left, op: _, right } => {
|
||||||
self.resolve_expr(left)?;
|
self.resolve_expr(left)?;
|
||||||
|
|
||||||
self.resolve_expr(right)?;
|
self.resolve_expr(right)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Logical { left, op: _, right } => {
|
Expr::Logical { left, op: _, right } => {
|
||||||
self.resolve_expr(left)?;
|
self.resolve_expr(left)?;
|
||||||
|
|
||||||
self.resolve_expr(right)?;
|
self.resolve_expr(right)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Grouping { expr } => self.resolve_expr(expr),
|
Expr::Grouping { expr } => self.resolve_expr(expr),
|
||||||
|
|
@ -228,6 +252,7 @@ impl Resolver {
|
||||||
self.resolve_expr(value)?;
|
self.resolve_expr(value)?;
|
||||||
|
|
||||||
let target = target.as_mut();
|
let target = target.as_mut();
|
||||||
|
|
||||||
if let Expr::Variable { name } = target {
|
if let Expr::Variable { name } = target {
|
||||||
*target = self.resolve_var(name)?;
|
*target = self.resolve_var(name)?;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -238,13 +263,16 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args } => {
|
||||||
self.resolve_expr(callee)?;
|
self.resolve_expr(callee)?;
|
||||||
|
|
||||||
for arg in args.iter_mut() {
|
for arg in args.iter_mut() {
|
||||||
self.resolve_expr(arg)?;
|
self.resolve_expr(arg)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Get { target, name: _ } => {
|
Expr::Get { target, name: _ } => {
|
||||||
self.resolve_expr(target)?;
|
self.resolve_expr(target)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Set {
|
Expr::Set {
|
||||||
|
|
@ -253,7 +281,9 @@ impl Resolver {
|
||||||
value,
|
value,
|
||||||
} => {
|
} => {
|
||||||
self.resolve_expr(target)?;
|
self.resolve_expr(target)?;
|
||||||
|
|
||||||
self.resolve_expr(value)?;
|
self.resolve_expr(value)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Function {
|
Expr::Function {
|
||||||
|
|
@ -266,11 +296,12 @@ impl Resolver {
|
||||||
|
|
||||||
self.push_frame();
|
self.push_frame();
|
||||||
|
|
||||||
self.declare(name.clone());
|
self.declare(name.clone())?;
|
||||||
|
|
||||||
self.define(name.clone());
|
self.define(name.clone());
|
||||||
|
|
||||||
for param_name in param_names.iter() {
|
for param_name in param_names.iter() {
|
||||||
self.declare(param_name.clone());
|
self.declare(param_name.clone())?;
|
||||||
self.define(param_name.clone());
|
self.define(param_name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,7 +322,7 @@ impl Resolver {
|
||||||
name,
|
name,
|
||||||
methods,
|
methods,
|
||||||
} => {
|
} => {
|
||||||
self.declare(name.clone());
|
self.declare(name.clone())?;
|
||||||
|
|
||||||
if let Some(superclass) = superclass {
|
if let Some(superclass) = superclass {
|
||||||
self.resolve_expr(superclass)?;
|
self.resolve_expr(superclass)?;
|
||||||
|
|
@ -301,10 +332,13 @@ impl Resolver {
|
||||||
|
|
||||||
// this is the scope "this" is defined in
|
// this is the scope "this" is defined in
|
||||||
self.enter_scope();
|
self.enter_scope();
|
||||||
self.declare("this");
|
|
||||||
|
// this should never fail! we just made a new scope
|
||||||
|
self.declare("this").unwrap();
|
||||||
|
|
||||||
if superclass.is_some() {
|
if superclass.is_some() {
|
||||||
self.declare("super");
|
// this should never fail either! same as `this`
|
||||||
|
self.declare("super").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for method in methods.iter_mut() {
|
for method in methods.iter_mut() {
|
||||||
|
|
@ -325,6 +359,7 @@ impl Resolver {
|
||||||
method: _,
|
method: _,
|
||||||
} => {
|
} => {
|
||||||
self.resolve_expr(super_var)?;
|
self.resolve_expr(super_var)?;
|
||||||
|
|
||||||
self.resolve_expr(this_var)
|
self.resolve_expr(this_var)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,16 @@ use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ResolverError {
|
pub enum ResolverError {
|
||||||
#[error("ResolverError: Can't read variable {name} in its own initializer.")]
|
#[error("can't read variable {name} in its own initializer.")]
|
||||||
VarInOwnInitializer { name: Box<str> },
|
VarInOwnInitializer { name: Box<str> },
|
||||||
#[error("ResolverError: Variable {name} not defined")]
|
#[error("variable {name} not defined")]
|
||||||
UnresolvableVariable { name: Box<str> },
|
UnresolvableVariable { name: Box<str> },
|
||||||
#[error("ResolverError: Return outside of function definition")]
|
#[error("return outside of function definition")]
|
||||||
ReturnOutsideFunction,
|
ReturnOutsideFunction,
|
||||||
#[error("ResolverError: this outside of method")]
|
#[error("this outside of method")]
|
||||||
ThisOutsideMethod,
|
ThisOutsideMethod,
|
||||||
#[error("ResolverError: super outside of subclass method")]
|
#[error("super outside of subclass method")]
|
||||||
SuperOutsideMethod,
|
SuperOutsideMethod,
|
||||||
|
#[error("local variable {name} declares multiple times")]
|
||||||
|
LocalMulipleDeclarations { name: Box<str> },
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,11 @@ pub fn run_test(path: impl Into<PathBuf>) {
|
||||||
.map(|s| s.to_owned())
|
.map(|s| s.to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(lines.len(), comments.len());
|
assert_eq!(
|
||||||
|
lines.len(),
|
||||||
|
comments.len(),
|
||||||
|
"Didn't get as many outputs as expected"
|
||||||
|
);
|
||||||
|
|
||||||
for (line, comment) in std::iter::zip(lines.into_iter(), comments.into_iter()) {
|
for (line, comment) in std::iter::zip(lines.into_iter(), comments.into_iter()) {
|
||||||
assert_eq!(line, comment);
|
assert_eq!(line, comment);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue