Clean up
This commit is contained in:
parent
9828d9c643
commit
020ebd8833
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
take_turn = |opponent_card, current_room, cards| => {
|
||||||
|
(remove_card opponent_card cards)
|
||||||
|
(make_guess current_room cards)
|
||||||
|
cards
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_card = |state opponent_card| => {
|
remove_card = |opponent_card cards| => {
|
||||||
rooms = filter card in state:rooms {
|
remove card from cards:rooms {
|
||||||
card != opponent_card
|
card == opponent_card
|
||||||
}
|
}
|
||||||
suspects = filter card in state:suspects {
|
remove card from cards:weapons {
|
||||||
card != opponent_card
|
card == opponent_card
|
||||||
}
|
}
|
||||||
weapons = filter card in state:weapons {
|
remove card from cards:suspects {
|
||||||
card != opponent_card
|
card == opponent_card
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
current_room = state:current_room
|
|
||||||
rooms = rooms
|
|
||||||
suspects = suspects
|
|
||||||
weapons = weapons
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)))
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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()?;
|
||||||
|
@ -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());
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
@ -312,6 +312,7 @@ module.exports = grammar({
|
|||||||
// General
|
// General
|
||||||
'assert',
|
'assert',
|
||||||
'assert_equal',
|
'assert_equal',
|
||||||
|
'context',
|
||||||
'download',
|
'download',
|
||||||
'help',
|
'help',
|
||||||
'length',
|
'length',
|
||||||
|
@ -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"
|
||||||
|
@ -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
Loading…
Reference in New Issue
Block a user