generic support for line numbers in error messages

This commit is contained in:
Shautvast 2025-11-02 13:02:10 +01:00
parent 8234e9d50a
commit de9f55842c
6 changed files with 118 additions and 98 deletions

View file

@ -10,8 +10,9 @@ use crate::tokens::{Token, TokenType};
use crate::value::Value; use crate::value::Value;
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
use crate::errors::CompilerErrorAtLine;
pub fn compile(tokens: Vec<Token>) -> Result<Vec<Statement>, CompilerError> { pub fn compile(tokens: Vec<Token>) -> Result<Vec<Statement>, CompilerErrorAtLine> {
let mut compiler = AstCompiler::new(tokens); let mut compiler = AstCompiler::new(tokens);
compiler.compile_tokens() compiler.compile_tokens()
} }
@ -49,13 +50,16 @@ impl AstCompiler {
self.current = 0; self.current = 0;
} }
fn compile_tokens(&mut self) -> Result<Vec<Statement>,CompilerError> { fn compile_tokens(&mut self) -> Result<Vec<Statement>,CompilerErrorAtLine> {
self.collect_functions()?; self.collect_functions()?;
self.reset(); self.reset();
self.compile() self.compile()
} }
fn compile(&mut self) -> Result<Vec<Statement>, CompilerError> {
fn compile(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> {
self.current_line();
if !self.had_error { if !self.had_error {
let mut statements = vec![]; let mut statements = vec![];
while !self.is_at_end() { while !self.is_at_end() {
@ -68,11 +72,15 @@ impl AstCompiler {
} }
Ok(statements) Ok(statements)
} else { } else {
Err(CompilerError::Failure) Err(self.raise(CompilerError::Failure))
} }
} }
fn collect_functions(&mut self) -> Result<(), CompilerError> { fn raise(&self, error: CompilerError) -> CompilerErrorAtLine {
CompilerErrorAtLine::raise(error, self.current_line())
}
fn collect_functions(&mut self) -> Result<(), CompilerErrorAtLine> {
while !self.is_at_end() { while !self.is_at_end() {
if self.match_token(vec![Fn]) { if self.match_token(vec![Fn]) {
let name_token = self.consume(Identifier, Expected("function name."))?; let name_token = self.consume(Identifier, Expected("function name."))?;
@ -80,7 +88,7 @@ impl AstCompiler {
let mut parameters = vec![]; let mut parameters = vec![];
while !self.check(RightParen) { while !self.check(RightParen) {
if parameters.len() >= 25 { if parameters.len() >= 25 {
return Err(TooManyParameters); return Err(self.raise(TooManyParameters));
} }
let parm_name = self.consume(Identifier, Expected("a parameter name."))?; let parm_name = self.consume(Identifier, Expected("a parameter name."))?;
@ -125,7 +133,7 @@ impl AstCompiler {
Ok(()) Ok(())
} }
fn indent(&mut self) -> Result<Option<Statement>, CompilerError> { fn indent(&mut self) -> Result<Option<Statement>, CompilerErrorAtLine> {
let expected_indent = *self.indent.last().unwrap(); let expected_indent = *self.indent.last().unwrap();
// skip empty lines // skip empty lines
while self.check(Eol) { while self.check(Eol) {
@ -138,9 +146,9 @@ impl AstCompiler {
indent_on_line += 1; indent_on_line += 1;
} }
if indent_on_line > expected_indent { if indent_on_line > expected_indent {
Err(UnexpectedIndent( Err(self.raise(UnexpectedIndent(
indent_on_line, expected_indent indent_on_line, expected_indent
)) )))
} else if indent_on_line < expected_indent { } else if indent_on_line < expected_indent {
self.indent.pop(); self.indent.pop();
return Ok(None); return Ok(None);
@ -149,7 +157,7 @@ impl AstCompiler {
} }
} }
fn declaration(&mut self) -> Result<Statement, CompilerError> { fn declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
if self.match_token(vec![Fn]) { if self.match_token(vec![Fn]) {
self.function_declaration() self.function_declaration()
} else if self.match_token(vec![Let]) { } else if self.match_token(vec![Let]) {
@ -161,7 +169,7 @@ impl AstCompiler {
} }
} }
fn object_declaration(&mut self) -> Result<Statement, CompilerError> { fn object_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
let type_name = self.consume(Identifier, Expected("object name."))?; let type_name = self.consume(Identifier, Expected("object name."))?;
self.consume(Colon, Expected("':' after object name."))?; self.consume(Colon, Expected("':' after object name."))?;
self.consume(Eol, Expected("end of line."))?; self.consume(Eol, Expected("end of line."))?;
@ -186,7 +194,7 @@ impl AstCompiler {
if field_type.is_type() { if field_type.is_type() {
self.advance(); self.advance();
} else { } else {
Err(Expected("a type"))? Err(self.raise(Expected("a type")))?
} }
fields.push(Parameter { fields.push(Parameter {
name: field_name, name: field_name,
@ -201,7 +209,7 @@ impl AstCompiler {
}) })
} }
fn function_declaration(&mut self) -> Result<Statement, CompilerError> { fn function_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
let name_token = self.consume(Identifier, Expected("function name."))?; let name_token = self.consume(Identifier, Expected("function name."))?;
self.consume(LeftParen, Expected("'(' after function name."))?; self.consume(LeftParen, Expected("'(' after function name."))?;
while !self.check(RightParen) { while !self.check(RightParen) {
@ -227,9 +235,9 @@ impl AstCompiler {
Ok(function_stmt) Ok(function_stmt)
} }
fn let_declaration(&mut self) -> Result<Statement, CompilerError> { fn let_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
if self.peek().token_type.is_type(){ if self.peek().token_type.is_type(){
return Err(CompilerError::KeywordNotAllowedAsIdentifier(self.peek().token_type)) return Err(self.raise(CompilerError::KeywordNotAllowedAsIdentifier(self.peek().token_type)))
} }
let name_token = self.consume(Identifier, Expected("variable name."))?; let name_token = self.consume(Identifier, Expected("variable name."))?;
@ -249,7 +257,7 @@ impl AstCompiler {
Ok(var_type) => var_type, Ok(var_type) => var_type,
Err(e) => { Err(e) => {
self.had_error = true; self.had_error = true;
return Err(TypeError(name_token.line, Box::new(e))); return Err(self.raise(TypeError(Box::new(e))));
} }
}; };
self.vars.push(Expression::Variable { self.vars.push(Expression::Variable {
@ -263,11 +271,11 @@ impl AstCompiler {
initializer, initializer,
}) })
} else { } else {
Err(UninitializedVariable)? Err(self.raise(UninitializedVariable))?
} }
} }
fn statement(&mut self) -> Result<Statement, CompilerError> { fn statement(&mut self) -> Result<Statement, CompilerErrorAtLine> {
if self.match_token(vec![Print]) { if self.match_token(vec![Print]) {
self.print_statement() self.print_statement()
} else { } else {
@ -275,68 +283,68 @@ impl AstCompiler {
} }
} }
fn print_statement(&mut self) -> Result<Statement, CompilerError> { fn print_statement(&mut self) -> Result<Statement, CompilerErrorAtLine> {
let expr = self.expression()?; let expr = self.expression()?;
self.consume(Eol, Expected("end of line after print statement."))?; self.consume(Eol, Expected("end of line after print statement."))?;
Ok(Statement::PrintStmt { value: expr }) Ok(Statement::PrintStmt { value: expr })
} }
fn expr_statement(&mut self) -> Result<Statement, CompilerError> { fn expr_statement(&mut self) -> Result<Statement, CompilerErrorAtLine> {
let expr = self.expression()?; let expr = self.expression()?;
self.consume(Eol, Expected("end of line after expression."))?; self.consume(Eol, Expected("end of line after expression."))?;
Ok(Statement::ExpressionStmt { expression: expr }) Ok(Statement::ExpressionStmt { expression: expr })
} }
fn expression(&mut self) -> Result<Expression, CompilerError> { fn expression(&mut self) -> Result<Expression, CompilerErrorAtLine> {
self.or() self.or()
} }
fn or(&mut self) -> Result<Expression, CompilerError> { fn or(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.and()?; let expr = self.and()?;
self.binary(vec![TokenType::LogicalOr], expr) self.binary(vec![TokenType::LogicalOr], expr)
} }
fn and(&mut self) -> Result<Expression, CompilerError> { fn and(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bit_and()?; let expr = self.bit_and()?;
self.binary(vec![TokenType::LogicalAnd], expr) self.binary(vec![TokenType::LogicalAnd], expr)
} }
fn bit_and(&mut self) -> Result<Expression, CompilerError> { fn bit_and(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bit_or()?; let expr = self.bit_or()?;
self.binary(vec![TokenType::BitAnd], expr) self.binary(vec![TokenType::BitAnd], expr)
} }
fn bit_or(&mut self) -> Result<Expression, CompilerError> { fn bit_or(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bit_xor()?; let expr = self.bit_xor()?;
self.binary(vec![TokenType::BitOr], expr) self.binary(vec![TokenType::BitOr], expr)
} }
fn bit_xor(&mut self) -> Result<Expression, CompilerError> { fn bit_xor(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.equality()?; let expr = self.equality()?;
self.binary(vec![TokenType::BitXor], expr) self.binary(vec![TokenType::BitXor], expr)
} }
fn equality(&mut self) -> Result<Expression, CompilerError> { fn equality(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.comparison()?; let expr = self.comparison()?;
self.binary(vec![TokenType::EqualEqual, TokenType::BangEqual], expr) self.binary(vec![TokenType::EqualEqual, TokenType::BangEqual], expr)
} }
fn comparison(&mut self) -> Result<Expression, CompilerError> { fn comparison(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bitshift()?; let expr = self.bitshift()?;
self.binary(vec![Greater, GreaterEqual, Less, LessEqual], expr) self.binary(vec![Greater, GreaterEqual, Less, LessEqual], expr)
} }
fn bitshift(&mut self) -> Result<Expression, CompilerError> { fn bitshift(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.term()?; let expr = self.term()?;
self.binary(vec![GreaterGreater, LessLess], expr) self.binary(vec![GreaterGreater, LessLess], expr)
} }
fn term(&mut self) -> Result<Expression, CompilerError> { fn term(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.factor()?; let expr = self.factor()?;
self.binary(vec![Minus, Plus], expr) self.binary(vec![Minus, Plus], expr)
} }
fn factor(&mut self) -> Result<Expression, CompilerError> { fn factor(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.unary()?; let expr = self.unary()?;
self.binary(vec![Slash, Star], expr) self.binary(vec![Slash, Star], expr)
} }
@ -345,7 +353,7 @@ impl AstCompiler {
&mut self, &mut self,
types: Vec<TokenType>, types: Vec<TokenType>,
mut expr: Expression, mut expr: Expression,
) -> Result<Expression, CompilerError> { ) -> Result<Expression, CompilerErrorAtLine> {
while self.match_token(types.clone()) { while self.match_token(types.clone()) {
let operator = self.previous().clone(); let operator = self.previous().clone();
let right = self.comparison()?; let right = self.comparison()?;
@ -359,7 +367,7 @@ impl AstCompiler {
Ok(expr) Ok(expr)
} }
fn unary(&mut self) -> Result<Expression, CompilerError> { fn unary(&mut self) -> Result<Expression, CompilerErrorAtLine> {
if self.match_token(vec![Bang, Minus]) { if self.match_token(vec![Bang, Minus]) {
let operator = self.previous().clone(); let operator = self.previous().clone();
let right = self.unary()?; let right = self.unary()?;
@ -373,7 +381,7 @@ impl AstCompiler {
} }
} }
fn primary(&mut self) -> Result<Expression, CompilerError> { fn primary(&mut self) -> Result<Expression, CompilerErrorAtLine> {
debug!("primary {:?}", self.peek()); debug!("primary {:?}", self.peek());
Ok(if self.match_token(vec![LeftBracket]) { Ok(if self.match_token(vec![LeftBracket]) {
self.list()? self.list()?
@ -395,13 +403,13 @@ impl AstCompiler {
Expression::Literal { Expression::Literal {
line: self.peek().line, line: self.peek().line,
literaltype: Integer, literaltype: Integer,
value: Value::I64(self.previous().lexeme.parse().map_err(|e|ParseError(format!("{:?}",e)))?), value: Value::I64(self.previous().lexeme.parse().map_err(|e|self.raise(ParseError(format!("{:?}",e))))?),
} }
} else if self.match_token(vec![FloatingPoint]) { } else if self.match_token(vec![FloatingPoint]) {
Expression::Literal { Expression::Literal {
line: self.peek().line, line: self.peek().line,
literaltype: FloatingPoint, literaltype: FloatingPoint,
value: Value::F64(self.previous().lexeme.parse().map_err(|e|ParseError(format!("{:?}",e)))?), value: Value::F64(self.previous().lexeme.parse().map_err(|e|self.raise(ParseError(format!("{:?}",e))))?),
} }
} else if self.match_token(vec![StringType]) { } else if self.match_token(vec![StringType]) {
Expression::Literal { Expression::Literal {
@ -433,7 +441,7 @@ impl AstCompiler {
}) })
} }
fn list(&mut self) -> Result<Expression, CompilerError> { fn list(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let mut list = vec![]; let mut list = vec![];
while !self.match_token(vec![RightBracket]) { while !self.match_token(vec![RightBracket]) {
list.push(self.expression()?); list.push(self.expression()?);
@ -451,7 +459,7 @@ impl AstCompiler {
}) })
} }
fn map(&mut self) -> Result<Expression, CompilerError> { fn map(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let mut entries = vec![]; let mut entries = vec![];
while !self.match_token(vec![RightBrace]) { while !self.match_token(vec![RightBrace]) {
let key = self.expression()?; let key = self.expression()?;
@ -472,7 +480,7 @@ impl AstCompiler {
}) })
} }
fn variable_lookup(&mut self, token: &Token) -> Result<Expression, CompilerError> { fn variable_lookup(&mut self, token: &Token) -> Result<Expression, CompilerErrorAtLine> {
let (var_name, var_type) = self let (var_name, var_type) = self
.vars .vars
.iter() .iter()
@ -484,7 +492,7 @@ impl AstCompiler {
} }
}) })
.find(|e| e.0 == &token.lexeme) .find(|e| e.0 == &token.lexeme)
.ok_or_else(|| return CompilerError::UndeclaredVariable(token.clone()))?; .ok_or_else(|| return self.raise(CompilerError::UndeclaredVariable(token.clone())))?;
Ok(Expression::Variable { Ok(Expression::Variable {
name: var_name.to_string(), name: var_name.to_string(),
var_type: var_type.clone(), var_type: var_type.clone(),
@ -492,22 +500,22 @@ impl AstCompiler {
}) })
} }
fn function_call(&mut self, name: String) -> Result<Expression, CompilerError> { fn function_call(&mut self, name: String) -> Result<Expression, CompilerErrorAtLine> {
let function_name = self.functions.get(&name).unwrap().name.lexeme.clone(); let function_name = self.functions.get(&name).unwrap().name.lexeme.clone();
let function = self.functions.get(&function_name).unwrap().clone(); let function = self.functions.get(&function_name).unwrap().clone();
let mut arguments = vec![]; let mut arguments = vec![];
while !self.match_token(vec![RightParen]) { while !self.match_token(vec![RightParen]) {
if arguments.len() >= 25 { if arguments.len() >= 25 {
return Err(TooManyParameters); return Err(self.raise(TooManyParameters));
} }
let arg = self.expression()?; let arg = self.expression()?;
let arg_type = arg.infer_type(); let arg_type = arg.infer_type();
if arg_type != function.parameters[arguments.len()].var_type { if arg_type != function.parameters[arguments.len()].var_type {
return Err(IncompatibleTypes( return Err(self.raise(IncompatibleTypes(
function.parameters[arguments.len()].var_type, function.parameters[arguments.len()].var_type,
arg_type arg_type
)); )));
} }
arguments.push(arg); arguments.push(arg);
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
@ -526,7 +534,7 @@ impl AstCompiler {
}) })
} }
fn consume(&mut self, token_type: TokenType, message: CompilerError) -> Result<Token, CompilerError> { fn consume(&mut self, token_type: TokenType, message: CompilerError) -> Result<Token, CompilerErrorAtLine> {
if self.check(token_type) { if self.check(token_type) {
self.advance(); self.advance();
} else { } else {
@ -536,7 +544,7 @@ impl AstCompiler {
// message.to_string(), // message.to_string(),
// self.peek() // self.peek()
// )); // ));
return Err(message); return Err(self.raise(message));
} }
Ok(self.previous().clone()) Ok(self.previous().clone())
} }
@ -577,6 +585,10 @@ impl AstCompiler {
fn is_at_end(&self) -> bool { fn is_at_end(&self) -> bool {
self.peek().token_type == Eof self.peek().token_type == Eof
} }
fn current_line(&self) -> usize {
self.peek().line
}
} }
fn calculate_type( fn calculate_type(

View file

@ -1,12 +1,11 @@
use crate::ast_compiler::{Expression, Function, Statement}; use crate::ast_compiler::{Expression, Function, Statement};
use crate::chunk::Chunk; use crate::chunk::Chunk;
use crate::errors::CompilerError; use crate::errors::{CompilerErrorAtLine};
use crate::tokens::TokenType; use crate::tokens::TokenType;
use crate::value::Value; use crate::value::Value;
use crate::vm::{ use crate::vm::{
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_LIST,
OP_DEF_CHAR, OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET,
OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEF_U32, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET,
OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR,
OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
}; };
@ -16,32 +15,31 @@ pub fn compile(
namespace: Option<&str>, namespace: Option<&str>,
ast: &Vec<Statement>, ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerErrorAtLine> {
compile_name(ast, namespace, registry) compile_in_namespace(ast, namespace, registry)
} }
pub(crate) fn compile_function( pub(crate) fn compile_function(
function: &Function, function: &Function,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> Result<Chunk, CompilerError> { ) -> Result<Chunk, CompilerErrorAtLine> {
let mut compiler = Compiler::new(&function.name.lexeme); let mut compiler = Compiler::new(&function.name.lexeme);
for parm in &function.parameters { for parm in &function.parameters {
let name = parm.name.lexeme.clone(); let name = parm.name.lexeme.clone();
let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme); let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme);
compiler.vars.insert(name, var_index); compiler.vars.insert(name, var_index);
// compiler.emit_bytes(OP_DEFINE, name_index as u16);
} }
Ok(compiler.compile(&function.body, registry, namespace)?) Ok(compiler.compile(&function.body, registry, namespace)?)
} }
pub(crate) fn compile_name( pub(crate) fn compile_in_namespace(
ast: &Vec<Statement>, ast: &Vec<Statement>,
namespace: Option<&str>, namespace: Option<&str>,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerErrorAtLine> {
let name = namespace.unwrap_or("main"); let name = namespace.unwrap_or("main");
let compiler = Compiler::new(name); let compiler = Compiler::new(name);
let chunk = compiler.compile(ast, registry, name)?; let chunk = compiler.compile(ast, registry, name)?;
@ -76,7 +74,7 @@ impl Compiler {
ast: &Vec<Statement>, ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> Result<Chunk, CompilerError> { ) -> Result<Chunk, CompilerErrorAtLine> {
for statement in ast { for statement in ast {
self.compile_statement(statement, registry, namespace)?; self.compile_statement(statement, registry, namespace)?;
} }
@ -90,7 +88,7 @@ impl Compiler {
statement: &Statement, statement: &Statement,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerErrorAtLine> {
self.current_line = statement.line(); self.current_line = statement.line();
match statement { match statement {
Statement::VarStmt { Statement::VarStmt {
@ -131,7 +129,7 @@ impl Compiler {
namespace: &str, namespace: &str,
expression: &Expression, expression: &Expression,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerErrorAtLine> {
match expression { match expression {
Expression::FunctionCall { Expression::FunctionCall {
name, arguments, .. name, arguments, ..

View file

@ -56,7 +56,7 @@ a"#),
if let Err(e) = &r { if let Err(e) = &r {
assert_eq!( assert_eq!(
e.to_string(), e.to_string(),
"Type mismatch at line 1: Incompatible types. Expected u32, found i32/64" "Compilation failed: error at line 1, Type mismatch: Expected u32, found i32/64"
); );
} }
} }
@ -68,7 +68,7 @@ a"#),
if let Err(e) = &r { if let Err(e) = &r {
assert_eq!( assert_eq!(
e.to_string(), e.to_string(),
"Type mismatch at line 1: Incompatible types. Expected u64, found i32/64" "Compilation failed: error at line 1, Type mismatch: Expected u64, found i32/64"
); );
} }
} }
@ -80,7 +80,7 @@ a"#),
if let Err(e) = &r { if let Err(e) = &r {
assert_eq!( assert_eq!(
e.to_string(), e.to_string(),
"Type mismatch at line 1: Incompatible types. Expected u64, found string" "Compilation failed: error at line 1, Type mismatch: Expected u64, found string"
); );
} }
} }
@ -154,6 +154,6 @@ m"#);
fn keyword_error(){ fn keyword_error(){
let result = run(r#"let map = {"name": "Dent"}"#); let result = run(r#"let map = {"name": "Dent"}"#);
assert!(result.is_err()); assert!(result.is_err());
assert_eq!("'map' is a keyword. You cannot use it as an identifier",result.unwrap_err().to_string()); assert_eq!("Compilation failed: error at line 1, 'map' is a keyword. You cannot use it as an identifier",result.unwrap_err().to_string());
} }
} }

View file

@ -1,16 +1,36 @@
use std::fmt::Display;
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
pub enum Error { pub enum Error {
#[error(transparent)] #[error("Compilation failed: {0}")]
Compiler(#[from] CompilerError), Compiler(#[from] CompilerErrorAtLine),
#[error(transparent)] #[error(transparent)]
Runtime(#[from] RuntimeError), Runtime(#[from] RuntimeError),
#[error("Platform error {0}")] #[error("Platform error {0}")]
Platform(String), Platform(String),
} }
#[derive(Error, Debug, PartialEq)]
pub struct CompilerErrorAtLine{
pub error: CompilerError,
pub line: usize
}
impl CompilerErrorAtLine {
pub(crate) fn raise(error:CompilerError, line: usize) -> Self{
Self {error, line}
}
}
impl Display for CompilerErrorAtLine {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error at line {}, {}", self.line, self.error)
}
}
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
pub enum CompilerError { pub enum CompilerError {
#[error("Compilation failed")] #[error("Compilation failed")]
@ -21,22 +41,22 @@ pub enum CompilerError {
Expected(&'static str), Expected(&'static str),
#[error("unexpected indent level {0} vs expected {1}")] #[error("unexpected indent level {0} vs expected {1}")]
UnexpectedIndent(usize, usize), UnexpectedIndent(usize, usize),
#[error("Type mismatch at line {0}: {1}")] #[error("Type mismatch: {0}")]
TypeError(usize, Box<CompilerError>), TypeError(Box<CompilerError>),
#[error("Uninitialized variables are not allowed.")] #[error("Uninitialized variables are not allowed.")]
UninitializedVariable, UninitializedVariable,
#[error("Incompatible types. Expected {0}, found {1}")] #[error("Expected {0}, found {1}")]
IncompatibleTypes(TokenType, TokenType), IncompatibleTypes(TokenType, TokenType),
#[error("Error parsing number {0}")] #[error("Error parsing number {0}")]
ParseError(String), ParseError(String),
#[error("Undeclared variable: {0:?}")] #[error("Undeclared variable: {0:?}")]
UndeclaredVariable(Token), UndeclaredVariable(Token),
#[error("Unexpected identifier at line {0}")] #[error("Unexpected identifier")]
UnexpectedIdentifier(usize), UnexpectedIdentifier,
#[error("Unterminated {0} at line {1}")] #[error("Unterminated {0}")]
Unterminated(&'static str, usize), Unterminated(&'static str),
#[error("Illegal char length for {0} at line {1}")] #[error("Illegal char length for {0}")]
IllegalCharLength(String, usize), IllegalCharLength(String),
#[error("Unexpected type {0}")] #[error("Unexpected type {0}")]
UnexpectedType(TokenType), UnexpectedType(TokenType),
#[error("'{0}' is a keyword. You cannot use it as an identifier")] #[error("'{0}' is a keyword. You cannot use it as an identifier")]

View file

@ -6,10 +6,10 @@ use crate::{
TokenType::{self}, TokenType::{self},
}, },
}; };
use crate::errors::CompilerError; use crate::errors::{CompilerError, CompilerErrorAtLine};
use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated}; use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated};
pub fn scan(source: &str) -> Result<Vec<Token>, CompilerError> { pub fn scan(source: &str) -> Result<Vec<Token>, CompilerErrorAtLine> {
let scanner = Scanner { let scanner = Scanner {
chars: source.chars().collect(), chars: source.chars().collect(),
current: 0, current: 0,
@ -22,7 +22,7 @@ pub fn scan(source: &str) -> Result<Vec<Token>, CompilerError> {
} }
impl Scanner { impl Scanner {
fn scan(mut self) -> Result<Vec<Token>, CompilerError> { fn scan(mut self) -> Result<Vec<Token>, CompilerErrorAtLine> {
while !self.is_at_end() { while !self.is_at_end() {
self.start = self.current; self.start = self.current;
self.scan_token()?; self.scan_token()?;
@ -32,7 +32,7 @@ impl Scanner {
Ok(self.tokens) Ok(self.tokens)
} }
fn scan_token(&mut self) -> Result<(),CompilerError> { fn scan_token(&mut self) -> Result<(),CompilerErrorAtLine> {
let c = self.advance(); let c = self.advance();
if self.new_line && (c == ' ' || c == '\t') { if self.new_line && (c == ' ' || c == '\t') {
self.add_token(TokenType::Indent); self.add_token(TokenType::Indent);
@ -139,7 +139,7 @@ impl Scanner {
} else if is_alpha(c) { } else if is_alpha(c) {
self.identifier(); self.identifier();
} else { } else {
return Err(UnexpectedIdentifier(self.line)); return Err(self.raise(UnexpectedIdentifier));
} }
} }
} }
@ -174,13 +174,13 @@ impl Scanner {
self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value); self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value);
} }
fn char(&mut self) -> Result<(), CompilerError>{ fn char(&mut self) -> Result<(), CompilerErrorAtLine> {
while self.peek() != '\'' && !self.is_at_end() { while self.peek() != '\'' && !self.is_at_end() {
self.advance(); self.advance();
} }
if self.is_at_end() { if self.is_at_end() {
return Err(Unterminated("char", self.line)) return Err(CompilerErrorAtLine::raise(Unterminated("char"), self.line))
} }
self.advance(); self.advance();
@ -189,13 +189,17 @@ impl Scanner {
.iter() .iter()
.collect(); .collect();
if value.len() != 1 { if value.len() != 1 {
return Err(IllegalCharLength(value, self.line)) return Err(self.raise(IllegalCharLength(value)));
} }
self.add_token_with_value(TokenType::Char, value); self.add_token_with_value(TokenType::Char, value);
Ok(()) Ok(())
} }
fn string(&mut self) -> Result<(),CompilerError> { fn raise(&self, error: CompilerError) -> CompilerErrorAtLine {
CompilerErrorAtLine::raise(error, self.line)
}
fn string(&mut self) -> Result<(),CompilerErrorAtLine> {
while self.peek() != '"' && !self.is_at_end() { while self.peek() != '"' && !self.is_at_end() {
if self.peek() == '\n' { if self.peek() == '\n' {
self.line += 1; self.line += 1;
@ -204,7 +208,7 @@ impl Scanner {
} }
if self.is_at_end() { if self.is_at_end() {
return Err(Unterminated("string", self.line)) return Err(self.raise(Unterminated("string")));
} }
self.advance(); self.advance();

View file

@ -6,20 +6,6 @@ use std::collections::HashMap;
use tracing::debug; use tracing::debug;
use crate::tokens::TokenType; use crate::tokens::TokenType;
macro_rules! define_var {
($self:ident, $variant:ident, $chunk:ident) => {{
let name = $self.read_name($chunk);
let value = $self.pop();
if let Value::$variant(_) = value {
$self.local_vars.insert(name, value);
} else {
return Err(RuntimeError::Expected(
stringify!($variant), stringify!(value),
));
}
}};
}
pub struct Vm<'a> { pub struct Vm<'a> {
ip: usize, ip: usize,
stack: Vec<Value>, stack: Vec<Value>,