mirror of https://github.com/rm-dr/daisy
Treeify now handles both left- and right-associative operators
parent
c92670c918
commit
a40ebb7500
|
@ -52,7 +52,6 @@ pub enum Token {
|
|||
}
|
||||
|
||||
impl Token {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_args(&mut self) -> Option<&mut VecDeque<Token>> {
|
||||
match self {
|
||||
|
@ -239,7 +238,78 @@ pub enum Operator {
|
|||
Power,
|
||||
|
||||
Negative,
|
||||
Factorial
|
||||
Factorial,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
#[inline(always)]
|
||||
pub fn is_binary(&self) -> bool {
|
||||
match self {
|
||||
Operator::Negative
|
||||
| Operator::Factorial
|
||||
=> false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_left_associative(&self) -> bool {
|
||||
match self {
|
||||
Operator::Negative
|
||||
| Operator::Power
|
||||
=> false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_token(&self, mut args: VecDeque<Token>) -> Token {
|
||||
match self {
|
||||
Operator::Add => Token::Add(args),
|
||||
|
||||
Operator::Multiply
|
||||
| Operator::ImplicitMultiply
|
||||
=> Token::Multiply(args),
|
||||
|
||||
Operator::Subtract => {
|
||||
if args.len() != 2 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
let b = args.pop_front().unwrap();
|
||||
|
||||
Token::Add(
|
||||
VecDeque::from(vec!(
|
||||
a,
|
||||
Token::Negative(VecDeque::from(vec!(b)))
|
||||
)))
|
||||
},
|
||||
|
||||
Operator::Divide => {
|
||||
if args.len() != 2 { panic!() }
|
||||
Token::Divide(args)
|
||||
},
|
||||
|
||||
Operator::ModuloLong |
|
||||
Operator::Modulo => {
|
||||
if args.len() != 2 { panic!() }
|
||||
Token::Modulo(args)
|
||||
},
|
||||
|
||||
Operator::Power => {
|
||||
if args.len() != 2 { panic!() }
|
||||
Token::Power(args)
|
||||
},
|
||||
|
||||
Operator::Negative => {
|
||||
if args.len() != 1 { panic!() }
|
||||
Token::Negative(args)
|
||||
},
|
||||
|
||||
Operator::Factorial => {
|
||||
if args.len() != 1 { panic!() }
|
||||
Token::Factorial(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the location of a token in an input string.
|
||||
|
|
|
@ -5,35 +5,11 @@ use crate::parser::LineLocation;
|
|||
use crate::parser::ParserError;
|
||||
use crate::parser::Operator;
|
||||
|
||||
#[inline(always)]
|
||||
fn select_op(k: Operator, mut new_token_args: VecDeque<Token>) -> Token {
|
||||
match k {
|
||||
Operator::Subtract => {
|
||||
let a = new_token_args.pop_front().unwrap();
|
||||
let b = new_token_args.pop_front().unwrap();
|
||||
|
||||
Token::Add(
|
||||
VecDeque::from(vec!(
|
||||
a,
|
||||
Token::Negative(VecDeque::from(vec!(b)))
|
||||
)))
|
||||
},
|
||||
Operator::Add => Token::Add(new_token_args),
|
||||
Operator::Divide => Token::Divide(new_token_args),
|
||||
Operator::Multiply => Token::Multiply(new_token_args),
|
||||
Operator::ImplicitMultiply => Token::Multiply(new_token_args),
|
||||
Operator::Modulo => Token::Modulo(new_token_args),
|
||||
Operator::ModuloLong => Token::Modulo(new_token_args),
|
||||
Operator::Power => Token::Power(new_token_args),
|
||||
Operator::Negative => Token::Negative(new_token_args),
|
||||
Operator::Factorial => Token::Factorial(new_token_args)
|
||||
}
|
||||
}
|
||||
|
||||
fn treeify_binary(
|
||||
mut i: usize,
|
||||
g_inner: &mut VecDeque<Token>
|
||||
) -> Result<usize, (LineLocation, ParserError)> {
|
||||
g_inner: &mut VecDeque<Token>,
|
||||
left_associative: bool
|
||||
) -> Result<(), (LineLocation, ParserError)> {
|
||||
|
||||
let this: &Token = &g_inner[i];
|
||||
|
||||
|
@ -46,7 +22,9 @@ fn treeify_binary(
|
|||
return Err((*l, ParserError::Syntax));
|
||||
}
|
||||
|
||||
let right: &Token = {
|
||||
let next: &Token;
|
||||
if left_associative {
|
||||
next = {
|
||||
if i < g_inner.len()-1 {
|
||||
&g_inner[i+1]
|
||||
} else {
|
||||
|
@ -57,37 +35,36 @@ fn treeify_binary(
|
|||
return Err((*l, ParserError::Syntax));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
next = {
|
||||
if i > 0 {
|
||||
&g_inner[i-1]
|
||||
} else {
|
||||
let l = match this {
|
||||
Token::PreOperator(l, _) => l,
|
||||
_ => panic!()
|
||||
};
|
||||
return Err((*l, ParserError::Syntax));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if let Token::PreOperator(l, o) = right {
|
||||
match o {
|
||||
// Binary operators
|
||||
Operator::ModuloLong |
|
||||
Operator::Subtract |
|
||||
Operator::Add |
|
||||
Operator::Divide |
|
||||
Operator::Multiply |
|
||||
Operator::ImplicitMultiply |
|
||||
Operator::Modulo |
|
||||
Operator::Power |
|
||||
// Right unary operators
|
||||
Operator::Factorial
|
||||
=> {
|
||||
// Binary and right-unary operators cannot
|
||||
// follow a binary operator.
|
||||
|
||||
if let Token::PreOperator(l, o) = next {
|
||||
if {
|
||||
(!o.is_binary()) &&
|
||||
!(o.is_left_associative() && left_associative)
|
||||
} {
|
||||
// Only right-associative unary operators can follow a binary operator
|
||||
return Ok(());
|
||||
} else {
|
||||
let tl = *this.get_line_location();
|
||||
return Err((
|
||||
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
|
||||
ParserError::Syntax
|
||||
));
|
||||
},
|
||||
|
||||
// Left unary operators
|
||||
Operator::Negative => {
|
||||
i += 1;
|
||||
return Ok(i);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
||||
// Precedence of this operator
|
||||
|
@ -96,7 +73,13 @@ fn treeify_binary(
|
|||
_ => panic!()
|
||||
};
|
||||
|
||||
// Precedence of the operator contesting the right argument.
|
||||
// Precedence of the operators contesting our arguments
|
||||
let left_val = if i > 1 {
|
||||
match &g_inner[i-2] {
|
||||
Token::PreOperator(_, q) => Some(*q as isize),
|
||||
_ => panic!()
|
||||
}
|
||||
} else { None };
|
||||
let right_val = if i < g_inner.len()-2 {
|
||||
match &g_inner[i+2] {
|
||||
Token::PreOperator(_, q) => Some(*q as isize),
|
||||
|
@ -104,8 +87,10 @@ fn treeify_binary(
|
|||
}
|
||||
} else { None };
|
||||
|
||||
|
||||
if right_val.is_none() || this_val > right_val.unwrap() {
|
||||
if {
|
||||
(left_val.is_none() || this_val >= left_val.unwrap()) &&
|
||||
(right_val.is_none() || this_val >= right_val.unwrap())
|
||||
} {
|
||||
// This operator has higher precedence, it takes both arguments
|
||||
let mut left = g_inner.remove(i-1).unwrap();
|
||||
let this = g_inner.remove(i-1).unwrap();
|
||||
|
@ -122,118 +107,26 @@ fn treeify_binary(
|
|||
new_token_args.push_back(left);
|
||||
new_token_args.push_back(right);
|
||||
|
||||
g_inner.insert(i-1, select_op(k, new_token_args));
|
||||
g_inner.insert(i-1, k.into_token(new_token_args));
|
||||
|
||||
if i > 1 { i -= 2; } else { i = 0; }
|
||||
return Ok(i);
|
||||
return Ok(());
|
||||
} else {
|
||||
// The operator to the right has higher precedence.
|
||||
// Move on, don't to anything yet.
|
||||
i += 2;
|
||||
return Ok(i);
|
||||
return Ok(());
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn treeify_unaryleft(
|
||||
mut i: usize,
|
||||
g_inner: &mut VecDeque<Token>
|
||||
) -> Result<usize, (LineLocation, ParserError)> {
|
||||
fn treeify_unary(
|
||||
i: usize,
|
||||
g_inner: &mut VecDeque<Token>,
|
||||
left_associative: bool
|
||||
) -> Result<(), (LineLocation, ParserError)> {
|
||||
|
||||
let this: &Token = &g_inner[i];
|
||||
let right: &Token = {
|
||||
if i < g_inner.len()-1 {
|
||||
&g_inner[i+1]
|
||||
} else {
|
||||
let l = match this {
|
||||
Token::PreOperator(l, _) => l,
|
||||
_ => panic!()
|
||||
};
|
||||
return Err((*l, ParserError::Syntax));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if let Token::PreOperator(l, o) = right {
|
||||
match o {
|
||||
// Binary operators
|
||||
Operator::ModuloLong |
|
||||
Operator::Subtract |
|
||||
Operator::Add |
|
||||
Operator::Divide |
|
||||
Operator::Multiply |
|
||||
Operator::ImplicitMultiply |
|
||||
Operator::Modulo |
|
||||
Operator::Power |
|
||||
// Right unary operators
|
||||
Operator::Factorial
|
||||
=> {
|
||||
// Binary and right-unary operators cannot
|
||||
// follow a binary operator.
|
||||
let tl = *this.get_line_location();
|
||||
return Err((
|
||||
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
|
||||
ParserError::Syntax
|
||||
));
|
||||
},
|
||||
|
||||
// Left unary operators
|
||||
Operator::Negative => {
|
||||
i += 1;
|
||||
return Ok(i);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
||||
// Precedence of this operator
|
||||
let this_val: isize = match this {
|
||||
Token::PreOperator(_, q) => *q as isize,
|
||||
_ => panic!()
|
||||
};
|
||||
|
||||
// Precedence of the operator contesting its argument
|
||||
let right_val = if i < g_inner.len()-2 {
|
||||
match &g_inner[i+2] {
|
||||
Token::PreOperator(_, q) => Some(*q as isize),
|
||||
_ => panic!()
|
||||
}
|
||||
} else { None };
|
||||
|
||||
|
||||
if right_val.is_none() || this_val > right_val.unwrap() {
|
||||
let this = g_inner.remove(i).unwrap();
|
||||
let mut right = g_inner.remove(i).unwrap();
|
||||
if let Token::PreGroup(_, _) = right { right = p_treeify(right)?; }
|
||||
|
||||
let k = match this {
|
||||
Token::PreOperator(_, k) => k,
|
||||
_ => panic!()
|
||||
};
|
||||
|
||||
let mut new_token_args: VecDeque<Token> = VecDeque::with_capacity(3);
|
||||
new_token_args.push_back(right);
|
||||
|
||||
g_inner.insert(i, select_op(k, new_token_args));
|
||||
|
||||
if i > 0 { i -= 1; } else { i = 0; }
|
||||
return Ok(i);
|
||||
} else {
|
||||
// The operator to the right has higher precedence.
|
||||
// Move on, don't to anything yet.
|
||||
i += 2;
|
||||
return Ok(i);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn treeify_unaryright(
|
||||
mut i: usize,
|
||||
g_inner: &mut VecDeque<Token>
|
||||
) -> Result<usize, (LineLocation, ParserError)> {
|
||||
|
||||
let this: &Token = &g_inner[i];
|
||||
let left: &Token = {
|
||||
let next: &Token;
|
||||
if left_associative {
|
||||
next = {
|
||||
if i > 0 {
|
||||
&g_inner[i-1]
|
||||
} else {
|
||||
|
@ -244,18 +137,33 @@ fn treeify_unaryright(
|
|||
return Err((*l, ParserError::Syntax));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// We need to check the element after unary right operators too.
|
||||
// Bad syntax like `3!3` won't be caught otherwise.
|
||||
let right: Option<&Token> = {
|
||||
} else {
|
||||
next = {
|
||||
if i < g_inner.len()-1 {
|
||||
Some(&g_inner[i+1])
|
||||
} else {None}
|
||||
&g_inner[i+1]
|
||||
} else {
|
||||
let l = match this {
|
||||
Token::PreOperator(l, _) => l,
|
||||
_ => panic!()
|
||||
};
|
||||
return Err((*l, ParserError::Syntax));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if right.is_some() {
|
||||
if let Token::PreOperator(l, o) = right.unwrap() {
|
||||
// We need to check the element after unary operators too.
|
||||
// Bad syntax like `3!3` won't be caught otherwise.
|
||||
let prev: Option<&Token>;
|
||||
if left_associative {
|
||||
prev = if i < g_inner.len()-1 { Some(&g_inner[i+1]) } else {None};
|
||||
|
||||
} else {
|
||||
prev = if i > 0 { Some(&g_inner[i-1]) } else {None};
|
||||
|
||||
}
|
||||
|
||||
if prev.is_some() {
|
||||
if let Token::PreOperator(l, o) = prev.unwrap() {
|
||||
match o {
|
||||
// Left unary operators
|
||||
Operator::Negative => {
|
||||
|
@ -275,13 +183,14 @@ fn treeify_unaryright(
|
|||
}
|
||||
}
|
||||
|
||||
if let Token::PreOperator(l, _) = left {
|
||||
|
||||
|
||||
if let Token::PreOperator(l, _) = next {
|
||||
let tl = *this.get_line_location();
|
||||
return Err((
|
||||
LineLocation{pos: tl.pos, len: l.pos - tl.pos + l.len},
|
||||
ParserError::Syntax
|
||||
));
|
||||
|
||||
} else {
|
||||
|
||||
// Precedence of this operator
|
||||
|
@ -290,19 +199,32 @@ fn treeify_unaryright(
|
|||
_ => panic!()
|
||||
};
|
||||
|
||||
// Precedence of the operator contesting its argument.
|
||||
let left_val = if i >= 2 {
|
||||
// Precedence of the operator contesting its argument
|
||||
let next_val = if left_associative {
|
||||
if i > 1 {
|
||||
match &g_inner[i-2] {
|
||||
Token::PreOperator(_, q) => Some(*q as isize),
|
||||
_ => panic!()
|
||||
}
|
||||
} else { None };
|
||||
} else { None }
|
||||
} else {
|
||||
if i < g_inner.len()-2 {
|
||||
match &g_inner[i+2] {
|
||||
Token::PreOperator(_, q) => Some(*q as isize),
|
||||
_ => panic!()
|
||||
}
|
||||
} else { None }
|
||||
};
|
||||
|
||||
|
||||
if left_val.is_none() || this_val > left_val.unwrap() {
|
||||
if next_val.is_none() || this_val > next_val.unwrap() {
|
||||
let this = g_inner.remove(i).unwrap();
|
||||
let mut left = g_inner.remove(i-1).unwrap();
|
||||
if let Token::PreGroup(_, _) = left { left = p_treeify(left)?; }
|
||||
let mut next;
|
||||
if left_associative {
|
||||
next = g_inner.remove(i-1).unwrap();
|
||||
} else {
|
||||
next = g_inner.remove(i).unwrap();
|
||||
}
|
||||
if let Token::PreGroup(_, _) = next { next = p_treeify(next)?; }
|
||||
|
||||
let k = match this {
|
||||
Token::PreOperator(_, k) => k,
|
||||
|
@ -310,17 +232,19 @@ fn treeify_unaryright(
|
|||
};
|
||||
|
||||
let mut new_token_args: VecDeque<Token> = VecDeque::with_capacity(3);
|
||||
new_token_args.push_back(left);
|
||||
new_token_args.push_back(next);
|
||||
|
||||
g_inner.insert(i-1, select_op(k, new_token_args));
|
||||
if left_associative {
|
||||
g_inner.insert(i-1, k.into_token(new_token_args));
|
||||
} else {
|
||||
g_inner.insert(i, k.into_token(new_token_args));
|
||||
}
|
||||
|
||||
if i > 2 { i -= 2; } else { i = 0; }
|
||||
return Ok(i);
|
||||
return Ok(());
|
||||
} else {
|
||||
// The operator to the right has higher precedence.
|
||||
// Move on, don't to anything yet.
|
||||
i += 1;
|
||||
return Ok(i);
|
||||
return Ok(());
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -334,37 +258,52 @@ pub fn p_treeify(
|
|||
_ => panic!()
|
||||
};
|
||||
|
||||
let mut i: usize = 0;
|
||||
let mut left_associative = true;
|
||||
let mut j: i64 = 0;
|
||||
while g_inner.len() > 1 {
|
||||
|
||||
|
||||
if j <= -1 {
|
||||
left_associative = true;
|
||||
j = 0;
|
||||
} else if j >= g_inner.len() as i64 {
|
||||
left_associative = false;
|
||||
j = (g_inner.len() - 1) as i64;
|
||||
}
|
||||
|
||||
let i = j as usize;
|
||||
let this_op = match &g_inner[i] {
|
||||
Token::PreOperator(_, o) => o,
|
||||
_ => { i+=1; continue; }
|
||||
_ => {
|
||||
if left_associative { j += 1 } else { j -= 1 };
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
match this_op {
|
||||
Operator::ModuloLong |
|
||||
Operator::Subtract |
|
||||
Operator::Add |
|
||||
Operator::Divide |
|
||||
Operator::Multiply |
|
||||
Operator::ImplicitMultiply |
|
||||
Operator::Modulo |
|
||||
Operator::Power
|
||||
=> { i = treeify_binary(i, g_inner)?; },
|
||||
|
||||
Operator::Negative
|
||||
=> { i = treeify_unaryleft(i, g_inner)?; },
|
||||
|
||||
Operator::Factorial
|
||||
=> { i = treeify_unaryright(i, g_inner)?; }
|
||||
|
||||
};
|
||||
if left_associative {
|
||||
if this_op.is_left_associative() {
|
||||
if this_op.is_binary() {
|
||||
treeify_binary(i, g_inner, left_associative)?;
|
||||
} else {
|
||||
treeify_unary(i, g_inner, left_associative)?;
|
||||
}
|
||||
}
|
||||
j += 1
|
||||
} else {
|
||||
if !this_op.is_left_associative() {
|
||||
if this_op.is_binary() {
|
||||
treeify_binary(i, g_inner, left_associative)?;
|
||||
} else {
|
||||
treeify_unary(i, g_inner, left_associative)?;
|
||||
}
|
||||
}
|
||||
j -= 1
|
||||
}
|
||||
}
|
||||
|
||||
g = g_inner.pop_front().unwrap();
|
||||
|
||||
// Catch the edge case where the entire group we're given
|
||||
// consists of one operator. This is always a syntax error.
|
||||
// Catch edge cases
|
||||
match g {
|
||||
Token::PreOperator(l, _) => {
|
||||
return Err((l, ParserError::Syntax));
|
||||
|
|
Loading…
Reference in New Issue