diff --git a/README.md b/README.md index 6735206..486ec15 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,8 @@ A basic dust program: Dust can do two (or more) things at the same time with effortless concurrency: ```dust -(run - (output 'will this one finish first?') - (output 'or will this one?')) +async (output 'will this one finish first?') +async (output 'or will this one?') ``` Dust is an interpreted, general purpose language with first class functions. It is *data-oriented*, with extensive tools to manage structured and relational data. Dust also includes built-in tooling to import and export data in a variety of formats, including JSON, TOML, YAML and CSV. @@ -172,6 +171,19 @@ print = function { } ``` +### Concurrency + +As a language written in Rust, Dust features effortless concurrency anywhere in your code. + +```dust +if (random_integer) % 2 == 0 { + run { + (output 1 + 1) + (output 1 + 1 == 2) + } +} +``` + ## Implementation Dust is formally defined as a Tree Sitter grammar in the tree-sitter-dust module. Tree sitter generates a parser, written in C, from a set of rules defined in Javascript. Dust itself is a rust binary that calls the C parser using FFI. diff --git a/examples/clue_solver.ds b/examples/clue_solver.ds index 4342d9c..6b81aa0 100644 --- a/examples/clue_solver.ds +++ b/examples/clue_solver.ds @@ -1,12 +1,18 @@ -suspects = ["White", "Green"]; rooms = ["Library", "Kitchen"]; +suspects = ["White", "Green"]; weapons = ["Rope", "Lead Pipe"]; +show_card = function { + (remove rooms card) + (remove suspects card) + (remove weapons card) +} + make_guess = function { if (length suspects) == 1 && (length rooms) == 1 && (length weapons) == 1 - then + { (output 'It was ' + suspects.0 + ' in the ' @@ -14,8 +20,8 @@ make_guess = function { + ' with the ' + weapons.0 + '!' - ); - else + ) + } else { (output 'I accuse ' + (random suspects) + ' in the ' @@ -23,4 +29,5 @@ make_guess = function { + ' with the ' + (random weapons) + '!') + } } diff --git a/src/abstract_tree/async.rs b/src/abstract_tree/async.rs new file mode 100644 index 0000000..142ea37 --- /dev/null +++ b/src/abstract_tree/async.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; + +use crate::{AbstractTree, Item}; + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] +pub struct Async { + item: Item, +} + +impl AbstractTree for Async { + fn from_syntax_node(source: &str, node: tree_sitter::Node) -> crate::Result { + debug_assert_eq!("async", node.kind()); + + let item_node = node.child(2).unwrap(); + let item = Item::from_syntax_node(source, item_node)?; + + Ok(Async { item }) + } + + fn run(&self, source: &str, context: &mut crate::VariableMap) -> crate::Result { + self.item.run_parallel(source, context) + } +} diff --git a/src/abstract_tree/item.rs b/src/abstract_tree/item.rs index 555dc21..273e774 100644 --- a/src/abstract_tree/item.rs +++ b/src/abstract_tree/item.rs @@ -1,5 +1,6 @@ //! Top-level unit of Dust code. +use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tree_sitter::Node; @@ -19,6 +20,16 @@ impl Item { pub fn new(statements: Vec) -> Self { Self { statements } } + + pub fn run_parallel(&self, source: &str, context: &mut VariableMap) -> Result { + self.statements.par_iter().for_each(|statement| { + let mut context = context.clone(); + + statement.run(source, &mut context); + }); + + Ok(Value::Empty) + } } impl AbstractTree for Item { diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index 9353c03..ae69e53 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -7,6 +7,7 @@ //! examples. pub mod assignment; +pub mod r#async; pub mod expression; pub mod function_call; pub mod identifier; diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index b89f137..625bdc1 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize}; use tree_sitter::Node; use crate::{ - r#while::While, AbstractTree, Assignment, Error, Expression, IfElse, Match, Result, Value, - VariableMap, + r#async::Async, r#while::While, AbstractTree, Assignment, Error, Expression, IfElse, Match, + Result, Value, VariableMap, }; /// Abstract representation of a statement. @@ -17,6 +17,7 @@ pub enum Statement { IfElse(Box), Match(Match), While(Box), + Run(Box), } impl AbstractTree for Statement { @@ -41,8 +42,11 @@ impl AbstractTree for Statement { "while" => Ok(Statement::While(Box::new(While::from_syntax_node( source, child, )?))), + "async" => Ok(Statement::Run(Box::new(Async::from_syntax_node( + source, child, + )?))), _ => Err(Error::UnexpectedSyntaxNode { - expected: "assignment, expression, if...else or tool", + expected: "assignment, expression, if...else, while, tool or async", actual: child.kind(), location: child.start_position(), relevant_source: source[child.byte_range()].to_string(), @@ -57,6 +61,7 @@ impl AbstractTree for Statement { Statement::IfElse(if_else) => if_else.run(source, context), Statement::Match(r#match) => r#match.run(source, context), Statement::While(r#while) => r#while.run(source, context), + Statement::Run(run) => run.run(source, context), } } } diff --git a/src/abstract_tree/tool.rs b/src/abstract_tree/tool.rs index 8611110..5c5f983 100644 --- a/src/abstract_tree/tool.rs +++ b/src/abstract_tree/tool.rs @@ -3,13 +3,14 @@ use std::fs::read_to_string; use rand::{random, thread_rng, Rng}; use serde::{Deserialize, Serialize}; -use crate::{Error, Result, Table, Value}; +use crate::{evaluate, Error, Result, Table, Value}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub enum Tool { Assert, AssertEqual, Output, + Run, Read, Help, @@ -96,6 +97,14 @@ impl Tool { }); } } + Tool::Run => { + Error::expect_tool_argument_amount("run", 1, values.len())?; + + let file_path = values[0].as_string()?; + let file_contents = read_to_string(file_path)?; + + evaluate(&file_contents)? + } Tool::Output => { if values.len() != 1 { return Err(Error::ExpectedToolArgumentAmount { diff --git a/tree-sitter-dust b/tree-sitter-dust index b47907e..0f58ea5 160000 --- a/tree-sitter-dust +++ b/tree-sitter-dust @@ -1 +1 @@ -Subproject commit b47907ee26b42990e5170ba9521de5ae66d596ef +Subproject commit 0f58ea5a7a05d49d99bbe24b9da03eea5249cbd7