Implement addition
This commit is contained in:
parent
b476818ba3
commit
3b0b9d044e
@ -12,7 +12,7 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
UnexpectedSourceNode {
|
UnexpectedSyntax {
|
||||||
expected: &'static str,
|
expected: &'static str,
|
||||||
actual: &'static str,
|
actual: &'static str,
|
||||||
location: tree_sitter::Point,
|
location: tree_sitter::Point,
|
||||||
@ -553,7 +553,7 @@ impl fmt::Display for Error {
|
|||||||
macro_info.identifier,
|
macro_info.identifier,
|
||||||
macro_info.inputs
|
macro_info.inputs
|
||||||
),
|
),
|
||||||
UnexpectedSourceNode { expected, actual, location } => write!(f, "Unexpected syntax at {location}. Expected {expected}, but found {actual}."),
|
UnexpectedSyntax { expected, actual, location } => write!(f, "Unexpected syntax at {location}. 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}."),
|
WrongTypeCombination { expected, actual } => write!(f, "Wrong type combination. Expected {expected}, found {actual}."),
|
||||||
ExpectedChildNode { empty_node_sexp } => write!(f, "Expected this node to have a child, {empty_node_sexp}."),
|
ExpectedChildNode { empty_node_sexp } => write!(f, "Expected this node to have a child, {empty_node_sexp}."),
|
||||||
|
298
src/interface.rs
298
src/interface.rs
@ -5,7 +5,7 @@
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::{Node, Parser, Tree as TSTree, TreeCursor};
|
use tree_sitter::{Node, Parser, Tree as TSTree};
|
||||||
|
|
||||||
use crate::{language, Error, Result, Value, VariableMap};
|
use crate::{language, Error, Result, Value, VariableMap};
|
||||||
|
|
||||||
@ -52,7 +52,8 @@ pub fn eval_with_context(source: &str, context: &mut VariableMap) -> Vec<Result<
|
|||||||
Evaluator::new(parser, context, source).run()
|
Evaluator::new(parser, context, source).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
trait EvaluatorTree: Sized {
|
/// This trait is implemented by the Evaluator's internal types.
|
||||||
|
pub trait EvaluatorTree: Sized {
|
||||||
/// Interpret the syntax tree at the given node and return the abstraction.
|
/// Interpret the syntax tree at the given node and return the abstraction.
|
||||||
///
|
///
|
||||||
/// This function is used to convert nodes in the Tree Sitter concrete
|
/// This function is used to convert nodes in the Tree Sitter concrete
|
||||||
@ -65,7 +66,7 @@ trait EvaluatorTree: Sized {
|
|||||||
fn new(node: Node, source: &str) -> Result<Self>;
|
fn new(node: Node, source: &str) -> Result<Self>;
|
||||||
|
|
||||||
/// Execute dust code by traversing the tree
|
/// Execute dust code by traversing the tree
|
||||||
fn run() -> Vec<Result<Value>>;
|
fn run(&self, context: &mut VariableMap) -> Result<Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of statements and comments interpreted from a syntax tree.
|
/// A collection of statements and comments interpreted from a syntax tree.
|
||||||
@ -109,8 +110,6 @@ impl<'context, 'code> Evaluator<'context, 'code> {
|
|||||||
Err(error) => results.push(Err(error)),
|
Err(error) => results.push(Err(error)),
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{node:?}");
|
|
||||||
|
|
||||||
// This iterator will run forever without this check.
|
// This iterator will run forever without this check.
|
||||||
if index == root_node.child_count() - 1 {
|
if index == root_node.child_count() - 1 {
|
||||||
break;
|
break;
|
||||||
@ -120,9 +119,7 @@ impl<'context, 'code> Evaluator<'context, 'code> {
|
|||||||
for item in &items {
|
for item in &items {
|
||||||
match item {
|
match item {
|
||||||
Item::Comment(comment) => results.push(Ok(Value::String(comment.to_string()))),
|
Item::Comment(comment) => results.push(Ok(Value::String(comment.to_string()))),
|
||||||
Item::Statement(statement) => {
|
Item::Statement(statement) => results.push(statement.run(self.context)),
|
||||||
results.push(statement.run(self.context, &mut cursor, self.source))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +141,7 @@ pub enum Item {
|
|||||||
impl Item {
|
impl Item {
|
||||||
fn new(node: Node, source: &str) -> Result<Self> {
|
fn new(node: Node, source: &str) -> Result<Self> {
|
||||||
if node.kind() != "item" {
|
if node.kind() != "item" {
|
||||||
return Err(Error::UnexpectedSourceNode {
|
return Err(Error::UnexpectedSyntax {
|
||||||
expected: "item",
|
expected: "item",
|
||||||
actual: node.kind(),
|
actual: node.kind(),
|
||||||
location: node.start_position(),
|
location: node.start_position(),
|
||||||
@ -159,10 +156,11 @@ impl Item {
|
|||||||
|
|
||||||
Ok(Item::Comment(comment_text.to_string()))
|
Ok(Item::Comment(comment_text.to_string()))
|
||||||
} else if child.kind() == "statement" {
|
} else if child.kind() == "statement" {
|
||||||
let child = node.child(0).unwrap();
|
let grandchild = child.child(0).unwrap();
|
||||||
Ok(Item::Statement(Statement::new(child, source)?))
|
|
||||||
|
Ok(Item::Statement(Statement::new(grandchild, source)?))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnexpectedSourceNode {
|
Err(Error::UnexpectedSyntax {
|
||||||
expected: "comment or statement",
|
expected: "comment or statement",
|
||||||
actual: node.kind(),
|
actual: node.kind(),
|
||||||
location: node.start_position(),
|
location: node.start_position(),
|
||||||
@ -171,7 +169,7 @@ impl Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of a statement in the .
|
/// Abstract representation of a statement.
|
||||||
///
|
///
|
||||||
/// Items are either comments, which do nothing, or statements, which can be run
|
/// Items are either comments, which do nothing, or statements, which can be run
|
||||||
/// to produce a single value or interact with a context by creating or
|
/// to produce a single value or interact with a context by creating or
|
||||||
@ -181,19 +179,13 @@ pub enum Statement {
|
|||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl EvaluatorTree for Statement {
|
||||||
pub fn new(node: Node, source: &str) -> Result<Self> {
|
fn new(node: Node, source: &str) -> Result<Self> {
|
||||||
let node = if node.kind() == "statement" {
|
|
||||||
node.child(0).unwrap()
|
|
||||||
} else {
|
|
||||||
node
|
|
||||||
};
|
|
||||||
|
|
||||||
let child = node.child(0).unwrap();
|
let child = node.child(0).unwrap();
|
||||||
|
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
"expression" => Ok(Self::Expression(Expression::new(child, source)?)),
|
"expression" => Ok(Self::Expression(Expression::new(child, source)?)),
|
||||||
_ => Err(Error::UnexpectedSourceNode {
|
_ => Err(Error::UnexpectedSyntax {
|
||||||
expected: "expression",
|
expected: "expression",
|
||||||
actual: node.kind(),
|
actual: node.kind(),
|
||||||
location: node.start_position(),
|
location: node.start_position(),
|
||||||
@ -201,14 +193,9 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
&self,
|
|
||||||
context: &mut VariableMap,
|
|
||||||
mut cursor: &mut TreeCursor,
|
|
||||||
source: &str,
|
|
||||||
) -> Result<Value> {
|
|
||||||
match self {
|
match self {
|
||||||
Statement::Expression(expression) => expression.run(context, &mut cursor, source),
|
Statement::Expression(expression) => expression.run(context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,6 +206,157 @@ pub enum Expression {
|
|||||||
Value(Value),
|
Value(Value),
|
||||||
ControlFlow(Box<ControlFlow>),
|
ControlFlow(Box<ControlFlow>),
|
||||||
Assignment(Box<Assignment>),
|
Assignment(Box<Assignment>),
|
||||||
|
Math(Box<Math>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct Math {
|
||||||
|
left: Expression,
|
||||||
|
operator: MathOperator,
|
||||||
|
right: Expression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EvaluatorTree for Math {
|
||||||
|
fn new(node: Node, source: &str) -> Result<Self> {
|
||||||
|
let left_node = node.child(0).unwrap();
|
||||||
|
let operator_node = node.child(1).unwrap().child(0).unwrap();
|
||||||
|
let right_node = node.child(2).unwrap();
|
||||||
|
println!("{left_node:#?}");
|
||||||
|
println!("{operator_node:#?}");
|
||||||
|
println!("{right_node:#?}");
|
||||||
|
let operator = match operator_node.kind() {
|
||||||
|
"+" => MathOperator::Add,
|
||||||
|
"-" => MathOperator::Subtract,
|
||||||
|
"*" => MathOperator::Multiply,
|
||||||
|
"/" => MathOperator::Divide,
|
||||||
|
"%" => MathOperator::Modulo,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedSyntax {
|
||||||
|
expected: "+, -, *, / or %",
|
||||||
|
actual: operator_node.kind(),
|
||||||
|
location: operator_node.start_position(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Math {
|
||||||
|
left: Expression::new(left_node, source)?,
|
||||||
|
operator,
|
||||||
|
right: Expression::new(right_node, source)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
|
let left_value = self.left.run(context)?.as_number()?;
|
||||||
|
let right_value = self.right.run(context)?.as_number()?;
|
||||||
|
let outcome = match self.operator {
|
||||||
|
MathOperator::Add => left_value + right_value,
|
||||||
|
MathOperator::Subtract => left_value - right_value,
|
||||||
|
MathOperator::Multiply => left_value * right_value,
|
||||||
|
MathOperator::Divide => left_value / right_value,
|
||||||
|
MathOperator::Modulo => left_value % right_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Value::Float(outcome))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub enum MathOperator {
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Multiply,
|
||||||
|
Divide,
|
||||||
|
Modulo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EvaluatorTree for Expression {
|
||||||
|
fn new(node: Node, source: &str) -> Result<Self> {
|
||||||
|
let node = if node.kind() == "expression" {
|
||||||
|
node.child(0).unwrap()
|
||||||
|
} else {
|
||||||
|
node
|
||||||
|
};
|
||||||
|
|
||||||
|
let expression = match node.kind() {
|
||||||
|
"identifier" => {
|
||||||
|
let byte_range = node.byte_range();
|
||||||
|
let identifier = &source[byte_range];
|
||||||
|
|
||||||
|
Self::Identifier(identifier.to_string())
|
||||||
|
}
|
||||||
|
"value" => Expression::Value(Value::new(node, source)?),
|
||||||
|
"control_flow" => Expression::ControlFlow(Box::new(ControlFlow::new(node, source)?)),
|
||||||
|
"assignment" => Expression::Assignment(Box::new(Assignment::new(node, source)?)),
|
||||||
|
"math" => Expression::Math(Box::new(Math::new(node, source)?)),
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedSyntax {
|
||||||
|
expected: "identifier, operation, control_flow, assignment, math or value",
|
||||||
|
actual: node.kind(),
|
||||||
|
location: node.start_position(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
|
match self {
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
let value = context.get_value(&identifier)?;
|
||||||
|
|
||||||
|
if let Some(value) = value {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Ok(Value::Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Value(value) => Ok(value.clone()),
|
||||||
|
Expression::ControlFlow(control_flow) => control_flow.run(context),
|
||||||
|
Expression::Assignment(_) => todo!(),
|
||||||
|
Expression::Math(math) => math.run(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct ControlFlow {
|
||||||
|
if_expression: Expression,
|
||||||
|
then_statement: Statement,
|
||||||
|
else_statement: Option<Statement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EvaluatorTree for ControlFlow {
|
||||||
|
fn new(node: Node, source: &str) -> Result<Self> {
|
||||||
|
// Skip the child nodes for the keywords "if", "then" and "else".
|
||||||
|
let second_child = node.child(1).unwrap();
|
||||||
|
let fourth_child = node.child(3).unwrap();
|
||||||
|
let sixth_child = node.child(5);
|
||||||
|
let else_statement = if let Some(child) = sixth_child {
|
||||||
|
Some(Statement::new(child, source)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ControlFlow {
|
||||||
|
if_expression: Expression::new(second_child, source)?,
|
||||||
|
then_statement: Statement::new(fourth_child, source)?,
|
||||||
|
else_statement,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
|
let if_boolean = self.if_expression.run(context)?.as_boolean()?;
|
||||||
|
|
||||||
|
if if_boolean {
|
||||||
|
self.then_statement.run(context)
|
||||||
|
} else if let Some(statement) = &self.else_statement {
|
||||||
|
statement.run(context)
|
||||||
|
} else {
|
||||||
|
Ok(Value::Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
@ -244,108 +382,6 @@ impl Assignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
|
||||||
pub fn new(node: Node, source: &str) -> Result<Self> {
|
|
||||||
let node = if node.kind() == "expression" {
|
|
||||||
node.child(0).unwrap()
|
|
||||||
} else {
|
|
||||||
node
|
|
||||||
};
|
|
||||||
|
|
||||||
if node.kind() == "identifier" {
|
|
||||||
let byte_range = node.byte_range();
|
|
||||||
let identifier = &source[byte_range];
|
|
||||||
|
|
||||||
Ok(Self::Identifier(identifier.to_string()))
|
|
||||||
} else if node.kind() == "value" {
|
|
||||||
Ok(Expression::Value(Value::new(node, source)?))
|
|
||||||
} else if node.kind() == "control_flow" {
|
|
||||||
Ok(Expression::ControlFlow(Box::new(ControlFlow::new(
|
|
||||||
node, source,
|
|
||||||
)?)))
|
|
||||||
} else if node.kind() == "assignment" {
|
|
||||||
Ok(Expression::Assignment(Box::new(Assignment::new(
|
|
||||||
node, source,
|
|
||||||
)?)))
|
|
||||||
} else {
|
|
||||||
Err(Error::UnexpectedSourceNode {
|
|
||||||
expected: "identifier, operation, control_flow, assignment or value",
|
|
||||||
actual: node.kind(),
|
|
||||||
location: node.start_position(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(
|
|
||||||
&self,
|
|
||||||
context: &mut VariableMap,
|
|
||||||
mut cursor: &mut TreeCursor,
|
|
||||||
source: &str,
|
|
||||||
) -> Result<Value> {
|
|
||||||
match self {
|
|
||||||
Expression::Identifier(identifier) => {
|
|
||||||
let value = context.get_value(&identifier)?;
|
|
||||||
|
|
||||||
if let Some(value) = value {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Ok(Value::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::Value(value) => Ok(value.clone()),
|
|
||||||
Expression::ControlFlow(control_flow) => control_flow.run(context, &mut cursor, source),
|
|
||||||
Expression::Assignment(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub struct ControlFlow {
|
|
||||||
if_expression: Expression,
|
|
||||||
then_statement: Statement,
|
|
||||||
else_statement: Option<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ControlFlow {
|
|
||||||
fn new(node: Node, source: &str) -> Result<Self> {
|
|
||||||
// Skip the child nodes for the keywords "if", "then" and "else".
|
|
||||||
let second_child = node.child(1).unwrap();
|
|
||||||
let fourth_child = node.child(3).unwrap();
|
|
||||||
let sixth_child = node.child(5);
|
|
||||||
let else_statement = if let Some(child) = sixth_child {
|
|
||||||
Some(Statement::new(child, source)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ControlFlow {
|
|
||||||
if_expression: Expression::new(second_child, source)?,
|
|
||||||
then_statement: Statement::new(fourth_child, source)?,
|
|
||||||
else_statement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
context: &mut VariableMap,
|
|
||||||
mut cursor: &mut TreeCursor,
|
|
||||||
source: &str,
|
|
||||||
) -> Result<Value> {
|
|
||||||
let if_boolean = self
|
|
||||||
.if_expression
|
|
||||||
.run(context, &mut cursor, source)?
|
|
||||||
.as_boolean()?;
|
|
||||||
|
|
||||||
if if_boolean {
|
|
||||||
self.then_statement.run(context, &mut cursor, source)
|
|
||||||
} else if let Some(statement) = &self.else_statement {
|
|
||||||
statement.run(context, &mut cursor, source)
|
|
||||||
} else {
|
|
||||||
Ok(Value::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Table;
|
use crate::Table;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Types that represent runtime values.
|
//! Types that represent runtime values.
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
Expression, Function, Statement, Table, Time, ValueType, VariableMap,
|
EvaluatorTree, Expression, Function, Statement, Table, Time, ValueType, VariableMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
use json::JsonValue;
|
use json::JsonValue;
|
||||||
@ -184,7 +184,7 @@ impl Value {
|
|||||||
Ok(Value::Function(Function::new(identifiers, statements)))
|
Ok(Value::Function(Function::new(identifiers, statements)))
|
||||||
}
|
}
|
||||||
"empty" => Ok(Value::Empty),
|
"empty" => Ok(Value::Empty),
|
||||||
_ => Err(Error::UnexpectedSourceNode {
|
_ => Err(Error::UnexpectedSyntax {
|
||||||
expected: "integer, string, boolean, float, list or empty",
|
expected: "integer, string, boolean, float, list or empty",
|
||||||
actual: node.kind(),
|
actual: node.kind(),
|
||||||
location: node.start_position(),
|
location: node.start_position(),
|
||||||
|
Loading…
Reference in New Issue
Block a user