Implement new grammar
This commit is contained in:
parent
fd9a4c04cb
commit
39692b3bd7
11
examples/fibonacci.ds
Normal file
11
examples/fibonacci.ds
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fibonacci = function <i> {
|
||||||
|
if i <= 1 {
|
||||||
|
1
|
||||||
|
} else if i == 2 {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
output (fibonacci - 1) + (fibonacci - 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(fibonacci 20)
|
@ -1,31 +1,15 @@
|
|||||||
fizzbuzz_basic = function <limit> {
|
count = 1
|
||||||
count = 1
|
|
||||||
|
|
||||||
while count < limit {
|
while count <= 15 {
|
||||||
if count % 3 == 0 && count % 5 == 0 {
|
(output count)
|
||||||
(output 'fizzbuzz')
|
|
||||||
} else f count % 3 == 0 {
|
|
||||||
(output 'fizz')
|
|
||||||
} else if count % 5 == 0 {
|
|
||||||
(output 'buzz')
|
|
||||||
} else
|
|
||||||
(output count)
|
|
||||||
|
|
||||||
count += 1
|
if count % 3 == 0 {
|
||||||
|
(output 'fizz')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fizzbuzz_match <limit> {
|
if count % 5 == 0 {
|
||||||
count = 1
|
(output 'buzz')
|
||||||
|
|
||||||
while count < 1 {
|
|
||||||
output match [count % 3 == 0, count % 5 == 0] {
|
|
||||||
[true, false] => 'fizz'
|
|
||||||
[false, true] => 'buzz'
|
|
||||||
[true, true] => 'fizzbuzz'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fizzbuzz_basic<15>
|
count += 1
|
||||||
fizzbuzz_match<15>
|
}
|
||||||
|
9
examples/hello_world.ds
Normal file
9
examples/hello_world.ds
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
(output 'Hello, world!')
|
||||||
|
|
||||||
|
main = function <message> {
|
||||||
|
while 1 == 1 {
|
||||||
|
(output message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(main 'Hello dust ~*')
|
@ -1,33 +1,71 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Result, Value, VariableMap};
|
use crate::{AbstractTree, Error, Result, Value, VariableMap};
|
||||||
|
|
||||||
use super::{identifier::Identifier, statement::Statement};
|
use super::{identifier::Identifier, statement::Statement};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Assignment {
|
pub struct Assignment {
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
|
operator: AssignmentOperator,
|
||||||
statement: Statement,
|
statement: Statement,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub enum AssignmentOperator {
|
||||||
|
Equal,
|
||||||
|
PlusEqual,
|
||||||
|
MinusEqual,
|
||||||
|
}
|
||||||
|
|
||||||
impl AbstractTree for Assignment {
|
impl AbstractTree for Assignment {
|
||||||
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
||||||
let identifier_node = node.child(0).unwrap();
|
let identifier_node = node.child(0).unwrap();
|
||||||
let identifier = Identifier::from_syntax_node(identifier_node, source)?;
|
let identifier = Identifier::from_syntax_node(identifier_node, source)?;
|
||||||
|
|
||||||
|
let operator_node = node.child(1).unwrap().child(0).unwrap();
|
||||||
|
let operator = match operator_node.kind() {
|
||||||
|
"=" => AssignmentOperator::Equal,
|
||||||
|
"+=" => AssignmentOperator::PlusEqual,
|
||||||
|
"-=" => AssignmentOperator::MinusEqual,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedSyntax {
|
||||||
|
expected: "=, += or -=",
|
||||||
|
actual: operator_node.kind(),
|
||||||
|
location: operator_node.start_position(),
|
||||||
|
relevant_source: source[node.byte_range()].to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let statement_node = node.child(2).unwrap();
|
let statement_node = node.child(2).unwrap();
|
||||||
let statement = Statement::from_syntax_node(statement_node, source)?;
|
let statement = Statement::from_syntax_node(statement_node, source)?;
|
||||||
|
|
||||||
Ok(Assignment {
|
Ok(Assignment {
|
||||||
identifier,
|
identifier,
|
||||||
|
operator,
|
||||||
statement,
|
statement,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
let key = self.identifier.clone().take_inner();
|
let key = self.identifier.clone().take_inner();
|
||||||
let value = self.statement.run(context)?;
|
let mut value = self.statement.run(context)?;
|
||||||
|
|
||||||
|
match self.operator {
|
||||||
|
AssignmentOperator::PlusEqual => {
|
||||||
|
if let Some(previous_value) = context.get_value(&key)? {
|
||||||
|
value += previous_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignmentOperator::MinusEqual => {
|
||||||
|
if let Some(previous_value) = context.get_value(&key)? {
|
||||||
|
value -= previous_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignmentOperator::Equal => {}
|
||||||
|
}
|
||||||
|
|
||||||
context.set_value(key, value)?;
|
context.set_value(key, value)?;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{tool::Tool, AbstractTree, Error, Identifier, Result, Value, VariableMap};
|
use crate::{AbstractTree, Error, Identifier, Result, Value, VariableMap};
|
||||||
|
|
||||||
use super::{function_call::FunctionCall, logic::Logic, math::Math};
|
use super::{function_call::FunctionCall, logic::Logic, math::Math};
|
||||||
|
|
||||||
@ -12,7 +12,6 @@ pub enum Expression {
|
|||||||
Math(Box<Math>),
|
Math(Box<Math>),
|
||||||
Logic(Box<Logic>),
|
Logic(Box<Logic>),
|
||||||
FunctionCall(FunctionCall),
|
FunctionCall(FunctionCall),
|
||||||
ToolCall(Box<Tool>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Expression {
|
impl AbstractTree for Expression {
|
||||||
@ -29,7 +28,6 @@ impl AbstractTree for Expression {
|
|||||||
"function_call" => {
|
"function_call" => {
|
||||||
Expression::FunctionCall(FunctionCall::from_syntax_node(child, source)?)
|
Expression::FunctionCall(FunctionCall::from_syntax_node(child, source)?)
|
||||||
}
|
}
|
||||||
"tool_call" => Expression::ToolCall(Box::new(Tool::from_syntax_node(child, source)?)),
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnexpectedSyntax {
|
return Err(Error::UnexpectedSyntax {
|
||||||
expected: "value, identifier, math or function_call",
|
expected: "value, identifier, math or function_call",
|
||||||
@ -50,7 +48,6 @@ impl AbstractTree for Expression {
|
|||||||
Expression::Math(math) => math.run(context),
|
Expression::Math(math) => math.run(context),
|
||||||
Expression::Logic(logic) => logic.run(context),
|
Expression::Logic(logic) => logic.run(context),
|
||||||
Expression::FunctionCall(function_call) => function_call.run(context),
|
Expression::FunctionCall(function_call) => function_call.run(context),
|
||||||
Expression::ToolCall(tool_call) => tool_call.run(context),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,72 +1,89 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Result, Value, VariableMap};
|
use crate::{tool::Tool, AbstractTree, Result, Value, VariableMap};
|
||||||
|
|
||||||
use super::{expression::Expression, identifier::Identifier};
|
use super::{expression::Expression, identifier::Identifier};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct FunctionCall {
|
pub struct FunctionCall {
|
||||||
identifier: Identifier,
|
name: FunctionName,
|
||||||
expressions: Vec<Expression>,
|
arguments: Vec<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub enum FunctionName {
|
||||||
|
Identifier(Identifier),
|
||||||
|
Tool(Tool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for FunctionCall {
|
impl AbstractTree for FunctionCall {
|
||||||
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
||||||
debug_assert_eq!("function_call", node.kind());
|
debug_assert_eq!("function_call", node.kind());
|
||||||
|
|
||||||
let identifier_node = node.child(1).unwrap();
|
let name_node = node.child(1).unwrap();
|
||||||
let identifier = Identifier::from_syntax_node(identifier_node, source)?;
|
|
||||||
|
|
||||||
let mut expressions = Vec::new();
|
let name = match name_node.kind() {
|
||||||
|
"identifier" => {
|
||||||
|
FunctionName::Identifier(Identifier::from_syntax_node(name_node, source)?)
|
||||||
|
}
|
||||||
|
"tool" => {
|
||||||
|
let tool_node = name_node.child(0).unwrap();
|
||||||
|
let tool = match tool_node.kind() {
|
||||||
|
"output" => Tool::Output,
|
||||||
|
"read" => Tool::Read,
|
||||||
|
_ => panic!(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
FunctionName::Tool(tool)
|
||||||
|
}
|
||||||
|
_ => panic!(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
let mut current_index = 2;
|
let mut current_index = 2;
|
||||||
while current_index < node.child_count() - 1 {
|
while current_index < node.child_count() - 1 {
|
||||||
let expression_node = node.child(current_index).unwrap();
|
let expression_node = node.child(current_index).unwrap();
|
||||||
let expression = Expression::from_syntax_node(expression_node, source)?;
|
let expression = Expression::from_syntax_node(expression_node, source)?;
|
||||||
|
|
||||||
expressions.push(expression);
|
arguments.push(expression);
|
||||||
|
|
||||||
current_index += 1;
|
current_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FunctionCall {
|
Ok(FunctionCall { name, arguments })
|
||||||
identifier,
|
|
||||||
expressions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
let identifier = &self.identifier;
|
let identifier = match &self.name {
|
||||||
|
FunctionName::Identifier(identifier) => identifier,
|
||||||
|
FunctionName::Tool(tool) => {
|
||||||
|
let value = self
|
||||||
|
.arguments
|
||||||
|
.first()
|
||||||
|
.map(|expression| expression.run(context))
|
||||||
|
.unwrap_or(Ok(Value::Empty))?;
|
||||||
|
|
||||||
|
return tool.run(&value);
|
||||||
|
}
|
||||||
|
};
|
||||||
let definition = if let Some(value) = context.get_value(identifier.inner())? {
|
let definition = if let Some(value) = context.get_value(identifier.inner())? {
|
||||||
value.as_function().cloned()?
|
value.as_function().cloned()?
|
||||||
} else {
|
} else {
|
||||||
return Err(crate::Error::FunctionIdentifierNotFound(identifier.clone()));
|
return Err(crate::Error::FunctionIdentifierNotFound(identifier.clone()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let id_expr_pairs = definition.identifiers().iter().zip(self.expressions.iter());
|
let id_expr_pairs = definition.identifiers().iter().zip(self.arguments.iter());
|
||||||
|
let mut function_context = context.clone();
|
||||||
|
|
||||||
for (identifier, expression) in id_expr_pairs {
|
for (identifier, expression) in id_expr_pairs {
|
||||||
let key = identifier.clone().take_inner();
|
let key = identifier.clone().take_inner();
|
||||||
let value = expression.run(context)?;
|
let value = expression.run(&mut function_context)?;
|
||||||
|
|
||||||
context.set_value(key, value)?;
|
function_context.set_value(key, value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results = Vec::with_capacity(self.expressions.len());
|
definition.body().run(&mut function_context)
|
||||||
|
|
||||||
for item in definition.items() {
|
|
||||||
let result = item.run(context)?;
|
|
||||||
|
|
||||||
results.push(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
for identifier in definition.identifiers() {
|
|
||||||
let key = identifier.inner();
|
|
||||||
|
|
||||||
context.remove(&key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::List(results))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,38 +11,52 @@ use crate::{AbstractTree, Error, Result, Statement, Value, VariableMap};
|
|||||||
/// 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
|
||||||
/// referencing variables.
|
/// referencing variables.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Item {
|
pub struct Item {
|
||||||
Comment(String),
|
statements: Vec<Statement>,
|
||||||
Statement(Statement),
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
pub fn new(statements: Vec<Statement>) -> Self {
|
||||||
|
Self { statements }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Item {
|
impl AbstractTree for Item {
|
||||||
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
||||||
debug_assert_eq!("item", node.kind());
|
debug_assert_eq!("item", node.kind());
|
||||||
|
|
||||||
let child = node.child(0).unwrap();
|
let child_count = node.child_count();
|
||||||
|
let mut statements = Vec::with_capacity(child_count);
|
||||||
|
|
||||||
if child.kind() == "comment" {
|
for index in 0..child_count {
|
||||||
let byte_range = child.byte_range();
|
let child = node.child(index).unwrap();
|
||||||
let comment_text = &source[byte_range];
|
|
||||||
|
|
||||||
Ok(Item::Comment(comment_text.to_string()))
|
let statement = match child.kind() {
|
||||||
} else if child.kind() == "statement" {
|
"statement" => Statement::from_syntax_node(child, source)?,
|
||||||
Ok(Item::Statement(Statement::from_syntax_node(child, source)?))
|
_ => {
|
||||||
} else {
|
return Err(Error::UnexpectedSyntax {
|
||||||
Err(Error::UnexpectedSyntax {
|
expected: "comment or statement",
|
||||||
expected: "comment or statement",
|
actual: child.kind(),
|
||||||
actual: child.kind(),
|
location: child.start_position(),
|
||||||
location: child.start_position(),
|
relevant_source: source[node.byte_range()].to_string(),
|
||||||
relevant_source: source[node.byte_range()].to_string(),
|
})
|
||||||
})
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
statements.push(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Item { statements })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
match self {
|
let mut prev_result = Ok(Value::Empty);
|
||||||
Item::Comment(text) => Ok(Value::String(text.clone())),
|
|
||||||
Item::Statement(statement) => statement.run(context),
|
for statement in &self.statements {
|
||||||
|
prev_result?;
|
||||||
|
prev_result = statement.run(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev_result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,10 @@ impl AbstractTree for Logic {
|
|||||||
"==" => LogicOperator::Equal,
|
"==" => LogicOperator::Equal,
|
||||||
"&&" => LogicOperator::And,
|
"&&" => LogicOperator::And,
|
||||||
"||" => LogicOperator::Or,
|
"||" => LogicOperator::Or,
|
||||||
|
">" => LogicOperator::Greater,
|
||||||
|
"<" => LogicOperator::Less,
|
||||||
|
">=" => LogicOperator::GreaterOrEqual,
|
||||||
|
"<=" => LogicOperator::LessOrEqaul,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnexpectedSyntax {
|
return Err(Error::UnexpectedSyntax {
|
||||||
expected: "==, && or ||",
|
expected: "==, && or ||",
|
||||||
@ -41,15 +45,25 @@ impl AbstractTree for Logic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
let left_value = self.left.run(context)?;
|
let left = self.left.run(context)?;
|
||||||
let right_value = self.right.run(context)?;
|
let right = self.right.run(context)?;
|
||||||
let outcome = match self.operator {
|
let result = match self.operator {
|
||||||
LogicOperator::Equal => left_value == right_value,
|
LogicOperator::Equal => {
|
||||||
LogicOperator::And => left_value.as_boolean()? && right_value.as_boolean()?,
|
if let (Ok(left_num), Ok(right_num)) = (left.as_number(), right.as_number()) {
|
||||||
LogicOperator::Or => left_value.as_boolean()? || right_value.as_boolean()?,
|
left_num == right_num
|
||||||
|
} else {
|
||||||
|
left == right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogicOperator::And => left.as_boolean()? && right.as_boolean()?,
|
||||||
|
LogicOperator::Or => left.as_boolean()? || right.as_boolean()?,
|
||||||
|
LogicOperator::Greater => left > right,
|
||||||
|
LogicOperator::Less => left < right,
|
||||||
|
LogicOperator::GreaterOrEqual => left >= right,
|
||||||
|
LogicOperator::LessOrEqaul => left <= right,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Value::Boolean(outcome))
|
Ok(Value::Boolean(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,4 +72,8 @@ pub enum LogicOperator {
|
|||||||
Equal,
|
Equal,
|
||||||
And,
|
And,
|
||||||
Or,
|
Or,
|
||||||
|
Greater,
|
||||||
|
Less,
|
||||||
|
GreaterOrEqual,
|
||||||
|
LessOrEqaul,
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,20 @@ use serde::{Deserialize, Serialize};
|
|||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
r#while::While, tool::Tool, AbstractTree, Assignment, Error, Expression, IfElse, Match, Result,
|
r#while::While, AbstractTree, Assignment, Error, Expression, IfElse, Match, Result, Value,
|
||||||
Value, VariableMap,
|
VariableMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Abstract representation of a statement.
|
/// Abstract representation of a statement.
|
||||||
///
|
///
|
||||||
/// Items are either comments, which do nothing, or statements, which can be run
|
/// A statement may evaluate to an Empty value when run. If a Statement is an
|
||||||
/// to produce a single value or interact with their context.
|
/// Expression, it will always return a non-empty value when run.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Assignment(Box<Assignment>),
|
Assignment(Box<Assignment>),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
IfElse(Box<IfElse>),
|
IfElse(Box<IfElse>),
|
||||||
Match(Match),
|
Match(Match),
|
||||||
Tool(Tool),
|
|
||||||
While(Box<While>),
|
While(Box<While>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +56,6 @@ impl AbstractTree for Statement {
|
|||||||
Statement::Expression(expression) => expression.run(context),
|
Statement::Expression(expression) => expression.run(context),
|
||||||
Statement::IfElse(if_else) => if_else.run(context),
|
Statement::IfElse(if_else) => if_else.run(context),
|
||||||
Statement::Match(r#match) => r#match.run(context),
|
Statement::Match(r#match) => r#match.run(context),
|
||||||
Statement::Tool(tool) => tool.run(context),
|
|
||||||
Statement::While(r#while) => r#while.run(context),
|
Statement::While(r#while) => r#while.run(context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,30 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use std::fs::read_to_string;
|
||||||
use tree_sitter::Node;
|
|
||||||
|
|
||||||
use crate::{AbstractTree, Error, Expression, Result, Value, VariableMap};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{Result, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Tool {
|
pub enum Tool {
|
||||||
Output(Expression),
|
Output,
|
||||||
|
Read,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Tool {
|
impl Tool {
|
||||||
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
pub fn run(&self, value: &Value) -> Result<Value> {
|
||||||
let tool_node = node.child(1).unwrap();
|
let value = match self {
|
||||||
let tool_name = tool_node.kind();
|
Tool::Output => {
|
||||||
|
println!("{value}");
|
||||||
|
|
||||||
match tool_name {
|
Value::Empty
|
||||||
"output" => {
|
|
||||||
let expression_node = tool_node.child(1).unwrap();
|
|
||||||
let expression = Expression::from_syntax_node(expression_node, source)?;
|
|
||||||
|
|
||||||
Ok(Tool::Output(expression))
|
|
||||||
}
|
}
|
||||||
_ => Err(Error::UnexpectedSyntax {
|
Tool::Read => {
|
||||||
expected: "output",
|
let file_contents = read_to_string(value.as_string()?)?;
|
||||||
actual: tool_name,
|
|
||||||
location: tool_node.start_position(),
|
|
||||||
relevant_source: tool_node.kind().to_string(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
Value::String(file_contents)
|
||||||
match self {
|
|
||||||
Tool::Output(expression) => {
|
|
||||||
let value = expression.run(context)?;
|
|
||||||
|
|
||||||
println!("{value}")
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Ok(Value::Empty)
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AbstractTree, Expression, Item};
|
use crate::{AbstractTree, Expression, Item, Result, Value, VariableMap};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct While {
|
pub struct While {
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
item: Item,
|
items: Vec<Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for While {
|
impl AbstractTree for While {
|
||||||
@ -15,15 +15,24 @@ impl AbstractTree for While {
|
|||||||
let expression_node = node.child(1).unwrap();
|
let expression_node = node.child(1).unwrap();
|
||||||
let expression = Expression::from_syntax_node(expression_node, source)?;
|
let expression = Expression::from_syntax_node(expression_node, source)?;
|
||||||
|
|
||||||
let item_node = node.child(3).unwrap();
|
let child_count = node.child_count();
|
||||||
let item = Item::from_syntax_node(item_node, source)?;
|
let mut items = Vec::with_capacity(child_count);
|
||||||
|
|
||||||
Ok(While { expression, item })
|
for index in 3..child_count - 1 {
|
||||||
|
let item_node = node.child(index).unwrap();
|
||||||
|
let item = Item::from_syntax_node(item_node, source)?;
|
||||||
|
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(While { expression, items })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, context: &mut crate::VariableMap) -> crate::Result<crate::Value> {
|
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||||
while self.expression.run(context)?.as_boolean()? {
|
while self.expression.run(context)?.as_boolean()? {
|
||||||
self.item.run(context)?;
|
for item in &self.items {
|
||||||
|
item.run(context)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(crate::Value::Empty)
|
Ok(crate::Value::Empty)
|
||||||
@ -36,9 +45,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evalualate_while_loop() {
|
fn evalualate_while_loop() {
|
||||||
assert_eq!(
|
assert_eq!(evaluate("while false { 'foo' }"), Ok(crate::Value::Empty))
|
||||||
evaluate("while false { 'foo' }"),
|
|
||||||
vec![Ok(crate::Value::Empty)]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
102
src/evaluator.rs
102
src/evaluator.rs
@ -19,7 +19,7 @@ use crate::{abstract_tree::item::Item, language, AbstractTree, Result, Value, Va
|
|||||||
/// # use dust::*;
|
/// # use dust::*;
|
||||||
/// assert_eq!(evaluate("1 + 2 + 3"), vec![Ok(Value::Integer(6))]);
|
/// assert_eq!(evaluate("1 + 2 + 3"), vec![Ok(Value::Integer(6))]);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn evaluate(source: &str) -> Vec<Result<Value>> {
|
pub fn evaluate(source: &str) -> Result<Value> {
|
||||||
let mut context = VariableMap::new();
|
let mut context = VariableMap::new();
|
||||||
|
|
||||||
evaluate_with_context(source, &mut context)
|
evaluate_with_context(source, &mut context)
|
||||||
@ -44,7 +44,7 @@ pub fn evaluate(source: &str) -> Vec<Result<Value>> {
|
|||||||
/// vec![Ok(Value::Empty), Ok(Value::Integer(10))]
|
/// vec![Ok(Value::Empty), Ok(Value::Integer(10))]
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn evaluate_with_context(source: &str, context: &mut VariableMap) -> Vec<Result<Value>> {
|
pub fn evaluate_with_context(source: &str, context: &mut VariableMap) -> Result<Value> {
|
||||||
let mut parser = Parser::new();
|
let mut parser = Parser::new();
|
||||||
parser.set_language(language()).unwrap();
|
parser.set_language(language()).unwrap();
|
||||||
|
|
||||||
@ -80,26 +80,17 @@ impl<'context, 'code> Evaluator<'context, 'code> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self) -> Vec<Result<Value>> {
|
fn run(self) -> Result<Value> {
|
||||||
let mut cursor = self.syntax_tree.walk();
|
let mut cursor = self.syntax_tree.walk();
|
||||||
let root_node = cursor.node();
|
let root_node = cursor.node();
|
||||||
let item_count = root_node.child_count();
|
let mut prev_result = Ok(Value::Empty);
|
||||||
let mut results = Vec::with_capacity(item_count);
|
|
||||||
|
|
||||||
for item_node in root_node.children(&mut cursor) {
|
for item_node in root_node.children(&mut cursor) {
|
||||||
let item_result = Item::from_syntax_node(item_node, self.source);
|
let item = Item::from_syntax_node(item_node, self.source)?;
|
||||||
|
prev_result = item.run(self.context);
|
||||||
match item_result {
|
|
||||||
Ok(item) => {
|
|
||||||
let eval_result = item.run(self.context);
|
|
||||||
|
|
||||||
results.push(eval_result);
|
|
||||||
}
|
|
||||||
Err(error) => results.push(Err(error)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results
|
prev_result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,49 +105,34 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_empty() {
|
fn evaluate_empty() {
|
||||||
assert_eq!(evaluate("x = 9"), vec![Ok(Value::Empty)]);
|
assert_eq!(evaluate("x = 9"), Ok(Value::Empty));
|
||||||
assert_eq!(evaluate("x = 1 + 1"), vec![Ok(Value::Empty)]);
|
assert_eq!(evaluate("x = 1 + 1"), Ok(Value::Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_integer() {
|
fn evaluate_integer() {
|
||||||
assert_eq!(evaluate("1"), vec![Ok(Value::Integer(1))]);
|
assert_eq!(evaluate("1"), Ok(Value::Integer(1)));
|
||||||
assert_eq!(evaluate("123"), vec![Ok(Value::Integer(123))]);
|
assert_eq!(evaluate("123"), Ok(Value::Integer(123)));
|
||||||
assert_eq!(evaluate("-666"), vec![Ok(Value::Integer(-666))]);
|
assert_eq!(evaluate("-666"), Ok(Value::Integer(-666)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_float() {
|
fn evaluate_float() {
|
||||||
assert_eq!(evaluate("0.1"), vec![Ok(Value::Float(0.1))]);
|
assert_eq!(evaluate("0.1"), Ok(Value::Float(0.1)));
|
||||||
assert_eq!(evaluate("12.3"), vec![Ok(Value::Float(12.3))]);
|
assert_eq!(evaluate("12.3"), Ok(Value::Float(12.3)));
|
||||||
assert_eq!(evaluate("-6.66"), vec![Ok(Value::Float(-6.66))]);
|
assert_eq!(evaluate("-6.66"), Ok(Value::Float(-6.66)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_string() {
|
fn evaluate_string() {
|
||||||
assert_eq!(
|
assert_eq!(evaluate("\"one\""), Ok(Value::String("one".to_string())));
|
||||||
evaluate("\"one\""),
|
assert_eq!(evaluate("'one'"), Ok(Value::String("one".to_string())));
|
||||||
vec![Ok(Value::String("one".to_string()))]
|
assert_eq!(evaluate("`one`"), Ok(Value::String("one".to_string())));
|
||||||
);
|
assert_eq!(evaluate("`'one'`"), Ok(Value::String("'one'".to_string())));
|
||||||
assert_eq!(
|
assert_eq!(evaluate("'`one`'"), Ok(Value::String("`one`".to_string())));
|
||||||
evaluate("'one'"),
|
|
||||||
vec![Ok(Value::String("one".to_string()))]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
evaluate("`one`"),
|
|
||||||
vec![Ok(Value::String("one".to_string()))]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
evaluate("`'one'`"),
|
|
||||||
vec![Ok(Value::String("'one'".to_string()))]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
evaluate("'`one`'"),
|
|
||||||
vec![Ok(Value::String("`one`".to_string()))]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate("\"'one'\""),
|
evaluate("\"'one'\""),
|
||||||
vec![Ok(Value::String("'one'".to_string()))]
|
Ok(Value::String("'one'".to_string()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,11 +140,11 @@ mod tests {
|
|||||||
fn evaluate_list() {
|
fn evaluate_list() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate("[1, 2, 'foobar']"),
|
evaluate("[1, 2, 'foobar']"),
|
||||||
vec![Ok(Value::List(vec![
|
Ok(Value::List(vec![
|
||||||
Value::Integer(1),
|
Value::Integer(1),
|
||||||
Value::Integer(2),
|
Value::Integer(2),
|
||||||
Value::String("foobar".to_string()),
|
Value::String("foobar".to_string()),
|
||||||
]))]
|
]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +156,7 @@ mod tests {
|
|||||||
map.set_value("foo".to_string(), Value::String("bar".to_string()))
|
map.set_value("foo".to_string(), Value::String("bar".to_string()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(evaluate("{ x = 1 foo = 'bar' }"), vec![Ok(Value::Map(map))]);
|
assert_eq!(evaluate("{ x = 1 foo = 'bar' }"), Ok(Value::Map(map)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -207,7 +183,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
),
|
),
|
||||||
vec![Ok(Value::Table(table))]
|
Ok(Value::Table(table))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,19 +191,16 @@ mod tests {
|
|||||||
fn evaluate_if_then() {
|
fn evaluate_if_then() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate("if true then 'true'"),
|
evaluate("if true then 'true'"),
|
||||||
vec![Ok(Value::String("true".to_string()))]
|
Ok(Value::String("true".to_string()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_if_then_else() {
|
fn evaluate_if_then_else() {
|
||||||
assert_eq!(
|
assert_eq!(evaluate("if false then 1 else 2"), Ok(Value::Integer(2)));
|
||||||
evaluate("if false then 1 else 2"),
|
|
||||||
vec![Ok(Value::Integer(2))]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate("if true then 1.0 else 42.0"),
|
evaluate("if true then 1.0 else 42.0"),
|
||||||
vec![Ok(Value::Float(1.0))]
|
Ok(Value::Float(1.0))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +217,7 @@ mod tests {
|
|||||||
'ok'
|
'ok'
|
||||||
"
|
"
|
||||||
),
|
),
|
||||||
vec![Ok(Value::String("ok".to_string()))]
|
Ok(Value::String("ok".to_string()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +237,7 @@ mod tests {
|
|||||||
else 'ok'
|
else 'ok'
|
||||||
"
|
"
|
||||||
),
|
),
|
||||||
vec![Ok(Value::String("ok".to_string()))]
|
Ok(Value::String("ok".to_string()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,14 +245,14 @@ mod tests {
|
|||||||
fn evaluate_function() {
|
fn evaluate_function() {
|
||||||
let function = Function::new(
|
let function = Function::new(
|
||||||
vec![Identifier::new("message".to_string())],
|
vec![Identifier::new("message".to_string())],
|
||||||
vec![Item::Statement(Statement::Expression(
|
Item::new(vec![Statement::Expression(Expression::Identifier(
|
||||||
Expression::Identifier(Identifier::new("message".to_string())),
|
Identifier::new("message".to_string()),
|
||||||
))],
|
))]),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate("function <message> { message }"),
|
evaluate("function <message> { message }"),
|
||||||
vec![Ok(Value::Function(function))]
|
Ok(Value::Function(function))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,15 +268,12 @@ mod tests {
|
|||||||
",
|
",
|
||||||
&mut context
|
&mut context
|
||||||
),
|
),
|
||||||
vec![
|
Ok(Value::String("Hiya".to_string()))
|
||||||
Ok(Value::Empty),
|
|
||||||
Ok(Value::List(vec![Value::String("Hiya".to_string())]))
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_tool_call() {
|
fn evaluate_tool_call() {
|
||||||
assert_eq!(evaluate("(output 'Hiya')"), vec![Ok(Value::Empty)]);
|
assert_eq!(evaluate("(output 'Hiya')"), Ok(Value::Empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/main.rs
30
src/main.rs
@ -32,21 +32,19 @@ fn main() {
|
|||||||
return run_cli_shell();
|
return run_cli_shell();
|
||||||
}
|
}
|
||||||
|
|
||||||
let eval_results = if let Some(path) = args.path {
|
let eval_result = if let Some(path) = args.path {
|
||||||
let file_contents = read_to_string(path).unwrap();
|
let file_contents = read_to_string(path).unwrap();
|
||||||
|
|
||||||
evaluate(&file_contents)
|
evaluate(&file_contents)
|
||||||
} else if let Some(command) = args.command {
|
} else if let Some(command) = args.command {
|
||||||
evaluate(&command)
|
evaluate(&command)
|
||||||
} else {
|
} else {
|
||||||
vec![Ok(Value::Empty)]
|
Ok(Value::Empty)
|
||||||
};
|
};
|
||||||
|
|
||||||
for result in eval_results {
|
match eval_result {
|
||||||
match result {
|
Ok(value) => println!("{value}"),
|
||||||
Ok(value) => println!("{value}"),
|
Err(error) => eprintln!("{error}"),
|
||||||
Err(error) => eprintln!("{error}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,21 +141,15 @@ fn run_cli_shell() {
|
|||||||
|
|
||||||
rl.add_history_entry(line).unwrap();
|
rl.add_history_entry(line).unwrap();
|
||||||
|
|
||||||
let eval_results = evaluate_with_context(line, &mut context);
|
let eval_result = evaluate_with_context(line, &mut context);
|
||||||
|
|
||||||
for result in eval_results {
|
match eval_result {
|
||||||
match result {
|
Ok(value) => println!("{value}"),
|
||||||
Ok(value) => println!("{value}"),
|
Err(error) => eprintln!("{error}"),
|
||||||
Err(error) => eprintln!("{error}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => break,
|
||||||
break;
|
Err(ReadlineError::Eof) => break,
|
||||||
}
|
|
||||||
Err(ReadlineError::Eof) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(error) => eprintln!("{error}"),
|
Err(error) => eprintln!("{error}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ use crate::{Identifier, Item};
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
parameters: Vec<Identifier>,
|
parameters: Vec<Identifier>,
|
||||||
body: Vec<Item>,
|
body: Item,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(identifiers: Vec<Identifier>, items: Vec<Item>) -> Self {
|
pub fn new(identifiers: Vec<Identifier>, items: Item) -> Self {
|
||||||
Function {
|
Function {
|
||||||
parameters: identifiers,
|
parameters: identifiers,
|
||||||
body: items,
|
body: items,
|
||||||
@ -22,7 +22,7 @@ impl Function {
|
|||||||
&self.parameters
|
&self.parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn items(&self) -> &Vec<Item> {
|
pub fn body(&self) -> &Item {
|
||||||
&self.body
|
&self.body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
use crate::Value;
|
|
||||||
|
|
||||||
pub struct Iter(Value);
|
|
||||||
|
|
||||||
impl IntoIterator for Value {
|
|
||||||
type Item = Value;
|
|
||||||
|
|
||||||
type IntoIter = Iter;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
Iter(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Iter {
|
|
||||||
type Item = Value;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
108
src/value/mod.rs
108
src/value/mod.rs
@ -17,11 +17,10 @@ use std::{
|
|||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Add, Sub},
|
ops::{Add, AddAssign, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod iter;
|
|
||||||
pub mod table;
|
pub mod table;
|
||||||
pub mod value_type;
|
pub mod value_type;
|
||||||
pub mod variable_map;
|
pub mod variable_map;
|
||||||
@ -157,13 +156,11 @@ impl Value {
|
|||||||
"function" => {
|
"function" => {
|
||||||
let child_count = child.child_count();
|
let child_count = child.child_count();
|
||||||
let mut identifiers = Vec::new();
|
let mut identifiers = Vec::new();
|
||||||
let mut items = Vec::new();
|
let mut item = None;
|
||||||
|
|
||||||
for index in 0..child_count {
|
for index in 0..child_count {
|
||||||
let child = child.child(index).unwrap();
|
let child = child.child(index).unwrap();
|
||||||
|
|
||||||
println!("{child:?}");
|
|
||||||
|
|
||||||
if child.kind() == "identifier" {
|
if child.kind() == "identifier" {
|
||||||
let identifier = Identifier::from_syntax_node(child, source)?;
|
let identifier = Identifier::from_syntax_node(child, source)?;
|
||||||
|
|
||||||
@ -171,13 +168,11 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if child.kind() == "item" {
|
if child.kind() == "item" {
|
||||||
let item = Item::from_syntax_node(child, source)?;
|
item = Some(Item::from_syntax_node(child, source)?);
|
||||||
|
|
||||||
items.push(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Function(Function::new(identifiers, items)))
|
Ok(Value::Function(Function::new(identifiers, item.unwrap())))
|
||||||
}
|
}
|
||||||
_ => Err(Error::UnexpectedSyntax {
|
_ => Err(Error::UnexpectedSyntax {
|
||||||
expected: "string, integer, float, boolean, list, table, map, function or empty",
|
expected: "string, integer, float, boolean, list, table, map, function or empty",
|
||||||
@ -370,54 +365,67 @@ impl Add for Value {
|
|||||||
type Output = Result<Value>;
|
type Output = Result<Value>;
|
||||||
|
|
||||||
fn add(self, other: Self) -> Self::Output {
|
fn add(self, other: Self) -> Self::Output {
|
||||||
match (self, other) {
|
let non_number = match (self, other) {
|
||||||
(Value::String(left), Value::String(right)) => {
|
(Value::Integer(left), Value::Integer(right)) => {
|
||||||
let concatenated = left + &right;
|
return Ok(Value::Integer(left + right))
|
||||||
|
}
|
||||||
|
(Value::Float(left), Value::Float(right)) => return Ok(Value::Float(left + right)),
|
||||||
|
(Value::Integer(left), Value::Float(right)) => {
|
||||||
|
return Ok(Value::Float(left as f64 + right))
|
||||||
|
}
|
||||||
|
(Value::Float(left), Value::Integer(right)) => {
|
||||||
|
return Ok(Value::Float(left + right as f64))
|
||||||
|
}
|
||||||
|
(non_number, Value::Integer(_)) | (non_number, Value::Float(_)) => non_number,
|
||||||
|
(non_number, _) => non_number,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Value::String(concatenated))
|
Err(Error::ExpectedNumber { actual: non_number })
|
||||||
}
|
|
||||||
(Value::String(_), other) | (other, Value::String(_)) => {
|
|
||||||
Err(Error::ExpectedString { actual: other })
|
|
||||||
}
|
|
||||||
(Value::Float(left), Value::Float(right)) => {
|
|
||||||
let addition = left + right;
|
|
||||||
|
|
||||||
Ok(Value::Float(addition))
|
|
||||||
}
|
|
||||||
(Value::Float(_), other) | (other, Value::Float(_)) => {
|
|
||||||
Err(Error::ExpectedFloat { actual: other })
|
|
||||||
}
|
|
||||||
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left + right)),
|
|
||||||
(Value::Integer(_), other) | (other, Value::Integer(_)) => {
|
|
||||||
Err(Error::ExpectedInt { actual: other })
|
|
||||||
}
|
|
||||||
(Value::Boolean(_), Value::Boolean(_)) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
(Value::Boolean(_), other) | (other, Value::Boolean(_)) => {
|
|
||||||
Err(Error::ExpectedBoolean { actual: other })
|
|
||||||
}
|
|
||||||
(Value::List(_), Value::List(_)) => todo!(),
|
|
||||||
(Value::List(_), other) | (other, Value::List(_)) => {
|
|
||||||
Err(Error::ExpectedList { actual: other })
|
|
||||||
}
|
|
||||||
(Value::Map(_), Value::Map(_)) => todo!(),
|
|
||||||
(Value::Map(_), other) | (other, Value::Map(_)) => {
|
|
||||||
Err(Error::ExpectedMap { actual: other })
|
|
||||||
}
|
|
||||||
(Value::Empty, Value::Empty) => Ok(Value::Empty),
|
|
||||||
_ => Err(Error::CustomMessage(
|
|
||||||
"Cannot add the given types.".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for Value {
|
impl Sub for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
|
|
||||||
fn sub(self, _other: Self) -> Self::Output {
|
fn sub(self, other: Self) -> Self::Output {
|
||||||
todo!()
|
let non_number = match (self, other) {
|
||||||
|
(Value::Integer(left), Value::Integer(right)) => {
|
||||||
|
return Ok(Value::Integer(left + right))
|
||||||
|
}
|
||||||
|
(Value::Float(left), Value::Float(right)) => return Ok(Value::Float(left + right)),
|
||||||
|
(Value::Integer(left), Value::Float(right)) => {
|
||||||
|
return Ok(Value::Float(left as f64 + right))
|
||||||
|
}
|
||||||
|
(Value::Float(left), Value::Integer(right)) => {
|
||||||
|
return Ok(Value::Float(left + right as f64))
|
||||||
|
}
|
||||||
|
(non_number, Value::Integer(_)) | (non_number, Value::Float(_)) => non_number,
|
||||||
|
(non_number, _) => non_number,
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(Error::ExpectedNumber { actual: non_number })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for Value {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
match (self, other) {
|
||||||
|
(Value::Integer(left), Value::Integer(right)) => *left += right,
|
||||||
|
(Value::Float(left), Value::Float(right)) => *left += right,
|
||||||
|
(Value::Float(left), Value::Integer(right)) => *left += right as f64,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign for Value {
|
||||||
|
fn sub_assign(&mut self, other: Self) {
|
||||||
|
match (self, other) {
|
||||||
|
(Value::Integer(left), Value::Integer(right)) => *left -= right,
|
||||||
|
(Value::Float(left), Value::Float(right)) => *left -= right,
|
||||||
|
(Value::Float(left), Value::Integer(right)) => *left -= right as f64,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,52 +6,40 @@ use dust::*;
|
|||||||
fn collections() {
|
fn collections() {
|
||||||
let file_contents = read_to_string("examples/collections.ds").unwrap();
|
let file_contents = read_to_string("examples/collections.ds").unwrap();
|
||||||
|
|
||||||
for result in evaluate(&file_contents) {
|
evaluate(&file_contents).unwrap();
|
||||||
result.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list() {
|
fn list() {
|
||||||
let file_contents = read_to_string("examples/list.ds").unwrap();
|
let file_contents = read_to_string("examples/list.ds").unwrap();
|
||||||
|
|
||||||
for result in evaluate(&file_contents) {
|
evaluate(&file_contents).unwrap();
|
||||||
result.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn table() {
|
fn table() {
|
||||||
let file_contents = read_to_string("examples/table.ds").unwrap();
|
let file_contents = read_to_string("examples/table.ds").unwrap();
|
||||||
|
|
||||||
for result in evaluate(&file_contents) {
|
evaluate(&file_contents).unwrap();
|
||||||
result.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variables() {
|
fn variables() {
|
||||||
let file_contents = read_to_string("examples/variables.ds").unwrap();
|
let file_contents = read_to_string("examples/variables.ds").unwrap();
|
||||||
|
|
||||||
for result in evaluate(&file_contents) {
|
evaluate(&file_contents).unwrap();
|
||||||
result.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope() {
|
fn scope() {
|
||||||
let file_contents = read_to_string("examples/scope.ds").unwrap();
|
let file_contents = read_to_string("examples/scope.ds").unwrap();
|
||||||
|
|
||||||
for result in evaluate(&file_contents) {
|
evaluate(&file_contents).unwrap();
|
||||||
result.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn data_formats() {
|
fn data_formats() {
|
||||||
let file_contents = read_to_string("examples/data_formats.ds").unwrap();
|
let file_contents = read_to_string("examples/data_formats.ds").unwrap();
|
||||||
|
|
||||||
for result in evaluate(&file_contents) {
|
evaluate(&file_contents).unwrap();
|
||||||
result.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit d7ff4e57c58d9ff0708dc2902262384b99c4bc2c
|
Subproject commit 532c751c36a1f8c33197b018dd6e947b73abbdc4
|
Loading…
Reference in New Issue
Block a user