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
|
||||
//! or use the MacroFailure variant if the error can only occur inside a macro.
|
||||
use crate::{
|
||||
operator::Operator, token::PartialToken, value::value_type::ValueType, value::Value, Node,
|
||||
ToolInfo,
|
||||
};
|
||||
use crate::{value::value_type::ValueType, value::Value, ToolInfo};
|
||||
|
||||
use std::{fmt, io, time::SystemTimeError};
|
||||
|
||||
@ -118,10 +115,6 @@ pub enum Error {
|
||||
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
|
||||
/// is not higher. This error should never occur. If it does, please file a
|
||||
/// bug report.
|
||||
@ -154,9 +147,9 @@ pub enum Error {
|
||||
/// An operator is used with a wrong combination of types.
|
||||
WrongTypeCombination {
|
||||
/// The operator that whose evaluation caused the error.
|
||||
operator: Operator,
|
||||
expected: ValueType,
|
||||
/// 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.
|
||||
@ -169,16 +162,6 @@ pub enum Error {
|
||||
/// For example, writing `4(5)` would yield this error, as the `4` does not have any operands.
|
||||
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.
|
||||
AdditionError {
|
||||
/// The first argument of the addition.
|
||||
@ -351,10 +334,6 @@ impl Error {
|
||||
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 {
|
||||
Error::ExpectedString { actual }
|
||||
}
|
||||
@ -410,13 +389,6 @@ impl Error {
|
||||
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 {
|
||||
Error::AdditionError { augend, addend }
|
||||
}
|
||||
@ -540,7 +512,6 @@ impl fmt::Display for Error {
|
||||
actual
|
||||
)
|
||||
}
|
||||
AppendedToLeafNode(node) => write!(f, "Syntax error at \"{node}\"."),
|
||||
PrecedenceViolation => write!(
|
||||
f,
|
||||
"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:?}.",
|
||||
)
|
||||
}
|
||||
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 '('."),
|
||||
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
|
||||
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 \
|
||||
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),
|
||||
SubtractionError {
|
||||
minuend,
|
||||
@ -637,6 +587,7 @@ impl fmt::Display for Error {
|
||||
),
|
||||
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."),
|
||||
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.
|
||||
|
||||
use std::ops::Range;
|
||||
//! The top level of Dust's API with functions to interpret Dust code.
|
||||
|
||||
use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
||||
|
||||
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
|
||||
///
|
||||
/// ```rust
|
||||
/// # use dust_lib::*;
|
||||
/// assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
|
||||
/// assert_eq!(eval("1 + 2 + 3"), vec![Ok(Value::from(6))]);
|
||||
/// ```
|
||||
///
|
||||
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
||||
pub fn eval(source: &str) -> Result<Value> {
|
||||
pub fn eval(source: &str) -> Vec<Result<Value>> {
|
||||
let mut context = VariableMap::new();
|
||||
|
||||
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
|
||||
///
|
||||
/// ```rust
|
||||
/// # use dust_lib::*;
|
||||
/// let mut context = VariableMap::new();
|
||||
///
|
||||
/// 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("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();
|
||||
|
||||
parser.set_language(language()).unwrap();
|
||||
|
||||
let tree = parser.parse(source, None).unwrap();
|
||||
let sexp = tree.root_node().to_sexp();
|
||||
|
||||
println!("{sexp}");
|
||||
|
||||
let evaluator = Evaluator::new(tree.clone(), source).unwrap();
|
||||
let mut cursor = tree.walk();
|
||||
|
||||
let results = evaluator.run(context, &mut cursor, source);
|
||||
|
||||
println!("{sexp}");
|
||||
println!("{evaluator:?}");
|
||||
println!("{results:?}");
|
||||
println!("{context:?}");
|
||||
|
||||
Ok(Value::Empty)
|
||||
results
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -159,7 +163,7 @@ impl Statement {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Expression {
|
||||
Identifier(&'static str),
|
||||
Identifier(String),
|
||||
Value(Value),
|
||||
Operation(Operation),
|
||||
}
|
||||
@ -176,7 +180,10 @@ impl Expression {
|
||||
let child = node.child(0).unwrap();
|
||||
|
||||
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" {
|
||||
Ok(Expression::Value(Value::new(child, source)?))
|
||||
} else if child.kind() == "operation" {
|
||||
@ -214,7 +221,7 @@ impl Expression {
|
||||
#[derive(Debug)]
|
||||
struct Operation {
|
||||
left: Box<Expression>,
|
||||
operator: &'static str,
|
||||
operator: String,
|
||||
right: Box<Expression>,
|
||||
}
|
||||
|
||||
@ -224,7 +231,7 @@ impl Operation {
|
||||
let second_child = node.child(1).unwrap();
|
||||
let third_child = node.child(2).unwrap();
|
||||
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)?) };
|
||||
|
||||
Ok(Operation {
|
||||
@ -242,11 +249,40 @@ impl Operation {
|
||||
) -> Result<Value> {
|
||||
let left = self.left.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,
|
||||
"=" => {
|
||||
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())),
|
||||
};
|
||||
|
||||
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::{
|
||||
error::*,
|
||||
interface::*,
|
||||
operator::Operator,
|
||||
token::PartialToken,
|
||||
tools::{Tool, ToolInfo, TOOL_LIST},
|
||||
tree::Node,
|
||||
value::{
|
||||
function::Function, table::Table, time::Time, value_type::ValueType,
|
||||
variable_map::VariableMap, Value,
|
||||
@ -21,9 +18,6 @@ pub mod tools;
|
||||
|
||||
mod error;
|
||||
mod interface;
|
||||
mod operator;
|
||||
mod token;
|
||||
mod tree;
|
||||
mod value;
|
||||
|
||||
use tree_sitter::Language;
|
||||
|
14
src/main.rs
14
src/main.rs
@ -32,21 +32,23 @@ fn main() {
|
||||
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();
|
||||
|
||||
eval(&file_contents)
|
||||
} else if let Some(command) = args.command {
|
||||
eval(&command)
|
||||
} else {
|
||||
Ok(Value::Empty)
|
||||
vec![Ok(Value::Empty)]
|
||||
};
|
||||
|
||||
match eval_result {
|
||||
for result in eval_results {
|
||||
match result {
|
||||
Ok(value) => println!("{value}"),
|
||||
Err(error) => eprintln!("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Helper, Completer, Validator)]
|
||||
struct DustReadline {
|
||||
@ -147,13 +149,15 @@ fn run_cli_shell() {
|
||||
|
||||
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}"),
|
||||
Err(error) => eprintln!("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {
|
||||
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() {
|
||||
self.eval_result =
|
||||
eval_with_context(&self.text_edit_buffer, &mut self.whale_context);
|
||||
todo!()
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -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> {
|
||||
eval(&self.0)
|
||||
todo!()
|
||||
}
|
||||
|
||||
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,
|
||||
Deserialize, Serialize, Serializer,
|
||||
};
|
||||
use tree_sitter::{Node, TreeCursor};
|
||||
use tree_sitter::Node;
|
||||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
@ -66,7 +66,12 @@ impl Value {
|
||||
|
||||
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 {
|
||||
expected: "raw value",
|
||||
actual: child.kind(),
|
||||
|
Loading…
Reference in New Issue
Block a user