Created CalcError enum and centralised error formatting into once place.

This commit is contained in:
PaddiM8 2020-06-06 16:28:48 +02:00
parent 4a02134b22
commit d0536d6bd6
4 changed files with 96 additions and 57 deletions

View File

@ -1,4 +1,5 @@
use crate::lexer::TokenKind; use crate::lexer::TokenKind;
use crate::parser::CalcError;
use crate::parser::Unit; use crate::parser::Unit;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -27,11 +28,11 @@ impl TokenKind {
} }
} }
pub fn to_unit(&self) -> Result<Unit, String> { pub fn to_unit(&self) -> Result<Unit, CalcError> {
match self { match self {
TokenKind::Deg => Ok(Unit::Degrees), TokenKind::Deg => Ok(Unit::Degrees),
TokenKind::Rad => Ok(Unit::Radians), TokenKind::Rad => Ok(Unit::Radians),
_ => Err(String::from("Invalid unit.")), _ => Err(CalcError::InvalidUnit),
} }
} }
} }

View File

@ -1,5 +1,6 @@
use crate::ast::{Expr, Stmt}; use crate::ast::{Expr, Stmt};
use crate::lexer::TokenKind; use crate::lexer::TokenKind;
use crate::parser::CalcError;
use crate::parser::Unit; use crate::parser::Unit;
use crate::prelude; use crate::prelude;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
@ -21,7 +22,7 @@ impl<'a> Context<'a> {
} }
} }
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<Float>, String> { pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<Float>, CalcError> {
for (i, stmt) in statements.iter().enumerate() { for (i, stmt) in statements.iter().enumerate() {
let value = eval_stmt(self, stmt); let value = eval_stmt(self, stmt);
@ -36,7 +37,7 @@ impl<'a> Context<'a> {
} }
} }
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, String> { fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
match stmt { match stmt {
Stmt::VarDecl(identifier, _) => eval_var_decl_stmt(context, stmt, identifier), Stmt::VarDecl(identifier, _) => eval_var_decl_stmt(context, stmt, identifier),
Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context), Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context),
@ -48,20 +49,20 @@ fn eval_var_decl_stmt(
context: &mut Context, context: &mut Context,
stmt: &Stmt, stmt: &Stmt,
identifier: &str, identifier: &str,
) -> Result<Float, String> { ) -> Result<Float, CalcError> {
context.symbol_table.insert(&identifier, stmt.clone()); context.symbol_table.insert(&identifier, stmt.clone());
Ok(Float::with_val(context.precision, 1)) Ok(Float::with_val(context.precision, 1))
} }
fn eval_fn_decl_stmt(context: &mut Context) -> Result<Float, String> { fn eval_fn_decl_stmt(context: &mut Context) -> Result<Float, CalcError> {
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. 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<Float, String> { fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
eval_expr(context, &expr) eval_expr(context, &expr)
} }
fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, String> { fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
match expr { match expr {
Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right), Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right),
Expr::Unary(op, expr) => eval_unary_expr(context, op, expr), Expr::Unary(op, expr) => eval_unary_expr(context, op, expr),
@ -80,7 +81,7 @@ fn eval_binary_expr(
left: &Expr, left: &Expr,
op: &TokenKind, op: &TokenKind,
right: &Expr, right: &Expr,
) -> Result<Float, String> { ) -> Result<Float, CalcError> {
let left = eval_expr(context, &left)?; let left = eval_expr(context, &left)?;
let right = eval_expr(context, &right)?; 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<Float, String> { fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result<Float, CalcError> {
let expr_value = eval_expr(context, &expr)?; let expr_value = eval_expr(context, &expr)?;
match op { match op {
@ -103,11 +104,15 @@ fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result
context.precision, context.precision,
prelude::special_funcs::factorial(expr_value), 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<Float, String> { fn eval_unit_expr(
context: &mut Context,
expr: &Expr,
kind: &TokenKind,
) -> Result<Float, CalcError> {
let x = eval_expr(context, &expr); let x = eval_expr(context, &expr);
let unit = kind.to_unit()?; 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<Float, String> { fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<Float, CalcError> {
// If there is a constant with this name, return a literal expression with its value // If there is a constant with this name, return a literal expression with its value
if let Some(value) = prelude::CONSTANTS.get(identifier) { if let Some(value) = prelude::CONSTANTS.get(identifier) {
return eval_expr(context, &Expr::Literal((*value).to_string())); return eval_expr(context, &Expr::Literal((*value).to_string()));
@ -136,18 +141,18 @@ fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<Float, Strin
let var_decl = context.symbol_table.get(identifier).cloned(); let var_decl = context.symbol_table.get(identifier).cloned();
match var_decl { match var_decl {
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr), Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr),
_ => Err(format!("Undefined variable: '{}'.", identifier)), _ => Err(CalcError::UndefinedVar(identifier.into())),
} }
} }
fn eval_literal_expr(context: &mut Context, value: &str) -> Result<Float, String> { fn eval_literal_expr(context: &mut Context, value: &str) -> Result<Float, CalcError> {
match Float::parse(value) { match Float::parse(value) {
Ok(parsed_value) => Ok(Float::with_val(context.precision, parsed_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<Float, String> { fn eval_group_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
eval_expr(context, expr) eval_expr(context, expr)
} }
@ -155,7 +160,7 @@ fn eval_fn_call_expr(
context: &mut Context, context: &mut Context,
identifier: &str, identifier: &str,
expressions: &[Expr], expressions: &[Expr],
) -> Result<Float, String> { ) -> Result<Float, CalcError> {
// Prelude // Prelude
let prelude_func = match expressions.len() { let prelude_func = match expressions.len() {
1 => { 1 => {
@ -179,9 +184,10 @@ fn eval_fn_call_expr(
"sum" | "Σ" => { "sum" | "Σ" => {
// Make sure exactly 3 arguments were supplied. // Make sure exactly 3 arguments were supplied.
if expressions.len() != 3 { if expressions.len() != 3 {
return Err(format!( return Err(CalcError::IncorrectAmountOfArguments(
"Expected 3 arguments but got {}.", 3,
expressions.len() "sum".into(),
expressions.len(),
)); ));
} }
@ -214,11 +220,10 @@ fn eval_fn_call_expr(
match stmt_definition { match stmt_definition {
Some(Stmt::FnDecl(_, arguments, fn_body)) => { Some(Stmt::FnDecl(_, arguments, fn_body)) => {
if arguments.len() != expressions.len() { if arguments.len() != expressions.len() {
return Err(format!( return Err(CalcError::IncorrectAmountOfArguments(
"Expected {} arguments in function '{}' but found {}.",
arguments.len(), arguments.len(),
identifier, identifier.into(),
expressions.len() expressions.len(),
)); ));
} }
@ -232,7 +237,7 @@ fn eval_fn_call_expr(
eval_expr(context, &*fn_body) 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; const PRECISION: u32 = 53;
fn interpret(stmt: Stmt) -> Result<Option<Float>, String> { fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> {
let mut symbol_table = SymbolTable::new(); let mut symbol_table = SymbolTable::new();
let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION);
context.interpret(vec![stmt]) context.interpret(vec![stmt])
@ -318,7 +323,7 @@ mod tests {
assert_eq!( assert_eq!(
interpret(stmt), interpret(stmt),
Err(String::from("Undefined variable: 'x'.")) Err(CalcError::UndefinedVar(String::from("x")))
); );
} }

View File

@ -12,11 +12,6 @@ pub struct Context {
symbol_table: SymbolTable, symbol_table: SymbolTable,
angle_unit: Unit, angle_unit: Unit,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum Unit {
Radians,
Degrees,
}
impl Context { impl Context {
pub fn new() -> Self { pub fn new() -> Self {
@ -41,7 +36,29 @@ impl Default for Context {
} }
} }
pub fn parse(context: &mut Context, input: &str, precision: u32) -> Result<Option<Float>, 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<Option<Float>, CalcError> {
context.tokens = Lexer::lex(input); context.tokens = Lexer::lex(input);
context.pos = 0; context.pos = 0;
@ -54,7 +71,7 @@ pub fn parse(context: &mut Context, input: &str, precision: u32) -> Result<Optio
interpreter.interpret(statements) interpreter.interpret(statements)
} }
fn parse_stmt(context: &mut Context) -> Result<Stmt, String> { fn parse_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
if match_token(context, TokenKind::Identifier) { if match_token(context, TokenKind::Identifier) {
return Ok(match peek_next(context).kind { return Ok(match peek_next(context).kind {
TokenKind::Equals => parse_var_decl_stmt(context)?, TokenKind::Equals => parse_var_decl_stmt(context)?,
@ -66,7 +83,7 @@ fn parse_stmt(context: &mut Context) -> Result<Stmt, String> {
Ok(Stmt::Expr(Box::new(parse_expr(context)?))) Ok(Stmt::Expr(Box::new(parse_expr(context)?)))
} }
fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, String> { fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
let began_at = context.pos; 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. 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<Stmt, String> {
return Ok(fn_decl); return Ok(fn_decl);
} }
Err("Parsing error.".into()) Err(CalcError::Unknown)
} else { } else {
// It is a function call, not a function declaration. // It is a function call, not a function declaration.
// Redo the parsing for this specific part. // Redo the parsing for this specific part.
@ -108,7 +125,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, String> {
} }
} }
fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, String> { fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
let identifier = advance(context).clone(); let identifier = advance(context).clone();
advance(context); // Equal sign advance(context); // Equal sign
let expr = parse_expr(context)?; let expr = parse_expr(context)?;
@ -116,11 +133,11 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, String> {
Ok(Stmt::VarDecl(identifier.value, Box::new(expr))) Ok(Stmt::VarDecl(identifier.value, Box::new(expr)))
} }
fn parse_expr(context: &mut Context) -> Result<Expr, String> { fn parse_expr(context: &mut Context) -> Result<Expr, CalcError> {
Ok(parse_sum(context)?) Ok(parse_sum(context)?)
} }
fn parse_sum(context: &mut Context) -> Result<Expr, String> { fn parse_sum(context: &mut Context) -> Result<Expr, CalcError> {
let mut left = parse_factor(context)?; let mut left = parse_factor(context)?;
while match_token(context, TokenKind::Plus) || match_token(context, TokenKind::Minus) { while match_token(context, TokenKind::Plus) || match_token(context, TokenKind::Minus) {
@ -134,7 +151,7 @@ fn parse_sum(context: &mut Context) -> Result<Expr, String> {
Ok(left) Ok(left)
} }
fn parse_factor(context: &mut Context) -> Result<Expr, String> { fn parse_factor(context: &mut Context) -> Result<Expr, CalcError> {
let mut left = parse_unary(context)?; let mut left = parse_unary(context)?;
while match_token(context, TokenKind::Star) while match_token(context, TokenKind::Star)
@ -155,7 +172,7 @@ fn parse_factor(context: &mut Context) -> Result<Expr, String> {
Ok(left) Ok(left)
} }
fn parse_unary(context: &mut Context) -> Result<Expr, String> { fn parse_unary(context: &mut Context) -> Result<Expr, CalcError> {
if match_token(context, TokenKind::Minus) { if match_token(context, TokenKind::Minus) {
let op = advance(context).kind.clone(); let op = advance(context).kind.clone();
let expr = Box::new(parse_unary(context)?); let expr = Box::new(parse_unary(context)?);
@ -165,7 +182,7 @@ fn parse_unary(context: &mut Context) -> Result<Expr, String> {
Ok(parse_exponent(context)?) Ok(parse_exponent(context)?)
} }
fn parse_exponent(context: &mut Context) -> Result<Expr, String> { fn parse_exponent(context: &mut Context) -> Result<Expr, CalcError> {
let left = parse_factorial(context)?; let left = parse_factorial(context)?;
if match_token(context, TokenKind::Power) { if match_token(context, TokenKind::Power) {
@ -177,7 +194,7 @@ fn parse_exponent(context: &mut Context) -> Result<Expr, String> {
Ok(left) Ok(left)
} }
fn parse_factorial(context: &mut Context) -> Result<Expr, String> { fn parse_factorial(context: &mut Context) -> Result<Expr, CalcError> {
let expr = parse_primary(context)?; let expr = parse_primary(context)?;
Ok(if match_token(context, TokenKind::Exclamation) { Ok(if match_token(context, TokenKind::Exclamation) {
@ -188,7 +205,7 @@ fn parse_factorial(context: &mut Context) -> Result<Expr, String> {
}) })
} }
fn parse_primary(context: &mut Context) -> Result<Expr, String> { fn parse_primary(context: &mut Context) -> Result<Expr, CalcError> {
let expr = match peek(context).kind { let expr = match peek(context).kind {
TokenKind::OpenParenthesis => parse_group(context)?, TokenKind::OpenParenthesis => parse_group(context)?,
TokenKind::Pipe => parse_abs(context)?, TokenKind::Pipe => parse_abs(context)?,
@ -203,7 +220,7 @@ fn parse_primary(context: &mut Context) -> Result<Expr, String> {
} }
} }
fn parse_group(context: &mut Context) -> Result<Expr, String> { fn parse_group(context: &mut Context) -> Result<Expr, CalcError> {
advance(context); advance(context);
let group_expr = Expr::Group(Box::new(parse_expr(context)?)); let group_expr = Expr::Group(Box::new(parse_expr(context)?));
consume(context, TokenKind::ClosedParenthesis)?; consume(context, TokenKind::ClosedParenthesis)?;
@ -211,7 +228,7 @@ fn parse_group(context: &mut Context) -> Result<Expr, String> {
Ok(group_expr) Ok(group_expr)
} }
fn parse_abs(context: &mut Context) -> Result<Expr, String> { fn parse_abs(context: &mut Context) -> Result<Expr, CalcError> {
advance(context); advance(context);
let group_expr = Expr::Group(Box::new(parse_expr(context)?)); let group_expr = Expr::Group(Box::new(parse_expr(context)?));
consume(context, TokenKind::Pipe)?; consume(context, TokenKind::Pipe)?;
@ -219,7 +236,7 @@ fn parse_abs(context: &mut Context) -> Result<Expr, String> {
Ok(Expr::FnCall(String::from("abs"), vec![group_expr])) Ok(Expr::FnCall(String::from("abs"), vec![group_expr]))
} }
fn parse_identifier(context: &mut Context) -> Result<Expr, String> { fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
let identifier = advance(context).clone(); let identifier = advance(context).clone();
// Eg. sqrt64 // Eg. sqrt64
@ -294,12 +311,12 @@ fn advance(context: &mut Context) -> &Token {
previous(context) previous(context)
} }
fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, String> { fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, CalcError> {
if match_token(context, kind) { if match_token(context, kind.clone()) {
return Ok(advance(context)); return Ok(advance(context));
} }
Err("Unexpected token".into()) Err(CalcError::UnexpectedToken(kind))
} }
fn is_at_end(context: &mut Context) -> bool { fn is_at_end(context: &mut Context) -> bool {
@ -313,13 +330,13 @@ mod tests {
use crate::test_helpers::*; use crate::test_helpers::*;
use test_case::test_case; use test_case::test_case;
fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, String> { fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> {
context.tokens = tokens; context.tokens = tokens;
parse_stmt(context) parse_stmt(context)
} }
fn parse(tokens: Vec<Token>) -> Result<Stmt, String> { fn parse(tokens: Vec<Token>) -> Result<Stmt, CalcError> {
let mut context = Context::new(); let mut context = Context::new();
context.tokens = tokens; context.tokens = tokens;

View File

@ -1,5 +1,5 @@
use ansi_term::Colour::Red; use ansi_term::Colour::Red;
use kalk::parser::{self}; use kalk::parser::{self, CalcError, CalcError::*};
pub fn eval(parser: &mut parser::Context, input: &str) { pub fn eval(parser: &mut parser::Context, input: &str) {
match parser::parse(parser, input, 53) { 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 }; let exp = if let Some(exp) = exp_option { exp } else { 0 };
if result.is_infinite() { if result.is_infinite() {
err("Too big to process."); print_err("Too big to process.");
/*} else if result.clone().fract() == 0 { /*} else if result.clone().fract() == 0 {
println!("{}", result.to_integer().unwrap());*/ println!("{}", result.to_integer().unwrap());*/
} else { } else {
@ -37,10 +37,26 @@ pub fn eval(parser: &mut parser::Context, input: &str) {
} }
} }
Ok(None) => print!(""), 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)); println!("{}", Red.paint(msg));
} }