Implement top-level API with docs
This commit is contained in:
parent
cdf79f8763
commit
4907dd9f8f
57
src/error.rs
57
src/error.rs
@ -2,10 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! To deal with errors from dependencies, either create a new error variant
|
//! To deal with errors from dependencies, either create a new error variant
|
||||||
//! or use the MacroFailure variant if the error can only occur inside a macro.
|
//! or use the MacroFailure variant if the error can only occur inside a macro.
|
||||||
use crate::{
|
use crate::{value::value_type::ValueType, value::Value, ToolInfo};
|
||||||
operator::Operator, token::PartialToken, value::value_type::ValueType, value::Value, Node,
|
|
||||||
ToolInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::{fmt, io, time::SystemTimeError};
|
use std::{fmt, io, time::SystemTimeError};
|
||||||
|
|
||||||
@ -118,10 +115,6 @@ pub enum Error {
|
|||||||
actual: Value,
|
actual: Value,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Tried to append a child to a leaf node.
|
|
||||||
/// Leaf nodes cannot have children.
|
|
||||||
AppendedToLeafNode(Node),
|
|
||||||
|
|
||||||
/// Tried to append a child to a node such that the precedence of the child
|
/// Tried to append a child to a node such that the precedence of the child
|
||||||
/// is not higher. This error should never occur. If it does, please file a
|
/// is not higher. This error should never occur. If it does, please file a
|
||||||
/// bug report.
|
/// bug report.
|
||||||
@ -154,9 +147,9 @@ pub enum Error {
|
|||||||
/// An operator is used with a wrong combination of types.
|
/// An operator is used with a wrong combination of types.
|
||||||
WrongTypeCombination {
|
WrongTypeCombination {
|
||||||
/// The operator that whose evaluation caused the error.
|
/// The operator that whose evaluation caused the error.
|
||||||
operator: Operator,
|
expected: ValueType,
|
||||||
/// The types that were used in the operator causing it to fail.
|
/// The types that were used in the operator causing it to fail.
|
||||||
actual: Vec<ValueType>,
|
actual: ValueType,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An opening brace without a matching closing brace was found.
|
/// An opening brace without a matching closing brace was found.
|
||||||
@ -169,16 +162,6 @@ pub enum Error {
|
|||||||
/// For example, writing `4(5)` would yield this error, as the `4` does not have any operands.
|
/// For example, writing `4(5)` would yield this error, as the `4` does not have any operands.
|
||||||
MissingOperatorOutsideOfBrace,
|
MissingOperatorOutsideOfBrace,
|
||||||
|
|
||||||
/// A `PartialToken` is unmatched, such that it cannot be combined into a full `Token`.
|
|
||||||
/// This happens if for example a single `=` is found, surrounded by whitespace.
|
|
||||||
/// It is not a token, but it is part of the string representation of some tokens.
|
|
||||||
UnmatchedPartialToken {
|
|
||||||
/// The unmatched partial token.
|
|
||||||
first: PartialToken,
|
|
||||||
/// The token that follows the unmatched partial token and that cannot be matched to the partial token, or `None`, if `first` is the last partial token in the stream.
|
|
||||||
second: Option<PartialToken>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// An addition operation performed by Rust failed.
|
/// An addition operation performed by Rust failed.
|
||||||
AdditionError {
|
AdditionError {
|
||||||
/// The first argument of the addition.
|
/// The first argument of the addition.
|
||||||
@ -351,10 +334,6 @@ impl Error {
|
|||||||
Error::TypeError { actual, expected }
|
Error::TypeError { actual, expected }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrong_type_combination(operator: Operator, actual: Vec<ValueType>) -> Self {
|
|
||||||
Error::WrongTypeCombination { operator, actual }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expected_string(actual: Value) -> Self {
|
pub fn expected_string(actual: Value) -> Self {
|
||||||
Error::ExpectedString { actual }
|
Error::ExpectedString { actual }
|
||||||
}
|
}
|
||||||
@ -410,13 +389,6 @@ impl Error {
|
|||||||
Error::ExpectedCollection { actual }
|
Error::ExpectedCollection { actual }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unmatched_partial_token(
|
|
||||||
first: PartialToken,
|
|
||||||
second: Option<PartialToken>,
|
|
||||||
) -> Self {
|
|
||||||
Error::UnmatchedPartialToken { first, second }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn addition_error(augend: Value, addend: Value) -> Self {
|
pub(crate) fn addition_error(augend: Value, addend: Value) -> Self {
|
||||||
Error::AdditionError { augend, addend }
|
Error::AdditionError { augend, addend }
|
||||||
}
|
}
|
||||||
@ -540,7 +512,6 @@ impl fmt::Display for Error {
|
|||||||
actual
|
actual
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AppendedToLeafNode(node) => write!(f, "Syntax error at \"{node}\"."),
|
|
||||||
PrecedenceViolation => write!(
|
PrecedenceViolation => write!(
|
||||||
f,
|
f,
|
||||||
"Tried to append a node to another node with higher precedence."
|
"Tried to append a node to another node with higher precedence."
|
||||||
@ -561,11 +532,6 @@ impl fmt::Display for Error {
|
|||||||
"Type Error. The value {actual} is not one of the following: {expected:?}.",
|
"Type Error. The value {actual} is not one of the following: {expected:?}.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
WrongTypeCombination { operator, actual } => write!(
|
|
||||||
f,
|
|
||||||
"The operator {:?} was called with a wrong combination of types: {:?}",
|
|
||||||
operator, actual
|
|
||||||
),
|
|
||||||
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
|
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
|
||||||
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
|
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
|
||||||
MissingOperatorOutsideOfBrace { .. } => write!(
|
MissingOperatorOutsideOfBrace { .. } => write!(
|
||||||
@ -574,22 +540,6 @@ impl fmt::Display for Error {
|
|||||||
any arguments on the right, or found a closing parenthesis that is succeeded by \
|
any arguments on the right, or found a closing parenthesis that is succeeded by \
|
||||||
something that does not take any arguments on the left."
|
something that does not take any arguments on the left."
|
||||||
),
|
),
|
||||||
UnmatchedPartialToken { first, second } => {
|
|
||||||
if let Some(second) = second {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Found a partial token '{}' that should not be followed by '{}'.",
|
|
||||||
first, second
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Found a partial token '{}' that should be followed by another partial \
|
|
||||||
token.",
|
|
||||||
first
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend),
|
AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend),
|
||||||
SubtractionError {
|
SubtractionError {
|
||||||
minuend,
|
minuend,
|
||||||
@ -637,6 +587,7 @@ impl fmt::Display for Error {
|
|||||||
),
|
),
|
||||||
UnexpectedSourceNode { expected, actual } => write!(f, "Unexpected source node. Expected {expected}, but found {actual}."),
|
UnexpectedSourceNode { expected, actual } => write!(f, "Unexpected source node. Expected {expected}, but found {actual}."),
|
||||||
ExpectedFieldName => write!(f, "Expected a field name for this node, but none was found."),
|
ExpectedFieldName => write!(f, "Expected a field name for this node, but none was found."),
|
||||||
|
WrongTypeCombination { expected, actual } => write!(f, "Wrong type combination. Expected {expected}, found {actual}."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,62 @@
|
|||||||
//! The top level of Dust's API with functions in interpret Dust code.
|
//! The top level of Dust's API with functions to interpret Dust code.
|
||||||
|
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
||||||
|
|
||||||
use crate::{language, Error, Result, Value, VariableMap};
|
use crate::{language, Error, Result, Value, VariableMap};
|
||||||
|
|
||||||
/// Evaluate the given expression string.
|
/// Evaluate the given source code.
|
||||||
|
///
|
||||||
|
/// Returns a vector of results from evaluating the source code. Each comment
|
||||||
|
/// and statemtent will have its own result.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use dust_lib::*;
|
/// # use dust_lib::*;
|
||||||
/// assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
|
/// assert_eq!(eval("1 + 2 + 3"), vec![Ok(Value::from(6))]);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
pub fn eval(source: &str) -> Vec<Result<Value>> {
|
||||||
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
|
||||||
pub fn eval(source: &str) -> Result<Value> {
|
|
||||||
let mut context = VariableMap::new();
|
let mut context = VariableMap::new();
|
||||||
|
|
||||||
eval_with_context(source, &mut context)
|
eval_with_context(source, &mut context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the given expression string with the given context.
|
/// Evaluate the given source code with the given context.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use dust_lib::*;
|
/// # use dust_lib::*;
|
||||||
/// let mut context = VariableMap::new();
|
/// let mut context = VariableMap::new();
|
||||||
|
///
|
||||||
/// context.set_value("one".into(), 1.into()).unwrap(); // Do proper error handling here
|
/// context.set_value("one".into(), 1.into()).unwrap(); // Do proper error handling here
|
||||||
/// context.set_value("two".into(), 2.into()).unwrap(); // Do proper error handling here
|
/// context.set_value("two".into(), 2.into()).unwrap(); // Do proper error handling here
|
||||||
/// context.set_value("three".into(), 3.into()).unwrap(); // Do proper error handling here
|
/// context.set_value("three".into(), 3.into()).unwrap(); // Do proper error handling here
|
||||||
/// assert_eq!(eval_with_context("one + two + three", &mut context), Ok(Value::from(6)));
|
///
|
||||||
|
/// let dust_code = "one + two + three; one - two - three;";
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// eval_with_context(dust_code, &mut context),
|
||||||
|
/// vec![Ok(Value::from(6)), Ok(Value::from(-4))]
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn eval_with_context(source: &str, context: &mut VariableMap) -> Result<Value> {
|
pub fn eval_with_context(source: &str, context: &mut VariableMap) -> Vec<Result<Value>> {
|
||||||
let mut parser = Parser::new();
|
let mut parser = Parser::new();
|
||||||
|
|
||||||
parser.set_language(language()).unwrap();
|
parser.set_language(language()).unwrap();
|
||||||
|
|
||||||
let tree = parser.parse(source, None).unwrap();
|
let tree = parser.parse(source, None).unwrap();
|
||||||
let sexp = tree.root_node().to_sexp();
|
let sexp = tree.root_node().to_sexp();
|
||||||
|
|
||||||
println!("{sexp}");
|
|
||||||
|
|
||||||
let evaluator = Evaluator::new(tree.clone(), source).unwrap();
|
let evaluator = Evaluator::new(tree.clone(), source).unwrap();
|
||||||
let mut cursor = tree.walk();
|
let mut cursor = tree.walk();
|
||||||
|
|
||||||
let results = evaluator.run(context, &mut cursor, source);
|
let results = evaluator.run(context, &mut cursor, source);
|
||||||
|
|
||||||
|
println!("{sexp}");
|
||||||
println!("{evaluator:?}");
|
println!("{evaluator:?}");
|
||||||
println!("{results:?}");
|
println!("{results:?}");
|
||||||
|
println!("{context:?}");
|
||||||
|
|
||||||
Ok(Value::Empty)
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -159,7 +163,7 @@ impl Statement {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Expression {
|
enum Expression {
|
||||||
Identifier(&'static str),
|
Identifier(String),
|
||||||
Value(Value),
|
Value(Value),
|
||||||
Operation(Operation),
|
Operation(Operation),
|
||||||
}
|
}
|
||||||
@ -176,7 +180,10 @@ impl Expression {
|
|||||||
let child = node.child(0).unwrap();
|
let child = node.child(0).unwrap();
|
||||||
|
|
||||||
if child.kind() == "identifier" {
|
if child.kind() == "identifier" {
|
||||||
todo!()
|
let byte_range = child.byte_range();
|
||||||
|
let identifier = &source[byte_range];
|
||||||
|
|
||||||
|
Ok(Self::Identifier(identifier.to_string()))
|
||||||
} else if child.kind() == "value" {
|
} else if child.kind() == "value" {
|
||||||
Ok(Expression::Value(Value::new(child, source)?))
|
Ok(Expression::Value(Value::new(child, source)?))
|
||||||
} else if child.kind() == "operation" {
|
} else if child.kind() == "operation" {
|
||||||
@ -214,7 +221,7 @@ impl Expression {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Operation {
|
struct Operation {
|
||||||
left: Box<Expression>,
|
left: Box<Expression>,
|
||||||
operator: &'static str,
|
operator: String,
|
||||||
right: Box<Expression>,
|
right: Box<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +231,7 @@ impl Operation {
|
|||||||
let second_child = node.child(1).unwrap();
|
let second_child = node.child(1).unwrap();
|
||||||
let third_child = node.child(2).unwrap();
|
let third_child = node.child(2).unwrap();
|
||||||
let left = { Box::new(Expression::new(first_child, source)?) };
|
let left = { Box::new(Expression::new(first_child, source)?) };
|
||||||
let operator = { second_child.child(0).unwrap().kind() };
|
let operator = { second_child.child(0).unwrap().kind().to_string() };
|
||||||
let right = { Box::new(Expression::new(third_child, source)?) };
|
let right = { Box::new(Expression::new(third_child, source)?) };
|
||||||
|
|
||||||
Ok(Operation {
|
Ok(Operation {
|
||||||
@ -242,11 +249,40 @@ impl Operation {
|
|||||||
) -> Result<Value> {
|
) -> Result<Value> {
|
||||||
let left = self.left.run(context, &mut cursor, source)?;
|
let left = self.left.run(context, &mut cursor, source)?;
|
||||||
let right = self.right.run(context, &mut cursor, source)?;
|
let right = self.right.run(context, &mut cursor, source)?;
|
||||||
let result = match self.operator {
|
let result = match self.operator.as_str() {
|
||||||
"+" => left + right,
|
"+" => left + right,
|
||||||
|
"=" => {
|
||||||
|
if let Expression::Identifier(key) = self.left.as_ref() {
|
||||||
|
context.set_value(key, right)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Empty)
|
||||||
|
}
|
||||||
_ => return Err(Error::CustomMessage("Operator not supported.".to_string())),
|
_ => return Err(Error::CustomMessage("Operator not supported.".to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result?)
|
Ok(result?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_empty() {
|
||||||
|
assert_eq!(eval("()"), vec![Ok(Value::Empty)]);
|
||||||
|
assert_eq!(eval("1;"), vec![Ok(Value::Empty)]);
|
||||||
|
assert_eq!(eval("'foobar';"), vec![Ok(Value::Empty)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_integer() {
|
||||||
|
assert_eq!(eval("1"), vec![Ok(Value::Integer(1))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_string() {
|
||||||
|
assert_eq!(eval("'one'"), vec![Ok(Value::String("one".to_string()))]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,10 +7,7 @@
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
error::*,
|
error::*,
|
||||||
interface::*,
|
interface::*,
|
||||||
operator::Operator,
|
|
||||||
token::PartialToken,
|
|
||||||
tools::{Tool, ToolInfo, TOOL_LIST},
|
tools::{Tool, ToolInfo, TOOL_LIST},
|
||||||
tree::Node,
|
|
||||||
value::{
|
value::{
|
||||||
function::Function, table::Table, time::Time, value_type::ValueType,
|
function::Function, table::Table, time::Time, value_type::ValueType,
|
||||||
variable_map::VariableMap, Value,
|
variable_map::VariableMap, Value,
|
||||||
@ -21,9 +18,6 @@ pub mod tools;
|
|||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod interface;
|
mod interface;
|
||||||
mod operator;
|
|
||||||
mod token;
|
|
||||||
mod tree;
|
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
use tree_sitter::Language;
|
use tree_sitter::Language;
|
||||||
|
14
src/main.rs
14
src/main.rs
@ -32,21 +32,23 @@ fn main() {
|
|||||||
return run_cli_shell();
|
return run_cli_shell();
|
||||||
}
|
}
|
||||||
|
|
||||||
let eval_result = if let Some(path) = args.path {
|
let eval_results = if let Some(path) = args.path {
|
||||||
let file_contents = read_to_string(path).unwrap();
|
let file_contents = read_to_string(path).unwrap();
|
||||||
|
|
||||||
eval(&file_contents)
|
eval(&file_contents)
|
||||||
} else if let Some(command) = args.command {
|
} else if let Some(command) = args.command {
|
||||||
eval(&command)
|
eval(&command)
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Empty)
|
vec![Ok(Value::Empty)]
|
||||||
};
|
};
|
||||||
|
|
||||||
match eval_result {
|
for result in eval_results {
|
||||||
|
match result {
|
||||||
Ok(value) => println!("{value}"),
|
Ok(value) => println!("{value}"),
|
||||||
Err(error) => eprintln!("{error}"),
|
Err(error) => eprintln!("{error}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Helper, Completer, Validator)]
|
#[derive(Helper, Completer, Validator)]
|
||||||
struct DustReadline {
|
struct DustReadline {
|
||||||
@ -147,13 +149,15 @@ fn run_cli_shell() {
|
|||||||
|
|
||||||
rl.add_history_entry(line).unwrap();
|
rl.add_history_entry(line).unwrap();
|
||||||
|
|
||||||
let eval_result = eval_with_context(line, &mut context);
|
let eval_results = eval_with_context(line, &mut context);
|
||||||
|
|
||||||
match eval_result {
|
for result in eval_results {
|
||||||
|
match result {
|
||||||
Ok(value) => println!("{value}"),
|
Ok(value) => println!("{value}"),
|
||||||
Err(error) => eprintln!("{error}"),
|
Err(error) => eprintln!("{error}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
547
src/operator.rs
547
src/operator.rs
@ -1,547 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::{error::*, value::Value, Result, VariableMap};
|
|
||||||
|
|
||||||
/// An enum that represents operators in the operator tree.
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Operator {
|
|
||||||
/// A root node in the operator tree.
|
|
||||||
/// The whole expression is stored under a root node, as well as each subexpression surrounded by parentheses.
|
|
||||||
RootNode,
|
|
||||||
|
|
||||||
/// A binary addition operator.
|
|
||||||
Add,
|
|
||||||
/// A binary subtraction operator.
|
|
||||||
Sub,
|
|
||||||
/// A unary negation operator.
|
|
||||||
Neg,
|
|
||||||
/// A binary multiplication operator.
|
|
||||||
Mul,
|
|
||||||
/// A binary division operator.
|
|
||||||
Div,
|
|
||||||
/// A binary modulo operator.
|
|
||||||
Mod,
|
|
||||||
/// A binary exponentiation operator.
|
|
||||||
Exp,
|
|
||||||
|
|
||||||
/// A binary equality comparator.
|
|
||||||
Eq,
|
|
||||||
/// A binary inequality comparator.
|
|
||||||
Neq,
|
|
||||||
/// A binary greater-than comparator.
|
|
||||||
Gt,
|
|
||||||
/// A binary lower-than comparator.
|
|
||||||
Lt,
|
|
||||||
/// A binary greater-than-or-equal comparator.
|
|
||||||
Geq,
|
|
||||||
/// A binary lower-than-or-equal comparator.
|
|
||||||
Leq,
|
|
||||||
/// A binary logical and operator.
|
|
||||||
And,
|
|
||||||
/// A binary logical or operator.
|
|
||||||
Or,
|
|
||||||
/// A binary logical not operator.
|
|
||||||
Not,
|
|
||||||
|
|
||||||
/// A binary assignment operator.
|
|
||||||
Assign,
|
|
||||||
/// A binary add-assign operator.
|
|
||||||
AddAssign,
|
|
||||||
/// A binary subtract-assign operator.
|
|
||||||
SubAssign,
|
|
||||||
/// A binary multiply-assign operator.
|
|
||||||
MulAssign,
|
|
||||||
/// A binary divide-assign operator.
|
|
||||||
DivAssign,
|
|
||||||
/// A binary modulo-assign operator.
|
|
||||||
ModAssign,
|
|
||||||
/// A binary exponentiate-assign operator.
|
|
||||||
ExpAssign,
|
|
||||||
/// A binary and-assign operator.
|
|
||||||
AndAssign,
|
|
||||||
/// A binary or-assign operator.
|
|
||||||
OrAssign,
|
|
||||||
|
|
||||||
/// An n-ary tuple constructor.
|
|
||||||
Tuple,
|
|
||||||
/// An n-ary subexpression chain.
|
|
||||||
Chain,
|
|
||||||
|
|
||||||
/// A constant value.
|
|
||||||
Const {
|
|
||||||
/** The value of the constant. */
|
|
||||||
value: Value,
|
|
||||||
},
|
|
||||||
/// A write to a variable identifier.
|
|
||||||
VariableIdentifierWrite {
|
|
||||||
/// The identifier of the variable.
|
|
||||||
identifier: String,
|
|
||||||
},
|
|
||||||
/// A read from a variable identifier.
|
|
||||||
VariableIdentifierRead {
|
|
||||||
/// The identifier of the variable.
|
|
||||||
identifier: String,
|
|
||||||
},
|
|
||||||
/// A function identifier.
|
|
||||||
FunctionIdentifier {
|
|
||||||
/// The identifier of the function.
|
|
||||||
identifier: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Operator {
|
|
||||||
pub(crate) fn value(value: Value) -> Self {
|
|
||||||
Operator::Const { value }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn variable_identifier_write(identifier: String) -> Self {
|
|
||||||
Operator::VariableIdentifierWrite { identifier }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn variable_identifier_read(identifier: String) -> Self {
|
|
||||||
Operator::VariableIdentifierRead { identifier }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn function_identifier(identifier: String) -> Self {
|
|
||||||
Operator::FunctionIdentifier { identifier }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the precedence of the operator.
|
|
||||||
/// A high precedence means that the operator has priority to be deeper in the tree.
|
|
||||||
pub(crate) const fn precedence(&self) -> i32 {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
match self {
|
|
||||||
RootNode => 200,
|
|
||||||
|
|
||||||
Add | Sub => 95,
|
|
||||||
Neg => 110,
|
|
||||||
Mul | Div | Mod => 100,
|
|
||||||
Exp => 120,
|
|
||||||
|
|
||||||
Eq | Neq | Gt | Lt | Geq | Leq => 80,
|
|
||||||
And => 75,
|
|
||||||
Or => 70,
|
|
||||||
Not => 110,
|
|
||||||
|
|
||||||
Assign | AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign
|
|
||||||
| AndAssign | OrAssign => 50,
|
|
||||||
|
|
||||||
Tuple => 40,
|
|
||||||
Chain => 0,
|
|
||||||
|
|
||||||
Const { .. } => 200,
|
|
||||||
VariableIdentifierWrite { .. } | VariableIdentifierRead { .. } => 200,
|
|
||||||
FunctionIdentifier { .. } => 190,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if chains of operators with the same precedence as this one should be evaluated left-to-right,
|
|
||||||
/// and false if they should be evaluated right-to-left.
|
|
||||||
/// Left-to-right chaining has priority if operators with different order but same precedence are chained.
|
|
||||||
pub(crate) const fn is_left_to_right(&self) -> bool {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
!matches!(self, Assign | FunctionIdentifier { .. })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if chains of this operator should be flattened into one operator with many arguments.
|
|
||||||
pub(crate) const fn is_sequence(&self) -> bool {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
matches!(self, Tuple | Chain)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True if this operator is a leaf, meaning it accepts no arguments.
|
|
||||||
// Make this a const fn as soon as whatever is missing gets stable (issue #57563)
|
|
||||||
pub(crate) fn is_leaf(&self) -> bool {
|
|
||||||
self.max_argument_amount() == Some(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the maximum amount of arguments required by this operator.
|
|
||||||
pub(crate) const fn max_argument_amount(&self) -> Option<usize> {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
match self {
|
|
||||||
Add | Sub | Mul | Div | Mod | Exp | Eq | Neq | Gt | Lt | Geq | Leq | And | Or
|
|
||||||
| Assign | AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign
|
|
||||||
| AndAssign | OrAssign => Some(2),
|
|
||||||
Tuple | Chain => None,
|
|
||||||
Not | Neg | RootNode => Some(1),
|
|
||||||
Const { .. } => Some(0),
|
|
||||||
VariableIdentifierWrite { .. } | VariableIdentifierRead { .. } => Some(0),
|
|
||||||
FunctionIdentifier { .. } => Some(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this operator is unary, i.e. it requires exactly one argument.
|
|
||||||
pub(crate) fn is_unary(&self) -> bool {
|
|
||||||
self.max_argument_amount() == Some(1) && *self != Operator::RootNode
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator with the given arguments and context.
|
|
||||||
pub(crate) fn eval(&self, arguments: &[Value], context: &VariableMap) -> Result<Value> {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
RootNode => {
|
|
||||||
if let Some(first) = arguments.first() {
|
|
||||||
Ok(first.clone())
|
|
||||||
} else {
|
|
||||||
Ok(Value::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Add => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
expect_number_or_string(&arguments[0])?;
|
|
||||||
expect_number_or_string(&arguments[1])?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_string(), arguments[1].as_string()) {
|
|
||||||
let mut result = String::with_capacity(a.len() + b.len());
|
|
||||||
result.push_str(a);
|
|
||||||
result.push_str(b);
|
|
||||||
Ok(Value::String(result))
|
|
||||||
} else if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
let result = a.checked_add(b);
|
|
||||||
if let Some(result) = result {
|
|
||||||
Ok(Value::Integer(result))
|
|
||||||
} else {
|
|
||||||
Err(Error::addition_error(
|
|
||||||
arguments[0].clone(),
|
|
||||||
arguments[1].clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else if let (Ok(a), Ok(b)) = (arguments[0].as_number(), arguments[1].as_number())
|
|
||||||
{
|
|
||||||
Ok(Value::Float(a + b))
|
|
||||||
} else {
|
|
||||||
Err(Error::wrong_type_combination(
|
|
||||||
self.clone(),
|
|
||||||
vec![
|
|
||||||
arguments.get(0).unwrap().into(),
|
|
||||||
arguments.get(1).unwrap().into(),
|
|
||||||
],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Sub => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
arguments[0].as_number()?;
|
|
||||||
arguments[1].as_number()?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
let result = a.checked_sub(b);
|
|
||||||
if let Some(result) = result {
|
|
||||||
Ok(Value::Integer(result))
|
|
||||||
} else {
|
|
||||||
Err(Error::subtraction_error(
|
|
||||||
arguments[0].clone(),
|
|
||||||
arguments[1].clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(
|
|
||||||
arguments[0].as_number()? - arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Neg => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 1)?;
|
|
||||||
arguments[0].as_number()?;
|
|
||||||
|
|
||||||
if let Ok(a) = arguments[0].as_int() {
|
|
||||||
let result = a.checked_neg();
|
|
||||||
if let Some(result) = result {
|
|
||||||
Ok(Value::Integer(result))
|
|
||||||
} else {
|
|
||||||
Err(Error::negation_error(arguments[0].clone()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(-arguments[0].as_number()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Mul => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
arguments[0].as_number()?;
|
|
||||||
arguments[1].as_number()?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
let result = a.checked_mul(b);
|
|
||||||
if let Some(result) = result {
|
|
||||||
Ok(Value::Integer(result))
|
|
||||||
} else {
|
|
||||||
Err(Error::multiplication_error(
|
|
||||||
arguments[0].clone(),
|
|
||||||
arguments[1].clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(
|
|
||||||
arguments[0].as_number()? * arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Div => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
arguments[0].as_number()?;
|
|
||||||
arguments[1].as_number()?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
let result = a.checked_div(b);
|
|
||||||
if let Some(result) = result {
|
|
||||||
Ok(Value::Integer(result))
|
|
||||||
} else {
|
|
||||||
Err(Error::division_error(
|
|
||||||
arguments[0].clone(),
|
|
||||||
arguments[1].clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(
|
|
||||||
arguments[0].as_number()? / arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Mod => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
arguments[0].as_number()?;
|
|
||||||
arguments[1].as_number()?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
let result = a.checked_rem(b);
|
|
||||||
if let Some(result) = result {
|
|
||||||
Ok(Value::Integer(result))
|
|
||||||
} else {
|
|
||||||
Err(Error::modulation_error(
|
|
||||||
arguments[0].clone(),
|
|
||||||
arguments[1].clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(
|
|
||||||
arguments[0].as_number()? % arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Exp => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
arguments[0].as_number()?;
|
|
||||||
arguments[1].as_number()?;
|
|
||||||
|
|
||||||
Ok(Value::Float(
|
|
||||||
arguments[0].as_number()?.powf(arguments[1].as_number()?),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Eq => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
|
|
||||||
Ok(Value::Boolean(arguments[0] == arguments[1]))
|
|
||||||
}
|
|
||||||
Neq => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
|
|
||||||
Ok(Value::Boolean(arguments[0] != arguments[1]))
|
|
||||||
}
|
|
||||||
Gt => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
expect_number_or_string(&arguments[0])?;
|
|
||||||
expect_number_or_string(&arguments[1])?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_string(), arguments[1].as_string()) {
|
|
||||||
Ok(Value::Boolean(a > b))
|
|
||||||
} else if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
Ok(Value::Boolean(a > b))
|
|
||||||
} else {
|
|
||||||
Ok(Value::Boolean(
|
|
||||||
arguments[0].as_number()? > arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Lt => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
expect_number_or_string(&arguments[0])?;
|
|
||||||
expect_number_or_string(&arguments[1])?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_string(), arguments[1].as_string()) {
|
|
||||||
Ok(Value::Boolean(a < b))
|
|
||||||
} else if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
Ok(Value::Boolean(a < b))
|
|
||||||
} else {
|
|
||||||
Ok(Value::Boolean(
|
|
||||||
arguments[0].as_number()? < arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Geq => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
expect_number_or_string(&arguments[0])?;
|
|
||||||
expect_number_or_string(&arguments[1])?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_string(), arguments[1].as_string()) {
|
|
||||||
Ok(Value::Boolean(a >= b))
|
|
||||||
} else if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
Ok(Value::Boolean(a >= b))
|
|
||||||
} else {
|
|
||||||
Ok(Value::Boolean(
|
|
||||||
arguments[0].as_number()? >= arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Leq => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
expect_number_or_string(&arguments[0])?;
|
|
||||||
expect_number_or_string(&arguments[1])?;
|
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_string(), arguments[1].as_string()) {
|
|
||||||
Ok(Value::Boolean(a <= b))
|
|
||||||
} else if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
|
||||||
Ok(Value::Boolean(a <= b))
|
|
||||||
} else {
|
|
||||||
Ok(Value::Boolean(
|
|
||||||
arguments[0].as_number()? <= arguments[1].as_number()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
And => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
let a = arguments[0].as_boolean()?;
|
|
||||||
let b = arguments[1].as_boolean()?;
|
|
||||||
|
|
||||||
Ok(Value::Boolean(a && b))
|
|
||||||
}
|
|
||||||
Or => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
let a = arguments[0].as_boolean()?;
|
|
||||||
let b = arguments[1].as_boolean()?;
|
|
||||||
|
|
||||||
Ok(Value::Boolean(a || b))
|
|
||||||
}
|
|
||||||
Not => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 1)?;
|
|
||||||
let a = arguments[0].as_boolean()?;
|
|
||||||
|
|
||||||
Ok(Value::Boolean(!a))
|
|
||||||
}
|
|
||||||
Assign | AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign
|
|
||||||
| AndAssign | OrAssign => Err(Error::ContextNotMutable),
|
|
||||||
Tuple => Ok(Value::List(arguments.into())),
|
|
||||||
Chain => {
|
|
||||||
if arguments.is_empty() {
|
|
||||||
return Err(Error::expect_operator_argument_amount(0, 1).unwrap_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(arguments.last().cloned().unwrap_or(Value::Empty))
|
|
||||||
}
|
|
||||||
Const { value } => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 0)?;
|
|
||||||
|
|
||||||
Ok(value.clone())
|
|
||||||
}
|
|
||||||
VariableIdentifierWrite { identifier } => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 0)?;
|
|
||||||
|
|
||||||
Ok(identifier.clone().into())
|
|
||||||
}
|
|
||||||
VariableIdentifierRead { identifier } => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 0)?;
|
|
||||||
|
|
||||||
if let Some(value) = context.get_value(identifier)? {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(Error::VariableIdentifierNotFound(identifier.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FunctionIdentifier { identifier } => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 1)?;
|
|
||||||
let arguments = &arguments[0];
|
|
||||||
|
|
||||||
context.call_function(identifier, arguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator with the given arguments and mutable context.
|
|
||||||
pub(crate) fn eval_mut(&self, arguments: &[Value], context: &mut VariableMap) -> Result<Value> {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
match self {
|
|
||||||
Assign => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
let target = arguments[0].as_string()?;
|
|
||||||
context.set_value(target, arguments[1].clone())?;
|
|
||||||
|
|
||||||
Ok(Value::Empty)
|
|
||||||
}
|
|
||||||
AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign | AndAssign
|
|
||||||
| OrAssign => {
|
|
||||||
Error::expect_operator_argument_amount(arguments.len(), 2)?;
|
|
||||||
|
|
||||||
let target = arguments[0].as_string()?;
|
|
||||||
let left_value = Operator::VariableIdentifierRead {
|
|
||||||
identifier: target.clone(),
|
|
||||||
}
|
|
||||||
.eval(&Vec::new(), context)?;
|
|
||||||
let arguments = vec![left_value, arguments[1].clone()];
|
|
||||||
|
|
||||||
let result = match self {
|
|
||||||
AddAssign => Operator::Add.eval(&arguments, context),
|
|
||||||
SubAssign => Operator::Sub.eval(&arguments, context),
|
|
||||||
MulAssign => Operator::Mul.eval(&arguments, context),
|
|
||||||
DivAssign => Operator::Div.eval(&arguments, context),
|
|
||||||
ModAssign => Operator::Mod.eval(&arguments, context),
|
|
||||||
ExpAssign => Operator::Exp.eval(&arguments, context),
|
|
||||||
AndAssign => Operator::And.eval(&arguments, context),
|
|
||||||
OrAssign => Operator::Or.eval(&arguments, context),
|
|
||||||
_ => unreachable!(
|
|
||||||
"Forgot to add a match arm for an assign operation: {}",
|
|
||||||
self
|
|
||||||
),
|
|
||||||
}?;
|
|
||||||
context.set_value(target, result)?;
|
|
||||||
|
|
||||||
Ok(Value::Empty)
|
|
||||||
}
|
|
||||||
_ => self.eval(arguments, context),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Operator {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
use crate::operator::Operator::*;
|
|
||||||
match self {
|
|
||||||
RootNode => Ok(()),
|
|
||||||
Add => write!(f, "+"),
|
|
||||||
Sub => write!(f, "-"),
|
|
||||||
Neg => write!(f, "-"),
|
|
||||||
Mul => write!(f, "*"),
|
|
||||||
Div => write!(f, "/"),
|
|
||||||
Mod => write!(f, "%"),
|
|
||||||
Exp => write!(f, "^"),
|
|
||||||
|
|
||||||
Eq => write!(f, "=="),
|
|
||||||
Neq => write!(f, "!="),
|
|
||||||
Gt => write!(f, ">"),
|
|
||||||
Lt => write!(f, "<"),
|
|
||||||
Geq => write!(f, ">="),
|
|
||||||
Leq => write!(f, "<="),
|
|
||||||
And => write!(f, "&&"),
|
|
||||||
Or => write!(f, "||"),
|
|
||||||
Not => write!(f, "!"),
|
|
||||||
|
|
||||||
Assign => write!(f, " = "),
|
|
||||||
AddAssign => write!(f, " += "),
|
|
||||||
SubAssign => write!(f, " -= "),
|
|
||||||
MulAssign => write!(f, " *= "),
|
|
||||||
DivAssign => write!(f, " /= "),
|
|
||||||
ModAssign => write!(f, " %= "),
|
|
||||||
ExpAssign => write!(f, " ^= "),
|
|
||||||
AndAssign => write!(f, " &&= "),
|
|
||||||
OrAssign => write!(f, " ||= "),
|
|
||||||
|
|
||||||
Tuple => write!(f, ", "),
|
|
||||||
Chain => write!(f, "; "),
|
|
||||||
|
|
||||||
Const { value } => write!(f, "{}", value),
|
|
||||||
VariableIdentifierWrite { identifier } | VariableIdentifierRead { identifier } => {
|
|
||||||
write!(f, "{}", identifier)
|
|
||||||
}
|
|
||||||
FunctionIdentifier { identifier } => write!(f, "{}", identifier),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
562
src/token.rs
562
src/token.rs
@ -1,562 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
pub enum Token {
|
|
||||||
// Arithmetic
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Star,
|
|
||||||
Slash,
|
|
||||||
Percent,
|
|
||||||
Hat,
|
|
||||||
|
|
||||||
// Logic
|
|
||||||
Eq,
|
|
||||||
Neq,
|
|
||||||
Gt,
|
|
||||||
Lt,
|
|
||||||
Geq,
|
|
||||||
Leq,
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
Not,
|
|
||||||
|
|
||||||
// Precedence
|
|
||||||
LBrace,
|
|
||||||
RBrace,
|
|
||||||
|
|
||||||
// Assignment
|
|
||||||
Assign,
|
|
||||||
PlusAssign,
|
|
||||||
MinusAssign,
|
|
||||||
StarAssign,
|
|
||||||
SlashAssign,
|
|
||||||
PercentAssign,
|
|
||||||
HatAssign,
|
|
||||||
AndAssign,
|
|
||||||
OrAssign,
|
|
||||||
|
|
||||||
// Special
|
|
||||||
Comma,
|
|
||||||
Semicolon,
|
|
||||||
Yield(String, String),
|
|
||||||
|
|
||||||
// Values, Variables and Functions
|
|
||||||
Identifier(String),
|
|
||||||
Float(f64),
|
|
||||||
Int(i64),
|
|
||||||
Boolean(bool),
|
|
||||||
String(String),
|
|
||||||
Function(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A partial token is an input character whose meaning depends on the characters around it.
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum PartialToken {
|
|
||||||
/// A partial token that unambiguously maps to a single token.
|
|
||||||
Token(Token),
|
|
||||||
/// A partial token that is a literal.
|
|
||||||
Literal(String),
|
|
||||||
/// A plus character '+'.
|
|
||||||
Plus,
|
|
||||||
/// A minus character '-'.
|
|
||||||
Minus,
|
|
||||||
/// A star character '*'.
|
|
||||||
Star,
|
|
||||||
/// A slash character '/'.
|
|
||||||
Slash,
|
|
||||||
/// A percent character '%'.
|
|
||||||
Percent,
|
|
||||||
/// A hat character '^'.
|
|
||||||
Hat,
|
|
||||||
/// A whitespace character, e.g. ' '.
|
|
||||||
Whitespace,
|
|
||||||
/// An equal-to character '='.
|
|
||||||
Eq,
|
|
||||||
/// An exclamation mark character '!'.
|
|
||||||
ExclamationMark,
|
|
||||||
/// A greater-than character '>'.
|
|
||||||
Gt,
|
|
||||||
/// A lower-than character '<'.
|
|
||||||
Lt,
|
|
||||||
/// An ampersand character '&'.
|
|
||||||
Ampersand,
|
|
||||||
/// A vertical bar character '|'.
|
|
||||||
VerticalBar,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make this a const fn as soon as is_whitespace and to_string get stable (issue #57563)
|
|
||||||
fn char_to_partial_token(c: char) -> PartialToken {
|
|
||||||
match c {
|
|
||||||
'+' => PartialToken::Plus,
|
|
||||||
'-' => PartialToken::Minus,
|
|
||||||
'*' => PartialToken::Star,
|
|
||||||
'/' => PartialToken::Slash,
|
|
||||||
'%' => PartialToken::Percent,
|
|
||||||
'^' => PartialToken::Hat,
|
|
||||||
|
|
||||||
'(' => PartialToken::Token(Token::LBrace),
|
|
||||||
')' => PartialToken::Token(Token::RBrace),
|
|
||||||
|
|
||||||
',' => PartialToken::Token(Token::Comma),
|
|
||||||
';' => PartialToken::Token(Token::Semicolon),
|
|
||||||
|
|
||||||
'=' => PartialToken::Eq,
|
|
||||||
'!' => PartialToken::ExclamationMark,
|
|
||||||
'>' => PartialToken::Gt,
|
|
||||||
'<' => PartialToken::Lt,
|
|
||||||
'&' => PartialToken::Ampersand,
|
|
||||||
'|' => PartialToken::VerticalBar,
|
|
||||||
|
|
||||||
c => {
|
|
||||||
if c.is_whitespace() {
|
|
||||||
PartialToken::Whitespace
|
|
||||||
} else {
|
|
||||||
PartialToken::Literal(c.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token {
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
|
||||||
pub(crate) const fn is_leftsided_value(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Token::Plus => false,
|
|
||||||
Token::Minus => false,
|
|
||||||
Token::Star => false,
|
|
||||||
Token::Slash => false,
|
|
||||||
Token::Percent => false,
|
|
||||||
Token::Hat => 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::Comma => false,
|
|
||||||
Token::Semicolon => false,
|
|
||||||
Token::Yield(_, _) => false,
|
|
||||||
|
|
||||||
Token::Assign => false,
|
|
||||||
Token::PlusAssign => false,
|
|
||||||
Token::MinusAssign => false,
|
|
||||||
Token::StarAssign => false,
|
|
||||||
Token::SlashAssign => false,
|
|
||||||
Token::PercentAssign => false,
|
|
||||||
Token::HatAssign => false,
|
|
||||||
Token::AndAssign => false,
|
|
||||||
Token::OrAssign => false,
|
|
||||||
|
|
||||||
Token::Identifier(_) => true,
|
|
||||||
Token::Float(_) => true,
|
|
||||||
Token::Int(_) => true,
|
|
||||||
Token::Boolean(_) => true,
|
|
||||||
Token::String(_) => true,
|
|
||||||
Token::Function(_) => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
|
||||||
pub(crate) const fn is_rightsided_value(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Token::Plus => false,
|
|
||||||
Token::Minus => false,
|
|
||||||
Token::Star => false,
|
|
||||||
Token::Slash => false,
|
|
||||||
Token::Percent => false,
|
|
||||||
Token::Hat => 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 => false,
|
|
||||||
Token::RBrace => true,
|
|
||||||
|
|
||||||
Token::Comma => false,
|
|
||||||
Token::Semicolon => false,
|
|
||||||
Token::Yield(_, _) => false,
|
|
||||||
|
|
||||||
Token::Assign => false,
|
|
||||||
Token::PlusAssign => false,
|
|
||||||
Token::MinusAssign => false,
|
|
||||||
Token::StarAssign => false,
|
|
||||||
Token::SlashAssign => false,
|
|
||||||
Token::PercentAssign => false,
|
|
||||||
Token::HatAssign => false,
|
|
||||||
Token::AndAssign => false,
|
|
||||||
Token::OrAssign => false,
|
|
||||||
|
|
||||||
Token::Identifier(_) => true,
|
|
||||||
Token::Float(_) => true,
|
|
||||||
Token::Int(_) => true,
|
|
||||||
Token::Boolean(_) => true,
|
|
||||||
Token::String(_) => true,
|
|
||||||
Token::Function(_) => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
|
||||||
pub(crate) fn is_assignment(&self) -> bool {
|
|
||||||
use Token::*;
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Assign
|
|
||||||
| PlusAssign
|
|
||||||
| MinusAssign
|
|
||||||
| StarAssign
|
|
||||||
| SlashAssign
|
|
||||||
| PercentAssign
|
|
||||||
| HatAssign
|
|
||||||
| AndAssign
|
|
||||||
| OrAssign
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses an escape sequence within a string literal.
|
|
||||||
fn parse_escape_sequence<Iter: Iterator<Item = char>>(iter: &mut Iter) -> Result<char> {
|
|
||||||
match iter.next() {
|
|
||||||
Some('"') => Ok('"'),
|
|
||||||
Some('\\') => Ok('\\'),
|
|
||||||
Some(c) => Err(Error::IllegalEscapeSequence(format!("\\{}", c))),
|
|
||||||
None => Err(Error::IllegalEscapeSequence("\\".to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a string value from the given character iterator.
|
|
||||||
///
|
|
||||||
/// The first character from the iterator is interpreted as first character of the string.
|
|
||||||
/// The string is terminated by a double quote `"`.
|
|
||||||
/// Occurrences of `"` within the string can be escaped with `\`.
|
|
||||||
/// The backslash needs to be escaped with another backslash `\`.
|
|
||||||
fn parse_string_literal<Iter: Iterator<Item = char>>(mut iter: &mut Iter) -> Result<PartialToken> {
|
|
||||||
let mut result = String::new();
|
|
||||||
|
|
||||||
while let Some(c) = iter.next() {
|
|
||||||
match c {
|
|
||||||
'"' => break,
|
|
||||||
'\\' => result.push(parse_escape_sequence(&mut iter)?),
|
|
||||||
c => result.push(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PartialToken::Token(Token::String(result)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_function<Iter: Iterator<Item = char>>(mut iter: &mut Iter) -> Result<PartialToken> {
|
|
||||||
let mut result = String::new();
|
|
||||||
|
|
||||||
while let Some(c) = iter.next() {
|
|
||||||
match c {
|
|
||||||
'\'' => break,
|
|
||||||
'\\' => result.push(parse_escape_sequence(&mut iter)?),
|
|
||||||
c => result.push(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PartialToken::Token(Token::Function(result)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a string to a vector of partial tokens.
|
|
||||||
fn str_to_partial_tokens(string: &str) -> Result<Vec<PartialToken>> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
let mut iter = string.chars().peekable();
|
|
||||||
|
|
||||||
while let Some(c) = iter.next() {
|
|
||||||
if c == '"' {
|
|
||||||
result.push(parse_string_literal(&mut iter)?);
|
|
||||||
} else if c == '\'' {
|
|
||||||
result.push(parse_function(&mut iter)?)
|
|
||||||
} else {
|
|
||||||
let partial_token = char_to_partial_token(c);
|
|
||||||
|
|
||||||
let if_let_successful =
|
|
||||||
if let (Some(PartialToken::Literal(last)), PartialToken::Literal(literal)) =
|
|
||||||
(result.last_mut(), &partial_token)
|
|
||||||
{
|
|
||||||
last.push_str(literal);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !if_let_successful {
|
|
||||||
result.push(partial_token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves all partial tokens by converting them to complex tokens.
|
|
||||||
fn partial_tokens_to_tokens(mut tokens: &[PartialToken]) -> Result<Vec<Token>> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
while !tokens.is_empty() {
|
|
||||||
let first = tokens[0].clone();
|
|
||||||
let second = tokens.get(1).cloned();
|
|
||||||
let third = tokens.get(2).cloned();
|
|
||||||
let mut cutoff = 2;
|
|
||||||
|
|
||||||
result.extend(
|
|
||||||
match first {
|
|
||||||
PartialToken::Token(token) => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(token)
|
|
||||||
}
|
|
||||||
PartialToken::Plus => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::PlusAssign),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Plus)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Minus => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::MinusAssign),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Minus)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Star => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::StarAssign),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Star)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Slash => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::SlashAssign),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Slash)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Percent => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::PercentAssign),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Percent)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Hat => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::HatAssign),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Hat)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Literal(literal) => {
|
|
||||||
cutoff = 1;
|
|
||||||
if let Ok(number) = parse_dec_or_hex(&literal) {
|
|
||||||
Some(Token::Int(number))
|
|
||||||
} else if let Ok(number) = literal.parse::<f64>() {
|
|
||||||
Some(Token::Float(number))
|
|
||||||
} else if let Ok(boolean) = literal.parse::<bool>() {
|
|
||||||
Some(Token::Boolean(boolean))
|
|
||||||
} else {
|
|
||||||
// If there are two tokens following this one, check if the next one is
|
|
||||||
// a plus or a minus. If so, then attempt to parse all three tokens as a
|
|
||||||
// scientific notation number of the form `<coefficient>e{+,-}<exponent>`,
|
|
||||||
// for example [Literal("10e"), Minus, Literal("3")] => "1e-3".parse().
|
|
||||||
match (second, third) {
|
|
||||||
(Some(second), Some(third))
|
|
||||||
if second == PartialToken::Minus
|
|
||||||
|| second == PartialToken::Plus =>
|
|
||||||
{
|
|
||||||
if let Ok(number) =
|
|
||||||
format!("{}{}{}", literal, second, third).parse::<f64>()
|
|
||||||
{
|
|
||||||
cutoff = 3;
|
|
||||||
Some(Token::Float(number))
|
|
||||||
} else {
|
|
||||||
Some(Token::Identifier(literal.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Some(Token::Identifier(literal.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PartialToken::Whitespace => {
|
|
||||||
cutoff = 1;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
PartialToken::Eq => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::Eq),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Assign)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::ExclamationMark => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::Neq),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Not)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Gt => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::Geq),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Gt)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Lt => match second {
|
|
||||||
Some(PartialToken::Eq) => Some(Token::Leq),
|
|
||||||
_ => {
|
|
||||||
cutoff = 1;
|
|
||||||
Some(Token::Lt)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartialToken::Ampersand => match second {
|
|
||||||
Some(PartialToken::Ampersand) => match third {
|
|
||||||
Some(PartialToken::Eq) => {
|
|
||||||
cutoff = 3;
|
|
||||||
Some(Token::AndAssign)
|
|
||||||
}
|
|
||||||
_ => Some(Token::And),
|
|
||||||
},
|
|
||||||
_ => return Err(Error::unmatched_partial_token(first, second)),
|
|
||||||
},
|
|
||||||
PartialToken::VerticalBar => match second {
|
|
||||||
Some(PartialToken::VerticalBar) => match third {
|
|
||||||
Some(PartialToken::Eq) => {
|
|
||||||
cutoff = 3;
|
|
||||||
Some(Token::OrAssign)
|
|
||||||
}
|
|
||||||
_ => Some(Token::Or),
|
|
||||||
},
|
|
||||||
_ => return Err(Error::unmatched_partial_token(first, second)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.into_iter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
tokens = &tokens[cutoff..];
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Token {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
use self::Token::*;
|
|
||||||
match self {
|
|
||||||
Plus => write!(f, "+"),
|
|
||||||
Minus => write!(f, "-"),
|
|
||||||
Star => write!(f, "*"),
|
|
||||||
Slash => write!(f, "/"),
|
|
||||||
Percent => write!(f, "%"),
|
|
||||||
Hat => write!(f, "^"),
|
|
||||||
|
|
||||||
// Logic
|
|
||||||
Eq => write!(f, "=="),
|
|
||||||
Neq => write!(f, "!="),
|
|
||||||
Gt => write!(f, ">"),
|
|
||||||
Lt => write!(f, "<"),
|
|
||||||
Geq => write!(f, ">="),
|
|
||||||
Leq => write!(f, "<="),
|
|
||||||
And => write!(f, "&&"),
|
|
||||||
Or => write!(f, "||"),
|
|
||||||
Not => write!(f, "!"),
|
|
||||||
|
|
||||||
// Precedence
|
|
||||||
LBrace => write!(f, "("),
|
|
||||||
RBrace => write!(f, ")"),
|
|
||||||
|
|
||||||
// Assignment
|
|
||||||
Assign => write!(f, "="),
|
|
||||||
PlusAssign => write!(f, "+="),
|
|
||||||
MinusAssign => write!(f, "-="),
|
|
||||||
StarAssign => write!(f, "*="),
|
|
||||||
SlashAssign => write!(f, "/="),
|
|
||||||
PercentAssign => write!(f, "%="),
|
|
||||||
HatAssign => write!(f, "^="),
|
|
||||||
AndAssign => write!(f, "&&="),
|
|
||||||
OrAssign => write!(f, "||="),
|
|
||||||
|
|
||||||
// Special
|
|
||||||
Comma => write!(f, ","),
|
|
||||||
Semicolon => write!(f, ";"),
|
|
||||||
|
|
||||||
// Values => write!(f, ""), Variables and Functions
|
|
||||||
Identifier(identifier) => identifier.fmt(f),
|
|
||||||
Float(float) => float.fmt(f),
|
|
||||||
Int(int) => int.fmt(f),
|
|
||||||
Boolean(boolean) => boolean.fmt(f),
|
|
||||||
String(string) => fmt::Debug::fmt(string, f),
|
|
||||||
Function(string) => write!(f, "'{string}'"),
|
|
||||||
Yield(_, _) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for PartialToken {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
use self::PartialToken::*;
|
|
||||||
match self {
|
|
||||||
Token(token) => token.fmt(f),
|
|
||||||
Literal(literal) => literal.fmt(f),
|
|
||||||
Whitespace => write!(f, " "),
|
|
||||||
Plus => write!(f, "+"),
|
|
||||||
Minus => write!(f, "-"),
|
|
||||||
Star => write!(f, "*"),
|
|
||||||
Slash => write!(f, "/"),
|
|
||||||
Percent => write!(f, "%"),
|
|
||||||
Hat => write!(f, "^"),
|
|
||||||
Eq => write!(f, "="),
|
|
||||||
ExclamationMark => write!(f, "!"),
|
|
||||||
Gt => write!(f, ">"),
|
|
||||||
Lt => write!(f, "<"),
|
|
||||||
Ampersand => write!(f, "&"),
|
|
||||||
VerticalBar => write!(f, "|"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn tokenize(string: &str) -> Result<Vec<Token>> {
|
|
||||||
partial_tokens_to_tokens(&str_to_partial_tokens(string)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_dec_or_hex(literal: &str) -> std::result::Result<i64, std::num::ParseIntError> {
|
|
||||||
if let Some(literal) = literal.strip_prefix("0x") {
|
|
||||||
literal.parse()
|
|
||||||
} else {
|
|
||||||
literal.parse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::token::{tokenize, Token};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn assignment_lhs_is_identifier() {
|
|
||||||
let tokens = tokenize("a = 1").unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
tokens.as_slice(),
|
|
||||||
[
|
|
||||||
Token::Identifier("a".to_string()),
|
|
||||||
Token::Assign,
|
|
||||||
Token::Int(1)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -233,8 +233,7 @@ impl eframe::App for GuiApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if submit.clicked() {
|
if submit.clicked() {
|
||||||
self.eval_result =
|
todo!()
|
||||||
eval_with_context(&self.text_edit_buffer, &mut self.whale_context);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
use crate::{operator::Operator, Node};
|
|
||||||
use std::slice::{Iter, IterMut};
|
|
||||||
|
|
||||||
/// An iterator that traverses an operator tree in pre-order.
|
|
||||||
pub struct NodeIter<'a> {
|
|
||||||
stack: Vec<Iter<'a, Node>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> NodeIter<'a> {
|
|
||||||
fn new(node: &'a Node) -> Self {
|
|
||||||
NodeIter {
|
|
||||||
stack: vec![node.children.iter()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for NodeIter<'a> {
|
|
||||||
type Item = &'a Node;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
loop {
|
|
||||||
let mut result = None;
|
|
||||||
|
|
||||||
if let Some(last) = self.stack.last_mut() {
|
|
||||||
if let Some(next) = last.next() {
|
|
||||||
result = Some(next);
|
|
||||||
} else {
|
|
||||||
// Can not fail because we just borrowed last.
|
|
||||||
// We just checked that the iterator is empty, so we can safely discard it.
|
|
||||||
let _ = self.stack.pop().unwrap();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(result) = result {
|
|
||||||
self.stack.push(result.children.iter());
|
|
||||||
return Some(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An iterator that mutably traverses an operator tree in pre-order.
|
|
||||||
pub struct OperatorIterMut<'a> {
|
|
||||||
stack: Vec<IterMut<'a, Node>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> OperatorIterMut<'a> {
|
|
||||||
fn new(node: &'a mut Node) -> Self {
|
|
||||||
OperatorIterMut {
|
|
||||||
stack: vec![node.children.iter_mut()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for OperatorIterMut<'a> {
|
|
||||||
type Item = &'a mut Operator;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
loop {
|
|
||||||
let mut result = None;
|
|
||||||
|
|
||||||
if let Some(last) = self.stack.last_mut() {
|
|
||||||
if let Some(next) = last.next() {
|
|
||||||
result = Some(next);
|
|
||||||
} else {
|
|
||||||
// Can not fail because we just borrowed last.
|
|
||||||
// We just checked that the iterator is empty, so we can safely discard it.
|
|
||||||
let _ = self.stack.pop().unwrap();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(result) = result {
|
|
||||||
self.stack.push(result.children.iter_mut());
|
|
||||||
return Some(&mut result.operator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
/// Returns an iterator over all nodes in this tree.
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &Node> {
|
|
||||||
NodeIter::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable iterator over all operators in this tree.
|
|
||||||
pub fn iter_operators_mut(&mut self) -> impl Iterator<Item = &mut Operator> {
|
|
||||||
OperatorIterMut::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
691
src/tree/mod.rs
691
src/tree/mod.rs
@ -1,691 +0,0 @@
|
|||||||
use crate::Function;
|
|
||||||
use crate::{token::Token, VariableMap};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{Error, Result},
|
|
||||||
operator::*,
|
|
||||||
value::Value,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
mem,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
|
||||||
mod iter;
|
|
||||||
|
|
||||||
/// A node in the operator tree.
|
|
||||||
/// The operator tree is created by the crate-level `build_operator_tree` method.
|
|
||||||
/// It can be evaluated for a given context with the `Node::eval` method.
|
|
||||||
///
|
|
||||||
/// The advantage of constructing the operator tree separately from the actual evaluation is that it can be evaluated arbitrarily often with different contexts.
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub struct Node {
|
|
||||||
operator: Operator,
|
|
||||||
children: Vec<Node>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
fn new(operator: Operator) -> Self {
|
|
||||||
Self {
|
|
||||||
children: Vec::new(),
|
|
||||||
operator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root_node() -> Self {
|
|
||||||
Self::new(Operator::RootNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_identifiers(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
|
||||||
Operator::VariableIdentifierWrite { identifier }
|
|
||||||
| Operator::VariableIdentifierRead { identifier }
|
|
||||||
| Operator::FunctionIdentifier { identifier } => Some(identifier.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all identifiers in this expression, allowing mutation.
|
|
||||||
/// Each occurrence of an identifier is returned separately.
|
|
||||||
pub fn iter_identifiers_mut(&mut self) -> impl Iterator<Item = &mut String> {
|
|
||||||
self.iter_operators_mut()
|
|
||||||
.filter_map(|operator| match operator {
|
|
||||||
Operator::VariableIdentifierWrite { identifier }
|
|
||||||
| Operator::VariableIdentifierRead { identifier }
|
|
||||||
| Operator::FunctionIdentifier { identifier } => Some(identifier),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all variable identifiers in this expression.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_variable_identifiers(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
|
||||||
Operator::VariableIdentifierWrite { identifier }
|
|
||||||
| Operator::VariableIdentifierRead { identifier } => Some(identifier.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all variable identifiers in this expression, allowing mutation.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_variable_identifiers_mut(&mut self) -> impl Iterator<Item = &mut String> {
|
|
||||||
self.iter_operators_mut()
|
|
||||||
.filter_map(|operator| match operator {
|
|
||||||
Operator::VariableIdentifierWrite { identifier }
|
|
||||||
| Operator::VariableIdentifierRead { identifier } => Some(identifier),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all read variable identifiers in this expression.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_read_variable_identifiers(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
|
||||||
Operator::VariableIdentifierRead { identifier } => Some(identifier.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all read variable identifiers in this expression, allowing mutation.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_read_variable_identifiers_mut(&mut self) -> impl Iterator<Item = &mut String> {
|
|
||||||
self.iter_operators_mut()
|
|
||||||
.filter_map(|operator| match operator {
|
|
||||||
Operator::VariableIdentifierRead { identifier } => Some(identifier),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all write variable identifiers in this expression.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_write_variable_identifiers(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
|
||||||
Operator::VariableIdentifierWrite { identifier } => Some(identifier.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all write variable identifiers in this expression, allowing mutation.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_write_variable_identifiers_mut(&mut self) -> impl Iterator<Item = &mut String> {
|
|
||||||
self.iter_operators_mut()
|
|
||||||
.filter_map(|operator| match operator {
|
|
||||||
Operator::VariableIdentifierWrite { identifier } => Some(identifier),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all function identifiers in this expression.
|
|
||||||
/// Each occurrence of a function identifier is returned separately.
|
|
||||||
pub fn iter_function_identifiers(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
|
||||||
Operator::FunctionIdentifier { identifier } => Some(identifier.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all function identifiers in this expression, allowing mutation.
|
|
||||||
/// Each occurrence of a variable identifier is returned separately.
|
|
||||||
pub fn iter_function_identifiers_mut(&mut self) -> impl Iterator<Item = &mut String> {
|
|
||||||
self.iter_operators_mut()
|
|
||||||
.filter_map(|operator| match operator {
|
|
||||||
Operator::FunctionIdentifier { identifier } => Some(identifier),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node with the given context. Fails if one of the
|
|
||||||
/// operators in the expression tree fails.
|
|
||||||
pub fn eval_with_context(&self, context: &VariableMap) -> Result<Value> {
|
|
||||||
let mut arguments = Vec::new();
|
|
||||||
for child in self.children() {
|
|
||||||
arguments.push(child.eval_with_context(context)?);
|
|
||||||
}
|
|
||||||
self.operator().eval(&arguments, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node with the given mutable context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_with_context_mut(&self, context: &mut VariableMap) -> Result<Value> {
|
|
||||||
let mut arguments = Vec::new();
|
|
||||||
for child in self.children() {
|
|
||||||
arguments.push(child.eval_with_context_mut(context)?);
|
|
||||||
}
|
|
||||||
self.operator().eval_mut(&arguments, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval(&self) -> Result<Value> {
|
|
||||||
self.eval_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a string with an the given context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_string_with_context(&self, context: &VariableMap) -> Result<String> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::String(string)) => Ok(string),
|
|
||||||
Ok(value) => Err(Error::expected_string(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a float with an the given context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_float_with_context(&self, context: &VariableMap) -> Result<f64> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::Float(float)) => Ok(float),
|
|
||||||
Ok(value) => Err(Error::expected_float(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into an integer with an the given context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_int_with_context(&self, context: &VariableMap) -> Result<i64> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::Integer(int)) => Ok(int),
|
|
||||||
Ok(value) => Err(Error::expected_int(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a float with an the given context. If
|
|
||||||
/// the result of the expression is an integer, it is silently converted into a float. Fails /// if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_number_with_context(&self, context: &VariableMap) -> Result<f64> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::Integer(int)) => Ok(int as f64),
|
|
||||||
Ok(Value::Float(float)) => Ok(float),
|
|
||||||
Ok(value) => Err(Error::expected_number(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a boolean with an the given context
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_boolean_with_context(&self, context: &VariableMap) -> Result<bool> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::Boolean(boolean)) => Ok(boolean),
|
|
||||||
Ok(value) => Err(Error::expected_boolean(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a tuple with an the given context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_tuple_with_context(&self, context: &VariableMap) -> Result<Vec<Value>> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::List(tuple)) => Ok(tuple),
|
|
||||||
Ok(value) => Err(Error::expected_list(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into an empty value with an the given context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_empty_with_context(&self, context: &VariableMap) -> Result<()> {
|
|
||||||
match self.eval_with_context(context) {
|
|
||||||
Ok(Value::Empty) => Ok(()),
|
|
||||||
Ok(value) => Err(Error::expected_empty(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a string with an the given mutable context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_string_with_context_mut(&self, context: &mut VariableMap) -> Result<String> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::String(string)) => Ok(string),
|
|
||||||
Ok(value) => Err(Error::expected_string(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a float with an the given mutable context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_float_with_context_mut(&self, context: &mut VariableMap) -> Result<f64> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::Float(float)) => Ok(float),
|
|
||||||
Ok(value) => Err(Error::expected_float(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into an integer with an the given mutable context.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_int_with_context_mut(&self, context: &mut VariableMap) -> Result<i64> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::Integer(int)) => Ok(int),
|
|
||||||
Ok(value) => Err(Error::expected_int(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a float with an the given mutable context.
|
|
||||||
/// If the result of the expression is an integer, it is silently converted into a float.
|
|
||||||
/// Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_number_with_context_mut(&self, context: &mut VariableMap) -> Result<f64> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::Integer(int)) => Ok(int as f64),
|
|
||||||
Ok(Value::Float(float)) => Ok(float),
|
|
||||||
Ok(value) => Err(Error::expected_number(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a boolean with an the given mutable /// context. Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_boolean_with_context_mut(&self, context: &mut VariableMap) -> Result<bool> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::Boolean(boolean)) => Ok(boolean),
|
|
||||||
Ok(value) => Err(Error::expected_boolean(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a tuple with an the given mutable /// context. Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_tuple_with_context_mut(&self, context: &mut VariableMap) -> Result<Vec<Value>> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::List(tuple)) => Ok(tuple),
|
|
||||||
Ok(value) => Err(Error::expected_list(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into an empty value with an the given /// mutable context. Fails if one of the operators in the expression tree fails.
|
|
||||||
pub fn eval_empty_with_context_mut(&self, context: &mut VariableMap) -> Result<()> {
|
|
||||||
match self.eval_with_context_mut(context) {
|
|
||||||
Ok(Value::Empty) => Ok(()),
|
|
||||||
Ok(value) => Err(Error::expected_empty(value)),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a string. Fails if one of the /// operators in the expression tree fails.
|
|
||||||
pub fn eval_string(&self) -> Result<String> {
|
|
||||||
self.eval_string_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a float. Fails if one of the operators /// in the expression tree fails.
|
|
||||||
pub fn eval_float(&self) -> Result<f64> {
|
|
||||||
self.eval_float_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into an integer. Fails if one of the
|
|
||||||
/// operators in the expression tree fails.
|
|
||||||
pub fn eval_int(&self) -> Result<i64> {
|
|
||||||
self.eval_int_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a float. If the result of the
|
|
||||||
/// expression is an integer, it is silently converted into a float. Fails if one of the /// operators in the expression tree fails.
|
|
||||||
pub fn eval_number(&self) -> Result<f64> {
|
|
||||||
self.eval_number_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a boolean. Fails if one of the /// operators in the expression tree fails.
|
|
||||||
pub fn eval_boolean(&self) -> Result<bool> {
|
|
||||||
self.eval_boolean_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into a tuple. Fails if one of the operators /// in the expression tree fails.
|
|
||||||
pub fn eval_tuple(&self) -> Result<Vec<Value>> {
|
|
||||||
self.eval_tuple_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the operator tree rooted at this node into an empty value. Fails if one of the /// operators in the expression tree fails.
|
|
||||||
pub fn eval_empty(&self) -> Result<()> {
|
|
||||||
self.eval_empty_with_context_mut(&mut VariableMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the children of this node as a slice.
|
|
||||||
pub fn children(&self) -> &[Node] {
|
|
||||||
&self.children
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the operator associated with this node.
|
|
||||||
pub fn operator(&self) -> &Operator {
|
|
||||||
&self.operator
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the vector containing the children of this node.
|
|
||||||
///
|
|
||||||
/// WARNING: Writing to this might have unexpected results, as some operators require certain amounts and types of arguments.
|
|
||||||
pub fn children_mut(&mut self) -> &mut Vec<Node> {
|
|
||||||
&mut self.children
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the operator associated with this node.
|
|
||||||
///
|
|
||||||
/// WARNING: Writing to this might have unexpected results, as some operators require different amounts and types of arguments.
|
|
||||||
pub fn operator_mut(&mut self) -> &mut Operator {
|
|
||||||
&mut self.operator
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_enough_children(&self) -> bool {
|
|
||||||
Some(self.children().len()) == self.operator().max_argument_amount()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_too_many_children(&self) -> bool {
|
|
||||||
if let Some(max_argument_amount) = self.operator().max_argument_amount() {
|
|
||||||
self.children().len() > max_argument_amount
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> Result<()> {
|
|
||||||
// println!(
|
|
||||||
// "Inserting {:?} into {:?}, is_root_node = {is_root_node}",
|
|
||||||
// node.operator(),
|
|
||||||
// self.operator()
|
|
||||||
// );
|
|
||||||
// println!("Self is {:?}", self);
|
|
||||||
if self.operator().precedence() < node.operator().precedence() || node.operator().is_unary() || is_root_node
|
|
||||||
// Right-to-left chaining
|
|
||||||
|| (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right())
|
|
||||||
{
|
|
||||||
if self.operator().is_leaf() {
|
|
||||||
Err(Error::AppendedToLeafNode(node))
|
|
||||||
} else if self.has_enough_children() {
|
|
||||||
// Unwrap cannot fail because is_leaf being false and has_enough_children being true implies that the operator wants and has at least one child
|
|
||||||
let last_child_operator = self.children.last().unwrap().operator();
|
|
||||||
|
|
||||||
if last_child_operator.precedence()
|
|
||||||
< node.operator().precedence() || node.operator().is_unary()
|
|
||||||
// Right-to-left chaining
|
|
||||||
|| (last_child_operator.precedence()
|
|
||||||
== node.operator().precedence() && !last_child_operator.is_left_to_right() && !node.operator().is_left_to_right())
|
|
||||||
{
|
|
||||||
// println!(
|
|
||||||
// "Recursing into {:?}",
|
|
||||||
// self.children.last().unwrap().operator()
|
|
||||||
// );
|
|
||||||
// Unwrap cannot fail because is_leaf being false and has_enough_children being true implies that the operator wants and has at least one child
|
|
||||||
self.children
|
|
||||||
.last_mut()
|
|
||||||
.unwrap()
|
|
||||||
.insert_back_prioritized(node, false)
|
|
||||||
} else {
|
|
||||||
// println!("Rotating");
|
|
||||||
if node.operator().is_leaf() {
|
|
||||||
return Err(Error::AppendedToLeafNode(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap cannot fail because is_leaf being false and has_enough_children being true implies that the operator wants and has at least one child
|
|
||||||
let last_child = self.children.pop().unwrap();
|
|
||||||
// Root nodes have at most one child
|
|
||||||
// TODO I am not sure if this is the correct error
|
|
||||||
if self.operator() == &Operator::RootNode && !self.children().is_empty() {
|
|
||||||
return Err(Error::MissingOperatorOutsideOfBrace);
|
|
||||||
}
|
|
||||||
// Do not insert root nodes into root nodes.
|
|
||||||
// TODO I am not sure if this is the correct error
|
|
||||||
if self.operator() == &Operator::RootNode
|
|
||||||
&& node.operator() == &Operator::RootNode
|
|
||||||
{
|
|
||||||
return Err(Error::MissingOperatorOutsideOfBrace);
|
|
||||||
}
|
|
||||||
self.children.push(node);
|
|
||||||
let node = self.children.last_mut().unwrap();
|
|
||||||
|
|
||||||
// Root nodes have at most one child
|
|
||||||
// TODO I am not sure if this is the correct error
|
|
||||||
if node.operator() == &Operator::RootNode && !node.children().is_empty() {
|
|
||||||
return Err(Error::MissingOperatorOutsideOfBrace);
|
|
||||||
}
|
|
||||||
// Do not insert root nodes into root nodes.
|
|
||||||
// TODO I am not sure if this is the correct error
|
|
||||||
if node.operator() == &Operator::RootNode
|
|
||||||
&& last_child.operator() == &Operator::RootNode
|
|
||||||
{
|
|
||||||
return Err(Error::MissingOperatorOutsideOfBrace);
|
|
||||||
}
|
|
||||||
node.children.push(last_child);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// println!("Inserting as specified");
|
|
||||||
self.children.push(node);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(Error::PrecedenceViolation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Node {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
self.operator.fmt(f)?;
|
|
||||||
for child in self.children() {
|
|
||||||
write!(f, " {}", child)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collapse_root_stack_to(
|
|
||||||
root_stack: &mut Vec<Node>,
|
|
||||||
mut root: Node,
|
|
||||||
collapse_goal: &Node,
|
|
||||||
) -> Result<Node> {
|
|
||||||
loop {
|
|
||||||
if let Some(mut potential_higher_root) = root_stack.pop() {
|
|
||||||
// TODO I'm not sure about this >, as I have no example for different sequence operators with the same precedence
|
|
||||||
if potential_higher_root.operator().precedence() > collapse_goal.operator().precedence()
|
|
||||||
{
|
|
||||||
potential_higher_root.children.push(root);
|
|
||||||
root = potential_higher_root;
|
|
||||||
} else {
|
|
||||||
root_stack.push(potential_higher_root);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is the only way the topmost root node could have been removed
|
|
||||||
return Err(Error::UnmatchedRBrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(root)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collapse_all_sequences(root_stack: &mut Vec<Node>) -> Result<()> {
|
|
||||||
// println!("Collapsing all sequences");
|
|
||||||
// println!("Initial root stack is: {:?}", root_stack);
|
|
||||||
let mut root = if let Some(root) = root_stack.pop() {
|
|
||||||
root
|
|
||||||
} else {
|
|
||||||
return Err(Error::UnmatchedRBrace);
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// println!("Root is: {:?}", root);
|
|
||||||
if root.operator() == &Operator::RootNode {
|
|
||||||
// This should fire if parsing something like `4(5)`
|
|
||||||
if root.has_too_many_children() {
|
|
||||||
return Err(Error::MissingOperatorOutsideOfBrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
root_stack.push(root);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mut potential_higher_root) = root_stack.pop() {
|
|
||||||
if root.operator().is_sequence() {
|
|
||||||
potential_higher_root.children.push(root);
|
|
||||||
root = potential_higher_root;
|
|
||||||
} else {
|
|
||||||
// This should fire if parsing something like `4(5)`
|
|
||||||
if root.has_too_many_children() {
|
|
||||||
return Err(Error::MissingOperatorOutsideOfBrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
root_stack.push(potential_higher_root);
|
|
||||||
root_stack.push(root);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is the only way the topmost root node could have been removed
|
|
||||||
return Err(Error::UnmatchedRBrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// println!("Root stack after collapsing all sequences is: {:?}", root_stack);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node> {
|
|
||||||
let mut root_stack = vec![Node::root_node()];
|
|
||||||
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();
|
|
||||||
|
|
||||||
let node = match token.clone() {
|
|
||||||
Token::Plus => Some(Node::new(Operator::Add)),
|
|
||||||
Token::Minus => {
|
|
||||||
if last_token_is_rightsided_value {
|
|
||||||
Some(Node::new(Operator::Sub))
|
|
||||||
} else {
|
|
||||||
Some(Node::new(Operator::Neg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Token::Star => Some(Node::new(Operator::Mul)),
|
|
||||||
Token::Slash => Some(Node::new(Operator::Div)),
|
|
||||||
Token::Percent => Some(Node::new(Operator::Mod)),
|
|
||||||
Token::Hat => Some(Node::new(Operator::Exp)),
|
|
||||||
|
|
||||||
Token::Eq => Some(Node::new(Operator::Eq)),
|
|
||||||
Token::Neq => Some(Node::new(Operator::Neq)),
|
|
||||||
Token::Gt => Some(Node::new(Operator::Gt)),
|
|
||||||
Token::Lt => Some(Node::new(Operator::Lt)),
|
|
||||||
Token::Geq => Some(Node::new(Operator::Geq)),
|
|
||||||
Token::Leq => Some(Node::new(Operator::Leq)),
|
|
||||||
Token::And => Some(Node::new(Operator::And)),
|
|
||||||
Token::Or => Some(Node::new(Operator::Or)),
|
|
||||||
Token::Not => Some(Node::new(Operator::Not)),
|
|
||||||
|
|
||||||
Token::LBrace => {
|
|
||||||
root_stack.push(Node::root_node());
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Token::RBrace => {
|
|
||||||
if root_stack.len() <= 1 {
|
|
||||||
return Err(Error::UnmatchedRBrace);
|
|
||||||
} else {
|
|
||||||
collapse_all_sequences(&mut root_stack)?;
|
|
||||||
root_stack.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Assign => Some(Node::new(Operator::Assign)),
|
|
||||||
Token::PlusAssign => Some(Node::new(Operator::AddAssign)),
|
|
||||||
Token::MinusAssign => Some(Node::new(Operator::SubAssign)),
|
|
||||||
Token::StarAssign => Some(Node::new(Operator::MulAssign)),
|
|
||||||
Token::SlashAssign => Some(Node::new(Operator::DivAssign)),
|
|
||||||
Token::PercentAssign => Some(Node::new(Operator::ModAssign)),
|
|
||||||
Token::HatAssign => Some(Node::new(Operator::ExpAssign)),
|
|
||||||
Token::AndAssign => Some(Node::new(Operator::AndAssign)),
|
|
||||||
Token::OrAssign => Some(Node::new(Operator::OrAssign)),
|
|
||||||
|
|
||||||
Token::Comma => Some(Node::new(Operator::Tuple)),
|
|
||||||
Token::Semicolon => Some(Node::new(Operator::Chain)),
|
|
||||||
Token::Identifier(identifier) => {
|
|
||||||
let mut result = Some(Node::new(Operator::variable_identifier_read(
|
|
||||||
identifier.clone(),
|
|
||||||
)));
|
|
||||||
if let Some(next) = next {
|
|
||||||
if next.is_assignment() {
|
|
||||||
result = Some(Node::new(Operator::variable_identifier_write(
|
|
||||||
identifier.clone(),
|
|
||||||
)));
|
|
||||||
} else if next.is_leftsided_value() {
|
|
||||||
result = Some(Node::new(Operator::function_identifier(identifier)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
Token::Float(float) => Some(Node::new(Operator::value(Value::Float(float)))),
|
|
||||||
Token::Int(int) => Some(Node::new(Operator::value(Value::Integer(int)))),
|
|
||||||
Token::Boolean(boolean) => Some(Node::new(Operator::value(Value::Boolean(boolean)))),
|
|
||||||
Token::String(string) => Some(Node::new(Operator::value(Value::String(string)))),
|
|
||||||
Token::Function(string) => Some(Node::new(Operator::value(Value::Function(
|
|
||||||
Function::new(&string),
|
|
||||||
)))),
|
|
||||||
Token::Yield(_, _) => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(mut node) = node {
|
|
||||||
// Need to pop and then repush here, because Rust 1.33.0 cannot release the mutable borrow of root_stack before the end of this complete if-statement
|
|
||||||
if let Some(mut root) = root_stack.pop() {
|
|
||||||
if node.operator().is_sequence() {
|
|
||||||
// println!("Found a sequence operator");
|
|
||||||
// println!("Stack before sequence operation: {:?}, {:?}", root_stack, root);
|
|
||||||
// If root.operator() and node.operator() are of the same variant, ...
|
|
||||||
if mem::discriminant(root.operator()) == mem::discriminant(node.operator()) {
|
|
||||||
// ... we create a new root node for the next expression in the sequence
|
|
||||||
root.children.push(Node::root_node());
|
|
||||||
root_stack.push(root);
|
|
||||||
} else if root.operator() == &Operator::RootNode {
|
|
||||||
// If the current root is an actual root node, we start a new sequence
|
|
||||||
node.children.push(root);
|
|
||||||
node.children.push(Node::root_node());
|
|
||||||
root_stack.push(Node::root_node());
|
|
||||||
root_stack.push(node);
|
|
||||||
} else {
|
|
||||||
// Otherwise, we combine the sequences based on their precedences
|
|
||||||
// TODO I'm not sure about this <, as I have no example for different sequence operators with the same precedence
|
|
||||||
if root.operator().precedence() < node.operator().precedence() {
|
|
||||||
// If the new sequence has a higher precedence, it is part of the last element of the current root sequence
|
|
||||||
if let Some(last_root_child) = root.children.pop() {
|
|
||||||
node.children.push(last_root_child);
|
|
||||||
node.children.push(Node::root_node());
|
|
||||||
root_stack.push(root);
|
|
||||||
root_stack.push(node);
|
|
||||||
} else {
|
|
||||||
// Once a sequence has been pushed on top of the stack, it also gets a child
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the new sequence doesn't have a higher precedence, then all sequences with a higher precedence are collapsed below this one
|
|
||||||
root = collapse_root_stack_to(&mut root_stack, root, &node)?;
|
|
||||||
node.children.push(root);
|
|
||||||
root_stack.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println!("Stack after sequence operation: {:?}", root_stack);
|
|
||||||
} else if root.operator().is_sequence() {
|
|
||||||
if let Some(mut last_root_child) = root.children.pop() {
|
|
||||||
last_root_child.insert_back_prioritized(node, true)?;
|
|
||||||
root.children.push(last_root_child);
|
|
||||||
root_stack.push(root);
|
|
||||||
} else {
|
|
||||||
// Once a sequence has been pushed on top of the stack, it also gets a child
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.insert_back_prioritized(node, true)?;
|
|
||||||
root_stack.push(root);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(Error::UnmatchedRBrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
last_token_is_rightsided_value = token.is_rightsided_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the end, all sequences are implicitly terminated
|
|
||||||
collapse_all_sequences(&mut root_stack)?;
|
|
||||||
|
|
||||||
if root_stack.len() > 1 {
|
|
||||||
Err(Error::UnmatchedLBrace)
|
|
||||||
} else if let Some(root) = root_stack.pop() {
|
|
||||||
Ok(root)
|
|
||||||
} else {
|
|
||||||
Err(Error::UnmatchedRBrace)
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,11 +13,11 @@ impl Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&self) -> Result<Value> {
|
pub fn run(&self) -> Result<Value> {
|
||||||
eval(&self.0)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_with_context(&self, context: &mut VariableMap) -> Result<Value> {
|
pub fn run_with_context(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
eval_with_context(&self.0, context)
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use serde::{
|
|||||||
ser::SerializeTuple,
|
ser::SerializeTuple,
|
||||||
Deserialize, Serialize, Serializer,
|
Deserialize, Serialize, Serializer,
|
||||||
};
|
};
|
||||||
use tree_sitter::{Node, TreeCursor};
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
@ -66,7 +66,12 @@ impl Value {
|
|||||||
|
|
||||||
Ok(Value::Integer(raw))
|
Ok(Value::Integer(raw))
|
||||||
}
|
}
|
||||||
"string" => Ok(Value::String(value_snippet.to_string())),
|
"string" => {
|
||||||
|
let without_quotes = &value_snippet[1..value_snippet.len() - 1];
|
||||||
|
|
||||||
|
Ok(Value::String(without_quotes.to_string()))
|
||||||
|
}
|
||||||
|
"empty" => Ok(Value::Empty),
|
||||||
_ => Err(Error::UnexpectedSourceNode {
|
_ => Err(Error::UnexpectedSourceNode {
|
||||||
expected: "raw value",
|
expected: "raw value",
|
||||||
actual: child.kind(),
|
actual: child.kind(),
|
||||||
|
Loading…
Reference in New Issue
Block a user