mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-01-21 20:48:34 +01:00
Created CalcError enum and centralised error formatting into once place.
This commit is contained in:
parent
4a02134b22
commit
d0536d6bd6
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user