Clean up; Complete async

This commit is contained in:
Jeff 2023-11-04 06:02:27 -04:00
parent cedf0a8c65
commit a3db9cb9f2
21 changed files with 140574 additions and 81115 deletions

View File

@ -137,8 +137,6 @@ list = [ 1, 2, 3 ]
for number in list { for number in list {
(output number + 1) (output number + 1)
} }
# The original list is left unchanged.
``` ```
To create a new list, use a **transform** loop, which modifies the values into a new list without changing the original. To create a new list, use a **transform** loop, which modifies the values into a new list without changing the original.
@ -150,6 +148,10 @@ new_list = transform number in list {
number - 1 number - 1
} }
list
-> filter()
-> ()
(output new_list) (output new_list)
# Output: [ 0 1 2 ] # Output: [ 0 1 2 ]

View File

@ -0,0 +1,11 @@
data = async { {
cast = (download "https://api.sampleapis.com/futurama/cast")
characters = (download "https://api.sampleapis.com/futurama/characters")
episodes = (download "https://api.sampleapis.com/futurama/episodes")
} }
cast_len = (length (from_json data:cast))
characters_len = (length (from_json data:characters))
episodes_len = (length (from_json data:episodes))
(output [cast_len, characters_len, episodes_len ])

View File

@ -1,44 +1,63 @@
rooms = ['Library' 'Kitchen'] all_rooms = ['Library' 'Kitchen']
suspects = ['White' 'Green'] all_suspects = ['White' 'Green']
weapons = ['Rope' 'Lead_Pipe'] all_weapons = ['Rope' 'Lead_Pipe']
cards = [rooms suspects weapons]
take_turn = |current_room opponent_card| => { is_ready_to_solve = |state| => {
(remove_card opponent_card) ((length state:suspects) == 1)
(make_guess current_room) && ((length state:rooms) == 1)
} && ((length state:weapons) == 1)
remove_card = |opponent_card| => {
for card_list in cards {
removed = remove card from card_list
card == opponent_card
}
if (type removed) == 'empty'
output 'Card not found.'
} }
make_guess = |current_room| => { take_turn = |opponent_card state| => {
if ((length suspects) == 1) state = (remove_card state opponent_card)
&& ((length rooms) == 1) (make_guess state)
&& ((length weapons) == 1) state
}
remove_card = |state opponent_card| => {
rooms = filter card in state:rooms {
card != opponent_card
}
suspects = filter card in state:suspects {
card != opponent_card
}
weapons = filter card in state:weapons {
card != opponent_card
}
{ {
current_room = state:current_room
rooms = rooms
suspects = suspects
weapons = weapons
}
}
make_guess = |state| => {
if (is_ready_to_solve state) {
(output 'It was ' (output 'It was '
+ suspects:0 + state:suspects:0
+ ' in the ' + ' in the '
+ rooms:0 + state:rooms:0
+ ' with the ' + ' with the '
+ weapons:0 + state:weapons:0
+ '!') + '!')
} else { } else {
(output 'I accuse ' (output 'I accuse '
+ (random suspects) + (random state:suspects)
+ ' in the ' + ' in the '
+ current_room # + state:current_room
+ ' with the ' + ' with the '
+ (random weapons) + (random state:weapons)
+ '!') + '!')
} }
} }
(make_guess 'Library') init_state = {
current_room = 'Library'
rooms = all_rooms
suspects = all_suspects
weapons = all_weapons
}
(take_turn 'Green' (take_turn 'Kitchen' (take_turn 'Rope' init_state)))

View File

@ -1,21 +0,0 @@
numbers = (from_json input)
flip_count = 0
checksum = 0
while numbers.0 != 1 {
(reverse numbers 0 numbers.0)
if flip_count % 2 == 0 {
checksum += flip_count
} else {
checksum -= flip_count
}
checksum += flip_count * 1
flip_count += 1
}
(output numbers)
(output flip_count)
(output checksum)

15
examples/select.ds Normal file
View File

@ -0,0 +1,15 @@
my_table = table |text number bool| [
["a", 1, true]
["b", 2, true]
["a", 3, true]
]
test_table = table |text bool| [
["a", true]
["b", true]
["a", true]
]
test_select = select |text bool| from my_table;
(assert_equal test_select, test_table)

View File

