switched to thiserror for better error handling

This commit is contained in:
Shautvast 2025-11-01 09:36:09 +01:00
parent 0bd6048083
commit 55a30afd06
11 changed files with 252 additions and 174 deletions

2
Cargo.lock generated
View file

@ -195,7 +195,6 @@ dependencies = [
name = "crudlang" name = "crudlang"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"axum", "axum",
"chrono", "chrono",
"dotenv", "dotenv",
@ -203,6 +202,7 @@ dependencies = [
"log4rs", "log4rs",
"reqwest", "reqwest",
"serde", "serde",
"thiserror",
"tokio", "tokio",
"tokio-postgres", "tokio-postgres",
"tower", "tower",

View file

@ -17,7 +17,7 @@ tower-http = { version = "0.6", features = ["fs"] }
tower = "0.5.0" tower = "0.5.0"
tracing = "0.1.41" tracing = "0.1.41"
tracing-subscriber = "0.3.20" tracing-subscriber = "0.3.20"
anyhow = "1.0"
tower-livereload = "0.9.6" tower-livereload = "0.9.6"
log = "0.4.28" log = "0.4.28"
walkdir = "2.5.0" walkdir = "2.5.0"
thiserror = "2.0.17"

View file

@ -1,17 +1,17 @@
use crate::errors::CompilerError::{self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, UnexpectedIndent, UninitializedVariable};
use crate::tokens::TokenType::{ use crate::tokens::TokenType::{
Bang, Bool, Char, Colon, Date, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater, Bang, Bool, Char, Colon, Date, Eof, Eol, Equal, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater,
GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace, LeftBracket, Identifier, Indent, Integer, LeftBrace, LeftBracket, LeftParen, Less, LessEqual, LessLess,
LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, Let, ListType, MapType, Minus, Object, Plus, Print, RightBrace, RightBracket, RightParen, SignedInteger,
RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star, StringType, SingleRightArrow, Slash, Star, StringType, True, UnsignedInteger, F32, F64,
True, U32, U64, UnsignedInteger, I32, I64, U32, U64,
}; };
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
use crate::value::Value; use crate::value::Value;
use anyhow::anyhow;
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> { pub fn compile(tokens: Vec<Token>) -> Result<Vec<Statement>, CompilerError> {
let mut compiler = AstCompiler::new(tokens); let mut compiler = AstCompiler::new(tokens);
compiler.compile_tokens() compiler.compile_tokens()
} }
@ -49,13 +49,13 @@ impl AstCompiler {
self.current = 0; self.current = 0;
} }
fn compile_tokens(&mut self) -> anyhow::Result<Vec<Statement>> { fn compile_tokens(&mut self) -> Result<Vec<Statement>,CompilerError> {
self.collect_functions()?; self.collect_functions()?;
self.reset(); self.reset();
self.compile() self.compile()
} }
fn compile(&mut self) -> anyhow::Result<Vec<Statement>> { fn compile(&mut self) -> Result<Vec<Statement>, CompilerError> {
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,23 +68,23 @@ impl AstCompiler {
} }
Ok(statements) Ok(statements)
} else { } else {
Err(anyhow::anyhow!("Compilation failed.")) Err(CompilerError::Failure)
} }
} }
fn collect_functions(&mut self) -> anyhow::Result<()> { fn collect_functions(&mut self) -> Result<(), CompilerError> {
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, "Expect function name.")?; let name_token = self.consume(Identifier, Expected("function name."))?;
self.consume(LeftParen, "Expect '(' after function name.")?; self.consume(LeftParen, Expected("'(' after function name."))?;
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(anyhow::anyhow!("Too many parameters.")); return Err(TooManyParameters);
} }
let parm_name = self.consume(Identifier, "Expect parameter name.")?; let parm_name = self.consume(Identifier, Expected("a parameter name."))?;
self.consume(Colon, "Expect : after parameter name")?; self.consume(Colon, Expected(": after parameter name"))?;
let var_type = self.peek().token_type; let var_type = self.peek().token_type;
self.vars.push(Expression::Variable { self.vars.push(Expression::Variable {
name: parm_name.lexeme.to_string(), name: parm_name.lexeme.to_string(),
@ -100,15 +100,15 @@ impl AstCompiler {
self.advance(); self.advance();
} }
} }
self.consume(RightParen, "Expect ')' after parameters.")?; self.consume(RightParen, Expected(" ')' after parameters."))?;
let return_type = if self.check(SingleRightArrow) { let return_type = if self.check(SingleRightArrow) {
self.consume(SingleRightArrow, "")?; self.consume(SingleRightArrow, Expected("->"))?;
self.advance().token_type self.advance().token_type
} else { } else {
TokenType::Void TokenType::Void
}; };
self.consume(Colon, "Expect colon (:) after function declaration.")?; self.consume(Colon, Expected("colon (:) after function declaration."))?;
self.consume(Eol, "Expect end of line.")?; self.consume(Eol, Expected("end of line."))?;
let function = Function { let function = Function {
name: name_token.clone(), name: name_token.clone(),
@ -125,7 +125,7 @@ impl AstCompiler {
Ok(()) Ok(())
} }
fn indent(&mut self) -> anyhow::Result<Option<Statement>> { fn indent(&mut self) -> Result<Option<Statement>, CompilerError> {
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,10 +138,9 @@ impl AstCompiler {
indent_on_line += 1; indent_on_line += 1;
} }
if indent_on_line > expected_indent { if indent_on_line > expected_indent {
panic!( Err(UnexpectedIndent(
"unexpected indent level {} vs expected {}",
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);
@ -150,7 +149,7 @@ impl AstCompiler {
} }
} }
fn declaration(&mut self) -> anyhow::Result<Statement> { fn declaration(&mut self) -> Result<Statement, CompilerError> {
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]) {
@ -162,10 +161,10 @@ impl AstCompiler {
} }
} }
fn object_declaration(&mut self) -> anyhow::Result<Statement> { fn object_declaration(&mut self) -> Result<Statement, CompilerError> {
let type_name = self.consume(Identifier, "Expect object name.")?; let type_name = self.consume(Identifier, Expected("object name."))?;
self.consume(Colon, "Expect ':' after object name.")?; self.consume(Colon, Expected("':' after object name."))?;
self.consume(Eol, "Expect end of line.")?; self.consume(Eol, Expected("end of line."))?;
let mut fields = vec![]; let mut fields = vec![];
@ -181,13 +180,13 @@ impl AstCompiler {
} }
} }
if !done { if !done {
let field_name = self.consume(Identifier, "Expect an object field name.")?; let field_name = self.consume(Identifier, Expected("an object field name."))?;
self.consume(Colon, "Expect ':' after field name.")?; self.consume(Colon, Expected("':' after field name."))?;
let field_type = self.peek().token_type; let field_type = self.peek().token_type;
if field_type.is_type() { if field_type.is_type() {
self.advance(); self.advance();
} else { } else {
Err(anyhow::anyhow!("Expected a type"))? Err(Expected("a type"))?
} }
fields.push(Parameter { fields.push(Parameter {
name: field_name, name: field_name,
@ -195,26 +194,26 @@ impl AstCompiler {
}); });
} }
} }
self.consume(Eol, "Expect end of line.")?; self.consume(Eol, Expected("end of line."))?;
Ok(Statement::ObjectStmt { Ok(Statement::ObjectStmt {
name: type_name, name: type_name,
fields, fields,
}) })
} }
fn function_declaration(&mut self) -> anyhow::Result<Statement> { fn function_declaration(&mut self) -> Result<Statement, CompilerError> {
let name_token = self.consume(Identifier, "Expect function name.")?; let name_token = self.consume(Identifier, Expected("function name."))?;
self.consume(LeftParen, "Expect '(' after function name.")?; self.consume(LeftParen, Expected("'(' after function name."))?;
while !self.check(RightParen) { while !self.check(RightParen) {
self.advance(); self.advance();
} }
self.consume(RightParen, "Expect ')' after parameters.")?; self.consume(RightParen, Expected("')' after parameters."))?;
while !self.check(Colon) { while !self.check(Colon) {
self.advance(); self.advance();
} }
self.consume(Colon, "2Expect colon (:) after function declaration.")?; self.consume(Colon, Expected("colon (:) after function declaration."))?;
self.consume(Eol, "Expect end of line.")?; self.consume(Eol, Expected("end of line."))?;
let current_indent = self.indent.last().unwrap(); let current_indent = self.indent.last().unwrap();
self.indent.push(current_indent + 1); self.indent.push(current_indent + 1);
@ -228,8 +227,8 @@ impl AstCompiler {
Ok(function_stmt) Ok(function_stmt)
} }
fn let_declaration(&mut self) -> anyhow::Result<Statement> { fn let_declaration(&mut self) -> Result<Statement, CompilerError> {
let name_token = self.consume(Identifier, "Expect variable name.")?; let name_token = self.consume(Identifier, Expected("variable name."))?;
let declared_type = if self.check(Colon) { let declared_type = if self.check(Colon) {
self.advance(); self.advance();
@ -240,14 +239,14 @@ impl AstCompiler {
if self.match_token(vec![Equal]) { if self.match_token(vec![Equal]) {
let initializer = self.expression()?; let initializer = self.expression()?;
self.consume(Eol, "Expect end of line after initializer.")?; self.consume(Eol, Expected("end of line after initializer."))?;
let inferred_type = initializer.infer_type(); let inferred_type = initializer.infer_type();
let var_type = match calculate_type(declared_type, inferred_type) { let var_type = match calculate_type(declared_type, inferred_type) {
Ok(var_type) => var_type, Ok(var_type) => var_type,
Err(e) => { Err(e) => {
self.had_error = true; self.had_error = true;
return Err(anyhow!("error at line {}: {}", name_token.line, e)); return Err(TypeError(name_token.line, Box::new(e)));
} }
}; };
self.vars.push(Expression::Variable { self.vars.push(Expression::Variable {
@ -261,11 +260,11 @@ impl AstCompiler {
initializer, initializer,
}) })
} else { } else {
Err(anyhow::anyhow!("Uninitialized variables are not allowed."))? Err(UninitializedVariable)?
} }
} }
fn statement(&mut self) -> anyhow::Result<Statement> { fn statement(&mut self) -> Result<Statement, CompilerError> {
if self.match_token(vec![Print]) { if self.match_token(vec![Print]) {
self.print_statement() self.print_statement()
} else { } else {
@ -273,68 +272,68 @@ impl AstCompiler {
} }
} }
fn print_statement(&mut self) -> anyhow::Result<Statement> { fn print_statement(&mut self) -> Result<Statement, CompilerError> {
let expr = self.expression()?; let expr = self.expression()?;
self.consume(Eol, "Expect 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) -> anyhow::Result<Statement> { fn expr_statement(&mut self) -> Result<Statement, CompilerError> {
let expr = self.expression()?; let expr = self.expression()?;
self.consume(Eol, "Expect 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) -> anyhow::Result<Expression> { fn expression(&mut self) -> Result<Expression, CompilerError> {
self.or() self.or()
} }
fn or(&mut self) -> anyhow::Result<Expression> { fn or(&mut self) -> Result<Expression, CompilerError> {
let expr = self.and()?; let expr = self.and()?;
self.binary(vec![TokenType::LogicalOr], expr) self.binary(vec![TokenType::LogicalOr], expr)
} }
fn and(&mut self) -> anyhow::Result<Expression> { fn and(&mut self) -> Result<Expression, CompilerError> {
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) -> anyhow::Result<Expression> { fn bit_and(&mut self) -> Result<Expression, CompilerError> {
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) -> anyhow::Result<Expression> { fn bit_or(&mut self) -> Result<Expression, CompilerError> {
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) -> anyhow::Result<Expression> { fn bit_xor(&mut self) -> Result<Expression, CompilerError> {
let expr = self.equality()?; let expr = self.equality()?;
self.binary(vec![TokenType::BitXor], expr) self.binary(vec![TokenType::BitXor], expr)
} }
fn equality(&mut self) -> anyhow::Result<Expression> { fn equality(&mut self) -> Result<Expression, CompilerError> {
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) -> anyhow::Result<Expression> { fn comparison(&mut self) -> Result<Expression, CompilerError> {
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) -> anyhow::Result<Expression> { fn bitshift(&mut self) -> Result<Expression, CompilerError> {
let expr = self.term()?; let expr = self.term()?;
self.binary(vec![GreaterGreater, LessLess], expr) self.binary(vec![GreaterGreater, LessLess], expr)
} }
fn term(&mut self) -> anyhow::Result<Expression> { fn term(&mut self) -> Result<Expression, CompilerError> {
let expr = self.factor()?; let expr = self.factor()?;
self.binary(vec![Minus, Plus], expr) self.binary(vec![Minus, Plus], expr)
} }
fn factor(&mut self) -> anyhow::Result<Expression> { fn factor(&mut self) -> Result<Expression, CompilerError> {
let expr = self.unary()?; let expr = self.unary()?;
self.binary(vec![Slash, Star], expr) self.binary(vec![Slash, Star], expr)
} }
@ -343,7 +342,7 @@ impl AstCompiler {
&mut self, &mut self,
types: Vec<TokenType>, types: Vec<TokenType>,
mut expr: Expression, mut expr: Expression,
) -> anyhow::Result<Expression> { ) -> Result<Expression, CompilerError> {
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()?;
@ -357,7 +356,7 @@ impl AstCompiler {
Ok(expr) Ok(expr)
} }
fn unary(&mut self) -> anyhow::Result<Expression> { fn unary(&mut self) -> Result<Expression, CompilerError> {
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()?;
@ -371,7 +370,7 @@ impl AstCompiler {
} }
} }
fn primary(&mut self) -> anyhow::Result<Expression> { fn primary(&mut self) -> Result<Expression, CompilerError> {
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()?
@ -393,13 +392,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()?), value: Value::I64(self.previous().lexeme.parse().map_err(|e|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()?), value: Value::F64(self.previous().lexeme.parse().map_err(|e|ParseError(format!("{:?}",e)))?),
} }
} else if self.match_token(vec![StringType]) { } else if self.match_token(vec![StringType]) {
Expression::Literal { Expression::Literal {
@ -415,7 +414,7 @@ impl AstCompiler {
} }
} else if self.match_token(vec![LeftParen]) { } else if self.match_token(vec![LeftParen]) {
let expr = self.expression()?; let expr = self.expression()?;
self.consume(RightParen, "Expect ')' after expression.")?; self.consume(RightParen, Expected("')' after expression."))?;
Expression::Grouping { Expression::Grouping {
line: self.peek().line, line: self.peek().line,
expression: Box::new(expr), expression: Box::new(expr),
@ -431,14 +430,14 @@ impl AstCompiler {
}) })
} }
fn list(&mut self) -> anyhow::Result<Expression> { fn list(&mut self) -> Result<Expression, CompilerError> {
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()?);
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
self.advance(); self.advance();
} else { } else {
self.consume(RightBracket, "Expect ']' after list.")?; self.consume(RightBracket, Expected("']' at the end of the list."))?;
break; break;
} }
} }
@ -449,17 +448,17 @@ impl AstCompiler {
}) })
} }
fn map(&mut self) -> anyhow::Result<Expression> { fn map(&mut self) -> Result<Expression, CompilerError> {
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()?;
self.consume(Colon, "Expect ':' after map key.")?; self.consume(Colon, Expected("':' after map key."))?;
let value = self.expression()?; let value = self.expression()?;
entries.push((key, value)); entries.push((key, value));
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
self.advance(); self.advance();
} else { } else {
self.consume(RightBrace, "Expect '}' after map.")?; self.consume(RightBrace, Expected("'}' after map."))?;
break; break;
} }
} }
@ -470,7 +469,7 @@ impl AstCompiler {
}) })
} }
fn variable_lookup(&mut self, token: &Token) -> anyhow::Result<Expression> { fn variable_lookup(&mut self, token: &Token) -> Result<Expression, CompilerError> {
let (var_name, var_type) = self let (var_name, var_type) = self
.vars .vars
.iter() .iter()
@ -482,7 +481,7 @@ impl AstCompiler {
} }
}) })
.find(|e| e.0 == &token.lexeme) .find(|e| e.0 == &token.lexeme)
.ok_or_else(|| return anyhow::anyhow!("Unknown variable: {:?}", token))?; .ok_or_else(|| return 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(),
@ -490,20 +489,19 @@ impl AstCompiler {
}) })
} }
fn function_call(&mut self, name: String) -> anyhow::Result<Expression> { fn function_call(&mut self, name: String) -> Result<Expression, CompilerError> {
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(anyhow::anyhow!("Too many parameters.")); return Err(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(anyhow::anyhow!( return Err(IncompatibleTypes(
"Incompatible argument types. Expected {}, found {}",
function.parameters[arguments.len()].var_type, function.parameters[arguments.len()].var_type,
arg_type arg_type
)); ));
@ -512,7 +510,7 @@ impl AstCompiler {
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
self.advance(); self.advance();
} else { } else {
self.consume(RightParen, "Expect ')' after arguments.")?; self.consume(RightParen, Expected("')' after arguments."))?;
break; break;
} }
} }
@ -525,16 +523,17 @@ impl AstCompiler {
}) })
} }
fn consume(&mut self, token_type: TokenType, message: &str) -> anyhow::Result<Token> { fn consume(&mut self, token_type: TokenType, message: CompilerError) -> Result<Token, CompilerError> {
if self.check(token_type) { if self.check(token_type) {
self.advance(); self.advance();
} else { } else {
self.had_error = true; self.had_error = true;
return Err(anyhow::anyhow!( // return Err(anyhow::anyhow!(
"{} at {:?}", // "{} at {:?}",
message.to_string(), // message.to_string(),
self.peek() // self.peek()
)); // ));
return Err(message);
} }
Ok(self.previous().clone()) Ok(self.previous().clone())
} }
@ -580,7 +579,7 @@ impl AstCompiler {
fn calculate_type( fn calculate_type(
declared_type: Option<TokenType>, declared_type: Option<TokenType>,
inferred_type: TokenType, inferred_type: TokenType,
) -> anyhow::Result<TokenType> { ) -> Result<TokenType, CompilerError> {
Ok(if let Some(declared_type) = declared_type { Ok(if let Some(declared_type) = declared_type {
if declared_type != inferred_type { if declared_type != inferred_type {
match (declared_type, inferred_type) { match (declared_type, inferred_type) {
@ -596,8 +595,7 @@ fn calculate_type(
(U64, I32) => U64, (U64, I32) => U64,
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress (StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
_ => { _ => {
return Err(anyhow::anyhow!( return Err(IncompatibleTypes(
"Incompatible types. Expected {}, found {}",
declared_type, declared_type,
inferred_type inferred_type
)); ));
@ -615,7 +613,7 @@ fn calculate_type(
ListType => ListType, ListType => ListType,
MapType => MapType, MapType => MapType,
Object => Object, Object => Object,
_ => panic!("Unexpected type"), _ => return Err(CompilerError::UnexpectedType(inferred_type)),
} }
}) })
} }

View file

@ -10,12 +10,13 @@ use crate::vm::{
OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
}; };
use std::collections::HashMap; use std::collections::HashMap;
use crate::errors::CompilerError;
pub fn compile( 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>,
) -> anyhow::Result<()> { ) -> Result<(),CompilerError> {
compile_name(ast, namespace, registry) compile_name(ast, namespace, registry)
} }
@ -23,7 +24,7 @@ pub(crate) fn compile_function(
function: &Function, function: &Function,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> anyhow::Result<Chunk> { ) -> Result<Chunk, CompilerError> {
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();
@ -39,7 +40,7 @@ pub(crate) fn compile_name(
ast: &Vec<Statement>, ast: &Vec<Statement>,
namespace: Option<&str>, namespace: Option<&str>,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
) -> anyhow::Result<()> { ) -> Result<(),CompilerError> {
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)?;
@ -74,7 +75,7 @@ impl Compiler {
ast: &Vec<Statement>, ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> anyhow::Result<Chunk> { ) -> Result<Chunk, CompilerError> {
//TODO can likely be removed //TODO can likely be removed
for statement in ast { for statement in ast {
if let Statement::FunctionStmt { function } = statement { if let Statement::FunctionStmt { function } = statement {
@ -99,7 +100,7 @@ impl Compiler {
statement: &Statement, statement: &Statement,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> anyhow::Result<()> { ) -> Result<(), CompilerError> {
self.current_line = statement.line(); self.current_line = statement.line();
match statement { match statement {
Statement::VarStmt { Statement::VarStmt {
@ -140,7 +141,7 @@ impl Compiler {
namespace: &str, namespace: &str,
expression: &Expression, expression: &Expression,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
) -> anyhow::Result<()> { ) -> Result<(), CompilerError> {
match expression { match expression {
Expression::FunctionCall { Expression::FunctionCall {
name, arguments, .. name, arguments, ..
@ -229,7 +230,7 @@ impl Compiler {
var_type: &TokenType, var_type: &TokenType,
name_index: usize, name_index: usize,
initializer: &Expression, initializer: &Expression,
) -> anyhow::Result<()> { ) -> Result<(), CompilerError> {
let def_op = match var_type { let def_op = match var_type {
TokenType::I32 => OP_DEF_I32, TokenType::I32 => OP_DEF_I32,
TokenType::I64 => OP_DEF_I64, TokenType::I64 => OP_DEF_I64,

View file

@ -1,13 +1,13 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::compile; use crate::{compile, run};
use crate::scanner::scan; use crate::scanner::scan;
use crate::value::Value; use crate::value::Value;
use crate::vm::interpret; use crate::vm::interpret;
#[test] #[test]
fn literal_int() { fn literal_int() {
assert!(compile("1").is_ok()); assert_eq!(run("1"), Ok(Value::I64(1)));
} }
#[test] #[test]
@ -52,7 +52,7 @@ mod tests {
if let Err(e) = &r { if let Err(e) = &r {
assert_eq!( assert_eq!(
e.to_string(), e.to_string(),
"error at line 1: Incompatible types. Expected u32, found i32/64" "Type mismatch at line 1: Incompatible types. Expected u32, found i32/64"
); );
} }
} }
@ -64,7 +64,7 @@ mod tests {
if let Err(e) = &r { if let Err(e) = &r {
assert_eq!( assert_eq!(
e.to_string(), e.to_string(),
"error at line 1: Incompatible types. Expected u64, found i32/64" "Type mismatch at line 1: Incompatible types. Expected u64, found i32/64"
); );
} }
} }
@ -76,7 +76,7 @@ mod tests {
if let Err(e) = &r { if let Err(e) = &r {
assert_eq!( assert_eq!(
e.to_string(), e.to_string(),
"error at line 1: Incompatible types. Expected u64, found string" "Type mismatch at line 1: Incompatible types. Expected u64, found string"
); );
} }
} }

61
src/errors.rs Normal file
View file

@ -0,0 +1,61 @@
use thiserror::Error;
use crate::tokens::{Token, TokenType};
#[derive(Error, Debug, PartialEq)]
pub enum Error {
#[error(transparent)]
Compiler(#[from] CompilerError),
#[error(transparent)]
Runtime(#[from] RuntimeError),
#[error("Platform error {0}")]
Platform(String),
}
#[derive(Error, Debug, PartialEq)]
pub enum CompilerError {
#[error("Compilation failed")]
Failure,
#[error("Too many parameters")]
TooManyParameters,
#[error("Expected {0}")]
Expected(&'static str),
#[error("unexpected indent level {0} vs expected {1}")]
UnexpectedIndent(usize,usize),
#[error("Type mismatch at line {0}: {1}")]
TypeError(usize, Box<CompilerError>),
#[error("Uninitialized variables are not allowed.")]
UninitializedVariable,
#[error("Incompatible types. Expected {0}, found {1}")]
IncompatibleTypes(TokenType, TokenType),
#[error("Error parsing number {0}")]
ParseError(String),
#[error("Undeclared variable: {0:?}")]
UndeclaredVariable(Token),
#[error("Unexpected identifier at line {0}")]
UnexpectedIdentifier(usize),
#[error("Unterminated {0} at line {1}")]
Unterminated(&'static str, usize),
#[error("Illegal char length for {0} at line {1}")]
IllegalCharLength(String, usize),
#[error("Unexpected type {0}")]
UnexpectedType(TokenType)
}
#[derive(Error, Debug, PartialEq)]
pub enum RuntimeError {
#[error("Error while executing")]
Value(#[from] ValueError),
#[error("Error occurred")]
Something,
#[error("Expected {0}, got {1}")]
Expected(&'static str, &'static str),
}
#[derive(Error, Debug, PartialEq)]
pub enum ValueError {
#[error("{0}")]
CannotAnd(&'static str),
#[error("{0}")]
Some(&'static str),
}

View file

@ -1,20 +1,32 @@
use std::collections::HashMap;
use crate::scanner::scan; use crate::scanner::scan;
use crate::value::Value;
use crate::vm::interpret;
use std::collections::HashMap;
use crate::errors::Error;
pub mod ast_compiler; pub mod ast_compiler;
pub mod bytecode_compiler; pub mod bytecode_compiler;
pub mod chunk; pub mod chunk;
mod compiler_tests;
mod keywords; mod keywords;
pub mod scanner; pub mod scanner;
mod compiler_tests;
mod tokens; mod tokens;
mod value; mod value;
pub mod vm; pub mod vm;
pub mod errors;
pub fn compile(src: &str) -> anyhow::Result<HashMap<String, chunk::Chunk>> { pub fn compile(src: &str) -> Result<HashMap<String, chunk::Chunk>, Error> {
let tokens = scan(src)?; let tokens = scan(src)?;
let mut registry = HashMap::new(); let mut registry = HashMap::new();
let ast= ast_compiler::compile(tokens)?; let ast = ast_compiler::compile(tokens)?;
bytecode_compiler::compile(None, &ast, &mut registry)?; bytecode_compiler::compile(None, &ast, &mut registry)?;
Ok(registry) Ok(registry)
} }
fn run(src: &str) -> Result<Value, Error> {
let tokens = scan(src)?;
let mut registry = HashMap::new();
let ast = ast_compiler::compile(tokens)?;
bytecode_compiler::compile(None, &ast, &mut registry)?;
interpret(&registry, "main").map_err(Error::from)
}

View file

@ -1,4 +1,4 @@
use axum::extract::{Path, State}; use axum::extract::{State};
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::routing::get; use axum::routing::get;
use axum::{Json, Router}; use axum::{Json, Router};
@ -9,12 +9,13 @@ use crudlang::scanner::scan;
use crudlang::vm::{interpret, interpret_async}; use crudlang::vm::{interpret, interpret_async};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::hash::Hash;
use std::sync::Arc; use std::sync::Arc;
use walkdir::WalkDir; use walkdir::WalkDir;
use crudlang::errors::Error;
use crudlang::errors::Error::Platform;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> Result<(), crudlang::errors::Error> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let mut paths = HashMap::new(); let mut paths = HashMap::new();
@ -23,12 +24,12 @@ async fn main() -> anyhow::Result<()> {
let path = entry.path(); let path = entry.path();
if path.is_file() && path.ends_with("web.crud") { if path.is_file() && path.ends_with("web.crud") {
print!("compiling {:?}: ", path); print!("compiling {:?}: ", path);
let source = fs::read_to_string(path)?; let source = fs::read_to_string(path).map_err(map_underlying())?;
let tokens = scan(&source)?; let tokens = scan(&source)?;
match ast_compiler::compile(tokens) { match ast_compiler::compile(tokens) {
Ok(statements) => { Ok(statements) => {
let path = path let path = path
.strip_prefix("source")? .strip_prefix("source").map_err(|e|Platform(e.to_string()))?
.to_str() .to_str()
.unwrap() .unwrap()
.replace(".crud", ""); .replace(".crud", "");
@ -58,13 +59,17 @@ async fn main() -> anyhow::Result<()> {
get(handle_get).with_state(state.clone()), get(handle_get).with_state(state.clone()),
); );
} }
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.map_err(map_underlying())?;
println!("listening on {}", listener.local_addr()?); println!("listening on {}", listener.local_addr().map_err(map_underlying())?);
axum::serve(listener, app).await?; axum::serve(listener, app).await.map_err(map_underlying())?;
} }
Ok(()) Ok(())
} }
fn map_underlying() -> fn(std::io::Error) -> Error {
|e| Error::Platform(e.to_string())
}
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
name: String, name: String,

View file

@ -1,4 +1,3 @@
use anyhow::anyhow;
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer}; use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer};
use crate::{ use crate::{
keywords, keywords,
@ -7,8 +6,10 @@ use crate::{
TokenType::{self}, TokenType::{self},
}, },
}; };
use crate::errors::CompilerError;
use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated};
pub fn scan(source: &str) -> anyhow::Result<Vec<Token>> { pub fn scan(source: &str) -> Result<Vec<Token>, CompilerError> {
let scanner = Scanner { let scanner = Scanner {
chars: source.chars().collect(), chars: source.chars().collect(),
current: 0, current: 0,
@ -21,7 +22,7 @@ pub fn scan(source: &str) -> anyhow::Result<Vec<Token>> {
} }
impl Scanner { impl Scanner {
fn scan(mut self) -> anyhow::Result<Vec<Token>> { fn scan(mut self) -> Result<Vec<Token>, CompilerError> {
while !self.is_at_end() { while !self.is_at_end() {
self.start = self.current; self.start = self.current;
self.scan_token()?; self.scan_token()?;
@ -31,7 +32,7 @@ impl Scanner {
Ok(self.tokens) Ok(self.tokens)
} }
fn scan_token(&mut self) -> anyhow::Result<()>{ fn scan_token(&mut self) -> Result<(),CompilerError> {
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);
@ -138,7 +139,7 @@ impl Scanner {
} else if is_alpha(c) { } else if is_alpha(c) {
self.identifier(); self.identifier();
} else { } else {
return Err(anyhow!("Unexpected identifier at line {}", self.line)); return Err(UnexpectedIdentifier(self.line));
} }
} }
} }
@ -173,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) -> anyhow::Result<()>{ fn char(&mut self) -> Result<(), CompilerError>{
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(anyhow!("Unterminated char at {}", self.line)) return Err(Unterminated("char", self.line))
} }
self.advance(); self.advance();
@ -188,13 +189,13 @@ impl Scanner {
.iter() .iter()
.collect(); .collect();
if value.len() != 1 { if value.len() != 1 {
return Err(anyhow!("Illegal char length for {} at line {}", value, self.line)) return Err(IllegalCharLength(value, self.line))
} }
self.add_token_with_value(TokenType::Char, value); self.add_token_with_value(TokenType::Char, value);
Ok(()) Ok(())
} }
fn string(&mut self) -> anyhow::Result<()>{ fn string(&mut self) -> Result<(),CompilerError> {
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;
@ -203,7 +204,7 @@ impl Scanner {
} }
if self.is_at_end() { if self.is_at_end() {
return Err(anyhow!("Unterminated string at {}", self.line)) return Err(Unterminated("string", self.line))
} }
self.advance(); self.advance();

View file

@ -1,10 +1,10 @@
use anyhow::anyhow;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
use crate::errors::ValueError;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Object { pub struct Object {
@ -143,7 +143,7 @@ fn to_string(f: &mut Formatter, map: &HashMap<Value, Value>) -> std::fmt::Result
} }
impl Neg for &Value { impl Neg for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
match self { match self {
@ -151,13 +151,13 @@ impl Neg for &Value {
Value::I64(i) => Ok(Value::I64(-i)), Value::I64(i) => Ok(Value::I64(-i)),
Value::F32(i) => Ok(Value::F32(-i)), Value::F32(i) => Ok(Value::F32(-i)),
Value::F64(i) => Ok(Value::F64(-i)), Value::F64(i) => Ok(Value::F64(-i)),
_ => Err(anyhow!("Cannot negate")), _ => Err(ValueError::Some("Cannot negate")),
} }
} }
} }
impl Add<&Value> for &Value { impl Add<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn add(self, rhs: &Value) -> Self::Output { fn add(self, rhs: &Value) -> Self::Output {
if let Value::List(s) = self { if let Value::List(s) = self {
@ -189,14 +189,14 @@ impl Add<&Value> for &Value {
} }
(Value::String(s1), Value::Map(m)) => Ok(Value::String(format!("{}{:?}", s1, m))), (Value::String(s1), Value::Map(m)) => Ok(Value::String(format!("{}{:?}", s1, m))),
//enum? //enum?
_ => Err(anyhow!("Cannot add")), _ => Err(ValueError::Some("Cannot add")),
} }
} }
} }
} }
impl Sub<&Value> for &Value { impl Sub<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn sub(self, rhs: &Value) -> Self::Output { fn sub(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
@ -207,13 +207,13 @@ impl Sub<&Value> for &Value {
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a - b)), (Value::F32(a), Value::F32(b)) => Ok(Value::F32(a - b)),
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a - b)), (Value::F64(a), Value::F64(b)) => Ok(Value::F64(a - b)),
//enum? //enum?
_ => Err(anyhow!("Cannot subtract")), _ => Err(ValueError::Some("Cannot subtract")),
} }
} }
} }
impl Mul<&Value> for &Value { impl Mul<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn mul(self, rhs: &Value) -> Self::Output { fn mul(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
@ -223,13 +223,13 @@ impl Mul<&Value> for &Value {
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a * b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a * b)),
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a * b)), (Value::F32(a), Value::F32(b)) => Ok(Value::F32(a * b)),
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a * b)), (Value::F64(a), Value::F64(b)) => Ok(Value::F64(a * b)),
_ => Err(anyhow!("Cannot multiply")), _ => Err(ValueError::Some("Cannot multiply")),
} }
} }
} }
impl Div<&Value> for &Value { impl Div<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn div(self, rhs: &Value) -> Self::Output { fn div(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
@ -239,52 +239,52 @@ impl Div<&Value> for &Value {
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a / b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a / b)),
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a / b)), (Value::F32(a), Value::F32(b)) => Ok(Value::F32(a / b)),
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a / b)), (Value::F64(a), Value::F64(b)) => Ok(Value::F64(a / b)),
_ => Err(anyhow!("Cannot divide")), _ => Err(ValueError::Some("Cannot divide")),
} }
} }
} }
impl BitAnd<&Value> for &Value { impl BitAnd<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn bitand(self, rhs: &Value) -> Self::Output { fn bitand(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a & b)), (Value::I32(a), Value::I32(b)) => Ok(Value::I32(a & b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a & b)), (Value::I64(a), Value::I64(b)) => Ok(Value::I64(a & b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a & b)), (Value::U32(a), Value::U32(b)) => Ok(Value::U32(a & b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a & b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a & b)),
_ => Err(anyhow!("Cannot do bitwise-and on")), _ => Err(ValueError::Some("Cannot do bitwise-and on")),
} }
} }
} }
impl BitOr<&Value> for &Value { impl BitOr<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn bitor(self, rhs: &Value) -> Self::Output { fn bitor(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a | b)), (Value::I32(a), Value::I32(b)) => Ok(Value::I32(a | b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a | b)), (Value::I64(a), Value::I64(b)) => Ok(Value::I64(a | b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a | b)), (Value::U32(a), Value::U32(b)) => Ok(Value::U32(a | b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a | b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a | b)),
_ => Err(anyhow!("Cannot do bitwise-or on")), _ => Err(ValueError::Some("Cannot do bitwise-or on")),
} }
} }
} }
impl BitXor<&Value> for &Value { impl BitXor<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn bitxor(self, rhs: &Value) -> Self::Output { fn bitxor(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a ^ b)), (Value::I32(a), Value::I32(b)) => Ok(Value::I32(a ^ b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a ^ b)), (Value::I64(a), Value::I64(b)) => Ok(Value::I64(a ^ b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a ^ b)), (Value::U32(a), Value::U32(b)) => Ok(Value::U32(a ^ b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a ^ b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a ^ b)),
_ => Err(anyhow!("Cannot do bitwise-xor on")), _ => Err(ValueError::Some("Cannot do bitwise-xor on")),
} }
} }
} }
impl Not for &Value { impl Not for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn not(self) -> Self::Output { fn not(self) -> Self::Output {
match self { match self {
@ -293,33 +293,33 @@ impl Not for &Value {
Value::I64(i64) => Ok(Value::I64(!i64)), Value::I64(i64) => Ok(Value::I64(!i64)),
Value::U32(u32) => Ok(Value::U32(!u32)), Value::U32(u32) => Ok(Value::U32(!u32)),
Value::U64(u64) => Ok(Value::U64(!u64)), Value::U64(u64) => Ok(Value::U64(!u64)),
_ => Err(anyhow!("Cannot calculate not")), _ => Err(ValueError::Some("Cannot calculate not")),
} }
} }
} }
impl Shl<&Value> for &Value { impl Shl<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn shl(self, rhs: &Value) -> Self::Output { fn shl(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a << b)), (Value::I32(a), Value::I32(b)) => Ok(Value::I32(a << b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a << b)), (Value::I64(a), Value::I64(b)) => Ok(Value::I64(a << b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a << b)), (Value::U32(a), Value::U32(b)) => Ok(Value::U32(a << b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a << b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a << b)),
_ => Err(anyhow!("Cannot shift left on")), _ => Err(ValueError::Some("Cannot shift left on")),
} }
} }
} }
impl Shr<&Value> for &Value { impl Shr<&Value> for &Value {
type Output = anyhow::Result<Value>; type Output = Result<Value, ValueError>;
fn shr(self, rhs: &Value) -> Self::Output { fn shr(self, rhs: &Value) -> Self::Output {
match (self, rhs) { match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a >> b)), (Value::I32(a), Value::I32(b)) => Ok(Value::I32(a >> b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a >> b)), (Value::I64(a), Value::I64(b)) => Ok(Value::I64(a >> b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a >> b)), (Value::U32(a), Value::U32(b)) => Ok(Value::U32(a >> b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a >> b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a >> b)),
_ => Err(anyhow!("Cannot shift right on")), _ => Err(ValueError::Some("Cannot shift right on")),
} }
} }
} }
@ -337,19 +337,19 @@ impl PartialEq for Value {
(Value::String(a), Value::String(b)) => a == b, (Value::String(a), Value::String(b)) => a == b,
(Value::Char(a), Value::Char(b)) => a == b, (Value::Char(a), Value::Char(b)) => a == b,
(Value::Date(a), Value::Date(b)) => a == b, (Value::Date(a), Value::Date(b)) => a == b,
// (Value::List(a), Value::List(b)) => a == b, (Value::List(a), Value::List(b)) => a == b,
// (Value::Map(a), Value::Map(b)) => { (Value::Map(a), Value::Map(b)) => {
// let mut equal = true; let mut equal = true;
// for (k, v) in a.iter() { for (k, v) in a.iter() {
// if !b.contains_key(k) || b.get(k).unwrap() != v { if !b.contains_key(k) || b.get(k).unwrap() != v {
// //safe unwrap //safe unwrap
// equal = false; equal = false;
// break; break;
// } }
// } }
// equal equal
// } }
// struct? // TODO objects
_ => false, //? _ => false, //?
} }
} }

