Evaluate arbitrary functions
This commit is contained in:
parent
22d0d2c3d0
commit
879f1fcd22
@ -28,8 +28,11 @@ pub enum Error {
|
|||||||
/// Tried to append a child to a node such that the precedence of the child is not higher.
|
/// Tried to append a child to a node such that the precedence of the child is not higher.
|
||||||
PrecedenceViolation,
|
PrecedenceViolation,
|
||||||
|
|
||||||
/// An identifier operation did not find its value in the configuration.
|
/// A `VariableIdentifier` operation did not find its value in the configuration.
|
||||||
IdentifierNotFound(String),
|
VariableIdentifierNotFound(String),
|
||||||
|
|
||||||
|
/// A `FunctionIdentifier` operation did not find its value in the configuration.
|
||||||
|
FunctionIdentifierNotFound(String),
|
||||||
|
|
||||||
/// A value has the wrong type.
|
/// A value has the wrong type.
|
||||||
TypeError,
|
TypeError,
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
use error::Error;
|
use error::{self, Error};
|
||||||
use value::Value;
|
use value::Value;
|
||||||
|
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
parameter_amount: usize,
|
argument_amount: usize,
|
||||||
function: fn() -> Result<Value, Error>, // TODO continue type
|
function: Box<Fn(&[Value]) -> Result<Value, Error>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
pub fn new(
|
||||||
|
argument_amount: usize,
|
||||||
|
function: Box<Fn(&[Value]) -> Result<Value, Error>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
argument_amount,
|
||||||
|
function,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(&self, arguments: &[Value]) -> Result<Value, Error> {
|
||||||
|
error::expect_argument_amount(self.argument_amount, arguments.len())?;
|
||||||
|
(self.function)(arguments)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
43
src/lib.rs
43
src/lib.rs
@ -30,6 +30,7 @@ mod test {
|
|||||||
use configuration::HashMapConfiguration;
|
use configuration::HashMapConfiguration;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use eval_with_configuration;
|
use eval_with_configuration;
|
||||||
|
use Function;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unary_examples() {
|
fn test_unary_examples() {
|
||||||
@ -39,7 +40,7 @@ mod test {
|
|||||||
assert_eq!(eval("false"), Ok(Value::Boolean(false)));
|
assert_eq!(eval("false"), Ok(Value::Boolean(false)));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
eval("blub"),
|
eval("blub"),
|
||||||
Err(Error::IdentifierNotFound("blub".to_string()))
|
Err(Error::VariableIdentifierNotFound("blub".to_string()))
|
||||||
);
|
);
|
||||||
assert_eq!(eval("-3"), Ok(Value::Int(-3)));
|
assert_eq!(eval("-3"), Ok(Value::Int(-3)));
|
||||||
assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6)));
|
assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6)));
|
||||||
@ -152,6 +153,46 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_functions() {
|
||||||
|
let mut configuration = HashMapConfiguration::new();
|
||||||
|
configuration.insert_function(
|
||||||
|
"sub2".to_string(),
|
||||||
|
Function::new(
|
||||||
|
1,
|
||||||
|
Box::new(|arguments| {
|
||||||
|
if let Value::Int(int) = arguments[0] {
|
||||||
|
Ok(Value::Int(int - 2))
|
||||||
|
} else {
|
||||||
|
Err(Error::expected_number(arguments[0].clone()))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
configuration.insert_variable("five".to_string(), Value::Int(5));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2 5", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(5)", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2 five", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(five)", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(3) + five", &configuration),
|
||||||
|
Ok(Value::Int(6))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_errors() {
|
fn test_errors() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{configuration::Configuration, error::*, value::Value};
|
use crate::{configuration::Configuration, error::*, value::Value};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub trait Operator {
|
pub trait Operator: Debug {
|
||||||
/// Returns the precedence of the operator.
|
/// Returns the precedence of the operator.
|
||||||
/// A high precedence means that the operator has priority to be deeper in the tree.
|
/// A high precedence means that the operator has priority to be deeper in the tree.
|
||||||
// Make this a const fn once #57563 is resolved
|
// Make this a const fn once #57563 is resolved
|
||||||
@ -20,25 +21,42 @@ pub trait Operator {
|
|||||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error>;
|
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct RootNode;
|
pub struct RootNode;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Add;
|
pub struct Add;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Sub;
|
pub struct Sub;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Neg;
|
pub struct Neg;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Mul;
|
pub struct Mul;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Div;
|
pub struct Div;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Mod;
|
pub struct Mod;
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
pub struct Eq;
|
pub struct Eq;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Neq;
|
pub struct Neq;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Gt;
|
pub struct Gt;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Lt;
|
pub struct Lt;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Geq;
|
pub struct Geq;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Leq;
|
pub struct Leq;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct And;
|
pub struct And;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Or;
|
pub struct Or;
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Not;
|
pub struct Not;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Const {
|
pub struct Const {
|
||||||
value: Value,
|
value: Value,
|
||||||
}
|
}
|
||||||
@ -49,11 +67,23 @@ impl Const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Identifier {
|
#[derive(Debug)]
|
||||||
|
pub struct VariableIdentifier {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identifier {
|
impl VariableIdentifier {
|
||||||
|
pub fn new(identifier: String) -> Self {
|
||||||
|
Self { identifier }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionIdentifier {
|
||||||
|
identifier: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionIdentifier {
|
||||||
pub fn new(identifier: String) -> Self {
|
pub fn new(identifier: String) -> Self {
|
||||||
Self { identifier }
|
Self { identifier }
|
||||||
}
|
}
|
||||||
@ -466,24 +496,39 @@ impl Operator for Const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operator for Identifier {
|
impl Operator for VariableIdentifier {
|
||||||
fn precedence(&self) -> i32 {
|
fn precedence(&self) -> i32 {
|
||||||
200
|
200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn argument_amount(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||||
|
if let Some(value) = configuration.get_value(&self.identifier).cloned() {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(Error::VariableIdentifierNotFound(self.identifier.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operator for FunctionIdentifier {
|
||||||
|
fn precedence(&self) -> i32 {
|
||||||
|
190
|
||||||
|
}
|
||||||
|
|
||||||
fn argument_amount(&self) -> usize {
|
fn argument_amount(&self) -> usize {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||||
if arguments.len() == 0 {
|
if let Some(function) = configuration.get_function(&self.identifier) {
|
||||||
if let Some(value) = configuration.get_value(&self.identifier).cloned() {
|
// Function::call checks for correct argument amount
|
||||||
Ok(value)
|
function.call(arguments)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::IdentifierNotFound(self.identifier.clone()))
|
Err(Error::FunctionIdentifierNotFound(self.identifier.clone()))
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ pub enum Token {
|
|||||||
// Precedence
|
// Precedence
|
||||||
LBrace,
|
LBrace,
|
||||||
RBrace,
|
RBrace,
|
||||||
Whitespace,
|
|
||||||
|
|
||||||
// Complex tokens
|
// Complex tokens
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
@ -38,6 +37,7 @@ pub enum Token {
|
|||||||
pub enum PartialToken {
|
pub enum PartialToken {
|
||||||
Token(Token),
|
Token(Token),
|
||||||
Literal(String),
|
Literal(String),
|
||||||
|
Whitespace,
|
||||||
Eq,
|
Eq,
|
||||||
ExclamationMark,
|
ExclamationMark,
|
||||||
Gt,
|
Gt,
|
||||||
@ -67,7 +67,7 @@ fn char_to_partial_token(c: char) -> PartialToken {
|
|||||||
|
|
||||||
c => {
|
c => {
|
||||||
if c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
PartialToken::Token(Token::Whitespace)
|
PartialToken::Whitespace
|
||||||
} else {
|
} else {
|
||||||
PartialToken::Literal(c.to_string())
|
PartialToken::Literal(c.to_string())
|
||||||
}
|
}
|
||||||
@ -77,7 +77,36 @@ fn char_to_partial_token(c: char) -> PartialToken {
|
|||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
// Make this a const fn as soon as match gets stable (issue #57563)
|
// Make this a const fn as soon as match gets stable (issue #57563)
|
||||||
pub fn is_value(&self) -> bool {
|
pub fn is_leftsided_value(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Token::Plus => false,
|
||||||
|
Token::Minus => false,
|
||||||
|
Token::Star => false,
|
||||||
|
Token::Slash => false,
|
||||||
|
Token::Percent => false,
|
||||||
|
|
||||||
|
Token::Eq => false,
|
||||||
|
Token::Neq => false,
|
||||||
|
Token::Gt => false,
|
||||||
|
Token::Lt => false,
|
||||||
|
Token::Geq => false,
|
||||||
|
Token::Leq => false,
|
||||||
|
Token::And => false,
|
||||||
|
Token::Or => false,
|
||||||
|
Token::Not => false,
|
||||||
|
|
||||||
|
Token::LBrace => true,
|
||||||
|
Token::RBrace => false,
|
||||||
|
|
||||||
|
Token::Identifier(_) => true,
|
||||||
|
Token::Float(_) => true,
|
||||||
|
Token::Int(_) => true,
|
||||||
|
Token::Boolean(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make this a const fn as soon as match gets stable (issue #57563)
|
||||||
|
pub fn is_rightsided_value(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Token::Plus => false,
|
Token::Plus => false,
|
||||||
Token::Minus => false,
|
Token::Minus => false,
|
||||||
@ -97,7 +126,6 @@ impl Token {
|
|||||||
|
|
||||||
Token::LBrace => false,
|
Token::LBrace => false,
|
||||||
Token::RBrace => true,
|
Token::RBrace => true,
|
||||||
Token::Whitespace => false,
|
|
||||||
|
|
||||||
Token::Identifier(_) => true,
|
Token::Identifier(_) => true,
|
||||||
Token::Float(_) => true,
|
Token::Float(_) => true,
|
||||||
@ -138,57 +166,64 @@ fn resolve_literals(mut tokens: &[PartialToken]) -> Result<Vec<Token>, Error> {
|
|||||||
let second = tokens.get(1).cloned();
|
let second = tokens.get(1).cloned();
|
||||||
let mut cutoff = 2;
|
let mut cutoff = 2;
|
||||||
|
|
||||||
result.push(match first {
|
result.extend(
|
||||||
|
match first {
|
||||||
PartialToken::Token(token) => {
|
PartialToken::Token(token) => {
|
||||||
cutoff = 1;
|
cutoff = 1;
|
||||||
token
|
Some(token)
|
||||||
}
|
}
|
||||||
PartialToken::Literal(literal) => {
|
PartialToken::Literal(literal) => {
|
||||||
cutoff = 1;
|
cutoff = 1;
|
||||||
if let Ok(number) = literal.parse::<IntType>() {
|
if let Ok(number) = literal.parse::<IntType>() {
|
||||||
Token::Int(number)
|
Some(Token::Int(number))
|
||||||
} else if let Ok(number) = literal.parse::<FloatType>() {
|
} else if let Ok(number) = literal.parse::<FloatType>() {
|
||||||
Token::Float(number)
|
Some(Token::Float(number))
|
||||||
} else if let Ok(boolean) = literal.parse::<bool>() {
|
} else if let Ok(boolean) = literal.parse::<bool>() {
|
||||||
Token::Boolean(boolean)
|
Some(Token::Boolean(boolean))
|
||||||
} else {
|
} else {
|
||||||
Token::Identifier(literal.to_string())
|
Some(Token::Identifier(literal.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PartialToken::Whitespace => {
|
||||||
|
cutoff = 1;
|
||||||
|
None
|
||||||
|
}
|
||||||
PartialToken::Eq => match second {
|
PartialToken::Eq => match second {
|
||||||
Some(PartialToken::Eq) => Token::Eq,
|
Some(PartialToken::Eq) => Some(Token::Eq),
|
||||||
_ => return Err(Error::unmatched_partial_token(first, second)),
|
_ => return Err(Error::unmatched_partial_token(first, second)),
|
||||||
},
|
},
|
||||||
PartialToken::ExclamationMark => match second {
|
PartialToken::ExclamationMark => match second {
|
||||||
Some(PartialToken::Eq) => Token::Eq,
|
Some(PartialToken::Eq) => Some(Token::Eq),
|
||||||
_ => {
|
_ => {
|
||||||
cutoff = 1;
|
cutoff = 1;
|
||||||
Token::Not
|
Some(Token::Not)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PartialToken::Gt => match second {
|
PartialToken::Gt => match second {
|
||||||
Some(PartialToken::Eq) => Token::Geq,
|
Some(PartialToken::Eq) => Some(Token::Geq),
|
||||||
_ => {
|
_ => {
|
||||||
cutoff = 1;
|
cutoff = 1;
|
||||||
Token::Gt
|
Some(Token::Gt)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PartialToken::Lt => match second {
|
PartialToken::Lt => match second {
|
||||||
Some(PartialToken::Eq) => Token::Leq,
|
Some(PartialToken::Eq) => Some(Token::Leq),
|
||||||
_ => {
|
_ => {
|
||||||
cutoff = 1;
|
cutoff = 1;
|
||||||
Token::Lt
|
Some(Token::Lt)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PartialToken::Ampersand => match second {
|
PartialToken::Ampersand => match second {
|
||||||
Some(PartialToken::Ampersand) => Token::And,
|
Some(PartialToken::Ampersand) => Some(Token::And),
|
||||||
_ => return Err(Error::unmatched_partial_token(first, second)),
|
_ => return Err(Error::unmatched_partial_token(first, second)),
|
||||||
},
|
},
|
||||||
PartialToken::VerticalBar => match second {
|
PartialToken::VerticalBar => match second {
|
||||||
Some(PartialToken::VerticalBar) => Token::Or,
|
Some(PartialToken::VerticalBar) => Some(Token::Or),
|
||||||
_ => return Err(Error::unmatched_partial_token(first, second)),
|
_ => return Err(Error::unmatched_partial_token(first, second)),
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
.into_iter(),
|
||||||
|
);
|
||||||
|
|
||||||
tokens = &tokens[cutoff..];
|
tokens = &tokens[cutoff..];
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{configuration::Configuration, error::Error, operator::*, value::Value};
|
use crate::{configuration::Configuration, error::Error, operator::*, value::Value};
|
||||||
use token::Token;
|
use token::Token;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
children: Vec<Node>,
|
children: Vec<Node>,
|
||||||
operator: Box<dyn Operator>,
|
operator: Box<dyn Operator>,
|
||||||
@ -43,25 +44,27 @@ impl Node {
|
|||||||
if self.operator().is_leaf() {
|
if self.operator().is_leaf() {
|
||||||
Err(Error::AppendedToLeafNode)
|
Err(Error::AppendedToLeafNode)
|
||||||
} else if self.has_correct_amount_of_children() {
|
} else if self.has_correct_amount_of_children() {
|
||||||
if self.children.last_mut().unwrap().operator().precedence()
|
if self.children.last().unwrap().operator().precedence()
|
||||||
< node.operator().precedence()
|
< node.operator().precedence()
|
||||||
|
// Function call
|
||||||
|
//|| self.children().last().unwrap()
|
||||||
{
|
{
|
||||||
self.children
|
self.children
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert_back_prioritized(node, false)
|
.insert_back_prioritized(node, false)
|
||||||
} else {
|
} else {
|
||||||
|
if node.operator().is_leaf() {
|
||||||
|
return Err(Error::AppendedToLeafNode);
|
||||||
|
}
|
||||||
|
|
||||||
let last_child = self.children.pop().unwrap();
|
let last_child = self.children.pop().unwrap();
|
||||||
self.children.push(node);
|
self.children.push(node);
|
||||||
let node = self.children.last_mut().unwrap();
|
let node = self.children.last_mut().unwrap();
|
||||||
|
|
||||||
if node.operator().is_leaf() {
|
|
||||||
Err(Error::AppendedToLeafNode)
|
|
||||||
} else {
|
|
||||||
node.children.push(last_child);
|
node.children.push(last_child);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.children.push(node);
|
self.children.push(node);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -74,13 +77,16 @@ impl Node {
|
|||||||
|
|
||||||
pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
|
pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
|
||||||
let mut root = vec![Node::root_node()];
|
let mut root = vec![Node::root_node()];
|
||||||
let mut last_non_whitespace_token_is_value = false;
|
let mut last_token_is_rightsided_value = false;
|
||||||
|
let mut token_iter = tokens.iter().peekable();
|
||||||
|
|
||||||
|
while let Some(token) = token_iter.next().cloned() {
|
||||||
|
let next = token_iter.peek().cloned();
|
||||||
|
|
||||||
for token in tokens {
|
|
||||||
let node = match token.clone() {
|
let node = match token.clone() {
|
||||||
Token::Plus => Some(Node::new(Add)),
|
Token::Plus => Some(Node::new(Add)),
|
||||||
Token::Minus => {
|
Token::Minus => {
|
||||||
if last_non_whitespace_token_is_value {
|
if last_token_is_rightsided_value {
|
||||||
Some(Node::new(Sub))
|
Some(Node::new(Sub))
|
||||||
} else {
|
} else {
|
||||||
Some(Node::new(Neg))
|
Some(Node::new(Neg))
|
||||||
@ -111,9 +117,16 @@ pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
|
|||||||
root.pop()
|
root.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::Whitespace => None,
|
|
||||||
|
|
||||||
Token::Identifier(identifier) => Some(Node::new(Identifier::new(identifier))),
|
Token::Identifier(identifier) => {
|
||||||
|
let mut result = Some(Node::new(VariableIdentifier::new(identifier.clone())));
|
||||||
|
if let Some(next) = next {
|
||||||
|
if next.is_leftsided_value() {
|
||||||
|
result = Some(Node::new(FunctionIdentifier::new(identifier)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
Token::Float(number) => Some(Node::new(Const::new(Value::Float(number)))),
|
Token::Float(number) => Some(Node::new(Const::new(Value::Float(number)))),
|
||||||
Token::Int(number) => Some(Node::new(Const::new(Value::Int(number)))),
|
Token::Int(number) => Some(Node::new(Const::new(Value::Int(number)))),
|
||||||
Token::Boolean(boolean) => Some(Node::new(Const::new(Value::Boolean(boolean)))),
|
Token::Boolean(boolean) => Some(Node::new(Const::new(Value::Boolean(boolean)))),
|
||||||
@ -127,9 +140,7 @@ pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if token != Token::Whitespace {
|
last_token_is_rightsided_value = token.is_rightsided_value();
|
||||||
last_non_whitespace_token_is_value = token.is_value();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if root.len() > 1 {
|
if root.len() > 1 {
|
||||||
|
Loading…
Reference in New Issue
Block a user