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
|
||||
|
||||
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
|
||||
while count <= 15 {
|
||||
(output count)
|
||||
|
||||
if count % 3 == 0 {
|
||||
(output 'fizz')
|
||||
}
|
||||
|
||||
if count % 5 == 0 {
|
||||
(output 'buzz')
|
||||
}
|
||||
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
fizzbuzz_match <limit> {
|
||||
count = 1
|
||||
|
||||
while count < 1 {
|
||||
output match [count % 3 == 0, count % 5 == 0] {
|
||||
[true, false] => 'fizz'
|
||||
[false, true] => 'buzz'
|
||||
[true, true] => 'fizzbuzz'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fizzbuzz_basic<15>
|
||||
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 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<Self> {
|
||||
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<Value> {
|
||||
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)?;
|
||||
|
||||
|
@ -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<Math>),
|
||||
Logic(Box<Logic>),
|
||||
FunctionCall(FunctionCall),
|
||||
ToolCall(Box<Tool>),
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Expression>,
|
||||
name: FunctionName,
|
||||
arguments: Vec<Expression>,
|
||||
}
|
||||
|
||||
#[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<Self> {
|
||||
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<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())? {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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<Statement>,
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn new(statements: Vec<Statement>) -> Self {
|
||||
Self { statements }
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for Item {
|
||||
fn from_syntax_node(node: Node, source: &str) -> Result<Self> {
|
||||
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 {
|
||||
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<Value> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<Value> {
|
||||
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,
|
||||
}
|
||||
|
@ -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<Assignment>),
|
||||
Expression(Expression),
|
||||
IfElse(Box<IfElse>),
|
||||
Match(Match),
|
||||
Tool(Tool),
|
||||
While(Box<While>),
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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<Self> {
|
||||
let tool_node = node.child(1).unwrap();
|
||||
let tool_name = tool_node.kind();
|
||||
impl Tool {
|
||||
pub fn run(&self, value: &Value) -> Result<Value> {
|
||||
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)?;
|
||||
Value::Empty
|
||||
}
|
||||
Tool::Read => {
|
||||
let file_contents = read_to_string(value.as_string()?)?;
|
||||
|
||||
Ok(Tool::Output(expression))
|
||||
}
|
||||
_ => Err(Error::UnexpectedSyntax {
|
||||
expected: "output",
|
||||
actual: tool_name,
|
||||
location: tool_node.start_position(),
|
||||
relevant_source: tool_node.kind().to_string(),
|
||||
}),
|
||||
}
|
||||
Value::String(file_contents)
|
||||
}
|
||||
};
|
||||
|
||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||
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 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<Item>,
|
||||
}
|
||||
|
||||
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 child_count = node.child_count();
|
||||
let mut items = Vec::with_capacity(child_count);
|
||||
|
||||
for index in 3..child_count - 1 {
|
||||
let item_node = node.child(index).unwrap();
|
||||
let item = Item::from_syntax_node(item_node, source)?;
|
||||
|
||||
Ok(While { expression, item })
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
fn run(&self, context: &mut crate::VariableMap) -> crate::Result<crate::Value> {
|
||||
Ok(While { expression, items })
|
||||
}
|
||||
|
||||
fn run(&self, context: &mut VariableMap) -> Result<Value> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
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::*;
|
||||
/// 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();
|
||||
|
||||
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))]
|
||||
/// );
|
||||
/// ```
|
||||
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();
|
||||
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 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> { 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));
|
||||
}
|
||||
}
|
||||
|
22
src/main.rs
22
src/main.rs
@ -32,23 +32,21 @@ 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 {
|
||||
match eval_result {
|
||||
Ok(value) => println!("{value}"),
|
||||
Err(error) => eprintln!("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Helper, Completer, Validator)]
|
||||
struct DustReadline {
|
||||
@ -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 {
|
||||
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}"),
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,11 @@ use crate::{Identifier, Item};
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Function {
|
||||
parameters: Vec<Identifier>,
|
||||
body: Vec<Item>,
|
||||
body: Item,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new(identifiers: Vec<Identifier>, items: Vec<Item>) -> Self {
|
||||
pub fn new(identifiers: Vec<Identifier>, items: Item) -> Self {
|
||||
Function {
|
||||
parameters: identifiers,
|
||||
body: items,
|
||||
@ -22,7 +22,7 @@ impl Function {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub fn items(&self) -> &Vec<Item> {
|
||||
pub fn body(&self) -> &Item {
|
||||
&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,
|
||||
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<Value>;
|
||||
|
||||
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<Self>;
|
||||
|
||||
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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d7ff4e57c58d9ff0708dc2902262384b99c4bc2c
|
||||
Subproject commit 532c751c36a1f8c33197b018dd6e947b73abbdc4
|
Loading…
Reference in New Issue
Block a user