mirror of
https://github.com/rm-dr/daisy
synced 2025-08-02 01:34:50 -07:00
Added linelocation to all errors
This commit is contained in:
@ -3,24 +3,26 @@ use crate::quantity::Quantity;
|
||||
|
||||
use super::Operator;
|
||||
use super::Constant;
|
||||
use super::super::LineLocation;
|
||||
|
||||
|
||||
/// Expressions represent logical objects in an expession.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
pub enum Expression {
|
||||
Variable(String),
|
||||
Quantity(Quantity),
|
||||
Constant(Constant),
|
||||
Operator(Operator, VecDeque<Expression>),
|
||||
Variable(LineLocation, String),
|
||||
Quantity(LineLocation, Quantity),
|
||||
Constant(LineLocation, Constant),
|
||||
Operator(LineLocation, Operator, VecDeque<Expression>),
|
||||
}
|
||||
|
||||
impl ToString for Expression {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Expression::Quantity(v) => v.to_string(),
|
||||
Expression::Constant(c) => c.to_string(),
|
||||
Expression::Variable(s) => s.clone(),
|
||||
Expression::Operator(o,a) => o.print(a)
|
||||
Expression::Quantity(_, v) => v.to_string(),
|
||||
Expression::Constant(_, c) => c.to_string(),
|
||||
Expression::Variable(_, s) => s.clone(),
|
||||
Expression::Operator(_, o,a) => o.print(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,16 +32,16 @@ impl Expression {
|
||||
// This sometimes leads to different--usually more verbose--behavior.
|
||||
pub fn to_string_outer(&self) -> String {
|
||||
match self {
|
||||
Expression::Quantity(v) => v.to_string_outer(),
|
||||
Expression::Constant(c) => c.to_string(),
|
||||
Expression::Variable(s) => s.clone(),
|
||||
Expression::Operator(o,a) => o.print(a)
|
||||
Expression::Quantity(_, v) => v.to_string_outer(),
|
||||
Expression::Constant(_, c) => c.to_string(),
|
||||
Expression::Variable(_, s) => s.clone(),
|
||||
Expression::Operator(_, o,a) => o.print(a)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_quantity(&self) -> bool {
|
||||
match self {
|
||||
Expression::Quantity(_) => true,
|
||||
Expression::Quantity(_,_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
@ -47,7 +49,7 @@ impl Expression {
|
||||
#[inline(always)]
|
||||
pub fn get_args_mut(&mut self) -> Option<&mut VecDeque<Expression>> {
|
||||
match self {
|
||||
Expression::Operator(_, ref mut a) => Some(a),
|
||||
Expression::Operator(_, _, ref mut a) => Some(a),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@ -55,7 +57,7 @@ impl Expression {
|
||||
#[inline(always)]
|
||||
pub fn get_args(&self) -> Option<&VecDeque<Expression>> {
|
||||
match self {
|
||||
Expression::Operator(_, ref a) => Some(a),
|
||||
Expression::Operator(_, _, ref a) => Some(a),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@ -83,4 +85,23 @@ impl Expression {
|
||||
}
|
||||
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 },
|
||||
}
|
||||
}
|
||||
}
|
@ -110,7 +110,7 @@ impl Operator {
|
||||
#[inline(always)]
|
||||
fn add_parens_to_arg(&self, arg: &Expression) -> String {
|
||||
let mut astr: String = arg.to_string();
|
||||
if let Expression::Operator(o,_) = arg {
|
||||
if let Expression::Operator(_, o,_) = arg {
|
||||
if o < self {
|
||||
astr = format!("({})", astr);
|
||||
}
|
||||
@ -121,7 +121,7 @@ impl Operator {
|
||||
#[inline(always)]
|
||||
fn add_parens_to_arg_strict(&self, arg: &Expression) -> String {
|
||||
let mut astr: String = arg.to_string();
|
||||
if let Expression::Operator(o,_) = arg {
|
||||
if let Expression::Operator(_, o,_) = arg {
|
||||
if o <= self {
|
||||
astr = format!("({})", astr);
|
||||
}
|
||||
@ -220,15 +220,15 @@ impl Operator {
|
||||
// multiplied by a unit (like 10 m)
|
||||
// Times sign should stay in all other cases.
|
||||
let no_times = {
|
||||
if let Expression::Quantity(p) = a {
|
||||
if let Expression::Quantity(q) = b {
|
||||
if let Expression::Quantity(_, p) = a {
|
||||
if let Expression::Quantity(_, q) = b {
|
||||
p.unitless() && !q.unitless()
|
||||
} else {false}
|
||||
} else {false}
|
||||
};
|
||||
|
||||
if no_times {
|
||||
let Expression::Quantity(u) = b else {panic!()};
|
||||
let Expression::Quantity(_, u) = b else {panic!()};
|
||||
if u.unit.no_space() {
|
||||
return format!("{}{}",
|
||||
self.add_parens_to_arg_strict(a),
|
||||
@ -252,7 +252,7 @@ impl Operator {
|
||||
let a = &args[0];
|
||||
let b = &args[1];
|
||||
|
||||
if let Expression::Quantity(q) = a {
|
||||
if let Expression::Quantity(_, q) = a {
|
||||
if q.is_one() {
|
||||
return format!("{}⁻¹",
|
||||
self.add_parens_to_arg_strict(b)
|
||||
|
59
src/parser/linelocation.rs
Normal file
59
src/parser/linelocation.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ mod stage;
|
||||
mod token;
|
||||
mod parsererror;
|
||||
mod expression;
|
||||
mod linelocation;
|
||||
|
||||
use self::{
|
||||
token::Token,
|
||||
parsererror::ParserError,
|
||||
parsererror::LineLocation
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
@ -15,6 +15,7 @@ pub use self::{
|
||||
expression::Constant,
|
||||
expression::Operator,
|
||||
expression::Function,
|
||||
linelocation::LineLocation,
|
||||
};
|
||||
|
||||
use crate::context::Context;
|
||||
@ -27,7 +28,6 @@ pub fn parse(
|
||||
let expressions = stage::tokenize(s);
|
||||
let (_, expressions) = stage::find_subs(expressions);
|
||||
let g = stage::groupify(expressions)?;
|
||||
|
||||
let g = stage::treeify(g, context)?;
|
||||
|
||||
return Ok(g);
|
||||
@ -50,11 +50,12 @@ pub fn substitute(
|
||||
|
||||
let l = s.chars().count();
|
||||
let expressions = stage::tokenize(s);
|
||||
let (subs, _) = stage::find_subs(expressions);
|
||||
let (mut subs, _) = stage::find_subs(expressions);
|
||||
let mut new_c = l - c;
|
||||
|
||||
for r in subs.iter() {
|
||||
// find_subs gives substitutions in reverse order.
|
||||
while subs.len() > 0 {
|
||||
let r = subs.pop_back().unwrap();
|
||||
// Apply substitutions in reverse order
|
||||
|
||||
if { // Don't substitute if our cursor is inside the substitution
|
||||
c >= r.0.pos &&
|
||||
|
@ -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.
|
||||
/// If we cannot parse a string, one of these is returned.
|
||||
#[derive(Debug)]
|
||||
|
@ -194,9 +194,12 @@ pub fn groupify(
|
||||
Token,
|
||||
(LineLocation, ParserError)
|
||||
> {
|
||||
|
||||
let last_linelocation = g.back().unwrap().get_line_location().clone();
|
||||
|
||||
// Vector of grouping levels
|
||||
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
|
||||
let mut i_level = 0;
|
||||
@ -212,10 +215,7 @@ pub fn groupify(
|
||||
},
|
||||
|
||||
Token::GroupEnd(l) => {
|
||||
let l = LineLocation {
|
||||
pos: l_now.pos,
|
||||
len: l.len + l.pos - l_now.pos
|
||||
};
|
||||
let l = *l_now + l;
|
||||
|
||||
if i_level == 0 { return Err((l, ParserError::ExtraCloseParen)) }
|
||||
if v_now.len() == 0 { return Err((l, ParserError::EmptyGroup)) }
|
||||
@ -259,5 +259,5 @@ pub fn groupify(
|
||||
let (_, mut v) = levels.pop().unwrap();
|
||||
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));
|
||||
}
|
@ -85,11 +85,8 @@ fn treeify_binary(
|
||||
} {
|
||||
return Ok(false);
|
||||
} else {
|
||||
let tl = *this.get_line_location();
|
||||
return Err((
|
||||
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
|
||||
ParserError::Syntax
|
||||
));
|
||||
let tl = *this.get_line_location() + *l;
|
||||
return Err((tl, ParserError::Syntax));
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,22 +123,33 @@ fn treeify_binary(
|
||||
let left_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 left: Expression; let right: Expression;
|
||||
if let Token::Group(_, _) = right_pre { right = treeify(right_pre, context)?; } else {right = right_pre.to_expression(context)?;}
|
||||
if let Token::Group(_, _) = left_pre { left = treeify(left_pre, context)?; } else {left = left_pre.to_expression(context)?;}
|
||||
let mut left: Expression; let mut right: Expression;
|
||||
if let Token::Group(l, _) = right_pre {
|
||||
right = treeify(right_pre, context)?;
|
||||
right.set_linelocation(&(right.get_linelocation() + l));
|
||||
} else {
|
||||
right = right_pre.to_expression(context)?;
|
||||
}
|
||||
|
||||
let o = {
|
||||
let Token::Operator(_, s) = this_pre else {panic!()};
|
||||
if let Token::Group(l, _) = left_pre {
|
||||
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);
|
||||
if o.is_none() { panic!() }
|
||||
o.unwrap()
|
||||
(l, o.unwrap())
|
||||
};
|
||||
|
||||
let mut new_token_args: VecDeque<Expression> = VecDeque::with_capacity(2);
|
||||
new_token_args.push_back(left);
|
||||
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);
|
||||
} else {
|
||||
@ -204,12 +212,8 @@ fn treeify_unary(
|
||||
}
|
||||
|
||||
if let Token::Operator(l, _) = next {
|
||||
let tl = *this.get_line_location();
|
||||
return Err((
|
||||
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
|
||||
ParserError::Syntax
|
||||
));
|
||||
|
||||
let tl = *this.get_line_location() + *l;
|
||||
return Err((tl, ParserError::Syntax));
|
||||
} else {
|
||||
|
||||
// This operator
|
||||
@ -239,28 +243,34 @@ fn treeify_unary(
|
||||
|
||||
if next_op.is_none() || this_op > next_op.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 {
|
||||
next_pre = g_inner.remove(i-1).unwrap();
|
||||
} else {
|
||||
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);
|
||||
if o.is_none() { panic!() }
|
||||
o.unwrap()
|
||||
(l, o.unwrap())
|
||||
};
|
||||
|
||||
let mut new_token_args: VecDeque<Expression> = VecDeque::with_capacity(3);
|
||||
new_token_args.push_back(next);
|
||||
|
||||
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 {
|
||||
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);
|
||||
|
@ -74,20 +74,20 @@ impl Token {
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
if c.is_some() { return Ok(Expression::Variable(s)); }
|
||||
return Ok(Expression::Variable(s));
|
||||
if c.is_some() { return Ok(Expression::Variable(l, s)); }
|
||||
return Ok(Expression::Variable(l, s));
|
||||
}
|
||||
|
||||
Token::Container(v) => { return Ok(v); }
|
||||
|
Reference in New Issue
Block a user