Added linelocation to all errors

pull/2/head
Mark 2023-07-28 15:01:36 -07:00
parent a489eec482
commit 241bb572a5
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
12 changed files with 361 additions and 187 deletions

View File

@ -59,6 +59,16 @@ fn do_expression(
let Ok(g) = g else {panic!()}; let Ok(g) = g else {panic!()};
// Evaluate expression
#[cfg(debug_assertions)]
RawTerminal::suspend_raw_mode(&stdout).unwrap();
let g_evaluated = evaluate(&g, context);
#[cfg(debug_assertions)]
RawTerminal::activate_raw_mode(&stdout).unwrap();
// Show output
if let Ok(q) = g_evaluated {
// Display parsed string // Display parsed string
write!( write!(
stdout, " {}{}=>{}{} {}\r\n", stdout, " {}{}=>{}{} {}\r\n",
@ -67,16 +77,6 @@ fn do_expression(
g.to_string() g.to_string()
).unwrap(); ).unwrap();
// Evaluate expression
#[cfg(debug_assertions)]
RawTerminal::suspend_raw_mode(&stdout).unwrap();
let g = evaluate(&g, context);
#[cfg(debug_assertions)]
RawTerminal::activate_raw_mode(&stdout).unwrap();
// Show output
if let Ok(q) = g {
write!( write!(
stdout, "\n {}{}={} {}{}\r\n\n", stdout, "\n {}{}={} {}{}\r\n\n",
style::Bold, style::Bold,
@ -88,12 +88,22 @@ fn do_expression(
return Ok(q); return Ok(q);
} else { } else {
match g { match g_evaluated {
Ok(_) => unreachable!(), Ok(_) => unreachable!(),
Err(EvalError::TooBig) => { Err((l, EvalError::TooBig)) => {
write!( write!(
stdout, "\n {}{}Mathematical Error: {}Number too big{}\r\n\n", stdout, "{}{}{}{}{}{}\r\n",
color::Fg(color::Red),
style::Bold,
" ".repeat(l.pos + 4),
"^".repeat(l.len),
color::Fg(color::Reset),
style::Reset,
).unwrap();
write!(
stdout, " {}{}Mathematical Error: {}Number too big{}\r\n\n",
style::Bold, style::Bold,
color::Fg(color::Red), color::Fg(color::Red),
style::Reset, style::Reset,
@ -101,9 +111,19 @@ fn do_expression(
).unwrap(); ).unwrap();
}, },
Err(EvalError::ZeroDivision) => { Err((l, EvalError::ZeroDivision)) => {
write!( write!(
stdout, "\n {}{}Mathematical Error: {}Division by zero{}\r\n\n", stdout, "{}{}{}{}{}{}\r\n",
color::Fg(color::Red),
style::Bold,
" ".repeat(l.pos + 4),
"^".repeat(l.len),
color::Fg(color::Reset),
style::Reset,
).unwrap();
write!(
stdout, " {}{}Mathematical Error: {}Division by zero{}\r\n\n",
style::Bold, style::Bold,
color::Fg(color::Red), color::Fg(color::Red),
style::Reset, style::Reset,
@ -111,9 +131,19 @@ fn do_expression(
).unwrap(); ).unwrap();
}, },
Err(EvalError::BadMath) => { Err((l, EvalError::BadMath)) => {
write!( write!(
stdout, "\n {}{}Mathematical Error: {}Failed to evaluate expression{}\r\n\n", stdout, "{}{}{}{}{}{}\r\n",
color::Fg(color::Red),
style::Bold,
" ".repeat(l.pos + 4),
"^".repeat(l.len),
color::Fg(color::Reset),
style::Reset,
).unwrap();
write!(
stdout, " {}{}Mathematical Error: {}Failed to evaluate expression{}\r\n\n",
style::Bold, style::Bold,
color::Fg(color::Red), color::Fg(color::Red),
style::Reset, style::Reset,
@ -121,9 +151,19 @@ fn do_expression(
).unwrap(); ).unwrap();
}, },
Err(EvalError::IncompatibleUnit) => { Err((l, EvalError::IncompatibleUnit)) => {
write!( write!(
stdout, "\n {}{}Evaluation Error: {}Incompatible units{}\r\n\n", stdout, "{}{}{}{}{}{}\r\n",
color::Fg(color::Red),
style::Bold,
" ".repeat(l.pos + 4),
"^".repeat(l.len),
color::Fg(color::Reset),
style::Reset,
).unwrap();
write!(
stdout, " {}{}Evaluation Error: {}Incompatible units{}\r\n\n",
style::Bold, style::Bold,
color::Fg(color::Red), color::Fg(color::Red),
style::Reset, style::Reset,
@ -131,9 +171,19 @@ fn do_expression(
).unwrap(); ).unwrap();
}, },
Err(EvalError::BadDefineName) => { Err((l, EvalError::BadDefineName)) => {
write!( write!(
stdout, "\n {}{}Evaluation Error: {}Invalid variable name{}\r\n\n", stdout, "{}{}{}{}{}{}\r\n",
color::Fg(color::Red),
style::Bold,
" ".repeat(l.pos + 4),
"^".repeat(l.len),
color::Fg(color::Reset),
style::Reset,
).unwrap();
write!(
stdout, " {}{}Evaluation Error: {}Invalid variable name{}\r\n\n",
style::Bold, style::Bold,
color::Fg(color::Red), color::Fg(color::Red),
style::Reset, style::Reset,

View File

@ -1,13 +1,13 @@
use crate::parser::Expression; use crate::parser::Expression;
use crate::parser::Operator; use crate::parser::Operator;
use crate::context::Context; use crate::context::Context;
use crate::parser::LineLocation;
use super::operator::eval_operator; use super::operator::eval_operator;
use super::function::eval_function; use super::function::eval_function;
use super::EvalError; use super::EvalError;
pub fn evaluate(t: &Expression, context: &mut Context) -> Result<Expression, EvalError> { pub fn evaluate(t: &Expression, context: &mut Context) -> Result<Expression, (LineLocation, EvalError)> {
// Keeps track of our position in the expression tree. // Keeps track of our position in the expression tree.
// For example, the coordinates [0, 2, 1] are interpreted as follows: // For example, the coordinates [0, 2, 1] are interpreted as follows:
@ -40,15 +40,23 @@ pub fn evaluate(t: &Expression, context: &mut Context) -> Result<Expression, Eva
let new = match g { let new = match g {
Expression::Quantity(_) => None, Expression::Quantity(_, _) => None,
Expression::Constant(c) => { Some(evaluate(&c.value(), context).unwrap()) }, Expression::Constant(_, c) => { Some(evaluate(&c.value(), context).unwrap()) },
Expression::Variable(s) => { context.get_variable(&s) }, Expression::Variable(_, s) => { context.get_variable(&s) },
Expression::Operator(Operator::Function(f), v) => { Some(eval_function(&f, &v)?) }, Expression::Operator(_, Operator::Function(_), _) => { Some(eval_function(g)?) },
Expression::Operator(o, v) => { eval_operator(&o, &v, context)? }, Expression::Operator(_, _, _) => { eval_operator(g, context)? },
}; };
if new.is_some() { *g = new.unwrap()} if let Some(mut new) = new {
if let Expression::Constant(_,_) = g {
// Fix constant line location.
// Constant expansion does not change the location of a value,
// but other operations might.
new.set_linelocation(&g.get_linelocation());
}
*g = new;
}
// Move up the tree // Move up the tree
@ -62,7 +70,7 @@ pub fn evaluate(t: &Expression, context: &mut Context) -> Result<Expression, Eva
// Don't evaluate the first argument of a define. // Don't evaluate the first argument of a define.
// This prevents variables from being expanded before a re-assignment. // This prevents variables from being expanded before a re-assignment.
if let Expression::Operator(Operator::Define, _) = g { if let Expression::Operator(_, Operator::Define, _) = g {
*coords.last_mut().unwrap() += 1; *coords.last_mut().unwrap() += 1;
coords.push(0); coords.push(0);
continue; continue;

View File

@ -1,56 +1,58 @@
use std::collections::VecDeque;
use crate::parser::Expression; use crate::parser::Expression;
use crate::parser::Function; use crate::parser::Function;
use crate::parser::Operator;
use crate::parser::LineLocation;
use super::EvalError; use super::EvalError;
pub fn eval_function(g: &Expression) -> Result<Expression, (LineLocation, EvalError)> {
let Expression::Operator(loc, Operator::Function(f), args) = g else {panic!()};
pub fn eval_function(f: &Function, args: &VecDeque<Expression>) -> Result<Expression, EvalError> {
if args.len() != 1 {panic!()}; if args.len() != 1 {panic!()};
let a = &args[0]; let a = &args[0];
let Expression::Quantity(q) = a else {panic!()}; let Expression::Quantity(l, q) = a else {panic!()};
match f { match f {
Function::NoUnit => { return Ok(Expression::Quantity(q.without_unit())); } Function::NoUnit => { return Ok(Expression::Quantity(*loc + *l, q.without_unit())); }
Function::ToBase => { return Ok(Expression::Quantity(q.convert_to_base())); } Function::ToBase => { return Ok(Expression::Quantity(*loc + *l, q.convert_to_base())); }
_ => {} _ => {}
} }
if !q.unitless() { if !q.unitless() {
return Err(EvalError::IncompatibleUnit); return Err((*loc + *l, EvalError::IncompatibleUnit));
} }
match f { match f {
Function::Abs => { return Ok(Expression::Quantity(q.abs())); }, Function::Abs => { return Ok(Expression::Quantity(*loc + *l, q.abs())); },
Function::Floor => { return Ok(Expression::Quantity(q.floor())); }, Function::Floor => { return Ok(Expression::Quantity(*loc + *l, q.floor())); },
Function::Ceil => { return Ok(Expression::Quantity(q.ceil())); }, Function::Ceil => { return Ok(Expression::Quantity(*loc + *l, q.ceil())); },
Function::Round => { return Ok(Expression::Quantity(q.round())); }, Function::Round => { return Ok(Expression::Quantity(*loc + *l, q.round())); },
Function::NaturalLog => { return Ok(Expression::Quantity(q.ln())); }, Function::NaturalLog => { return Ok(Expression::Quantity(*loc + *l, q.ln())); },
Function::TenLog => { return Ok(Expression::Quantity(q.log10())); }, Function::TenLog => { return Ok(Expression::Quantity(*loc + *l, q.log10())); },
Function::Sin => { return Ok(Expression::Quantity(q.sin())); }, Function::Sin => { return Ok(Expression::Quantity(*loc + *l, q.sin())); },
Function::Cos => { return Ok(Expression::Quantity(q.cos())); }, Function::Cos => { return Ok(Expression::Quantity(*loc + *l, q.cos())); },
Function::Tan => { return Ok(Expression::Quantity(q.tan())); }, Function::Tan => { return Ok(Expression::Quantity(*loc + *l, q.tan())); },
Function::Asin => { return Ok(Expression::Quantity(q.asin())); }, Function::Asin => { return Ok(Expression::Quantity(*loc + *l, q.asin())); },
Function::Acos => { return Ok(Expression::Quantity(q.acos())); }, Function::Acos => { return Ok(Expression::Quantity(*loc + *l, q.acos())); },
Function::Atan => { return Ok(Expression::Quantity(q.atan())); }, Function::Atan => { return Ok(Expression::Quantity(*loc + *l, q.atan())); },
Function::Csc => { return Ok(Expression::Quantity(q.csc())); }, Function::Csc => { return Ok(Expression::Quantity(*loc + *l, q.csc())); },
Function::Sec => { return Ok(Expression::Quantity(q.sec())); }, Function::Sec => { return Ok(Expression::Quantity(*loc + *l, q.sec())); },
Function::Cot => { return Ok(Expression::Quantity(q.cot())); }, Function::Cot => { return Ok(Expression::Quantity(*loc + *l, q.cot())); },
Function::Sinh => { return Ok(Expression::Quantity(q.sinh())); }, Function::Sinh => { return Ok(Expression::Quantity(*loc + *l, q.sinh())); },
Function::Cosh => { return Ok(Expression::Quantity(q.cosh())); }, Function::Cosh => { return Ok(Expression::Quantity(*loc + *l, q.cosh())); },
Function::Tanh => { return Ok(Expression::Quantity(q.tanh())); }, Function::Tanh => { return Ok(Expression::Quantity(*loc + *l, q.tanh())); },
Function::Asinh => { return Ok(Expression::Quantity(q.asinh())); }, Function::Asinh => { return Ok(Expression::Quantity(*loc + *l, q.asinh())); },
Function::Acosh => { return Ok(Expression::Quantity(q.acosh())); }, Function::Acosh => { return Ok(Expression::Quantity(*loc + *l, q.acosh())); },
Function::Atanh => { return Ok(Expression::Quantity(q.atanh())); }, Function::Atanh => { return Ok(Expression::Quantity(*loc + *l, q.atanh())); },
Function::Csch => { return Ok(Expression::Quantity(q.csch())); }, Function::Csch => { return Ok(Expression::Quantity(*loc + *l, q.csch())); },
Function::Sech => { return Ok(Expression::Quantity(q.sech())); }, Function::Sech => { return Ok(Expression::Quantity(*loc + *l, q.sech())); },
Function::Coth => { return Ok(Expression::Quantity(q.coth())); }, Function::Coth => { return Ok(Expression::Quantity(*loc + *l, q.coth())); },
Function::ToBase Function::ToBase
| Function::NoUnit | Function::NoUnit

View File

@ -1,12 +1,15 @@
use std::collections::VecDeque; use crate::parser::LineLocation;
use crate::quantity::Quantity; use crate::quantity::Quantity;
use crate::parser::Operator; use crate::parser::Operator;
use crate::parser::Expression; use crate::parser::Expression;
use super::EvalError; use super::EvalError;
use crate::context::Context; use crate::context::Context;
pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut Context) -> Result<Option<Expression>, EvalError> {
pub fn eval_operator(g: &Expression, context: &mut Context) -> Result<Option<Expression>, (LineLocation, EvalError)> {
let Expression::Operator(op_loc, op, args) = g else {panic!()};
match op { match op {
Operator::Function(_) => unreachable!("Functions are handled seperately."), Operator::Function(_) => unreachable!("Functions are handled seperately."),
@ -14,42 +17,61 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
if args.len() != 2 { panic!() }; if args.len() != 2 { panic!() };
let b = &args[1]; let b = &args[1];
if let Expression::Variable(s) = &args[0] { if let Expression::Variable(l, s) = &args[0] {
let r = context.push_var(s.clone(), b.clone()); let r = context.push_var(s.clone(), b.clone());
if r.is_err() { return Err(EvalError::BadDefineName); } if r.is_err() { return Err((*l, EvalError::BadDefineName)); }
return Ok(Some(b.clone())); return Ok(Some(b.clone()));
} else { return Err(EvalError::BadDefineName); } } else { return Err((args[0].get_linelocation(), EvalError::BadDefineName)); }
}, },
Operator::Negative => { Operator::Negative => {
if args.len() != 1 { panic!() }; if args.len() != 1 { panic!() };
let args = &args[0]; let args = &args[0];
if let Expression::Quantity(v) = args { if let Expression::Quantity(l, v) = args {
return Ok(Some(Expression::Quantity(-v.clone()))); return Ok(Some(Expression::Quantity(*l, -v.clone())));
} else { return Ok(None); } } else { return Ok(None); }
}, },
Operator::Add => { Operator::Add => {
let mut sum: Quantity; let mut sum: Quantity;
if let Expression::Quantity(s) = &args[0] { let mut loc: LineLocation;
if let Expression::Quantity(l, s) = &args[0] {
sum = s.clone(); sum = s.clone();
loc = l.clone();
} else { return Ok(None); }; } else { return Ok(None); };
// Flag that is set to true if we find incompatible units.
// We don't stop right away because we need to add all linelocations
// to show a pretty error.
let mut incompatible_units = false;
let mut i: usize = 1; let mut i: usize = 1;
while i < args.len() { while i < args.len() {
let j = &args[i]; let j = &args[i];
if let Expression::Quantity(v) = j { if let Expression::Quantity(l, v) = j {
if !sum.unit.compatible_with(&v.unit) { if !sum.unit.compatible_with(&v.unit) {
return Err(EvalError::IncompatibleUnit); incompatible_units = true;
} }
sum += v.clone(); if !incompatible_units { sum += v.clone(); }
} else { return Ok(None); } loc += *l;
} else {
if incompatible_units {
return Err((loc + *op_loc, EvalError::IncompatibleUnit));
}
return Ok(None);
}
i += 1; i += 1;
} }
return Ok(Some(Expression::Quantity(sum)));
if incompatible_units {
return Err((loc + *op_loc, EvalError::IncompatibleUnit));
}
return Ok(Some(Expression::Quantity(loc + *op_loc, sum)));
}, },
Operator::Subtract => { Operator::Subtract => {
@ -57,9 +79,9 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
let a = &args[0]; let a = &args[0];
let b = &args[1]; let b = &args[1];
if let Expression::Quantity(a) = a { if let Expression::Quantity(la, a) = a {
if let Expression::Quantity(b) = b { if let Expression::Quantity(lb, b) = b {
return Ok(Some(Expression::Quantity(a.clone() - b.clone()))); return Ok(Some(Expression::Quantity(*la + *lb, a.clone() - b.clone())));
} }
} }
@ -73,10 +95,10 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
let a = &args[0]; let a = &args[0];
let b = &args[1]; let b = &args[1];
if let Expression::Quantity(a) = a { if let Expression::Quantity(la, a) = a {
if let Expression::Quantity(b) = b { if let Expression::Quantity(lb, b) = b {
if b.is_zero() { return Err(EvalError::ZeroDivision); } if b.is_zero() { return Err((*la + *lb, EvalError::ZeroDivision)); }
return Ok(Some(Expression::Quantity(a.clone() / b.clone()))); return Ok(Some(Expression::Quantity(*la + *lb, a.clone() / b.clone())));
} }
} }
@ -85,14 +107,23 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
Operator::ImplicitMultiply | Operator::ImplicitMultiply |
Operator::Multiply => { Operator::Multiply => {
let mut prod = Quantity::new_rational(1f64).unwrap(); let mut prod: Quantity;
for i in args.iter() { let mut loc: LineLocation;
let j = i; if let Expression::Quantity(l, s) = &args[0] {
if let Expression::Quantity(v) = j { prod = s.clone();
loc = l.clone();
} else { return Ok(None); };
let mut i: usize = 1;
while i < args.len() {
let j = &args[i];
if let Expression::Quantity(l, v) = j {
prod *= v.clone(); prod *= v.clone();
loc += *l;
} else { return Ok(None); } } else { return Ok(None); }
i += 1;
} }
return Ok(Some(Expression::Quantity(prod))); return Ok(Some(Expression::Quantity(loc + *op_loc, prod)));
}, },
Operator::ModuloLong Operator::ModuloLong
@ -101,18 +132,18 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
let a = &args[0]; let a = &args[0];
let b = &args[1]; let b = &args[1];
if let Expression::Quantity(va) = a { if let Expression::Quantity(la, va) = a {
if let Expression::Quantity(vb) = b { if let Expression::Quantity(lb, vb) = b {
if !(va.unitless() && vb.unitless()) { if !(va.unitless() && vb.unitless()) {
return Err(EvalError::IncompatibleUnit); return Err((*la + *lb + *op_loc, EvalError::IncompatibleUnit));
} }
if vb <= &Quantity::new_rational(1f64).unwrap() { return Err(EvalError::BadMath); } if vb <= &Quantity::new_rational(1f64).unwrap() { return Err((*la + *lb + *op_loc, EvalError::BadMath)); }
if va.fract() != Quantity::new_rational(0f64).unwrap() { return Err(EvalError::BadMath); } if va.fract() != Quantity::new_rational(0f64).unwrap() { return Err((*la + *lb + *op_loc, EvalError::BadMath)); }
if vb.fract() != Quantity::new_rational(0f64).unwrap() { return Err(EvalError::BadMath); } if vb.fract() != Quantity::new_rational(0f64).unwrap() { return Err((*la + *lb + *op_loc, EvalError::BadMath)); }
return Ok(Some(Expression::Quantity(va.clone() % vb.clone()))); return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, va.clone() % vb.clone())));
} else { return Ok(None); } } else { return Ok(None); }
} else { return Ok(None); } } else { return Ok(None); }
}, },
@ -122,13 +153,13 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
let a = &args[0]; let a = &args[0];
let b = &args[1]; let b = &args[1];
if let Expression::Quantity(va) = a { if let Expression::Quantity(la, va) = a {
if let Expression::Quantity(vb) = b { if let Expression::Quantity(lb, vb) = b {
let n = va.clone().convert_to(vb.clone()); let n = va.clone().convert_to(vb.clone());
if n.is_none() { if n.is_none() {
return Err(EvalError::IncompatibleUnit); return Err((*la + *lb + *op_loc, EvalError::IncompatibleUnit));
} }
return Ok(Some(Expression::Quantity(n.unwrap()))); return Ok(Some(Expression::Quantity(*la + *lb, n.unwrap())));
} else { return Ok(None); } } else { return Ok(None); }
} else { return Ok(None); } } else { return Ok(None); }
}, },
@ -138,11 +169,11 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
if args.len() != 1 { panic!() } if args.len() != 1 { panic!() }
let a = &args[0]; let a = &args[0];
if let Expression::Quantity(va) = a { if let Expression::Quantity(l, v) = a {
if va.is_negative() { return Err(EvalError::BadMath); } if v.is_negative() { return Err((*l + *op_loc, EvalError::BadMath)); }
let p = va.pow(Quantity::new_rational_from_string("0.5").unwrap()); let p = v.pow(Quantity::new_rational_from_string("0.5").unwrap());
if p.is_nan() {return Err(EvalError::BadMath);} if p.is_nan() {return Err((*l + *op_loc, EvalError::BadMath));}
return Ok(Some(Expression::Quantity(p))); return Ok(Some(Expression::Quantity(*l, p)));
} else { return Ok(None); } } else { return Ok(None); }
}, },
@ -151,20 +182,20 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
let a = &args[0]; let a = &args[0];
let b = &args[1]; let b = &args[1];
if let Expression::Quantity(va) = a { if let Expression::Quantity(la, va) = a {
if let Expression::Quantity(vb) = b { if let Expression::Quantity(lb, vb) = b {
if !vb.unitless() { if !vb.unitless() {
return Err(EvalError::IncompatibleUnit); return Err((*lb, EvalError::IncompatibleUnit));
} }
if va.is_zero() && vb.is_negative() { if va.is_zero() && vb.is_negative() {
return Err(EvalError::ZeroDivision); return Err((*la + *lb + *op_loc, EvalError::ZeroDivision));
} }
let p = va.pow(vb.clone()); let p = va.pow(vb.clone());
if p.is_nan() {return Err(EvalError::BadMath);} if p.is_nan() {return Err((*la + *lb + *op_loc, EvalError::BadMath));}
return Ok(Some(Expression::Quantity(p))); return Ok(Some(Expression::Quantity(*la + *lb + *op_loc, p)));
} else { return Ok(None); } } else { return Ok(None); }
} else { return Ok(None); } } else { return Ok(None); }
}, },
@ -173,14 +204,14 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
if args.len() != 1 {panic!()}; if args.len() != 1 {panic!()};
let args = &args[0]; let args = &args[0];
if let Expression::Quantity(v) = args { if let Expression::Quantity(l, v) = args {
if !v.unitless() { if !v.unitless() {
return Err(EvalError::IncompatibleUnit); return Err((*l + *op_loc, EvalError::IncompatibleUnit));
} }
if !v.fract().is_zero() { return Err(EvalError::BadMath); } if !v.fract().is_zero() { return Err((*l + *op_loc, EvalError::BadMath)); }
if v > &Quantity::new_rational(50_000f64).unwrap() { return Err(EvalError::TooBig); } if v > &Quantity::new_rational(50_000f64).unwrap() { return Err((*l + *op_loc, EvalError::TooBig)); }
let mut prod = Quantity::new_rational(1f64).unwrap(); let mut prod = Quantity::new_rational(1f64).unwrap();
let mut u = v.clone(); let mut u = v.clone();
@ -189,7 +220,7 @@ pub fn eval_operator(op: &Operator, args: &VecDeque<Expression>, context: &mut C
u = u - Quantity::new_rational(1f64).unwrap(); u = u - Quantity::new_rational(1f64).unwrap();
} }
return Ok(Some(Expression::Quantity(prod))); return Ok(Some(Expression::Quantity(*l + *op_loc, prod)));
} else { return Ok(None); } } else { return Ok(None); }
} }
}; };

View File

@ -3,24 +3,26 @@ use crate::quantity::Quantity;
use super::Operator; use super::Operator;
use super::Constant; use super::Constant;
use super::super::LineLocation;
/// Expressions represent logical objects in an expession. /// Expressions represent logical objects in an expession.
#[derive(Debug)] #[derive(Debug)]
#[derive(Clone)] #[derive(Clone)]
pub enum Expression { pub enum Expression {
Variable(String), Variable(LineLocation, String),
Quantity(Quantity), Quantity(LineLocation, Quantity),
Constant(Constant), Constant(LineLocation, Constant),
Operator(Operator, VecDeque<Expression>), Operator(LineLocation, Operator, VecDeque<Expression>),
} }
impl ToString for Expression { impl ToString for Expression {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
Expression::Quantity(v) => v.to_string(), Expression::Quantity(_, v) => v.to_string(),
Expression::Constant(c) => c.to_string(), Expression::Constant(_, c) => c.to_string(),
Expression::Variable(s) => s.clone(), Expression::Variable(_, s) => s.clone(),
Expression::Operator(o,a) => o.print(a) Expression::Operator(_, o,a) => o.print(a)
} }
} }
} }
@ -30,16 +32,16 @@ impl Expression {
// This sometimes leads to different--usually more verbose--behavior. // This sometimes leads to different--usually more verbose--behavior.
pub fn to_string_outer(&self) -> String { pub fn to_string_outer(&self) -> String {
match self { match self {
Expression::Quantity(v) => v.to_string_outer(), Expression::Quantity(_, v) => v.to_string_outer(),
Expression::Constant(c) => c.to_string(), Expression::Constant(_, c) => c.to_string(),
Expression::Variable(s) => s.clone(), Expression::Variable(_, s) => s.clone(),
Expression::Operator(o,a) => o.print(a) Expression::Operator(_, o,a) => o.print(a)
} }
} }
pub fn is_quantity(&self) -> bool { pub fn is_quantity(&self) -> bool {
match self { match self {
Expression::Quantity(_) => true, Expression::Quantity(_,_) => true,
_ => false _ => false
} }
} }
@ -47,7 +49,7 @@ impl Expression {
#[inline(always)] #[inline(always)]
pub fn get_args_mut(&mut self) -> Option<&mut VecDeque<Expression>> { pub fn get_args_mut(&mut self) -> Option<&mut VecDeque<Expression>> {
match self { match self {
Expression::Operator(_, ref mut a) => Some(a), Expression::Operator(_, _, ref mut a) => Some(a),
_ => None _ => None
} }
} }
@ -55,7 +57,7 @@ impl Expression {
#[inline(always)] #[inline(always)]
pub fn get_args(&self) -> Option<&VecDeque<Expression>> { pub fn get_args(&self) -> Option<&VecDeque<Expression>> {
match self { match self {
Expression::Operator(_, ref a) => Some(a), Expression::Operator(_, _, ref a) => Some(a),
_ => None _ => None
} }
} }
@ -83,4 +85,23 @@ impl Expression {
} }
return Some(g); return Some(g);
} }
pub fn get_linelocation(&self) -> LineLocation {
match self {
Expression::Quantity(l, _)
| Expression::Constant(l, _)
| Expression::Variable(l, _)
| Expression::Operator(l, _,_)
=> { l.clone() }
}
}
pub fn set_linelocation(&mut self, loc: &LineLocation) {
match self {
Expression::Quantity(l, _) => { *l = *loc },
Expression::Constant(l, _) => { *l = *loc },
Expression::Variable(l, _) => { *l = *loc },
Expression::Operator(l, _,_) => { *l = *loc },
}
}
} }

View File

@ -110,7 +110,7 @@ impl Operator {
#[inline(always)] #[inline(always)]
fn add_parens_to_arg(&self, arg: &Expression) -> String { fn add_parens_to_arg(&self, arg: &Expression) -> String {
let mut astr: String = arg.to_string(); let mut astr: String = arg.to_string();
if let Expression::Operator(o,_) = arg { if let Expression::Operator(_, o,_) = arg {
if o < self { if o < self {
astr = format!("({})", astr); astr = format!("({})", astr);
} }
@ -121,7 +121,7 @@ impl Operator {
#[inline(always)] #[inline(always)]
fn add_parens_to_arg_strict(&self, arg: &Expression) -> String { fn add_parens_to_arg_strict(&self, arg: &Expression) -> String {
let mut astr: String = arg.to_string(); let mut astr: String = arg.to_string();
if let Expression::Operator(o,_) = arg { if let Expression::Operator(_, o,_) = arg {
if o <= self { if o <= self {
astr = format!("({})", astr); astr = format!("({})", astr);
} }
@ -220,15 +220,15 @@ impl Operator {
// multiplied by a unit (like 10 m) // multiplied by a unit (like 10 m)
// Times sign should stay in all other cases. // Times sign should stay in all other cases.
let no_times = { let no_times = {
if let Expression::Quantity(p) = a { if let Expression::Quantity(_, p) = a {
if let Expression::Quantity(q) = b { if let Expression::Quantity(_, q) = b {
p.unitless() && !q.unitless() p.unitless() && !q.unitless()
} else {false} } else {false}
} else {false} } else {false}
}; };
if no_times { if no_times {
let Expression::Quantity(u) = b else {panic!()}; let Expression::Quantity(_, u) = b else {panic!()};
if u.unit.no_space() { if u.unit.no_space() {
return format!("{}{}", return format!("{}{}",
self.add_parens_to_arg_strict(a), self.add_parens_to_arg_strict(a),
@ -252,7 +252,7 @@ impl Operator {
let a = &args[0]; let a = &args[0];
let b = &args[1]; let b = &args[1];
if let Expression::Quantity(q) = a { if let Expression::Quantity(_, q) = a {
if q.is_one() { if q.is_one() {
return format!("{}⁻¹", return format!("{}⁻¹",
self.add_parens_to_arg_strict(b) self.add_parens_to_arg_strict(b)

View File

@ -0,0 +1,59 @@
use std::cmp;
use std::ops::Add;
use std::ops::AddAssign;
use std::cmp::Ordering;
/// Specifies the location of a token in an input string.
#[derive(Debug)]
#[derive(Copy, Clone)]
pub struct LineLocation {
pub pos: usize,
pub len: usize
}
impl LineLocation {
pub fn zero(&self) -> bool {
return self.pos == 0 && self.len == 0
}
}
impl PartialEq for LineLocation {
fn eq(&self, other: &Self) -> bool {
self.pos == other.pos &&
self.len ==other.len
}
}
impl PartialOrd for LineLocation {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return self.pos.partial_cmp(&other.pos);
}
}
impl Add for LineLocation {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
if self.zero() { return other; }
if other.zero() { return self; }
let start = cmp::min(self.pos, other.pos);
let end = cmp::max(self.pos+self.len-1, other.pos+other.len-1);
return LineLocation{
pos: start,
len: end - start+1
};
}
}
impl AddAssign for LineLocation where {
fn add_assign(&mut self, other: Self) {
if self.zero() {*self = other}
if other.zero() { return }
let start = cmp::min(self.pos, other.pos);
let end = cmp::max(self.pos+self.len-1, other.pos+other.len-1);
self.pos = start;
self.len = end - start + 1;
}
}

View File

@ -3,11 +3,11 @@ mod stage;
mod token; mod token;
mod parsererror; mod parsererror;
mod expression; mod expression;
mod linelocation;
use self::{ use self::{
token::Token, token::Token,
parsererror::ParserError, parsererror::ParserError,
parsererror::LineLocation
}; };
pub use self::{ pub use self::{
@ -15,6 +15,7 @@ pub use self::{
expression::Constant, expression::Constant,
expression::Operator, expression::Operator,
expression::Function, expression::Function,
linelocation::LineLocation,
}; };
use crate::context::Context; use crate::context::Context;
@ -27,7 +28,6 @@ pub fn parse(
let expressions = stage::tokenize(s); let expressions = stage::tokenize(s);
let (_, expressions) = stage::find_subs(expressions); let (_, expressions) = stage::find_subs(expressions);
let g = stage::groupify(expressions)?; let g = stage::groupify(expressions)?;
let g = stage::treeify(g, context)?; let g = stage::treeify(g, context)?;
return Ok(g); return Ok(g);
@ -50,11 +50,12 @@ pub fn substitute(
let l = s.chars().count(); let l = s.chars().count();
let expressions = stage::tokenize(s); let expressions = stage::tokenize(s);
let (subs, _) = stage::find_subs(expressions); let (mut subs, _) = stage::find_subs(expressions);
let mut new_c = l - c; let mut new_c = l - c;
for r in subs.iter() { while subs.len() > 0 {
// find_subs gives substitutions in reverse order. let r = subs.pop_back().unwrap();
// Apply substitutions in reverse order
if { // Don't substitute if our cursor is inside the substitution if { // Don't substitute if our cursor is inside the substitution
c >= r.0.pos && c >= r.0.pos &&

View File

@ -1,11 +1,3 @@
/// Specifies the location of a token in an input string.
#[derive(Debug)]
#[derive(Copy, Clone)]
pub struct LineLocation {
pub pos: usize,
pub len: usize
}
/// Types of parser errors. /// Types of parser errors.
/// If we cannot parse a string, one of these is returned. /// If we cannot parse a string, one of these is returned.
#[derive(Debug)] #[derive(Debug)]

View File

@ -194,9 +194,12 @@ pub fn groupify(
Token, Token,
(LineLocation, ParserError) (LineLocation, ParserError)
> { > {
let last_linelocation = g.back().unwrap().get_line_location().clone();
// Vector of grouping levels // Vector of grouping levels
let mut levels: Vec<(LineLocation, VecDeque<Token>)> = Vec::with_capacity(8); let mut levels: Vec<(LineLocation, VecDeque<Token>)> = Vec::with_capacity(8);
levels.push((LineLocation{pos: 0, len: 0}, VecDeque::with_capacity(8))); levels.push((LineLocation{pos: 0, len: last_linelocation.pos + last_linelocation.len}, VecDeque::with_capacity(8)));
// Makes sure parenthesis are matched // Makes sure parenthesis are matched
let mut i_level = 0; let mut i_level = 0;
@ -212,10 +215,7 @@ pub fn groupify(
}, },
Token::GroupEnd(l) => { Token::GroupEnd(l) => {
let l = LineLocation { let l = *l_now + l;
pos: l_now.pos,
len: l.len + l.pos - l_now.pos
};
if i_level == 0 { return Err((l, ParserError::ExtraCloseParen)) } if i_level == 0 { return Err((l, ParserError::ExtraCloseParen)) }
if v_now.len() == 0 { return Err((l, ParserError::EmptyGroup)) } if v_now.len() == 0 { return Err((l, ParserError::EmptyGroup)) }
@ -259,5 +259,5 @@ pub fn groupify(
let (_, mut v) = levels.pop().unwrap(); let (_, mut v) = levels.pop().unwrap();
lookback(&mut v)?; lookback(&mut v)?;
return Ok(Token::Group(LineLocation{pos:0, len:0}, v)); return Ok(Token::Group(LineLocation{pos:0, len:last_linelocation.pos + last_linelocation.len}, v));
} }

View File

@ -85,11 +85,8 @@ fn treeify_binary(
} { } {
return Ok(false); return Ok(false);
} else { } else {
let tl = *this.get_line_location(); let tl = *this.get_line_location() + *l;
return Err(( return Err((tl, ParserError::Syntax));
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
ParserError::Syntax
));
} }
} }
@ -126,22 +123,33 @@ fn treeify_binary(
let left_pre = g_inner.remove(i-1).unwrap(); let left_pre = g_inner.remove(i-1).unwrap();
let this_pre = g_inner.remove(i-1).unwrap(); let this_pre = g_inner.remove(i-1).unwrap();
let right_pre = g_inner.remove(i-1).unwrap(); let right_pre = g_inner.remove(i-1).unwrap();
let left: Expression; let right: Expression; let mut left: Expression; let mut right: Expression;
if let Token::Group(_, _) = right_pre { right = treeify(right_pre, context)?; } else {right = right_pre.to_expression(context)?;} if let Token::Group(l, _) = right_pre {
if let Token::Group(_, _) = left_pre { left = treeify(left_pre, context)?; } else {left = left_pre.to_expression(context)?;} right = treeify(right_pre, context)?;
right.set_linelocation(&(right.get_linelocation() + l));
} else {
right = right_pre.to_expression(context)?;
}
let o = { if let Token::Group(l, _) = left_pre {
let Token::Operator(_, s) = this_pre else {panic!()}; left = treeify(left_pre, context)?;
left.set_linelocation(&(left.get_linelocation() + l));
} else {
left = left_pre.to_expression(context)?;
}
let (l, o) = {
let Token::Operator(l, s) = this_pre else {panic!()};
let o = Operator::from_string(&s); let o = Operator::from_string(&s);
if o.is_none() { panic!() } if o.is_none() { panic!() }
o.unwrap() (l, o.unwrap())
}; };
let mut new_token_args: VecDeque<Expression> = VecDeque::with_capacity(2); let mut new_token_args: VecDeque<Expression> = VecDeque::with_capacity(2);
new_token_args.push_back(left); new_token_args.push_back(left);
new_token_args.push_back(right); new_token_args.push_back(right);
g_inner.insert(i-1, Token::Container(Expression::Operator(o, new_token_args))); g_inner.insert(i-1, Token::Container(Expression::Operator(l, o, new_token_args)));
return Ok(true); return Ok(true);
} else { } else {
@ -204,12 +212,8 @@ fn treeify_unary(
} }
if let Token::Operator(l, _) = next { if let Token::Operator(l, _) = next {
let tl = *this.get_line_location(); let tl = *this.get_line_location() + *l;
return Err(( return Err((tl, ParserError::Syntax));
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
ParserError::Syntax
));
} else { } else {
// This operator // This operator
@ -239,28 +243,34 @@ fn treeify_unary(
if next_op.is_none() || this_op > next_op.unwrap() { if next_op.is_none() || this_op > next_op.unwrap() {
let this_pre = g_inner.remove(i).unwrap(); let this_pre = g_inner.remove(i).unwrap();
let next_pre: Token; let next: Expression; let next_pre: Token; let mut next: Expression;
if left_associative { if left_associative {
next_pre = g_inner.remove(i-1).unwrap(); next_pre = g_inner.remove(i-1).unwrap();
} else { } else {
next_pre = g_inner.remove(i).unwrap(); next_pre = g_inner.remove(i).unwrap();
} }
if let Token::Group(_, _) = next_pre { next = treeify(next_pre, context)?; } else { next = next_pre.to_expression(context)? } if let Token::Group(l, _) = next_pre {
next = treeify(next_pre, context)?;
next.set_linelocation(&(next.get_linelocation() + l));
} else {
next = next_pre.to_expression(context)?;
}
let o = {
let Token::Operator(_, s) = this_pre else {panic!()}; let (l, o) = {
let Token::Operator(l, s) = this_pre else {panic!()};
let o = Operator::from_string(&s); let o = Operator::from_string(&s);
if o.is_none() { panic!() } if o.is_none() { panic!() }
o.unwrap() (l, o.unwrap())
}; };
let mut new_token_args: VecDeque<Expression> = VecDeque::with_capacity(3); let mut new_token_args: VecDeque<Expression> = VecDeque::with_capacity(3);
new_token_args.push_back(next); new_token_args.push_back(next);
if left_associative { if left_associative {
g_inner.insert(i-1, Token::Container(Expression::Operator(o, new_token_args))); g_inner.insert(i-1, Token::Container(Expression::Operator(l, o, new_token_args)));
} else { } else {
g_inner.insert(i, Token::Container(Expression::Operator(o, new_token_args))); g_inner.insert(i, Token::Container(Expression::Operator(l, o, new_token_args)));
} }
return Ok(true); return Ok(true);

View File

@ -74,20 +74,20 @@ impl Token {
return Err((l, ParserError::BadNumber)) return Err((l, ParserError::BadNumber))
} }
return Ok(Expression::Quantity(r.unwrap())); return Ok(Expression::Quantity(l, r.unwrap()));
}, },
Token::Word(_l, s) => { Token::Word(l, s) => {
let c = Constant::from_string(&s); let c = Constant::from_string(&s);
if c.is_some() { return Ok(Expression::Constant(c.unwrap())); } if c.is_some() { return Ok(Expression::Constant(l, c.unwrap())); }
let c = Unit::from_string(&s); let c = Unit::from_string(&s);
if c.is_some() { return Ok(Expression::Quantity(c.unwrap())); } if c.is_some() { return Ok(Expression::Quantity(l, c.unwrap())); }
let c = context.get_variable(&s); let c = context.get_variable(&s);
if c.is_some() { return Ok(Expression::Variable(s)); } if c.is_some() { return Ok(Expression::Variable(l, s)); }
return Ok(Expression::Variable(s)); return Ok(Expression::Variable(l, s));
} }
Token::Container(v) => { return Ok(v); } Token::Container(v) => { return Ok(v); }