View file

@ -1,6 +1,7 @@
use crate::chunk::Chunk; use crate::chunk::Chunk;
use crate::errors::RuntimeError::{Something};
use crate::errors::{RuntimeError, ValueError};
use crate::value::Value; use crate::value::Value;
use anyhow::anyhow;
use std::collections::HashMap; use std::collections::HashMap;
use tracing::debug; use tracing::debug;
@ -11,9 +12,8 @@ macro_rules! define_var {
if let Value::$variant(_) = value { if let Value::$variant(_) = value {
$self.local_vars.insert(name, value); $self.local_vars.insert(name, value);
} else { } else {
return Err(anyhow!( return Err(RuntimeError::Expected(
concat!("Expected ", stringify!($variant), ", got {:?}"), stringify!($variant), stringify!(value),
value
)); ));
} }
}}; }};
@ -27,7 +27,7 @@ pub struct Vm<'a> {
registry: &'a HashMap<String, Chunk>, registry: &'a HashMap<String, Chunk>,
} }
pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> anyhow::Result<Value> { pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> Result<Value, RuntimeError> {
let chunk = registry.get(function).unwrap().clone(); let chunk = registry.get(function).unwrap().clone();
// for (key,value) in registry.iter() { // for (key,value) in registry.iter() {
// println!("{}", key); // println!("{}", key);
@ -43,7 +43,7 @@ pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> anyhow::R
vm.run(&chunk, vec![]) vm.run(&chunk, vec![])
} }
pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str) -> anyhow::Result<Value> { pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str) -> Result<Value, RuntimeError> {
let chunk = registry.get(function).unwrap().clone(); let chunk = registry.get(function).unwrap().clone();
let mut vm = Vm { let mut vm = Vm {
ip: 0, ip: 0,
@ -55,7 +55,7 @@ pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str)
vm.run(&chunk, vec![]) vm.run(&chunk, vec![])
} }
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> { pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
let mut vm = Vm { let mut vm = Vm {
ip: 0, ip: 0,
stack: vec![], stack: vec![],
@ -67,13 +67,13 @@ pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Val
} }
impl <'a> Vm<'a> { impl <'a> Vm<'a> {
fn run(&mut self, chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> { fn run(&mut self, chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
for arg in args { for arg in args {
self.push(arg); self.push(arg);
} }
loop { loop {
if self.error_occurred { if self.error_occurred {
return Err(anyhow!("Error occurred")); return Err(Something);
} }
debug!("{:?}", self.stack); debug!("{:?}", self.stack);
let opcode = chunk.code[self.ip]; let opcode = chunk.code[self.ip];
@ -91,14 +91,14 @@ impl <'a> Vm<'a> {
if let (Value::Bool(a), Value::Bool(b)) = (a, b) { if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
Ok(Value::Bool(*a && *b)) Ok(Value::Bool(*a && *b))
} else { } else {
Err(anyhow!("Cannot and")) Err(ValueError::Some("Cannot and"))
} }
}), }),
OP_OR => binary_op(self, |a, b| { OP_OR => binary_op(self, |a, b| {
if let (Value::Bool(a), Value::Bool(b)) = (a, b) { if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
Ok(Value::Bool(*a || *b)) Ok(Value::Bool(*a || *b))
} else { } else {
Err(anyhow!("Cannot compare")) Err(ValueError::Some("Cannot compare"))
} }
}), }),
OP_NOT => unary_op(self, |a| !a), OP_NOT => unary_op(self, |a| !a),
@ -210,7 +210,7 @@ impl <'a> Vm<'a> {
} }
} }
fn binary_op(vm: &mut Vm, op: impl Fn(&Value, &Value) -> anyhow::Result<Value> + Copy) { fn binary_op(vm: &mut Vm, op: impl Fn(&Value, &Value) -> Result<Value, ValueError> + Copy) {
let b = vm.pop(); let b = vm.pop();
let a = vm.pop(); let a = vm.pop();
@ -218,13 +218,13 @@ fn binary_op(vm: &mut Vm, op: impl Fn(&Value, &Value) -> anyhow::Result<Value> +
match result { match result {
Ok(result) => vm.push(result), Ok(result) => vm.push(result),
Err(e) => { Err(e) => {
println!("Error: {} {:?} and {:?}", e.to_string(), a, b);
vm.error_occurred = true; vm.error_occurred = true;
println!("Error: {} {:?} and {:?}", e.to_string(), a, b);
} }
} }
} }
fn unary_op(stack: &mut Vm, op: impl Fn(&Value) -> anyhow::Result<Value> + Copy) { fn unary_op(stack: &mut Vm, op: impl Fn(&Value) -> Result<Value, ValueError> + Copy) {
let a = stack.pop(); let a = stack.pop();
let result = op(&a); let result = op(&a);
match result { match result {