diff --git a/kalk/src/ast.rs b/kalk/src/ast.rs index 47f572c..c4ebd74 100644 --- a/kalk/src/ast.rs +++ b/kalk/src/ast.rs @@ -1,4 +1,5 @@ use crate::lexer::TokenKind; +use crate::parser::CalcError; use crate::parser::Unit; #[derive(Debug, Clone, PartialEq)] @@ -27,11 +28,11 @@ impl TokenKind { } } - pub fn to_unit(&self) -> Result { + pub fn to_unit(&self) -> Result { match self { TokenKind::Deg => Ok(Unit::Degrees), TokenKind::Rad => Ok(Unit::Radians), - _ => Err(String::from("Invalid unit.")), + _ => Err(CalcError::InvalidUnit), } } } diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 73e0a79..8a2c6ea 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -1,5 +1,6 @@ use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; +use crate::parser::CalcError; use crate::parser::Unit; use crate::prelude; use crate::symbol_table::SymbolTable; @@ -21,7 +22,7 @@ impl<'a> Context<'a> { } } - pub fn interpret(&mut self, statements: Vec) -> Result, String> { + pub fn interpret(&mut self, statements: Vec) -> Result, CalcError> { for (i, stmt) in statements.iter().enumerate() { let value = eval_stmt(self, stmt); @@ -36,7 +37,7 @@ impl<'a> Context<'a> { } } -fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result { +fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result { match stmt { Stmt::VarDecl(identifier, _) => eval_var_decl_stmt(context, stmt, identifier), Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context), @@ -48,20 +49,20 @@ fn eval_var_decl_stmt( context: &mut Context, stmt: &Stmt, identifier: &str, -) -> Result { +) -> Result { context.symbol_table.insert(&identifier, stmt.clone()); Ok(Float::with_val(context.precision, 1)) } -fn eval_fn_decl_stmt(context: &mut Context) -> Result { +fn eval_fn_decl_stmt(context: &mut Context) -> Result { Ok(Float::with_val(context.precision, 1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table. } -fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result { +fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result { eval_expr(context, &expr) } -fn eval_expr(context: &mut Context, expr: &Expr) -> Result { +fn eval_expr(context: &mut Context, expr: &Expr) -> Result { match expr { Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right), Expr::Unary(op, expr) => eval_unary_expr(context, op, expr), @@ -80,7 +81,7 @@ fn eval_binary_expr( left: &Expr, op: &TokenKind, right: &Expr, -) -> Result { +) -> Result { let left = eval_expr(context, &left)?; let right = eval_expr(context, &right)?; @@ -94,7 +95,7 @@ fn eval_binary_expr( }) } -fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result { +fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result { let expr_value = eval_expr(context, &expr)?; match op { @@ -103,11 +104,15 @@ fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result context.precision, prelude::special_funcs::factorial(expr_value), )), - _ => Err(String::from("Invalid operator for unary expression.")), + _ => Err(CalcError::InvalidOperator), } } -fn eval_unit_expr(context: &mut Context, expr: &Expr, kind: &TokenKind) -> Result { +fn eval_unit_expr( + context: &mut Context, + expr: &Expr, + kind: &TokenKind, +) -> Result { let x = eval_expr(context, &expr); let unit = kind.to_unit()?; @@ -126,7 +131,7 @@ fn eval_unit_expr(context: &mut Context, expr: &Expr, kind: &TokenKind) -> Resul } } -fn eval_var_expr(context: &mut Context, identifier: &str) -> Result { +fn eval_var_expr(context: &mut Context, identifier: &str) -> Result { // If there is a constant with this name, return a literal expression with its value if let Some(value) = prelude::CONSTANTS.get(identifier) { return eval_expr(context, &Expr::Literal((*value).to_string())); @@ -136,18 +141,18 @@ fn eval_var_expr(context: &mut Context, identifier: &str) -> Result eval_expr(context, &expr), - _ => Err(format!("Undefined variable: '{}'.", identifier)), + _ => Err(CalcError::UndefinedVar(identifier.into())), } } -fn eval_literal_expr(context: &mut Context, value: &str) -> Result { +fn eval_literal_expr(context: &mut Context, value: &str) -> Result { match Float::parse(value) { Ok(parsed_value) => Ok(Float::with_val(context.precision, parsed_value)), - Err(_) => Err(format!("Invalid number literal: '{}'.", value)), + Err(_) => Err(CalcError::InvalidNumberLiteral(value.into())), } } -fn eval_group_expr(context: &mut Context, expr: &Expr) -> Result { +fn eval_group_expr(context: &mut Context, expr: &Expr) -> Result { eval_expr(context, expr) } @@ -155,7 +160,7 @@ fn eval_fn_call_expr( context: &mut Context, identifier: &str, expressions: &[Expr], -) -> Result { +) -> Result { // Prelude let prelude_func = match expressions.len() { 1 => { @@ -179,9 +184,10 @@ fn eval_fn_call_expr( "sum" | "Σ" => { // Make sure exactly 3 arguments were supplied. if expressions.len() != 3 { - return Err(format!( - "Expected 3 arguments but got {}.", - expressions.len() + return Err(CalcError::IncorrectAmountOfArguments( + 3, + "sum".into(), + expressions.len(), )); } @@ -214,11 +220,10 @@ fn eval_fn_call_expr( match stmt_definition { Some(Stmt::FnDecl(_, arguments, fn_body)) => { if arguments.len() != expressions.len() { - return Err(format!( - "Expected {} arguments in function '{}' but found {}.", + return Err(CalcError::IncorrectAmountOfArguments( arguments.len(), - identifier, - expressions.len() + identifier.into(), + expressions.len(), )); } @@ -232,7 +237,7 @@ fn eval_fn_call_expr( eval_expr(context, &*fn_body) } - _ => Err(format!("Undefined function: '{}'.", identifier)), + _ => Err(CalcError::UndefinedFn(identifier.into())), } } @@ -244,7 +249,7 @@ mod tests { const PRECISION: u32 = 53; - fn interpret(stmt: Stmt) -> Result, String> { + fn interpret(stmt: Stmt) -> Result, CalcError> { let mut symbol_table = SymbolTable::new(); let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); context.interpret(vec![stmt]) @@ -318,7 +323,7 @@ mod tests { assert_eq!( interpret(stmt), - Err(String::from("Undefined variable: 'x'.")) + Err(CalcError::UndefinedVar(String::from("x"))) ); } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index d4fb686..d970f75 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -12,11 +12,6 @@ pub struct Context { symbol_table: SymbolTable, angle_unit: Unit, } -#[derive(Debug, Clone, PartialEq)] -pub enum Unit { - Radians, - Degrees, -} impl Context { pub fn new() -> Self { @@ -41,7 +36,29 @@ impl Default for Context { } } -pub fn parse(context: &mut Context, input: &str, precision: u32) -> Result, String> { +#[derive(Debug, Clone, PartialEq)] +pub enum Unit { + Radians, + Degrees, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum CalcError { + IncorrectAmountOfArguments(usize, String, usize), + InvalidNumberLiteral(String), + InvalidOperator, + InvalidUnit, + UnexpectedToken(TokenKind), + UndefinedFn(String), + UndefinedVar(String), + Unknown, +} + +pub fn parse( + context: &mut Context, + input: &str, + precision: u32, +) -> Result, CalcError> { context.tokens = Lexer::lex(input); context.pos = 0; @@ -54,7 +71,7 @@ pub fn parse(context: &mut Context, input: &str, precision: u32) -> Result Result { +fn parse_stmt(context: &mut Context) -> Result { if match_token(context, TokenKind::Identifier) { return Ok(match peek_next(context).kind { TokenKind::Equals => parse_var_decl_stmt(context)?, @@ -66,7 +83,7 @@ fn parse_stmt(context: &mut Context) -> Result { Ok(Stmt::Expr(Box::new(parse_expr(context)?))) } -fn parse_identifier_stmt(context: &mut Context) -> Result { +fn parse_identifier_stmt(context: &mut Context) -> Result { let began_at = context.pos; let primary = parse_primary(context)?; // Since function declarations and function calls look the same at first, simply parse a "function call", and re-use the data. @@ -99,7 +116,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result { return Ok(fn_decl); } - Err("Parsing error.".into()) + Err(CalcError::Unknown) } else { // It is a function call, not a function declaration. // Redo the parsing for this specific part. @@ -108,7 +125,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result { } } -fn parse_var_decl_stmt(context: &mut Context) -> Result { +fn parse_var_decl_stmt(context: &mut Context) -> Result { let identifier = advance(context).clone(); advance(context); // Equal sign let expr = parse_expr(context)?; @@ -116,11 +133,11 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result { Ok(Stmt::VarDecl(identifier.value, Box::new(expr))) } -fn parse_expr(context: &mut Context) -> Result { +fn parse_expr(context: &mut Context) -> Result { Ok(parse_sum(context)?) } -fn parse_sum(context: &mut Context) -> Result { +fn parse_sum(context: &mut Context) -> Result { let mut left = parse_factor(context)?; while match_token(context, TokenKind::Plus) || match_token(context, TokenKind::Minus) { @@ -134,7 +151,7 @@ fn parse_sum(context: &mut Context) -> Result { Ok(left) } -fn parse_factor(context: &mut Context) -> Result { +fn parse_factor(context: &mut Context) -> Result { let mut left = parse_unary(context)?; while match_token(context, TokenKind::Star) @@ -155,7 +172,7 @@ fn parse_factor(context: &mut Context) -> Result { Ok(left) } -fn parse_unary(context: &mut Context) -> Result { +fn parse_unary(context: &mut Context) -> Result { if match_token(context, TokenKind::Minus) { let op = advance(context).kind.clone(); let expr = Box::new(parse_unary(context)?); @@ -165,7 +182,7 @@ fn parse_unary(context: &mut Context) -> Result { Ok(parse_exponent(context)?) } -fn parse_exponent(context: &mut Context) -> Result { +fn parse_exponent(context: &mut Context) -> Result { let left = parse_factorial(context)?; if match_token(context, TokenKind::Power) { @@ -177,7 +194,7 @@ fn parse_exponent(context: &mut Context) -> Result { Ok(left) } -fn parse_factorial(context: &mut Context) -> Result { +fn parse_factorial(context: &mut Context) -> Result { let expr = parse_primary(context)?; Ok(if match_token(context, TokenKind::Exclamation) { @@ -188,7 +205,7 @@ fn parse_factorial(context: &mut Context) -> Result { }) } -fn parse_primary(context: &mut Context) -> Result { +fn parse_primary(context: &mut Context) -> Result { let expr = match peek(context).kind { TokenKind::OpenParenthesis => parse_group(context)?, TokenKind::Pipe => parse_abs(context)?, @@ -203,7 +220,7 @@ fn parse_primary(context: &mut Context) -> Result { } } -fn parse_group(context: &mut Context) -> Result { +fn parse_group(context: &mut Context) -> Result { advance(context); let group_expr = Expr::Group(Box::new(parse_expr(context)?)); consume(context, TokenKind::ClosedParenthesis)?; @@ -211,7 +228,7 @@ fn parse_group(context: &mut Context) -> Result { Ok(group_expr) } -fn parse_abs(context: &mut Context) -> Result { +fn parse_abs(context: &mut Context) -> Result { advance(context); let group_expr = Expr::Group(Box::new(parse_expr(context)?)); consume(context, TokenKind::Pipe)?; @@ -219,7 +236,7 @@ fn parse_abs(context: &mut Context) -> Result { Ok(Expr::FnCall(String::from("abs"), vec![group_expr])) } -fn parse_identifier(context: &mut Context) -> Result { +fn parse_identifier(context: &mut Context) -> Result { let identifier = advance(context).clone(); // Eg. sqrt64 @@ -294,12 +311,12 @@ fn advance(context: &mut Context) -> &Token { previous(context) } -fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, String> { - if match_token(context, kind) { +fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, CalcError> { + if match_token(context, kind.clone()) { return Ok(advance(context)); } - Err("Unexpected token".into()) + Err(CalcError::UnexpectedToken(kind)) } fn is_at_end(context: &mut Context) -> bool { @@ -313,13 +330,13 @@ mod tests { use crate::test_helpers::*; use test_case::test_case; - fn parse_with_context(context: &mut Context, tokens: Vec) -> Result { + fn parse_with_context(context: &mut Context, tokens: Vec) -> Result { context.tokens = tokens; parse_stmt(context) } - fn parse(tokens: Vec) -> Result { + fn parse(tokens: Vec) -> Result { let mut context = Context::new(); context.tokens = tokens; diff --git a/kalk_cli/src/output.rs b/kalk_cli/src/output.rs index 3787e73..87db08e 100644 --- a/kalk_cli/src/output.rs +++ b/kalk_cli/src/output.rs @@ -1,5 +1,5 @@ use ansi_term::Colour::Red; -use kalk::parser::{self}; +use kalk::parser::{self, CalcError, CalcError::*}; pub fn eval(parser: &mut parser::Context, input: &str) { match parser::parse(parser, input, 53) { @@ -8,7 +8,7 @@ pub fn eval(parser: &mut parser::Context, input: &str) { let exp = if let Some(exp) = exp_option { exp } else { 0 }; if result.is_infinite() { - err("Too big to process."); + print_err("Too big to process."); /*} else if result.clone().fract() == 0 { println!("{}", result.to_integer().unwrap());*/ } else { @@ -37,10 +37,26 @@ pub fn eval(parser: &mut parser::Context, input: &str) { } } Ok(None) => print!(""), - Err(msg) => err(&msg), + Err(err) => print_calc_err(err), } } -fn err(msg: &str) { +fn print_calc_err(err: CalcError) { + print_err(&match err { + IncorrectAmountOfArguments(expected, func, got) => format!( + "Expected {} arguments for function {}, but got {}.", + expected, func, got + ), + InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x), + InvalidOperator => format!("Invalid operator."), + InvalidUnit => format!("Invalid unit."), + UnexpectedToken(kind) => format!("Unexpected token: '{:?}'.", kind), + UndefinedFn(name) => format!("Undefined function: '{}'.", name), + UndefinedVar(name) => format!("Undefined variable: '{}'.", name), + Unknown => format!("Unknown error."), + }); +} + +fn print_err(msg: &str) { println!("{}", Red.paint(msg)); }