@ -10,11 +10,11 @@ test_table = table |text bool| [
["a", true] ["a", true]
] ]
test_select = select |text bool| from my_table test_select = select |text bool| from my_table;
(assert_equal test_select, test_table) (assert_equal test_select, test_table)
test_table = table |text number bool| [ test_table = table |number bool| [
[1, true] [1, true]
[3, true] [3, true]
] ]

View File

@ -29,7 +29,6 @@ impl AbstractTree for Async {
let mut context = context.clone(); let mut context = context.clone();
let result = statement.run(source, &mut context); let result = statement.run(source, &mut context);
result.clone().unwrap();
if result.is_err() { if result.is_err() {
Some(result) Some(result)
} else if index == statements.len() - 1 { } else if index == statements.len() - 1 {

View File

@ -5,7 +5,7 @@ use crate::{AbstractTree, Result, Statement};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Block { pub struct Block {
statements: Vec<Statement>, pub statements: Vec<Statement>,
} }
impl Block { impl Block {

View File

@ -572,11 +572,8 @@ impl AbstractTree for BuiltInFunction {
let value = expressions[0].run(source, context)?; let value = expressions[0].run(source, context)?;
let list = value.as_list()?.items(); let list = value.as_list()?.items();
if list.len() < 2 { if list.len() == 1 {
return Err(Error::ExpectedMinLengthList { return Ok(list.first().cloned().unwrap());
minimum_len: 2,
actual_len: list.len(),
});
} }
let range = 0..list.len(); let range = 0..list.len();

View File

@ -19,13 +19,13 @@ impl AbstractTree for Filter {
None => None, None => None,
}; };
let item_id_node = node.child(1).unwrap(); let item_id_node = node.child_by_field_name("item_id").unwrap();
let item_id = Identifier::from_syntax_node(source, item_id_node)?; let item_id = Identifier::from_syntax_node(source, item_id_node)?;
let collection_node = node.child(3).unwrap(); let collection_node = node.child_by_field_name("collection").unwrap();
let collection = Expression::from_syntax_node(source, collection_node)?; let collection = Expression::from_syntax_node(source, collection_node)?;
let predicate_node = node.child(5).unwrap(); let predicate_node = node.child_by_field_name("predicate").unwrap();
let predicate = Block::from_syntax_node(source, predicate_node)?; let predicate = Block::from_syntax_node(source, predicate_node)?;
Ok(Filter { Ok(Filter {
@ -45,6 +45,7 @@ impl AbstractTree for Filter {
Some(expression) => Some(expression.run(source, context)?.as_integer()? as usize), Some(expression) => Some(expression.run(source, context)?.as_integer()? as usize),
None => None, None => None,
}; };
let loop_context = Map::clone_from(context);
values.par_iter().try_for_each(|value| { values.par_iter().try_for_each(|value| {
if let Some(max) = count { if let Some(max) = count {
@ -53,11 +54,16 @@ impl AbstractTree for Filter {
} }
} }
let mut context = Map::new(); let mut iter_context = loop_context.clone();
context.variables_mut().insert(key.clone(), value.clone()); iter_context
.variables_mut()
.insert(key.clone(), value.clone());
let should_include = self.predicate.run(source, &mut context)?.as_boolean()?; let should_include = self
.predicate
.run(source, &mut iter_context)?
.as_boolean()?;
if should_include { if should_include {
new_values.items_mut().push(value.clone()); new_values.items_mut().push(value.clone());

View File

@ -7,9 +7,9 @@ use crate::{AbstractTree, Block, Error, Expression, Identifier, Map, Result, Val
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct For { pub struct For {
is_async: bool, is_async: bool,
identifier: Identifier, item_id: Identifier,
expression: Expression, collection: Expression,
item: Block, block: Block,
} }
impl AbstractTree for For { impl AbstractTree for For {
@ -17,9 +17,10 @@ impl AbstractTree for For {
let for_node = node.child(0).unwrap(); let for_node = node.child(0).unwrap();
let is_async = match for_node.kind() { let is_async = match for_node.kind() {
"for" => false, "for" => false,
"async for" => true,
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(Error::UnexpectedSyntaxNode {
expected: "for", expected: "for or async for",
actual: for_node.kind(), actual: for_node.kind(),
location: for_node.start_position(), location: for_node.start_position(),
relevant_source: source[for_node.byte_range()].to_string(), relevant_source: source[for_node.byte_range()].to_string(),
@ -38,32 +39,35 @@ impl AbstractTree for For {
Ok(For { Ok(For {
is_async, is_async,
identifier, item_id: identifier,
expression, collection: expression,
item, block: item,
}) })
} }
fn run(&self, source: &str, context: &mut Map) -> Result<Value> { fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
let expression_run = self.expression.run(source, context)?; let expression_run = self.collection.run(source, context)?;
let values = expression_run.as_list()?.items(); let values = expression_run.as_list()?.items();
let key = self.identifier.inner(); let key = self.item_id.inner();
let mut loop_context = Map::clone_from(context);
if self.is_async { if self.is_async {
values.par_iter().try_for_each(|value| { values.par_iter().try_for_each(|value| {
let mut iter_context = Map::new(); let mut iter_context = loop_context.clone();
iter_context iter_context
.variables_mut() .variables_mut()
.insert(key.clone(), value.clone()); .insert(key.clone(), value.clone());
self.item.run(source, &mut iter_context).map(|_value| ()) self.block.run(source, &mut iter_context).map(|_value| ())
})?; })?;
} else { } else {
for value in values.iter() { for value in values.iter() {
context.variables_mut().insert(key.clone(), value.clone()); loop_context
.variables_mut()
.insert(key.clone(), value.clone());
self.item.run(source, context)?; self.block.run(source, &mut loop_context)?;
} }
} }

View File

@ -22,10 +22,10 @@ impl AbstractTree for FunctionCall {
let mut arguments = Vec::new(); let mut arguments = Vec::new();
for index in 1..node.child_count() { for index in 1..node.child_count() {
let child = node.child(index).unwrap(); let node = node.child(index).unwrap();
if child.is_named() { if node.is_named() {
let expression = Expression::from_syntax_node(source, child)?; let expression = Expression::from_syntax_node(source, node)?;
arguments.push(expression); arguments.push(expression);
} }

View File

@ -1,63 +1,53 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node; use tree_sitter::Node;
use crate::{AbstractTree, Block, Expression, Identifier, Map, Result, Value}; use crate::{AbstractTree, Block, Error, Expression, Identifier, Map, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Remove { pub struct Remove {
identifier: Identifier, item_id: Identifier,
expression: Expression, collection: Expression,
item: Block, predicate: Block,
} }
impl AbstractTree for Remove { impl AbstractTree for Remove {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> { fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
let identifier_node = node.child(1).unwrap(); let identifier_node = node.child(1).unwrap();
let identifier = Identifier::from_syntax_node(source, identifier_node)?; let item_id = Identifier::from_syntax_node(source, identifier_node)?;
let expression_node = node.child(3).unwrap(); let expression_node = node.child(3).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?; let collection = Expression::from_syntax_node(source, expression_node)?;
let item_node = node.child(4).unwrap(); let block_node = node.child(4).unwrap();
let item = Block::from_syntax_node(source, item_node)?; let predicate = Block::from_syntax_node(source, block_node)?;
Ok(Remove { Ok(Remove {
identifier, item_id,
expression, collection,
item, predicate,
}) })
} }
fn run(&self, source: &str, context: &mut Map) -> Result<Value> { fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
let expression_run = self.expression.run(source, context)?; let value = self.collection.run(source, context)?;
let values = expression_run.into_inner_list()?; let mut values = value.as_list()?.items_mut();
let key = self.identifier.inner(); let key = self.item_id.inner();
let mut sub_context = context.clone();
let mut should_remove_index = None; let mut should_remove_index = None;
for (index, value) in values.items().iter().enumerate() { values.iter().enumerate().try_for_each(|(index, value)| {
sub_context context.variables_mut().insert(key.clone(), value.clone());
.variables_mut()
.insert(key.clone(), value.clone());
let should_remove = self.item.run(source, &mut sub_context)?.as_boolean()?; let should_remove = self.predicate.run(source, context)?.as_boolean()?;
if should_remove { if should_remove {
should_remove_index = Some(index); should_remove_index = Some(index);
match &self.expression {
Expression::Identifier(identifier) => {
sub_context
.variables_mut()
.insert(identifier.inner().clone(), Value::List(values.clone()));
}
_ => {}
}
} }
}
Ok::<(), Error>(())
})?;
if let Some(index) = should_remove_index { if let Some(index) = should_remove_index {
Ok(values.items_mut().remove(index)) Ok(values.remove(index))
} else { } else {
Ok(Value::Empty) Ok(Value::Empty)
} }

View File

@ -7,7 +7,7 @@ use crate::{AbstractTree, Block, Expression, Identifier, Map, Result, Table, Val
pub struct Select { pub struct Select {
identifiers: Vec<Identifier>, identifiers: Vec<Identifier>,
expression: Expression, expression: Expression,
item: Option<Block>, predicate: Option<Block>,
} }
impl AbstractTree for Select { impl AbstractTree for Select {
@ -15,41 +15,32 @@ impl AbstractTree for Select {
let child_count = node.child_count(); let child_count = node.child_count();
let mut identifiers = Vec::new(); let mut identifiers = Vec::new();
for index in 2..child_count - 4 { let identifier_list = node.child(1).unwrap();
let node = node.child(index).unwrap();
if node.kind() == "identifier" { for index in 1..identifier_list.child_count() - 1 {
let node = identifier_list.child(index).unwrap();
if node.is_named() {
let identifier = Identifier::from_syntax_node(source, node)?; let identifier = Identifier::from_syntax_node(source, node)?;
identifiers.push(identifier); identifiers.push(identifier);
} }
if node.kind() == ">" {
break;
}
} }
let expression_node = node.child(3).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
let final_node = node.child(child_count - 1).unwrap(); let final_node = node.child(child_count - 1).unwrap();
let item = if final_node.kind() == "}" { let predicate = if final_node.kind() == "block" {
let item_node = node.child(child_count - 2).unwrap(); Some(Block::from_syntax_node(source, final_node)?)
Some(Block::from_syntax_node(source, item_node)?)
} else { } else {
None None
}; };
let expression_node = if item.is_some() {
node.child(child_count - 4).unwrap()
} else {
node.child(child_count - 1).unwrap()
};
let expression = Expression::from_syntax_node(source, expression_node)?;
Ok(Select { Ok(Select {
identifiers, identifiers,
expression, expression,
item, predicate,
}) })
} }
@ -60,7 +51,7 @@ impl AbstractTree for Select {
self.identifiers self.identifiers
.iter() .iter()
.cloned() .cloned()
.map(|identifierier| identifierier.take_inner()) .map(|identifier| identifier.take_inner())
.collect() .collect()
} else { } else {
old_table.headers().clone() old_table.headers().clone()
@ -99,7 +90,7 @@ impl AbstractTree for Select {
} }
} }
if let Some(where_clause) = &self.item { if let Some(where_clause) = &self.predicate {
let should_include = where_clause.run(source, &mut row_context)?.as_boolean()?; let should_include = where_clause.run(source, &mut row_context)?.as_boolean()?;
if should_include { if should_include {

View File

@ -33,9 +33,13 @@ pub fn evaluate(source: &str) -> Result<Value> {
/// # use dust_lang::*; /// # use dust_lang::*;
/// let mut context = Map::new(); /// let mut context = Map::new();
/// ///
/// context.set_value("one".into(), 1.into()); /// {
/// context.set_value("two".into(), 2.into()); /// let mut variables = context.variables_mut();
/// context.set_value("three".into(), 3.into()); ///
/// variables.insert("one".into(), 1.into());
/// variables.insert("two".into(), 2.into());
/// variables.insert("three".into(), 3.into());
/// }
/// ///
/// let dust_code = "four = 4 one + two + three + four"; /// let dust_code = "four = 4 one + two + three + four";
/// ///

View File

@ -9,6 +9,14 @@ fn clue_solver() {
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test]
#[ignore]
fn download_async() {
let file_contents = read_to_string("examples/download_async.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test] #[test]
#[ignore] #[ignore]
fn fetch() { fn fetch() {
@ -59,6 +67,13 @@ fn remove_loop() {
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test]
fn select() {
let file_contents = read_to_string("examples/select.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test] #[test]
fn table() { fn table() {
let file_contents = read_to_string("examples/table.ds").unwrap(); let file_contents = read_to_string("examples/table.ds").unwrap();

View File

@ -1,52 +1,47 @@
================== ================================================================================
Full Line Comments Full Line Comments
================== ================================================================================
not_a_comment not_a_comment
# comment # comment
--- --------------------------------------------------------------------------------
(root (root
(block (block
(statement (statement
(expression (expression
(identifier)))) (identifier)))))
(comment))
================== ================================================================================
Partial Line Comments Partial Line Comments
================== ================================================================================
not_a_comment # comment not_a_comment # comment
--- --------------------------------------------------------------------------------
(root (root
(block (block
(statement (statement
(expression (expression
(identifier)))) (identifier)))))
(comment))
================== ================================================================================
Multiline Comments Multiline Comments
================== ================================================================================
# comment # # comment #
not_a_comment # not_a_comment #
# comment # "not a comment" # comment # "not a comment"
--- --------------------------------------------------------------------------------
(root (root
(comment)
(block (block
(statement (statement
(expression (expression
(identifier))) (identifier)))
(comment)
(comment)
(statement (statement
(expression (expression
(value (value

View File

@ -3,7 +3,7 @@ module.exports = grammar({
word: $ => $.identifier, word: $ => $.identifier,
extras: $ => [ /\s/, $.comment ], extras: $ => [ /\s/, $._comment ],
conflicts: $ => [ conflicts: $ => [
[$.block], [$.block],
@ -13,7 +13,7 @@ module.exports = grammar({
rules: { rules: {
root: $ => repeat1($.block), root: $ => repeat1($.block),
comment: $ => /[#][^#\n]*[#|\n]/, _comment: $ => /[#][^#\n]*[#|\n]/,
block: $ => choice( block: $ => choice(
repeat1($.statement), repeat1($.statement),
@ -200,7 +200,10 @@ module.exports = grammar({
), ),
for: $ => seq( for: $ => seq(
'for', choice(
'for',
'async for',
),
$.identifier, $.identifier,
'in', 'in',
$.expression, $.expression,
@ -218,7 +221,7 @@ module.exports = grammar({
filter: $ => seq( filter: $ => seq(
'filter', 'filter',
field('count', optional($.expression)), field('count', optional($.expression)),
field('statement_id', $.identifier), field('item_id', $.identifier),
'in', 'in',
field('collection', $.expression), field('collection', $.expression),
field('predicate', $.block), field('predicate', $.block),

View File

@ -9,7 +9,7 @@
"name": "block" "name": "block"
} }
}, },
"comment": { "_comment": {
"type": "PATTERN", "type": "PATTERN",
"value": "[#][^#\\n]*[#|\\n]" "value": "[#][^#\\n]*[#|\\n]"
}, },
@ -708,8 +708,17 @@
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
"type": "SYMBOL", "type": "CHOICE",
"name": "identifier" "members": [
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "index"
}
]
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
@ -875,8 +884,17 @@
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
"type": "STRING", "type": "CHOICE",
"value": "for" "members": [
{
"type": "STRING",
"value": "for"
},
{
"type": "STRING",
"value": "async for"
}
]
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
@ -946,7 +964,7 @@
}, },
{ {
"type": "FIELD", "type": "FIELD",
"name": "statement_id", "name": "item_id",
"content": { "content": {
"type": "SYMBOL", "type": "SYMBOL",
"name": "identifier" "name": "identifier"
@ -1428,7 +1446,7 @@
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "comment" "name": "_comment"
} }
], ],
"conflicts": [ "conflicts": [

View File

@ -15,6 +15,10 @@
"type": "identifier", "type": "identifier",
"named": true "named": true
}, },
{
"type": "index",
"named": true
},
{ {
"type": "statement", "type": "statement",
"named": true "named": true
@ -170,6 +174,16 @@
} }
] ]
}, },
"item_id": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
},
"predicate": { "predicate": {
"multiple": false, "multiple": false,
"required": true, "required": true,
@ -179,16 +193,6 @@
"named": true "named": true
} }
] ]
},
"statement_id": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
} }
} }
}, },
@ -846,6 +850,10 @@
"type": "async", "type": "async",
"named": false "named": false
}, },
{
"type": "async for",
"named": false
},
{ {
"type": "bash", "type": "bash",
"named": false "named": false
@ -854,10 +862,6 @@
"type": "columns", "type": "columns",
"named": false "named": false
}, },
{
"type": "comment",
"named": true
},
{ {
"type": "download", "type": "download",
"named": false "named": false

File diff suppressed because it is too large Load Diff