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