From a9ef75dc12e780a6980df80d3ee59813e7f3ddbc Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 16 Oct 2023 21:13:58 -0400 Subject: [PATCH] Add async statements --- README.md | 25 +++++++++++++----- src/abstract_tree/async.rs | 53 ++++++++++++++++++++++++++++++++------ src/abstract_tree/item.rs | 11 +++++--- tree-sitter-dust | 2 +- 4 files changed, 71 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 486ec15..01aeaef 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,10 @@ A basic dust program: Dust can do two (or more) things at the same time with effortless concurrency: ```dust -async (output 'will this one finish first?') -async (output 'or will this one?') +async { + (output 'will this one finish first?') + (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. @@ -28,6 +30,7 @@ Dust is an interpreted, general purpose language with first class functions. It - [Maps](#maps) - [Tables](#tables) - [Functions](#functions) + - [Concurrency](#concurrency) - [Implementation](#implementation) @@ -176,14 +179,22 @@ print = function { 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) - } +async { + await 1 + 1 } ``` +The **await** keyword can be used in an asnyc block to indicate what value the async block should evaluate to. In this case, we want "data" to be read from a file. + +```dust +data = async { + (output "Reading a file...") + (read "examples/assets/faithful.csv") +} + +(output data) +``` + ## 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/src/abstract_tree/async.rs b/src/abstract_tree/async.rs index 142ea37..d84e7e5 100644 --- a/src/abstract_tree/async.rs +++ b/src/abstract_tree/async.rs @@ -1,23 +1,60 @@ +use rayon::prelude::*; use serde::{Deserialize, Serialize}; +use tree_sitter::Node; -use crate::{AbstractTree, Item}; +use crate::{AbstractTree, Error, Result, Statement, Value, VariableMap}; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct Async { - item: Item, + statements: Vec, } impl AbstractTree for Async { - fn from_syntax_node(source: &str, node: tree_sitter::Node) -> crate::Result { + fn from_syntax_node(source: &str, node: Node) -> Result { debug_assert_eq!("async", node.kind()); - let item_node = node.child(2).unwrap(); - let item = Item::from_syntax_node(source, item_node)?; + let child_count = node.child_count(); + let mut statements = Vec::with_capacity(child_count); - Ok(Async { item }) + for index in 2..child_count - 1 { + let child = node.child(index).unwrap(); + + let statement = match child.kind() { + "statement" => Statement::from_syntax_node(source, child)?, + _ => { + return Err(Error::UnexpectedSyntaxNode { + expected: "comment or statement", + actual: child.kind(), + location: child.start_position(), + relevant_source: source[child.byte_range()].to_string(), + }) + } + }; + + statements.push(statement); + } + + Ok(Async { statements }) } - fn run(&self, source: &str, context: &mut crate::VariableMap) -> crate::Result { - self.item.run_parallel(source, context) + fn run(&self, source: &str, context: &mut VariableMap) -> Result { + let statements = &self.statements; + + statements + .into_par_iter() + .enumerate() + .find_map_last(|(index, statement)| { + let mut context = context.clone(); + let result = statement.run(source, &mut context).unwrap_or_default(); + + if index == statements.len() - 1 { + Some(result) + } else { + None + } + }) + .ok_or(Error::CustomMessage( + "Async block has nothing to run.".to_string(), + )) } } diff --git a/src/abstract_tree/item.rs b/src/abstract_tree/item.rs index 273e774..b311e8e 100644 --- a/src/abstract_tree/item.rs +++ b/src/abstract_tree/item.rs @@ -22,13 +22,16 @@ impl Item { } pub fn run_parallel(&self, source: &str, context: &mut VariableMap) -> Result { - self.statements.par_iter().for_each(|statement| { + let statements = &self.statements; + let run_result = statements.into_par_iter().try_for_each(|statement| { let mut context = context.clone(); - - statement.run(source, &mut context); + statement.run(source, &mut context).map(|_| ()) }); - Ok(Value::Empty) + match run_result { + Ok(()) => Ok(Value::Empty), + Err(error) => Err(error), + } } } diff --git a/tree-sitter-dust b/tree-sitter-dust index 0f58ea5..cf22950 160000 --- a/tree-sitter-dust +++ b/tree-sitter-dust @@ -1 +1 @@ -Subproject commit 0f58ea5a7a05d49d99bbe24b9da03eea5249cbd7 +Subproject commit cf22950b7ee6f40c7840b6af4d7b2dda69f4965b