From 39692b3bd78dc6c9e64a5f4fd56d1a6f91b16438 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 9 Oct 2023 15:54:47 -0400 Subject: [PATCH] Implement new grammar --- examples/fibonacci.ds | 11 +++ examples/fizzbuzz.ds | 34 +++------ examples/hello_world.ds | 9 +++ src/abstract_tree/assignment.rs | 42 ++++++++++- src/abstract_tree/expression.rs | 5 +- src/abstract_tree/function_call.rs | 77 ++++++++++++-------- src/abstract_tree/item.rs | 54 +++++++++------ src/abstract_tree/logic.rs | 32 +++++++-- src/abstract_tree/statement.rs | 10 ++- src/abstract_tree/tool.rs | 47 +++++-------- src/abstract_tree/while.rs | 28 +++++--- src/evaluator.rs | 102 ++++++++++----------------- src/main.rs | 30 +++----- src/value/function.rs | 6 +- src/value/iter.rs | 21 ------ src/value/mod.rs | 108 ++++++++++++++++------------- tests/dust_examples.rs | 24 ++----- tree-sitter-dust | 2 +- 18 files changed, 329 insertions(+), 313 deletions(-) create mode 100644 examples/fibonacci.ds create mode 100644 examples/hello_world.ds delete mode 100644 src/value/iter.rs diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds new file mode 100644 index 0000000..03c358c --- /dev/null +++ b/examples/fibonacci.ds @@ -0,0 +1,11 @@ +fibonacci = function { + if i <= 1 { + 1 + } else if i == 2 { + 2 + } else { + output (fibonacci - 1) + (fibonacci - 2) + } +} + +(fibonacci 20) diff --git a/examples/fizzbuzz.ds b/examples/fizzbuzz.ds index 38d519e..c4c9cb0 100644 --- a/examples/fizzbuzz.ds +++ b/examples/fizzbuzz.ds @@ -1,31 +1,15 @@ -fizzbuzz_basic = function { - count = 1 +count = 1 - while count < limit { - if count % 3 == 0 && count % 5 == 0 { - (output 'fizzbuzz') - } else f count % 3 == 0 { - (output 'fizz') - } else if count % 5 == 0 { - (output 'buzz') - } else - (output count) +while count <= 15 { + (output count) - count += 1 + if count % 3 == 0 { + (output 'fizz') } -} - -fizzbuzz_match { - count = 1 - while count < 1 { - output match [count % 3 == 0, count % 5 == 0] { - [true, false] => 'fizz' - [false, true] => 'buzz' - [true, true] => 'fizzbuzz' - } + if count % 5 == 0 { + (output 'buzz') } + + count += 1 } - -fizzbuzz_basic<15> -fizzbuzz_match<15> diff --git a/examples/hello_world.ds b/examples/hello_world.ds new file mode 100644 index 0000000..62cd327 --- /dev/null +++ b/examples/hello_world.ds @@ -0,0 +1,9 @@ +(output 'Hello, world!') + +main = function { + while 1 == 1 { + (output message) + } +} + +(main 'Hello dust ~*') diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index 3be7869..9a9c817 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -1,33 +1,71 @@ use serde::{Deserialize, Serialize}; use tree_sitter::Node; -use crate::{AbstractTree, Result, Value, VariableMap}; +use crate::{AbstractTree, Error, Result, Value, VariableMap}; use super::{identifier::Identifier, statement::Statement}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct Assignment { identifier: Identifier, + operator: AssignmentOperator, statement: Statement, } +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] +pub enum AssignmentOperator { + Equal, + PlusEqual, + MinusEqual, +} + impl AbstractTree for Assignment { fn from_syntax_node(node: Node, source: &str) -> Result { let identifier_node = node.child(0).unwrap(); 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 = Statement::from_syntax_node(statement_node, source)?; Ok(Assignment { identifier, + operator, statement, }) } fn run(&self, context: &mut VariableMap) -> Result { 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)?; diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index c18cc7f..3481be5 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; 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}; @@ -12,7 +12,6 @@ pub enum Expression { Math(Box), Logic(Box), FunctionCall(FunctionCall), - ToolCall(Box), } impl AbstractTree for Expression { @@ -29,7 +28,6 @@ impl AbstractTree for Expression { "function_call" => { Expression::FunctionCall(FunctionCall::from_syntax_node(child, source)?) } - "tool_call" => Expression::ToolCall(Box::new(Tool::from_syntax_node(child, source)?)), _ => { return Err(Error::UnexpectedSyntax { expected: "value, identifier, math or function_call", @@ -50,7 +48,6 @@ impl AbstractTree for Expression { Expression::Math(math) => math.run(context), Expression::Logic(logic) => logic.run(context), Expression::FunctionCall(function_call) => function_call.run(context), - Expression::ToolCall(tool_call) => tool_call.run(context), } } } diff --git a/src/abstract_tree/function_call.rs b/src/abstract_tree/function_call.rs index ab5eae1..f594733 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -1,72 +1,89 @@ use serde::{Deserialize, Serialize}; use tree_sitter::Node; -use crate::{AbstractTree, Result, Value, VariableMap}; +use crate::{tool::Tool, AbstractTree, Result, Value, VariableMap}; use super::{expression::Expression, identifier::Identifier}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct FunctionCall { - identifier: Identifier, - expressions: Vec, + name: FunctionName, + arguments: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] +pub enum FunctionName { + Identifier(Identifier), + Tool(Tool), } impl AbstractTree for FunctionCall { fn from_syntax_node(node: Node, source: &str) -> Result { debug_assert_eq!("function_call", node.kind()); - let identifier_node = node.child(1).unwrap(); - let identifier = Identifier::from_syntax_node(identifier_node, source)?; + let name_node = node.child(1).unwrap(); - 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; while current_index < node.child_count() - 1 { let expression_node = node.child(current_index).unwrap(); let expression = Expression::from_syntax_node(expression_node, source)?; - expressions.push(expression); + arguments.push(expression); current_index += 1; } - Ok(FunctionCall { - identifier, - expressions, - }) + Ok(FunctionCall { name, arguments }) } fn run(&self, context: &mut VariableMap) -> Result { - 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())? { value.as_function().cloned()? } else { 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 { 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()); - - 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)) + definition.body().run(&mut function_context) } } diff --git a/src/abstract_tree/item.rs b/src/abstract_tree/item.rs index 70426a3..2ba194f 100644 --- a/src/abstract_tree/item.rs +++ b/src/abstract_tree/item.rs @@ -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 /// referencing variables. #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] -pub enum Item { - Comment(String), - Statement(Statement), +pub struct Item { + statements: Vec, +} + +impl Item { + pub fn new(statements: Vec) -> Self { + Self { statements } + } } impl AbstractTree for Item { fn from_syntax_node(node: Node, source: &str) -> Result { 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" { - let byte_range = child.byte_range(); - let comment_text = &source[byte_range]; + for index in 0..child_count { + let child = node.child(index).unwrap(); - Ok(Item::Comment(comment_text.to_string())) - } else if child.kind() == "statement" { - Ok(Item::Statement(Statement::from_syntax_node(child, source)?)) - } else { - Err(Error::UnexpectedSyntax { - expected: "comment or statement", - actual: child.kind(), - location: child.start_position(), - relevant_source: source[node.byte_range()].to_string(), - }) + let statement = match child.kind() { + "statement" => Statement::from_syntax_node(child, source)?, + _ => { + return Err(Error::UnexpectedSyntax { + expected: "comment or statement", + actual: child.kind(), + location: child.start_position(), + relevant_source: source[node.byte_range()].to_string(), + }) + } + }; + + statements.push(statement); } + + Ok(Item { statements }) } fn run(&self, context: &mut VariableMap) -> Result { - match self { - Item::Comment(text) => Ok(Value::String(text.clone())), - Item::Statement(statement) => statement.run(context), + let mut prev_result = Ok(Value::Empty); + + for statement in &self.statements { + prev_result?; + prev_result = statement.run(context); } + + prev_result } } diff --git a/src/abstract_tree/logic.rs b/src/abstract_tree/logic.rs index 5a8e828..f4d12e2 100644 --- a/src/abstract_tree/logic.rs +++ b/src/abstract_tree/logic.rs @@ -20,6 +20,10 @@ impl AbstractTree for Logic { "==" => LogicOperator::Equal, "&&" => LogicOperator::And, "||" => LogicOperator::Or, + ">" => LogicOperator::Greater, + "<" => LogicOperator::Less, + ">=" => LogicOperator::GreaterOrEqual, + "<=" => LogicOperator::LessOrEqaul, _ => { return Err(Error::UnexpectedSyntax { expected: "==, && or ||", @@ -41,15 +45,25 @@ impl AbstractTree for Logic { } fn run(&self, context: &mut VariableMap) -> Result { - let left_value = self.left.run(context)?; - let right_value = self.right.run(context)?; - let outcome = match self.operator { - LogicOperator::Equal => left_value == right_value, - LogicOperator::And => left_value.as_boolean()? && right_value.as_boolean()?, - LogicOperator::Or => left_value.as_boolean()? || right_value.as_boolean()?, + let left = self.left.run(context)?; + let right = self.right.run(context)?; + let result = match self.operator { + LogicOperator::Equal => { + if let (Ok(left_num), Ok(right_num)) = (left.as_number(), right.as_number()) { + 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, And, Or, + Greater, + Less, + GreaterOrEqual, + LessOrEqaul, } diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index c84d5e0..3a791d5 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -2,21 +2,20 @@ use serde::{Deserialize, Serialize}; use tree_sitter::Node; use crate::{ - r#while::While, tool::Tool, AbstractTree, Assignment, Error, Expression, IfElse, Match, Result, - Value, VariableMap, + r#while::While, AbstractTree, Assignment, Error, Expression, IfElse, Match, Result, Value, + VariableMap, }; /// Abstract representation of a statement. /// -/// Items are either comments, which do nothing, or statements, which can be run -/// to produce a single value or interact with their context. +/// A statement may evaluate to an Empty value when run. If a Statement is an +/// Expression, it will always return a non-empty value when run. #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub enum Statement { Assignment(Box), Expression(Expression), IfElse(Box), Match(Match), - Tool(Tool), While(Box), } @@ -57,7 +56,6 @@ impl AbstractTree for Statement { Statement::Expression(expression) => expression.run(context), Statement::IfElse(if_else) => if_else.run(context), Statement::Match(r#match) => r#match.run(context), - Statement::Tool(tool) => tool.run(context), Statement::While(r#while) => r#while.run(context), } } diff --git a/src/abstract_tree/tool.rs b/src/abstract_tree/tool.rs index 05a4b48..55df2b1 100644 --- a/src/abstract_tree/tool.rs +++ b/src/abstract_tree/tool.rs @@ -1,43 +1,30 @@ -use serde::{Deserialize, Serialize}; -use tree_sitter::Node; +use std::fs::read_to_string; -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)] pub enum Tool { - Output(Expression), + Output, + Read, } -impl AbstractTree for Tool { - fn from_syntax_node(node: Node, source: &str) -> Result { - let tool_node = node.child(1).unwrap(); - let tool_name = tool_node.kind(); +impl Tool { + pub fn run(&self, value: &Value) -> Result { + let value = match self { + Tool::Output => { + println!("{value}"); - match tool_name { - "output" => { - let expression_node = tool_node.child(1).unwrap(); - let expression = Expression::from_syntax_node(expression_node, source)?; - - Ok(Tool::Output(expression)) + Value::Empty } - _ => Err(Error::UnexpectedSyntax { - expected: "output", - actual: tool_name, - location: tool_node.start_position(), - relevant_source: tool_node.kind().to_string(), - }), - } - } + Tool::Read => { + let file_contents = read_to_string(value.as_string()?)?; - fn run(&self, context: &mut VariableMap) -> Result { - match self { - Tool::Output(expression) => { - let value = expression.run(context)?; - - println!("{value}") + Value::String(file_contents) } - } + }; - Ok(Value::Empty) + Ok(value) } } diff --git a/src/abstract_tree/while.rs b/src/abstract_tree/while.rs index 96f5d7e..524370c 100644 --- a/src/abstract_tree/while.rs +++ b/src/abstract_tree/while.rs @@ -1,11 +1,11 @@ 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)] pub struct While { expression: Expression, - item: Item, + items: Vec, } impl AbstractTree for While { @@ -15,15 +15,24 @@ impl AbstractTree for While { let expression_node = node.child(1).unwrap(); let expression = Expression::from_syntax_node(expression_node, source)?; - let item_node = node.child(3).unwrap(); - let item = Item::from_syntax_node(item_node, source)?; + let child_count = node.child_count(); + 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 { + fn run(&self, context: &mut VariableMap) -> Result { while self.expression.run(context)?.as_boolean()? { - self.item.run(context)?; + for item in &self.items { + item.run(context)?; + } } Ok(crate::Value::Empty) @@ -36,9 +45,6 @@ mod tests { #[test] fn evalualate_while_loop() { - assert_eq!( - evaluate("while false { 'foo' }"), - vec![Ok(crate::Value::Empty)] - ) + assert_eq!(evaluate("while false { 'foo' }"), Ok(crate::Value::Empty)) } } diff --git a/src/evaluator.rs b/src/evaluator.rs index 5fe27ab..a640fa0 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -19,7 +19,7 @@ use crate::{abstract_tree::item::Item, language, AbstractTree, Result, Value, Va /// # use dust::*; /// assert_eq!(evaluate("1 + 2 + 3"), vec![Ok(Value::Integer(6))]); /// ``` -pub fn evaluate(source: &str) -> Vec> { +pub fn evaluate(source: &str) -> Result { let mut context = VariableMap::new(); evaluate_with_context(source, &mut context) @@ -44,7 +44,7 @@ pub fn evaluate(source: &str) -> Vec> { /// vec![Ok(Value::Empty), Ok(Value::Integer(10))] /// ); /// ``` -pub fn evaluate_with_context(source: &str, context: &mut VariableMap) -> Vec> { +pub fn evaluate_with_context(source: &str, context: &mut VariableMap) -> Result { let mut parser = Parser::new(); parser.set_language(language()).unwrap(); @@ -80,26 +80,17 @@ impl<'context, 'code> Evaluator<'context, 'code> { } } - fn run(self) -> Vec> { + fn run(self) -> Result { let mut cursor = self.syntax_tree.walk(); let root_node = cursor.node(); - let item_count = root_node.child_count(); - let mut results = Vec::with_capacity(item_count); + let mut prev_result = Ok(Value::Empty); for item_node in root_node.children(&mut cursor) { - let item_result = Item::from_syntax_node(item_node, self.source); - - match item_result { - Ok(item) => { - let eval_result = item.run(self.context); - - results.push(eval_result); - } - Err(error) => results.push(Err(error)), - } + let item = Item::from_syntax_node(item_node, self.source)?; + prev_result = item.run(self.context); } - results + prev_result } } @@ -114,49 +105,34 @@ mod tests { #[test] fn evaluate_empty() { - assert_eq!(evaluate("x = 9"), vec![Ok(Value::Empty)]); - assert_eq!(evaluate("x = 1 + 1"), vec![Ok(Value::Empty)]); + assert_eq!(evaluate("x = 9"), Ok(Value::Empty)); + assert_eq!(evaluate("x = 1 + 1"), Ok(Value::Empty)); } #[test] fn evaluate_integer() { - assert_eq!(evaluate("1"), vec![Ok(Value::Integer(1))]); - assert_eq!(evaluate("123"), vec![Ok(Value::Integer(123))]); - assert_eq!(evaluate("-666"), vec![Ok(Value::Integer(-666))]); + assert_eq!(evaluate("1"), Ok(Value::Integer(1))); + assert_eq!(evaluate("123"), Ok(Value::Integer(123))); + assert_eq!(evaluate("-666"), Ok(Value::Integer(-666))); } #[test] fn evaluate_float() { - assert_eq!(evaluate("0.1"), vec![Ok(Value::Float(0.1))]); - assert_eq!(evaluate("12.3"), vec![Ok(Value::Float(12.3))]); - assert_eq!(evaluate("-6.66"), vec![Ok(Value::Float(-6.66))]); + assert_eq!(evaluate("0.1"), Ok(Value::Float(0.1))); + assert_eq!(evaluate("12.3"), Ok(Value::Float(12.3))); + assert_eq!(evaluate("-6.66"), Ok(Value::Float(-6.66))); } #[test] fn evaluate_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!( - evaluate("`'one'`"), - vec![Ok(Value::String("'one'".to_string()))] - ); - assert_eq!( - evaluate("'`one`'"), - 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!(evaluate("`one`"), 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!( evaluate("\"'one'\""), - vec![Ok(Value::String("'one'".to_string()))] + Ok(Value::String("'one'".to_string())) ); } @@ -164,11 +140,11 @@ mod tests { fn evaluate_list() { assert_eq!( evaluate("[1, 2, 'foobar']"), - vec![Ok(Value::List(vec![ + Ok(Value::List(vec![ Value::Integer(1), Value::Integer(2), Value::String("foobar".to_string()), - ]))] + ])) ); } @@ -180,7 +156,7 @@ mod tests { map.set_value("foo".to_string(), Value::String("bar".to_string())) .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] @@ -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() { assert_eq!( evaluate("if true then 'true'"), - vec![Ok(Value::String("true".to_string()))] + Ok(Value::String("true".to_string())) ); } #[test] fn evaluate_if_then_else() { - assert_eq!( - evaluate("if false then 1 else 2"), - vec![Ok(Value::Integer(2))] - ); + assert_eq!(evaluate("if false then 1 else 2"), Ok(Value::Integer(2))); assert_eq!( 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' " ), - vec![Ok(Value::String("ok".to_string()))] + Ok(Value::String("ok".to_string())) ); } @@ -264,7 +237,7 @@ mod tests { else 'ok' " ), - vec![Ok(Value::String("ok".to_string()))] + Ok(Value::String("ok".to_string())) ); } @@ -272,14 +245,14 @@ mod tests { fn evaluate_function() { let function = Function::new( vec![Identifier::new("message".to_string())], - vec![Item::Statement(Statement::Expression( - Expression::Identifier(Identifier::new("message".to_string())), - ))], + Item::new(vec![Statement::Expression(Expression::Identifier( + Identifier::new("message".to_string()), + ))]), ); assert_eq!( evaluate("function { message }"), - vec![Ok(Value::Function(function))] + Ok(Value::Function(function)) ); } @@ -295,15 +268,12 @@ mod tests { ", &mut context ), - vec![ - Ok(Value::Empty), - Ok(Value::List(vec![Value::String("Hiya".to_string())])) - ] + Ok(Value::String("Hiya".to_string())) ); } #[test] fn evaluate_tool_call() { - assert_eq!(evaluate("(output 'Hiya')"), vec![Ok(Value::Empty)]); + assert_eq!(evaluate("(output 'Hiya')"), Ok(Value::Empty)); } } diff --git a/src/main.rs b/src/main.rs index dd99380..275c7ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,21 +32,19 @@ fn main() { 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(); evaluate(&file_contents) } else if let Some(command) = args.command { evaluate(&command) } else { - vec![Ok(Value::Empty)] + Ok(Value::Empty) }; - for result in eval_results { - match result { - Ok(value) => println!("{value}"), - Err(error) => eprintln!("{error}"), - } + match eval_result { + Ok(value) => println!("{value}"), + Err(error) => eprintln!("{error}"), } } @@ -143,21 +141,15 @@ fn run_cli_shell() { 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 result { - Ok(value) => println!("{value}"), - Err(error) => eprintln!("{error}"), - } + match eval_result { + Ok(value) => println!("{value}"), + Err(error) => eprintln!("{error}"), } } - Err(ReadlineError::Interrupted) => { - break; - } - Err(ReadlineError::Eof) => { - break; - } + Err(ReadlineError::Interrupted) => break, + Err(ReadlineError::Eof) => break, Err(error) => eprintln!("{error}"), } } diff --git a/src/value/function.rs b/src/value/function.rs index 7dbb297..c3a9c8a 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -7,11 +7,11 @@ use crate::{Identifier, Item}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct Function { parameters: Vec, - body: Vec, + body: Item, } impl Function { - pub fn new(identifiers: Vec, items: Vec) -> Self { + pub fn new(identifiers: Vec, items: Item) -> Self { Function { parameters: identifiers, body: items, @@ -22,7 +22,7 @@ impl Function { &self.parameters } - pub fn items(&self) -> &Vec { + pub fn body(&self) -> &Item { &self.body } } diff --git a/src/value/iter.rs b/src/value/iter.rs deleted file mode 100644 index 1bc9e3f..0000000 --- a/src/value/iter.rs +++ /dev/null @@ -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 { - todo!() - } -} diff --git a/src/value/mod.rs b/src/value/mod.rs index 422b320..cf1054f 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -17,11 +17,10 @@ use std::{ convert::TryFrom, fmt::{self, Display, Formatter}, marker::PhantomData, - ops::{Add, Sub}, + ops::{Add, AddAssign, Sub, SubAssign}, }; pub mod function; -pub mod iter; pub mod table; pub mod value_type; pub mod variable_map; @@ -157,13 +156,11 @@ impl Value { "function" => { let child_count = child.child_count(); let mut identifiers = Vec::new(); - let mut items = Vec::new(); + let mut item = None; for index in 0..child_count { let child = child.child(index).unwrap(); - println!("{child:?}"); - if child.kind() == "identifier" { let identifier = Identifier::from_syntax_node(child, source)?; @@ -171,13 +168,11 @@ impl Value { } if child.kind() == "item" { - let item = Item::from_syntax_node(child, source)?; - - items.push(item) + item = Some(Item::from_syntax_node(child, source)?); } } - Ok(Value::Function(Function::new(identifiers, items))) + Ok(Value::Function(Function::new(identifiers, item.unwrap()))) } _ => Err(Error::UnexpectedSyntax { expected: "string, integer, float, boolean, list, table, map, function or empty", @@ -370,54 +365,67 @@ impl Add for Value { type Output = Result; fn add(self, other: Self) -> Self::Output { - match (self, other) { - (Value::String(left), Value::String(right)) => { - let concatenated = left + &right; + 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, + }; - Ok(Value::String(concatenated)) - } - (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(), - )), - } + Err(Error::ExpectedNumber { actual: non_number }) } } impl Sub for Value { type Output = Result; - fn sub(self, _other: Self) -> Self::Output { - todo!() + fn sub(self, other: Self) -> Self::Output { + 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, + _ => {} + } } } diff --git a/tests/dust_examples.rs b/tests/dust_examples.rs index 32d9d71..65d92e9 100644 --- a/tests/dust_examples.rs +++ b/tests/dust_examples.rs @@ -6,52 +6,40 @@ use dust::*; fn collections() { let file_contents = read_to_string("examples/collections.ds").unwrap(); - for result in evaluate(&file_contents) { - result.unwrap(); - } + evaluate(&file_contents).unwrap(); } #[test] fn list() { let file_contents = read_to_string("examples/list.ds").unwrap(); - for result in evaluate(&file_contents) { - result.unwrap(); - } + evaluate(&file_contents).unwrap(); } #[test] fn table() { let file_contents = read_to_string("examples/table.ds").unwrap(); - for result in evaluate(&file_contents) { - result.unwrap(); - } + evaluate(&file_contents).unwrap(); } #[test] fn variables() { let file_contents = read_to_string("examples/variables.ds").unwrap(); - for result in evaluate(&file_contents) { - result.unwrap(); - } + evaluate(&file_contents).unwrap(); } #[test] fn scope() { let file_contents = read_to_string("examples/scope.ds").unwrap(); - for result in evaluate(&file_contents) { - result.unwrap(); - } + evaluate(&file_contents).unwrap(); } #[test] fn data_formats() { let file_contents = read_to_string("examples/data_formats.ds").unwrap(); - for result in evaluate(&file_contents) { - result.unwrap(); - } + evaluate(&file_contents).unwrap(); } diff --git a/tree-sitter-dust b/tree-sitter-dust index d7ff4e5..532c751 160000 --- a/tree-sitter-dust +++ b/tree-sitter-dust @@ -1 +1 @@ -Subproject commit d7ff4e57c58d9ff0708dc2902262384b99c4bc2c +Subproject commit 532c751c36a1f8c33197b018dd6e947b73abbdc4