switched to thiserror for better error handling
This commit is contained in:
parent
0bd6048083
commit
55a30afd06
11 changed files with 252 additions and 174 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -195,7 +195,6 @@ dependencies = [
|
|||
name = "crudlang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"chrono",
|
||||
"dotenv",
|
||||
|
|
@ -203,6 +202,7 @@ dependencies = [
|
|||
"log4rs",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
"tower",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ tower-http = { version = "0.6", features = ["fs"] }
|
|||
tower = "0.5.0"
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.20"
|
||||
anyhow = "1.0"
|
||||
tower-livereload = "0.9.6"
|
||||
log = "0.4.28"
|
||||
walkdir = "2.5.0"
|
||||
thiserror = "2.0.17"
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
use crate::errors::CompilerError::{self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, UnexpectedIndent, UninitializedVariable};
|
||||
use crate::tokens::TokenType::{
|
||||
Bang, Bool, Char, Colon, Date, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater,
|
||||
GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace, LeftBracket,
|
||||
LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print,
|
||||
RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star, StringType,
|
||||
True, U32, U64, UnsignedInteger,
|
||||
Bang, Bool, Char, Colon, Date, Eof, Eol, Equal, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater,
|
||||
Identifier, Indent, Integer, LeftBrace, LeftBracket, LeftParen, Less, LessEqual, LessLess,
|
||||
Let, ListType, MapType, Minus, Object, Plus, Print, RightBrace, RightBracket, RightParen, SignedInteger,
|
||||
SingleRightArrow, Slash, Star, StringType, True, UnsignedInteger, F32, F64,
|
||||
I32, I64, U32, U64,
|
||||
};
|
||||
use crate::tokens::{Token, TokenType};
|
||||
use crate::value::Value;
|
||||
use anyhow::anyhow;
|
||||
use log::debug;
|
||||
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);
|
||||
compiler.compile_tokens()
|
||||
}
|
||||
|
|
@ -49,13 +49,13 @@ impl AstCompiler {
|
|||
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.reset();
|
||||
self.compile()
|
||||
}
|
||||
|
||||
fn compile(&mut self) -> anyhow::Result<Vec<Statement>> {
|
||||
fn compile(&mut self) -> Result<Vec<Statement>, CompilerError> {
|
||||
if !self.had_error {
|
||||
let mut statements = vec![];
|
||||
while !self.is_at_end() {
|
||||
|
|
@ -68,23 +68,23 @@ impl AstCompiler {
|
|||
}
|
||||
Ok(statements)
|
||||
} 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() {
|
||||
if self.match_token(vec![Fn]) {
|
||||
let name_token = self.consume(Identifier, "Expect function name.")?;
|
||||
self.consume(LeftParen, "Expect '(' after function name.")?;
|
||||
let name_token = self.consume(Identifier, Expected("function name."))?;
|
||||
self.consume(LeftParen, Expected("'(' after function name."))?;
|
||||
let mut parameters = vec![];
|
||||
while !self.check(RightParen) {
|
||||
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;
|
||||
self.vars.push(Expression::Variable {
|
||||
name: parm_name.lexeme.to_string(),
|
||||
|
|
@ -100,15 +100,15 @@ impl AstCompiler {
|
|||
self.advance();
|
||||
}
|
||||
}
|
||||
self.consume(RightParen, "Expect ')' after parameters.")?;
|
||||
self.consume(RightParen, Expected(" ')' after parameters."))?;
|
||||
let return_type = if self.check(SingleRightArrow) {
|
||||
self.consume(SingleRightArrow, "")?;
|
||||
self.consume(SingleRightArrow, Expected("->"))?;
|
||||
self.advance().token_type
|
||||
} else {
|
||||
TokenType::Void
|
||||
};
|
||||
self.consume(Colon, "Expect colon (:) after function declaration.")?;
|
||||
self.consume(Eol, "Expect end of line.")?;
|
||||
self.consume(Colon, Expected("colon (:) after function declaration."))?;
|
||||
self.consume(Eol, Expected("end of line."))?;
|
||||
|
||||
let function = Function {
|
||||
name: name_token.clone(),
|
||||
|
|
@ -125,7 +125,7 @@ impl AstCompiler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn indent(&mut self) -> anyhow::Result<Option<Statement>> {
|
||||
fn indent(&mut self) -> Result<Option<Statement>, CompilerError> {
|
||||
let expected_indent = *self.indent.last().unwrap();
|
||||
// skip empty lines
|
||||
while self.check(Eol) {
|
||||
|
|
@ -138,10 +138,9 @@ impl AstCompiler {
|
|||
indent_on_line += 1;
|
||||
}
|
||||
if indent_on_line > expected_indent {
|
||||
panic!(
|
||||
"unexpected indent level {} vs expected {}",
|
||||
Err(UnexpectedIndent(
|
||||
indent_on_line, expected_indent
|
||||
);
|
||||
))
|
||||
} else if indent_on_line < expected_indent {
|
||||
self.indent.pop();
|
||||
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]) {
|
||||
self.function_declaration()
|
||||
} else if self.match_token(vec![Let]) {
|
||||
|
|
@ -162,10 +161,10 @@ impl AstCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
fn object_declaration(&mut self) -> anyhow::Result<Statement> {
|
||||
let type_name = self.consume(Identifier, "Expect object name.")?;
|
||||
self.consume(Colon, "Expect ':' after object name.")?;
|
||||
self.consume(Eol, "Expect end of line.")?;
|
||||
fn object_declaration(&mut self) -> Result<Statement, CompilerError> {
|
||||
let type_name = self.consume(Identifier, Expected("object name."))?;
|
||||
self.consume(Colon, Expected("':' after object name."))?;
|
||||
self.consume(Eol, Expected("end of line."))?;
|
||||
|
||||
let mut fields = vec![];
|
||||
|
||||
|
|
@ -181,13 +180,13 @@ impl AstCompiler {
|
|||
}
|
||||
}
|
||||
if !done {
|
||||
let field_name = self.consume(Identifier, "Expect an object field name.")?;
|
||||
self.consume(Colon, "Expect ':' after field name.")?;
|
||||
let field_name = self.consume(Identifier, Expected("an object field name."))?;
|
||||
self.consume(Colon, Expected("':' after field name."))?;
|
||||
let field_type = self.peek().token_type;
|
||||
if field_type.is_type() {
|
||||
self.advance();
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Expected a type"))?
|
||||
Err(Expected("a type"))?
|
||||
}
|
||||
fields.push(Parameter {
|
||||
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 {
|
||||
name: type_name,
|
||||
fields,
|
||||
})
|
||||
}
|
||||
|
||||
fn function_declaration(&mut self) -> anyhow::Result<Statement> {
|
||||
let name_token = self.consume(Identifier, "Expect function name.")?;
|
||||
self.consume(LeftParen, "Expect '(' after function name.")?;
|
||||
fn function_declaration(&mut self) -> Result<Statement, CompilerError> {
|
||||
let name_token = self.consume(Identifier, Expected("function name."))?;
|
||||
self.consume(LeftParen, Expected("'(' after function name."))?;
|
||||
while !self.check(RightParen) {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
self.consume(RightParen, "Expect ')' after parameters.")?;
|
||||
self.consume(RightParen, Expected("')' after parameters."))?;
|
||||
while !self.check(Colon) {
|
||||
self.advance();
|
||||
}
|
||||
self.consume(Colon, "2Expect colon (:) after function declaration.")?;
|
||||
self.consume(Eol, "Expect end of line.")?;
|
||||
self.consume(Colon, Expected("colon (:) after function declaration."))?;
|
||||
self.consume(Eol, Expected("end of line."))?;
|
||||
|
||||
let current_indent = self.indent.last().unwrap();
|
||||
self.indent.push(current_indent + 1);
|
||||
|
|
@ -228,8 +227,8 @@ impl AstCompiler {
|
|||
Ok(function_stmt)
|
||||
}
|
||||
|
||||
fn let_declaration(&mut self) -> anyhow::Result<Statement> {
|
||||
let name_token = self.consume(Identifier, "Expect variable name.")?;
|
||||
fn let_declaration(&mut self) -> Result<Statement, CompilerError> {
|
||||
let name_token = self.consume(Identifier, Expected("variable name."))?;
|
||||
|
||||
let declared_type = if self.check(Colon) {
|
||||
self.advance();
|
||||
|
|
@ -240,14 +239,14 @@ impl AstCompiler {
|
|||
|
||||
if self.match_token(vec![Equal]) {
|
||||
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 var_type = match calculate_type(declared_type, inferred_type) {
|
||||
Ok(var_type) => var_type,
|
||||
Err(e) => {
|
||||
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 {
|
||||
|
|
@ -261,11 +260,11 @@ impl AstCompiler {
|
|||
initializer,
|
||||
})
|
||||
} 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]) {
|
||||
self.print_statement()
|
||||
} 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()?;
|
||||
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 })
|
||||
}
|
||||
|
||||
fn expr_statement(&mut self) -> anyhow::Result<Statement> {
|
||||
fn expr_statement(&mut self) -> Result<Statement, CompilerError> {
|
||||
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 })
|
||||
}
|
||||
|
||||
fn expression(&mut self) -> anyhow::Result<Expression> {
|
||||
fn expression(&mut self) -> Result<Expression, CompilerError> {
|
||||
self.or()
|
||||
}
|
||||
|
||||
fn or(&mut self) -> anyhow::Result<Expression> {
|
||||
fn or(&mut self) -> Result<Expression, CompilerError> {
|
||||
let expr = self.and()?;
|
||||
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()?;
|
||||
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()?;
|
||||
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()?;
|
||||
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()?;
|
||||
self.binary(vec![TokenType::BitXor], expr)
|
||||
}
|
||||
|
||||
fn equality(&mut self) -> anyhow::Result<Expression> {
|
||||
fn equality(&mut self) -> Result<Expression, CompilerError> {
|
||||
let expr = self.comparison()?;
|
||||
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()?;
|
||||
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()?;
|
||||
self.binary(vec![GreaterGreater, LessLess], expr)
|
||||
}
|
||||
|
||||
fn term(&mut self) -> anyhow::Result<Expression> {
|
||||
fn term(&mut self) -> Result<Expression, CompilerError> {
|
||||
let expr = self.factor()?;
|
||||
self.binary(vec![Minus, Plus], expr)
|
||||
}
|
||||
|
||||
fn factor(&mut self) -> anyhow::Result<Expression> {
|
||||
fn factor(&mut self) -> Result<Expression, CompilerError> {
|
||||
let expr = self.unary()?;
|
||||
self.binary(vec![Slash, Star], expr)
|
||||
}
|
||||
|
|
@ -343,7 +342,7 @@ impl AstCompiler {
|
|||
&mut self,
|
||||
types: Vec<TokenType>,
|
||||
mut expr: Expression,
|
||||
) -> anyhow::Result<Expression> {
|
||||
) -> Result<Expression, CompilerError> {
|
||||
while self.match_token(types.clone()) {
|
||||
let operator = self.previous().clone();
|
||||
let right = self.comparison()?;
|
||||
|
|
@ -357,7 +356,7 @@ impl AstCompiler {
|
|||
Ok(expr)
|
||||
}
|
||||
|
||||
fn unary(&mut self) -> anyhow::Result<Expression> {
|
||||
fn unary(&mut self) -> Result<Expression, CompilerError> {
|
||||
if self.match_token(vec![Bang, Minus]) {
|
||||
let operator = self.previous().clone();
|
||||
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());
|
||||
Ok(if self.match_token(vec![LeftBracket]) {
|
||||
self.list()?
|
||||
|
|
@ -393,13 +392,13 @@ impl AstCompiler {
|
|||
Expression::Literal {
|
||||
line: self.peek().line,
|
||||
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]) {
|
||||
Expression::Literal {
|
||||
line: self.peek().line,
|
||||
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]) {
|
||||
Expression::Literal {
|
||||
|
|
@ -415,7 +414,7 @@ impl AstCompiler {
|
|||
}
|
||||
} else if self.match_token(vec![LeftParen]) {
|
||||
let expr = self.expression()?;
|
||||
self.consume(RightParen, "Expect ')' after expression.")?;
|
||||
self.consume(RightParen, Expected("')' after expression."))?;
|
||||
Expression::Grouping {
|
||||
line: self.peek().line,
|
||||
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![];
|
||||
while !self.match_token(vec![RightBracket]) {
|
||||
list.push(self.expression()?);
|
||||
if self.peek().token_type == TokenType::Comma {
|
||||
self.advance();
|
||||
} else {
|
||||
self.consume(RightBracket, "Expect ']' after list.")?;
|
||||
self.consume(RightBracket, Expected("']' at the end of the list."))?;
|
||||
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![];
|
||||
while !self.match_token(vec![RightBrace]) {
|
||||
let key = self.expression()?;
|
||||
self.consume(Colon, "Expect ':' after map key.")?;
|
||||
self.consume(Colon, Expected("':' after map key."))?;
|
||||
let value = self.expression()?;
|
||||
entries.push((key, value));
|
||||
if self.peek().token_type == TokenType::Comma {
|
||||
self.advance();
|
||||
} else {
|
||||
self.consume(RightBrace, "Expect '}' after map.")?;
|
||||
self.consume(RightBrace, Expected("'}' after map."))?;
|
||||
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
|
||||
.vars
|
||||
.iter()
|
||||
|
|
@ -482,7 +481,7 @@ impl AstCompiler {
|
|||
}
|
||||
})
|
||||
.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 {
|
||||
name: var_name.to_string(),
|
||||
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 = self.functions.get(&function_name).unwrap().clone();
|
||||
|
||||
let mut arguments = vec![];
|
||||
while !self.match_token(vec![RightParen]) {
|
||||
if arguments.len() >= 25 {
|
||||
return Err(anyhow::anyhow!("Too many parameters."));
|
||||
return Err(TooManyParameters);
|
||||
}
|
||||
let arg = self.expression()?;
|
||||
let arg_type = arg.infer_type();
|
||||
if arg_type != function.parameters[arguments.len()].var_type {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Incompatible argument types. Expected {}, found {}",
|
||||
return Err(IncompatibleTypes(
|
||||
function.parameters[arguments.len()].var_type,
|
||||
arg_type
|
||||
));
|
||||
|
|
@ -512,7 +510,7 @@ impl AstCompiler {
|
|||
if self.peek().token_type == TokenType::Comma {
|
||||
self.advance();
|
||||
} else {
|
||||
self.consume(RightParen, "Expect ')' after arguments.")?;
|
||||
self.consume(RightParen, Expected("')' after arguments."))?;
|
||||
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) {
|
||||
self.advance();
|
||||
} else {
|
||||
self.had_error = true;
|
||||
return Err(anyhow::anyhow!(
|
||||
"{} at {:?}",
|
||||
message.to_string(),
|
||||
self.peek()
|
||||
));
|
||||
// return Err(anyhow::anyhow!(
|
||||
// "{} at {:?}",
|
||||
// message.to_string(),
|
||||
// self.peek()
|
||||
// ));
|
||||
return Err(message);
|
||||
}
|
||||
Ok(self.previous().clone())
|
||||
}
|
||||
|
|
@ -580,7 +579,7 @@ impl AstCompiler {
|
|||
fn calculate_type(
|
||||
declared_type: Option<TokenType>,
|
||||
inferred_type: TokenType,
|
||||
) -> anyhow::Result<TokenType> {
|
||||
) -> Result<TokenType, CompilerError> {
|
||||
Ok(if let Some(declared_type) = declared_type {
|
||||
if declared_type != inferred_type {
|
||||
match (declared_type, inferred_type) {
|
||||
|
|
@ -596,8 +595,7 @@ fn calculate_type(
|
|||
(U64, I32) => U64,
|
||||
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Incompatible types. Expected {}, found {}",
|
||||
return Err(IncompatibleTypes(
|
||||
declared_type,
|
||||
inferred_type
|
||||
));
|
||||
|
|
@ -615,7 +613,7 @@ fn calculate_type(
|
|||
ListType => ListType,
|
||||
MapType => MapType,
|
||||
Object => Object,
|
||||
_ => panic!("Unexpected type"),
|
||||
_ => return Err(CompilerError::UnexpectedType(inferred_type)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ use crate::vm::{
|
|||
OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use crate::errors::CompilerError;
|
||||
|
||||
pub fn compile(
|
||||
namespace: Option<&str>,
|
||||
ast: &Vec<Statement>,
|
||||
registry: &mut HashMap<String, Chunk>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(),CompilerError> {
|
||||
compile_name(ast, namespace, registry)
|
||||
}
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ pub(crate) fn compile_function(
|
|||
function: &Function,
|
||||
registry: &mut HashMap<String, Chunk>,
|
||||
namespace: &str,
|
||||
) -> anyhow::Result<Chunk> {
|
||||
) -> Result<Chunk, CompilerError> {
|
||||
let mut compiler = Compiler::new(&function.name.lexeme);
|
||||
for parm in &function.parameters {
|
||||
let name = parm.name.lexeme.clone();
|
||||
|
|
@ -39,7 +40,7 @@ pub(crate) fn compile_name(
|
|||
ast: &Vec<Statement>,
|
||||
namespace: Option<&str>,
|
||||
registry: &mut HashMap<String, Chunk>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(),CompilerError> {
|
||||
let name=namespace.unwrap_or("main");
|
||||
let compiler = Compiler::new(name);
|
||||
let chunk = compiler.compile(ast, registry, name)?;
|
||||
|
|
@ -74,7 +75,7 @@ impl Compiler {
|
|||
ast: &Vec<Statement>,
|
||||
registry: &mut HashMap<String, Chunk>,
|
||||
namespace: &str,
|
||||
) -> anyhow::Result<Chunk> {
|
||||
) -> Result<Chunk, CompilerError> {
|
||||
//TODO can likely be removed
|
||||
for statement in ast {
|
||||
if let Statement::FunctionStmt { function } = statement {
|
||||
|
|
@ -99,7 +100,7 @@ impl Compiler {
|
|||
statement: &Statement,
|
||||
registry: &mut HashMap<String, Chunk>,
|
||||
namespace: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(), CompilerError> {
|
||||
self.current_line = statement.line();
|
||||
match statement {
|
||||
Statement::VarStmt {
|
||||
|
|
@ -140,7 +141,7 @@ impl Compiler {
|
|||
namespace: &str,
|
||||
expression: &Expression,
|
||||
registry: &mut HashMap<String, Chunk>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(), CompilerError> {
|
||||
match expression {
|
||||
Expression::FunctionCall {
|
||||
name, arguments, ..
|
||||
|
|
@ -229,7 +230,7 @@ impl Compiler {
|
|||
var_type: &TokenType,
|
||||
name_index: usize,
|
||||
initializer: &Expression,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(), CompilerError> {
|
||||
let def_op = match var_type {
|
||||
TokenType::I32 => OP_DEF_I32,
|
||||
TokenType::I64 => OP_DEF_I64,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::compile;
|
||||
use crate::{compile, run};
|
||||
use crate::scanner::scan;
|
||||
use crate::value::Value;
|
||||
use crate::vm::interpret;
|
||||
|
||||
#[test]
|
||||
fn literal_int() {
|
||||
assert!(compile("1").is_ok());
|
||||
assert_eq!(run("1"), Ok(Value::I64(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -52,7 +52,7 @@ mod tests {
|
|||
if let Err(e) = &r {
|
||||
assert_eq!(
|
||||
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 {
|
||||
assert_eq!(
|
||||
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 {
|
||||
assert_eq!(
|
||||
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
61
src/errors.rs
Normal 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),
|
||||
}
|
||||
18
src/lib.rs
18
src/lib.rs
|
|
@ -1,20 +1,32 @@
|
|||
use std::collections::HashMap;
|
||||
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 bytecode_compiler;
|
||||
pub mod chunk;
|
||||
mod compiler_tests;
|
||||
mod keywords;
|
||||
pub mod scanner;
|
||||
mod compiler_tests;
|
||||
mod tokens;
|
||||
mod value;
|
||||
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 mut registry = HashMap::new();
|
||||
let ast = ast_compiler::compile(tokens)?;
|
||||
bytecode_compiler::compile(None, &ast, &mut 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(®istry, "main").map_err(Error::from)
|
||||
}
|
||||
|
|
|
|||
21
src/main.rs
21
src/main.rs
|
|
@ -1,4 +1,4 @@
|
|||
use axum::extract::{Path, State};
|
||||
use axum::extract::{State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::get;
|
||||
use axum::{Json, Router};
|
||||
|
|
@ -9,12 +9,13 @@ use crudlang::scanner::scan;
|
|||
use crudlang::vm::{interpret, interpret_async};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
use walkdir::WalkDir;
|
||||
use crudlang::errors::Error;
|
||||
use crudlang::errors::Error::Platform;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
async fn main() -> Result<(), crudlang::errors::Error> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let mut paths = HashMap::new();
|
||||
|
|
@ -23,12 +24,12 @@ async fn main() -> anyhow::Result<()> {
|
|||
let path = entry.path();
|
||||
if path.is_file() && path.ends_with("web.crud") {
|
||||
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)?;
|
||||
match ast_compiler::compile(tokens) {
|
||||
Ok(statements) => {
|
||||
let path = path
|
||||
.strip_prefix("source")?
|
||||
.strip_prefix("source").map_err(|e|Platform(e.to_string()))?
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.replace(".crud", "");
|
||||
|
|
@ -58,13 +59,17 @@ async fn main() -> anyhow::Result<()> {
|
|||
get(handle_get).with_state(state.clone()),
|
||||
);
|
||||
}
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||
println!("listening on {}", listener.local_addr()?);
|
||||
axum::serve(listener, app).await?;
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.map_err(map_underlying())?;
|
||||
println!("listening on {}", listener.local_addr().map_err(map_underlying())?);
|
||||
axum::serve(listener, app).await.map_err(map_underlying())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_underlying() -> fn(std::io::Error) -> Error {
|
||||
|e| Error::Platform(e.to_string())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
name: String,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use anyhow::anyhow;
|
||||
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer};
|
||||
use crate::{
|
||||
keywords,
|
||||
|
|
@ -7,8 +6,10 @@ use crate::{
|
|||
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 {
|
||||
chars: source.chars().collect(),
|
||||
current: 0,
|
||||
|
|
@ -21,7 +22,7 @@ pub fn scan(source: &str) -> anyhow::Result<Vec<Token>> {
|
|||
}
|
||||
|
||||
impl Scanner {
|
||||
fn scan(mut self) -> anyhow::Result<Vec<Token>> {
|
||||
fn scan(mut self) -> Result<Vec<Token>, CompilerError> {
|
||||
while !self.is_at_end() {
|
||||
self.start = self.current;
|
||||
self.scan_token()?;
|
||||
|
|
@ -31,7 +32,7 @@ impl Scanner {
|
|||
Ok(self.tokens)
|
||||
}
|
||||
|
||||
fn scan_token(&mut self) -> anyhow::Result<()>{
|
||||
fn scan_token(&mut self) -> Result<(),CompilerError> {
|
||||
let c = self.advance();
|
||||
if self.new_line && (c == ' ' || c == '\t') {
|
||||
self.add_token(TokenType::Indent);
|
||||
|
|
@ -138,7 +139,7 @@ impl Scanner {
|
|||
} else if is_alpha(c) {
|
||||
self.identifier();
|
||||
} 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);
|
||||
}
|
||||
|
||||
fn char(&mut self) -> anyhow::Result<()>{
|
||||
fn char(&mut self) -> Result<(), CompilerError>{
|
||||
while self.peek() != '\'' && !self.is_at_end() {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
if self.is_at_end() {
|
||||
return Err(anyhow!("Unterminated char at {}", self.line))
|
||||
return Err(Unterminated("char", self.line))
|
||||
}
|
||||
|
||||
self.advance();
|
||||
|
|
@ -188,13 +189,13 @@ impl Scanner {
|
|||
.iter()
|
||||
.collect();
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn string(&mut self) -> anyhow::Result<()>{
|
||||
fn string(&mut self) -> Result<(),CompilerError> {
|
||||
while self.peek() != '"' && !self.is_at_end() {
|
||||
if self.peek() == '\n' {
|
||||
self.line += 1;
|
||||
|
|
@ -203,7 +204,7 @@ impl Scanner {
|
|||
}
|
||||
|
||||
if self.is_at_end() {
|
||||
return Err(anyhow!("Unterminated string at {}", self.line))
|
||||
return Err(Unterminated("string", self.line))
|
||||
}
|
||||
|
||||
self.advance();
|
||||
|
|
|
|||
72
src/value.rs
72
src/value.rs
|
|
@ -1,10 +1,10 @@
|
|||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
|
||||
use crate::errors::ValueError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Object {
|
||||
|
|
@ -143,7 +143,7 @@ fn to_string(f: &mut Formatter, map: &HashMap<Value, Value>) -> std::fmt::Result
|
|||
}
|
||||
|
||||
impl Neg for &Value {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
match self {
|
||||
|
|
@ -151,13 +151,13 @@ impl Neg for &Value {
|
|||
Value::I64(i) => Ok(Value::I64(-i)),
|
||||
Value::F32(i) => Ok(Value::F32(-i)),
|
||||
Value::F64(i) => Ok(Value::F64(-i)),
|
||||
_ => Err(anyhow!("Cannot negate")),
|
||||
_ => Err(ValueError::Some("Cannot negate")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Value> for &Value {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
|
||||
fn add(self, rhs: &Value) -> Self::Output {
|
||||
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))),
|
||||
//enum?
|
||||
_ => Err(anyhow!("Cannot add")),
|
||||
_ => Err(ValueError::Some("Cannot add")),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<&Value> for &Value {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
|
||||
fn sub(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
|
|
@ -207,13 +207,13 @@ impl Sub<&Value> for &Value {
|
|||
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a - b)),
|
||||
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a - b)),
|
||||
//enum?
|
||||
_ => Err(anyhow!("Cannot subtract")),
|
||||
_ => Err(ValueError::Some("Cannot subtract")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<&Value> for &Value {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
|
||||
fn mul(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
|
|
@ -223,13 +223,13 @@ impl Mul<&Value> for &Value {
|
|||
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a * b)),
|
||||
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(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 {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
|
||||
fn div(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
|
|
@ -239,52 +239,52 @@ impl Div<&Value> for &Value {
|
|||
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a / b)),
|
||||
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(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 {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
fn bitand(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(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::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 {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
fn bitor(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(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::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 {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
fn bitxor(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(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::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 {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
|
|
@ -293,33 +293,33 @@ impl Not for &Value {
|
|||
Value::I64(i64) => Ok(Value::I64(!i64)),
|
||||
Value::U32(u32) => Ok(Value::U32(!u32)),
|
||||
Value::U64(u64) => Ok(Value::U64(!u64)),
|
||||
_ => Err(anyhow!("Cannot calculate not")),
|
||||
_ => Err(ValueError::Some("Cannot calculate not")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl<&Value> for &Value {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
fn shl(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(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::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 {
|
||||
type Output = anyhow::Result<Value>;
|
||||
type Output = Result<Value, ValueError>;
|
||||
fn shr(self, rhs: &Value) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(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::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::Char(a), Value::Char(b)) => a == b,
|
||||
(Value::Date(a), Value::Date(b)) => a == b,
|
||||
// (Value::List(a), Value::List(b)) => a == b,
|
||||
// (Value::Map(a), Value::Map(b)) => {
|
||||
// let mut equal = true;
|
||||
// for (k, v) in a.iter() {
|
||||
// if !b.contains_key(k) || b.get(k).unwrap() != v {
|
||||
// //safe unwrap
|
||||
// equal = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// equal
|
||||
// }
|
||||
// struct?
|
||||
(Value::List(a), Value::List(b)) => a == b,
|
||||
(Value::Map(a), Value::Map(b)) => {
|
||||
let mut equal = true;
|
||||
for (k, v) in a.iter() {
|
||||
if !b.contains_key(k) || b.get(k).unwrap() != v {
|
||||
//safe unwrap
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
equal
|
||||
}
|
||||
// TODO objects
|
||||
_ => false, //?
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
src/vm.rs
28
src/vm.rs
|
|
@ -1,6 +1,7 @@
|
|||
use crate::chunk::Chunk;
|
||||
use crate::errors::RuntimeError::{Something};
|
||||
use crate::errors::{RuntimeError, ValueError};
|
||||
use crate::value::Value;
|
||||
use anyhow::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -11,9 +12,8 @@ macro_rules! define_var {
|
|||
if let Value::$variant(_) = value {
|
||||
$self.local_vars.insert(name, value);
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
concat!("Expected ", stringify!($variant), ", got {:?}"),
|
||||
value
|
||||
return Err(RuntimeError::Expected(
|
||||
stringify!($variant), stringify!(value),
|
||||
));
|
||||
}
|
||||
}};
|
||||
|
|
@ -27,7 +27,7 @@ pub struct Vm<'a> {
|
|||
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();
|
||||
// for (key,value) in registry.iter() {
|
||||
// println!("{}", key);
|
||||
|
|
@ -43,7 +43,7 @@ pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> anyhow::R
|
|||
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 mut vm = Vm {
|
||||
ip: 0,
|
||||
|
|
@ -55,7 +55,7 @@ pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str)
|
|||
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 {
|
||||
ip: 0,
|
||||
stack: vec![],
|
||||
|
|
@ -67,13 +67,13 @@ pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Val
|
|||
}
|
||||
|
||||
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 {
|
||||
self.push(arg);
|
||||
}
|
||||
loop {
|
||||
if self.error_occurred {
|
||||
return Err(anyhow!("Error occurred"));
|
||||
return Err(Something);
|
||||
}
|
||||
debug!("{:?}", self.stack);
|
||||
let opcode = chunk.code[self.ip];
|
||||
|
|
@ -91,14 +91,14 @@ impl <'a> Vm<'a> {
|
|||
if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
|
||||
Ok(Value::Bool(*a && *b))
|
||||
} else {
|
||||
Err(anyhow!("Cannot and"))
|
||||
Err(ValueError::Some("Cannot and"))
|
||||
}
|
||||
}),
|
||||
OP_OR => binary_op(self, |a, b| {
|
||||
if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
|
||||
Ok(Value::Bool(*a || *b))
|
||||
} else {
|
||||
Err(anyhow!("Cannot compare"))
|
||||
Err(ValueError::Some("Cannot compare"))
|
||||
}
|
||||
}),
|
||||
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 a = vm.pop();
|
||||
|
||||
|
|
@ -218,13 +218,13 @@ fn binary_op(vm: &mut Vm, op: impl Fn(&Value, &Value) -> anyhow::Result<Value> +
|
|||
match result {
|
||||
Ok(result) => vm.push(result),
|
||||
Err(e) => {
|
||||
println!("Error: {} {:?} and {:?}", e.to_string(), a, b);
|
||||
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 result = op(&a);
|
||||
match result {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue