This commit is contained in:
Jeff 2023-11-10 16:24:19 -05:00
parent 9828d9c643
commit 020ebd8833
15 changed files with 4099 additions and 3139 deletions

View File

@ -1,63 +1,57 @@
all_rooms = ['Library' 'Kitchen'] all_cards = {
all_suspects = ['White' 'Green'] rooms = ['Library' 'Kitchen' 'Conservatory']
all_weapons = ['Rope' 'Lead_Pipe'] suspects = ['White' 'Green' 'Scarlett']
weapons = ['Rope' 'Lead_Pipe' 'Knife']
is_ready_to_solve = |state| => {
((length state:suspects) == 1)
&& ((length state:rooms) == 1)
&& ((length state:weapons) == 1)
} }
take_turn = |opponent_card state| => { is_ready_to_solve = |cards| => {
state = (remove_card state opponent_card) ((length cards:suspects) == 1)
(make_guess state) && ((length cards:rooms) == 1)
state && ((length cards:weapons) == 1)
} }
remove_card = |state opponent_card| => { take_turn = |opponent_card, current_room, cards| => {
rooms = filter card in state:rooms { (remove_card opponent_card cards)
card != opponent_card (make_guess current_room cards)
} cards
suspects = filter card in state:suspects { }
card != opponent_card
}
weapons = filter card in state:weapons {
card != opponent_card
}
{ remove_card = |opponent_card cards| => {
current_room = state:current_room remove card from cards:rooms {
rooms = rooms card == opponent_card
suspects = suspects }
weapons = weapons remove card from cards:weapons {
card == opponent_card
}
remove card from cards:suspects {
card == opponent_card
} }
} }
make_guess = |state| => { make_guess = |current_room cards| => {
if (is_ready_to_solve state) { if (is_ready_to_solve cards) {
(output 'It was ' (output 'It was '
+ state:suspects:0 + cards:suspects:0
+ ' in the ' + ' in the '
+ state:rooms:0 + cards:rooms:0
+ ' with the ' + ' with the '
+ state:weapons:0 + cards:weapons:0
+ '!') + '!')
} else { } else {
(output 'I accuse ' (output 'I accuse '
+ (random state:suspects) + (random cards:suspects)
+ ' in the ' + ' in the '
# + state:current_room + current_room
+ ' with the ' + ' with the '
+ (random state:weapons) + (random cards:weapons)
+ '!') + '!')
} }
} }
init_state = { (take_turn 'Rope' 'Kitchen'
current_room = 'Library' (take_turn 'Library' 'Kitchen'
rooms = all_rooms (take_turn 'Conservatory' 'Kitchen'
suspects = all_suspects (take_turn 'White' 'Kitchen'
weapons = all_weapons (take_turn 'Green' 'Kitchen'
} (take_turn 'Knife' 'Kitchen' all_cards))))))
(take_turn 'Green' (take_turn 'Kitchen' (take_turn 'Rope' init_state)))

View File

@ -1,7 +1,7 @@
numbers = [1, 2, 3] numbers = [1, 2, 3]
x = numbers.{0} x = numbers:0
y = numbers.{1} y = numbers:1
z = numbers.{2} z = numbers:2
(assert_equal x + y, z) (assert_equal x + y, z)

View File

@ -5,7 +5,7 @@ dictionary = {
(output (output
'Dust is ' 'Dust is '
+ dictionary.dust + dictionary:dust
+ '! The answer is ' + '! The answer is '
+ dictionary.answer + dictionary:answer
) )

View File

@ -19,6 +19,7 @@ pub enum BuiltInFunction {
Assert(Vec<Expression>), Assert(Vec<Expression>),
AssertEqual(Vec<Expression>), AssertEqual(Vec<Expression>),
Download(Expression), Download(Expression),
Context,
Help(Option<Expression>), Help(Option<Expression>),
Length(Expression), Length(Expression),
Output(Vec<Expression>), Output(Vec<Expression>),
@ -93,6 +94,7 @@ impl AbstractTree for BuiltInFunction {
BuiltInFunction::AssertEqual(expressions) BuiltInFunction::AssertEqual(expressions)
} }
"context" => BuiltInFunction::Context,
"download" => { "download" => {
let expression_node = node.child(1).unwrap(); let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?; let expression = Expression::from_syntax_node(source, expression_node)?;
@ -306,6 +308,7 @@ impl AbstractTree for BuiltInFunction {
Ok(Value::Empty) Ok(Value::Empty)
} }
BuiltInFunction::Context => Ok(Value::Map(context.clone())),
BuiltInFunction::Download(expression) => { BuiltInFunction::Download(expression) => {
let value = expression.run(source, context)?; let value = expression.run(source, context)?;
let url = value.as_string()?; let url = value.as_string()?;

View File

@ -32,12 +32,14 @@ impl AbstractTree for Find {
let value = self.expression.run(source, context)?; let value = self.expression.run(source, context)?;
let values = value.as_list()?.items(); let values = value.as_list()?.items();
let key = self.identifier.inner(); let key = self.identifier.inner();
let mut loop_context = Map::clone_from(context)?;
let mut variables = context.variables_mut()?; let mut variables = context.variables_mut()?;
for value in values.iter() { for value in values.iter() {
variables.insert(key.clone(), value.clone()); variables.insert(key.clone(), value.clone());
let should_return = self.item.run(source, &mut context.clone())?.as_boolean()?; let should_return = self.item.run(source, &mut loop_context)?.as_boolean()?;
if should_return { if should_return {
return Ok(value.clone()); return Ok(value.clone());

View File

@ -49,11 +49,10 @@ impl AbstractTree for For {
let expression_run = self.collection.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.item_id.inner(); let key = self.item_id.inner();
let 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 = loop_context.clone(); let mut iter_context = Map::clone_from(context)?;
iter_context iter_context
.variables_mut()? .variables_mut()?
@ -62,10 +61,12 @@ impl AbstractTree for For {
self.block.run(source, &mut iter_context).map(|_value| ()) self.block.run(source, &mut iter_context).map(|_value| ())
})?; })?;
} else { } else {
let mut variables = loop_context.variables_mut()?; let loop_context = Map::clone_from(context)?;
for value in values.iter() { for value in values.iter() {
variables.insert(key.clone(), value.clone()); loop_context
.variables_mut()?
.insert(key.clone(), value.clone());
self.block.run(source, &mut loop_context.clone())?; self.block.run(source, &mut loop_context.clone())?;
} }
@ -74,3 +75,4 @@ impl AbstractTree for For {
Ok(Value::Empty) Ok(Value::Empty)
} }
} }

View File

@ -50,8 +50,17 @@ impl AbstractTree for Index {
Ok(item) Ok(item)
} }
Value::Map(mut map) => { Value::Map(map) => {
let value = self.index.run(source, &mut map)?; let value = if let Expression::Identifier(identifier) = &self.index {
let key = identifier.inner();
map.variables()?.get(key).cloned().unwrap_or(Value::Empty)
} else {
let value = self.index.run(source, context)?;
let key = value.as_string()?;
map.variables()?.get(key).cloned().unwrap_or(Value::Empty)
};
Ok(value) Ok(value)
} }
@ -65,3 +74,30 @@ impl AbstractTree for Index {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::evaluate;
#[test]
fn evaluate_list_index() {
let test = evaluate("x = [1 [2] 3] x:1:0").unwrap();
assert_eq!(Value::Integer(2), test);
}
#[test]
fn evaluate_map_index() {
let test = evaluate("x = {y = {z = 2}} x:y:z").unwrap();
assert_eq!(Value::Integer(2), test);
}
#[test]
fn evaluate_complex_index() {
let test = evaluate("{x = [1 2 3]; y = || => {0}; x:((y));}").unwrap();
assert_eq!(Value::Integer(1), test);
}
}

View File

@ -1,3 +1,6 @@
use std::sync::RwLock;
use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node; use tree_sitter::Node;
@ -30,28 +33,41 @@ impl AbstractTree for Remove {
fn run(&self, source: &str, context: &mut Map) -> Result<Value> { fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
let value = self.collection.run(source, context)?; let value = self.collection.run(source, context)?;
let mut values = value.as_list()?.items_mut(); let values = value.as_list()?;
let key = self.item_id.inner(); let key = self.item_id.inner();
let mut should_remove_index = None; let should_remove_index = RwLock::new(None);
let mut variables = context.variables_mut()?;
values.iter().enumerate().try_for_each(|(index, value)| { values
variables.insert(key.clone(), value.clone()); .items()
.par_iter()
.enumerate()
.try_for_each(|(index, value)| {
if should_remove_index.read()?.is_some() {
return Ok(());
}
let should_remove = self let iter_context = Map::clone_from(context)?;
.predicate
.run(source, &mut context.clone())?
.as_boolean()?;
if should_remove { iter_context
should_remove_index = Some(index); .variables_mut()?
} .insert(key.clone(), value.clone());
Ok::<(), Error>(()) let should_remove = self
})?; .predicate
.run(source, &mut iter_context.clone())?
.as_boolean()?;
if let Some(index) = should_remove_index { if should_remove {
Ok(values.remove(index)) let _ = should_remove_index.write()?.insert(index);
}
Ok::<(), Error>(())
})?;
let index = should_remove_index.read()?;
if let Some(index) = *index {
Ok(values.items_mut().remove(index))
} else { } else {
Ok(Value::Empty) Ok(Value::Empty)
} }

View File

@ -61,12 +61,13 @@ impl AbstractTree for Select {
for row in old_table.rows() { for row in old_table.rows() {
let mut new_row = Vec::new(); let mut new_row = Vec::new();
let row_context = Map::new(); let row_context = Map::new();
let mut row_variables = row_context.variables_mut()?;
for (i, value) in row.iter().enumerate() { for (i, value) in row.iter().enumerate() {
let column_name = old_table.headers().get(i).unwrap(); let column_name = old_table.headers().get(i).unwrap();
row_variables.insert(column_name.clone(), value.clone()); row_context
.variables_mut()?
.insert(column_name.clone(), value.clone());
let new_table_column_index = let new_table_column_index =
new_table new_table

View File

@ -36,20 +36,16 @@ impl AbstractTree for Transform {
let new_values = values let new_values = values
.par_iter() .par_iter()
.map(|value| { .map(|value| {
let iter_context = Map::new(); let iter_context = Map::clone_from(context).unwrap();
let mut iter_variables = match iter_context.variables_mut() {
Ok(variables) => variables,
Err(_) => return Value::Empty,
};
iter_variables.insert(key.clone(), value.clone()); iter_context
.variables_mut()
.unwrap()
.insert(key.clone(), value.clone());
let item_run = self.item.run(source, &mut iter_context.clone()); self.item
.run(source, &mut iter_context.clone())
match item_run { .unwrap_or_default()
Ok(value) => value,
Err(_) => Value::Empty,
}
}) })
.filter(|value| !value.is_empty()) .filter(|value| !value.is_empty())
.collect(); .collect();

View File

@ -3,16 +3,23 @@ use std::fs::read_to_string;
use dust_lang::*; use dust_lang::*;
#[test] #[test]
fn clue_solver() { fn r#async() {
let file_contents = read_to_string("examples/clue_solver.ds").unwrap(); let file_contents = read_to_string("examples/async.ds").unwrap();
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test] #[test]
#[ignore] #[ignore]
fn download_async() { fn async_download() {
let file_contents = read_to_string("examples/download_async.ds").unwrap(); let file_contents = read_to_string("examples/async_download.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test]
fn clue_solver() {
let file_contents = read_to_string("examples/clue_solver.ds").unwrap();
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
@ -32,6 +39,13 @@ fn fibonacci() {
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test]
fn filter_loop() {
let file_contents = read_to_string("examples/filter_loop.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test] #[test]
fn find_loop() { fn find_loop() {
let file_contents = read_to_string("examples/find_loop.ds").unwrap(); let file_contents = read_to_string("examples/find_loop.ds").unwrap();
@ -60,6 +74,34 @@ fn hello_world() {
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test]
fn jq_data() {
let file_contents = read_to_string("examples/jq_data.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test]
fn list() {
let file_contents = read_to_string("examples/list.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test]
fn map() {
let file_contents = read_to_string("examples/map.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test]
fn random() {
let file_contents = read_to_string("examples/random.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test] #[test]
fn remove_loop() { fn remove_loop() {
let file_contents = read_to_string("examples/remove_loop.ds").unwrap(); let file_contents = read_to_string("examples/remove_loop.ds").unwrap();
@ -67,6 +109,13 @@ fn remove_loop() {
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test]
fn sea_creatures() {
let file_contents = read_to_string("examples/sea_creatures.ds").unwrap();
evaluate(&file_contents).unwrap();
}
#[test] #[test]
fn select() { fn select() {
let file_contents = read_to_string("examples/select.ds").unwrap(); let file_contents = read_to_string("examples/select.ds").unwrap();
@ -101,3 +150,10 @@ fn while_loop() {
evaluate(&file_contents).unwrap(); evaluate(&file_contents).unwrap();
} }
#[test]
fn r#yield() {
let file_contents = read_to_string("examples/yield.ds").unwrap();
evaluate(&file_contents).unwrap();
}

View File

@ -312,6 +312,7 @@ module.exports = grammar({
// General // General
'assert', 'assert',
'assert_equal', 'assert_equal',
'context',
'download', 'download',
'help', 'help',
'length', 'length',

View File

@ -1315,6 +1315,10 @@
"type": "STRING", "type": "STRING",
"value": "assert_equal" "value": "assert_equal"
}, },
{
"type": "STRING",
"value": "context"
},
{ {
"type": "STRING", "type": "STRING",
"value": "download" "value": "download"

View File

@ -858,6 +858,10 @@
"type": "columns", "type": "columns",
"named": false "named": false
}, },
{
"type": "context",
"named": false
},
{ {
"type": "download", "type": "download",
"named": false "named": false

File diff suppressed because it is too large Load Diff