mirror of
				https://github.com/rm-dr/daisy
				synced 2025-10-30 14:04:47 -07:00 
			
		
		
		
	Treeify now handles both left- and right-associative operators
This commit is contained in:
		| @ -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,49 +22,50 @@ fn treeify_binary( | ||||
| 		return Err((*l, ParserError::Syntax)); | ||||
| 	} | ||||
|  | ||||
| 	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); | ||||
| 	let next: &Token; | ||||
| 	if left_associative { | ||||
| 		next = { | ||||
| 			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)); | ||||
| 			} | ||||
| 		}; | ||||
| 	} 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) = 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 | ||||
| 			)); | ||||
| 		} | ||||
| 	} else { | ||||
|  | ||||
| 		// Precedence of this operator | ||||
| 		let this_val: isize = match this { | ||||
| @ -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,140 +107,63 @@ 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); | ||||
| 	let next: &Token; | ||||
| 	if left_associative { | ||||
| 		next = { | ||||
| 			if i > 0 { | ||||
| 				&g_inner[i-1] | ||||
| 			} else { | ||||
| 				let l = match this { | ||||
| 					Token::PreOperator(l, _) => l, | ||||
| 					_ => panic!() | ||||
| 				}; | ||||
| 				return Err((*l, ParserError::Syntax)); | ||||
| 			} | ||||
| 		}; | ||||
| 	} 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!() | ||||
| 		next = { | ||||
| 			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)); | ||||
| 			} | ||||
| 		} 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 = { | ||||
| 		if i > 0 { | ||||
| 			&g_inner[i-1] | ||||
| 		} else { | ||||
| 			let l = match this { | ||||
| 				Token::PreOperator(l, _) => l, | ||||
| 				_ => panic!() | ||||
| 			}; | ||||
| 			return Err((*l, ParserError::Syntax)); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
|  | ||||
| 	// We need to check the element after unary right operators too. | ||||
| 	// We need to check the element after unary operators too. | ||||
| 	// Bad syntax like `3!3` won't be caught otherwise. | ||||
| 	let right: Option<&Token> = { | ||||
| 		if i < g_inner.len()-1 { | ||||
| 			Some(&g_inner[i+1]) | ||||
| 		} else {None} | ||||
| 	}; | ||||
| 	let prev: Option<&Token>; | ||||
| 	if left_associative { | ||||
| 		prev = if i < g_inner.len()-1 { Some(&g_inner[i+1]) } else {None}; | ||||
|  | ||||
| 	if right.is_some() { | ||||
| 		if let Token::PreOperator(l, o) = right.unwrap() { | ||||
| 	} 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 { | ||||
| 			match &g_inner[i-2] { | ||||
| 				Token::PreOperator(_, q) => Some(*q as isize), | ||||
| 				_ => panic!() | ||||
| 			} | ||||
| 		} else { None }; | ||||
| 		// 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 { | ||||
| 			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)); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user