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::parser::CalcError;
use crate::parser::Unit;
#[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 {
TokenKind::Deg => Ok(Unit::Degrees),
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::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<Stmt>) -> Result<Option<Float>, String> {
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<Float>, 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<Float, String> {
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
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<Float, String> {
) -> Result<Float, CalcError> {
context.symbol_table.insert(&identifier, stmt.clone());
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.
}
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)
}
fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, String> {
fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
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<Float, String> {
) -> Result<Float, CalcError> {
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<Float, String> {
fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result<Float, CalcError> {
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<Float, String> {
fn eval_unit_expr(
context: &mut Context,
expr: &Expr,
kind: &TokenKind,
) -> Result<Float, CalcError> {
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<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 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<Float, Strin
let var_decl = context.symbol_table.get(identifier).cloned();
match var_decl {
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) {
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)
}
@ -155,7 +160,7 @@ fn eval_fn_call_expr(
context: &mut Context,
identifier: &str,
expressions: &[Expr],
) -> Result<Float, String> {
) -> Result<Float, CalcError> {
// 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<Option<Float>, String> {
fn interpret(stmt: Stmt) -> Result<Option<Float>, 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")))
);
}

View File

@ -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<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.pos = 0;
@ -54,7 +71,7 @@ pub fn parse(context: &mut Context, input: &str, precision: u32) -> Result<Optio
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) {
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<Stmt, String> {
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 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);
}
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<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();
advance(context); // Equal sign
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)))
}
fn parse_expr(context: &mut Context) -> Result<Expr, String> {
fn parse_expr(context: &mut Context) -> Result<Expr, CalcError> {
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)?;
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)
}
fn parse_factor(context: &mut Context) -> Result<Expr, String> {
fn parse_factor(context: &mut Context) -> Result<Expr, CalcError> {
let mut left = parse_unary(context)?;
while match_token(context, TokenKind::Star)
@ -155,7 +172,7 @@ fn parse_factor(context: &mut Context) -> Result<Expr, String> {
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) {
let op = advance(context).kind.clone();
let expr = Box::new(parse_unary(context)?);
@ -165,7 +182,7 @@ fn parse_unary(context: &mut Context) -> Result<Expr, String> {
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)?;
if match_token(context, TokenKind::Power) {
@ -177,7 +194,7 @@ fn parse_exponent(context: &mut Context) -> Result<Expr, String> {
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)?;
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 {
TokenKind::OpenParenthesis => parse_group(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);
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<Expr, String> {
Ok(group_expr)
}
fn parse_abs(context: &mut Context) -> Result<Expr, String> {
fn parse_abs(context: &mut Context) -> Result<Expr, CalcError> {
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<Expr, String> {
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();
// 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<Token>) -> Result<Stmt, String> {
fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> {
context.tokens = tokens;
parse_stmt(context)
}
fn parse(tokens: Vec<Token>) -> Result<Stmt, String> {
fn parse(tokens: Vec<Token>) -> Result<Stmt, CalcError> {
let mut context = Context::new();
context.tokens = tokens;

View File

@ -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));
}