1
0

Replace tools

This commit is contained in:
Jeff 2023-10-21 13:04:17 -04:00
parent eefb6e5fb6
commit 3b82c6d900
13 changed files with 5180 additions and 6595 deletions

View File

@ -1,24 +1,26 @@
table = create_table <text number bool> [ my_table = table <text number bool> [
["a", 1, true] ["a", 1, true]
["b", 2, true] ["b", 2, true]
["a", 3, true] ["a", 3, true]
] ]
test_table = create_table <text bool> [ test_table = table <text bool> [
["a", true] ["a", true]
["b", true] ["b", true]
["a", true] ["a", true]
] ]
select = select <text bool> from table test_select = select <text bool> from my_table
(assert_equal select, test_table) (assert_equal test_select, test_table)
test_table = create_table <text number bool> [ test_table = table <text number bool> [
["a", 1, true] ["a", 1, true]
["a", 3, true] ["a", 3, true]
] ]
select_where = select * from table where text == "a" test_select_where = select <> from my_table {
text == "a"
}
assert_equal(select_where, test_table); (assert_equal test_select_where, test_table)

View File

@ -1,39 +1,22 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node; use tree_sitter::Node;
use crate::{tool::Tool, AbstractTree, Result, Value, VariableMap}; use crate::{AbstractTree, Error, Result, Value, VariableMap, BUILT_IN_FUNCTIONS};
use super::{expression::Expression, identifier::Identifier}; use super::{expression::Expression, identifier::Identifier};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct FunctionCall { pub struct FunctionCall {
name: FunctionName, name: Identifier,
arguments: Vec<Expression>, arguments: Vec<Expression>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum FunctionName {
Identifier(Identifier),
Tool(Tool),
}
impl AbstractTree for FunctionCall { impl AbstractTree for FunctionCall {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> { fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
debug_assert_eq!("function_call", node.kind()); debug_assert_eq!("function_call", node.kind());
let name_node = node.child(1).unwrap(); let name_node = node.child(1).unwrap();
let name = match name_node.kind() { let name = Identifier::from_syntax_node(source, name_node)?;
"identifier" => {
FunctionName::Identifier(Identifier::from_syntax_node(source, name_node)?)
}
"tool" => {
let tool_node = name_node.child(0).unwrap();
let tool = Tool::new(tool_node.kind())?;
FunctionName::Tool(tool)
}
_ => panic!(""),
};
let mut arguments = Vec::new(); let mut arguments = Vec::new();
@ -51,25 +34,17 @@ impl AbstractTree for FunctionCall {
} }
fn run(&self, source: &str, context: &mut VariableMap) -> Result<Value> { fn run(&self, source: &str, context: &mut VariableMap) -> Result<Value> {
let identifier = match &self.name { let key = self.name.inner();
FunctionName::Identifier(identifier) => identifier,
FunctionName::Tool(tool) => {
let mut values = Vec::with_capacity(self.arguments.len());
for expression in &self.arguments {
let value = expression.run(source, context)?;
values.push(value);
}
return tool.run(&values);
}
};
let key = identifier.inner();
let definition = if let Some(value) = context.get_value(key)? { let definition = if let Some(value) = context.get_value(key)? {
value.as_function().cloned()? value.as_function().cloned()?
} else { } else {
return Err(crate::Error::FunctionIdentifierNotFound(identifier.clone())); for function in BUILT_IN_FUNCTIONS {
if key == function.name() {
return function.run(source, context);
}
}
return Err(Error::FunctionIdentifierNotFound(self.name.clone()));
}; };
let id_expr_pairs = definition.identifiers().iter().zip(self.arguments.iter()); let id_expr_pairs = definition.identifiers().iter().zip(self.arguments.iter());

View File

@ -8,6 +8,7 @@
pub mod assignment; pub mod assignment;
pub mod r#async; pub mod r#async;
pub mod built_in_functions;
pub mod expression; pub mod expression;
pub mod filter; pub mod filter;
pub mod find; pub mod find;
@ -20,16 +21,16 @@ pub mod logic;
pub mod r#match; pub mod r#match;
pub mod math; pub mod math;
pub mod remove; pub mod remove;
pub mod select;
pub mod statement; pub mod statement;
pub mod tool;
pub mod transform; pub mod transform;
pub mod value_node; pub mod value_node;
pub mod r#while; pub mod r#while;
pub use { pub use {
assignment::*, expression::*, filter::*, find::*, function_call::*, identifier::*, if_else::*, assignment::*, built_in_functions::*, expression::*, filter::*, find::*, function_call::*,
item::*, logic::*, math::*, r#async::*, r#for::*, r#match::*, r#while::*, remove::*, identifier::*, if_else::*, item::*, logic::*, math::*, r#async::*, r#for::*, r#match::*,
statement::*, transform::*, r#while::*, remove::*, select::*, statement::*, transform::*,
}; };
use tree_sitter::Node; use tree_sitter::Node;

View File

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{expression, AbstractTree, Expression, Identifier, Item, Value}; use crate::{AbstractTree, Expression, Identifier, Item, 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 {

View File

@ -0,0 +1,71 @@
use serde::{Deserialize, Serialize};
use crate::{identifier, AbstractTree, Expression, Identifier, Item, Table, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Select {
identifiers: Vec<Identifier>,
expression: Expression,
item: Option<Item>,
}
impl AbstractTree for Select {
fn from_syntax_node(source: &str, node: tree_sitter::Node) -> crate::Result<Self> {
let child_count = node.child_count();
let mut identifiers = Vec::new();
for index in 2..child_count - 4 {
let node = node.child(index).unwrap();
if node.kind() == "identifier" {
let identifier = Identifier::from_syntax_node(source, node)?;
identifiers.push(identifier);
}
if node.kind() == ">" {
break;
}
}
let final_node = node.child(child_count - 1).unwrap();
let item = if final_node.kind() == "}" {
let item_node = node.child(child_count - 2).unwrap();
Some(Item::from_syntax_node(source, item_node)?)
} else {
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 {
identifiers,
expression,
item,
})
}
fn run(&self, source: &str, context: &mut crate::VariableMap) -> crate::Result<crate::Value> {
let value = self.expression.run(source, context)?;
let table = value.as_table()?;
let column_names = if self.identifiers.len() > 0 {
self.identifiers
.iter()
.cloned()
.map(|identifierier| identifierier.take_inner())
.collect()
} else {
table.column_names().clone()
};
let new_table = table.select(&column_names);
Ok(Value::Table(new_table))
}
}

View File

@ -3,7 +3,7 @@ use tree_sitter::Node;
use crate::{ use crate::{
AbstractTree, Assignment, Async, Error, Expression, Filter, Find, For, IfElse, Match, Remove, AbstractTree, Assignment, Async, Error, Expression, Filter, Find, For, IfElse, Match, Remove,
Result, Transform, Value, VariableMap, While, Result, Select, Transform, Value, VariableMap, While,
}; };
/// Abstract representation of a statement. /// Abstract representation of a statement.
@ -23,6 +23,7 @@ pub enum Statement {
Filter(Box<Filter>), Filter(Box<Filter>),
Find(Box<Find>), Find(Box<Find>),
Remove(Box<Remove>), Remove(Box<Remove>),
Select(Box<Select>),
} }
impl AbstractTree for Statement { impl AbstractTree for Statement {
@ -65,6 +66,9 @@ impl AbstractTree for Statement {
"remove" => Ok(Statement::Remove(Box::new(Remove::from_syntax_node( "remove" => Ok(Statement::Remove(Box::new(Remove::from_syntax_node(
source, child, source, child,
)?))), )?))),
"select" => Ok(Statement::Select(Box::new(Select::from_syntax_node(
source, child,
)?))),
_ => Err(Error::UnexpectedSyntaxNode { _ => Err(Error::UnexpectedSyntaxNode {
expected: "assignment, expression, if...else, while, for, transform, filter, tool or async", expected: "assignment, expression, if...else, while, for, transform, filter, tool or async",
actual: child.kind(), actual: child.kind(),
@ -87,6 +91,7 @@ impl AbstractTree for Statement {
Statement::Filter(filter) => filter.run(source, context), Statement::Filter(filter) => filter.run(source, context),
Statement::Find(find) => find.run(source, context), Statement::Find(find) => find.run(source, context),
Statement::Remove(remove) => remove.run(source, context), Statement::Remove(remove) => remove.run(source, context),
Statement::Select(select) => select.run(source, context),
} }
} }
} }

View File

@ -1,275 +0,0 @@
use std::{fs::read_to_string, process::Command};
use rand::{random, thread_rng, Rng};
use serde::{Deserialize, Serialize};
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,
Length,
Raw,
Sh,
Bash,
Fish,
Zsh,
FromCsv,
ToCsv,
FromJson,
ToJson,
Random,
RandomFloat,
RandomInteger,
RandomString,
}
impl Tool {
pub fn new(kind: &str) -> Result<Self> {
let tool = match kind {
"assert" => Tool::Assert,
"assert_equal" => Tool::AssertEqual,
"output" => Tool::Output,
"length" => Tool::Length,
"raw" => Tool::Raw,
"sh" => Tool::Sh,
"bash" => Tool::Bash,
"fish" => Tool::Fish,
"zsh" => Tool::Zsh,
"from_csv" => Tool::FromCsv,
"to_csv" => Tool::ToCsv,
"from_json" => Tool::FromJson,
"to_json" => Tool::ToJson,
"random" => Tool::Random,
"random_integer" => Tool::RandomInteger,
"random_float" => Tool::RandomFloat,
"random_string" => Tool::RandomString,
"read" => Tool::Read,
"help" => Tool::Help,
_ => todo!("Tool name not recognized."),
};
Ok(tool)
}
pub fn run(&self, values: &[Value]) -> Result<Value> {
let value = match self {
Tool::Assert => {
Error::expect_tool_argument_amount("assert", 1, values.len())?;
if values[0].as_boolean()? {
Value::Empty
} else {
return Err(Error::AssertFailed);
}
}
Tool::AssertEqual => {
Error::expect_tool_argument_amount("assert_equal", 2, values.len())?;
if values[0] == values[1] {
Value::Empty
} else {
return Err(Error::AssertEqualFailed {
expected: values[0].clone(),
actual: values[1].clone(),
});
}
}
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 => {
Error::expect_tool_argument_amount("output", 1, values.len())?;
println!("{}", values[0]);
Value::Empty
}
Tool::Length => {
Error::expect_tool_argument_amount("length", 1, values.len())?;
let length = if let Ok(list) = values[0].as_list() {
list.len()
} else if let Ok(map) = values[0].as_map() {
map.len()
} else if let Ok(table) = values[0].as_table() {
table.len()
} else if let Ok(string) = values[0].as_string() {
string.chars().count()
} else {
1
};
Value::Integer(length as i64)
}
Tool::Read => {
Error::expect_tool_argument_amount("read", 1, values.len())?;
let file_contents = read_to_string(values[0].as_string()?)?;
Value::String(file_contents)
}
Tool::Random => todo!(),
Tool::RandomFloat => todo!(),
Tool::RandomString => todo!(),
Tool::RandomInteger => {
if values.len() == 0 {
Value::Integer(random())
} else if values.len() == 2 {
let mut rng = thread_rng();
let range = values[0].as_int()?..=values[1].as_int()?;
let random = rng.gen_range(range);
Value::Integer(random)
} else {
return Err(Error::ExpectedToolArgumentAmount {
tool_name: "random_integer",
expected: 2,
actual: values.len(),
});
}
}
Tool::Help => {
Error::expect_tool_argument_amount("help", 0, values.len())?;
let mut help_table =
Table::new(vec!["name".to_string(), "description".to_string()]);
help_table.insert(vec![
Value::String("help".to_string()),
Value::String("List available tools.".to_string()),
])?;
help_table.insert(vec![
Value::String("assert".to_string()),
Value::String("Panic if an expression is false.".to_string()),
])?;
help_table.insert(vec![
Value::String("assert_equal".to_string()),
Value::String("Panic if two values are not equal.".to_string()),
])?;
help_table.insert(vec![
Value::String("output".to_string()),
Value::String("Emit a value to stdout.".to_string()),
])?;
help_table.insert(vec![
Value::String("read".to_string()),
Value::String("Get a file's content.".to_string()),
])?;
help_table.insert(vec![
Value::String("from_json".to_string()),
Value::String("Convert a JSON string to a value.".to_string()),
])?;
help_table.insert(vec![
Value::String("to_json".to_string()),
Value::String("Convert a value to a JSON string.".to_string()),
])?;
Value::Table(help_table)
}
Tool::Raw => {
let program = values[0].as_string()?;
let mut command = Command::new(program);
for value in &values[1..] {
let arg = value.as_string()?;
command.arg(arg);
}
command.spawn()?.wait()?;
Value::Empty
}
Tool::Sh => {
let mut command = Command::new("sh");
for value in values {
let arg = value.as_string()?;
command.arg(arg);
}
command.spawn()?.wait()?;
Value::Empty
}
Tool::Bash => {
let mut command = Command::new("bash");
for value in values {
let arg = value.as_string()?;
command.arg(arg);
}
command.spawn()?.wait()?;
Value::Empty
}
Tool::Fish => {
let mut command = Command::new("fish");
for value in values {
let arg = value.as_string()?;
command.arg(arg);
}
command.spawn()?.wait()?;
Value::Empty
}
Tool::Zsh => {
let mut command = Command::new("zsh");
for value in values {
let arg = value.as_string()?;
command.arg(arg);
}
command.spawn()?.wait()?;
Value::Empty
}
Tool::FromCsv => todo!(),
Tool::ToCsv => todo!(),
Tool::FromJson => {
Error::expect_tool_argument_amount("from_json", 1, values.len())?;
let json_string = values[0].as_string()?;
serde_json::from_str(json_string)?
}
Tool::ToJson => {
Error::expect_tool_argument_amount("to_json", 1, values.len())?;
let value = &values[0];
let json_string = serde_json::to_string(value)?;
Value::String(json_string)
}
};
Ok(value)
}
}

View File

@ -0,0 +1,24 @@
==================
Assert Boolean
==================
assert true
---
==================
Assert Expression
==================
assert 42 % 2 == 0
---
==================
Assert Equal
==================
assert_equal 42 "the answer"
---

View File

@ -67,7 +67,9 @@ foobar = table <text, number> [
Table Access Table Access
================== ==================
select number from foobar where text == 'answer' select <number> from foobar {
text == 'answer'
}
--- ---
@ -75,17 +77,19 @@ select number from foobar where text == 'answer'
(item (item
(statement (statement
(select (select
(identifier)
(identifier) (identifier)
(expression (expression
(logic (identifier))
(item
(statement
(expression (expression
(identifier)) (logic
(logic_operator) (expression
(expression (identifier))
(value (logic_operator)
(string))))))))) (expression
(value
(string)))))))))))
================== ==================
Table Insert Table Insert

View File

@ -163,7 +163,7 @@ module.exports = grammar({
function_call: $ => prec.right(seq( function_call: $ => prec.right(seq(
'(', '(',
choice($.identifier, $.tool), $.identifier,
repeat(seq($.expression, optional(','))), repeat(seq($.expression, optional(','))),
')', ')',
)), )),
@ -226,47 +226,14 @@ module.exports = grammar({
'}', '}',
), ),
tool: $ => choice(
'assert',
'assert_equal',
'output',
'read',
'write',
'raw',
'sh',
'bash',
'fish',
'zsh',
'random',
'random_boolean',
'random_float',
'random_string',
'random_integer',
'length',
'sort',
'transform',
'filter',
'to_csv',
'from_csv',
'to_json',
'from_json',
'help',
),
select: $ => prec.right(seq( select: $ => prec.right(seq(
'select', 'select',
$.identifier, '<',
repeat(seq($.identifier, optional(','))),
'>',
'from', 'from',
$.identifier, $.expression,
optional( optional(seq('{', $.item, '}')),
seq('where', $.expression)
),
)), )),
insert: $ => prec.right(seq( insert: $ => prec.right(seq(

View File

@ -663,17 +663,8 @@
"value": "(" "value": "("
}, },
{ {
"type": "CHOICE", "type": "SYMBOL",
"members": [ "name": "identifier"
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "tool"
}
]
}, },
{ {
"type": "REPEAT", "type": "REPEAT",
@ -896,107 +887,6 @@
} }
] ]
}, },
"tool": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "assert"
},
{
"type": "STRING",
"value": "assert_equal"
},
{
"type": "STRING",
"value": "output"
},
{
"type": "STRING",
"value": "read"
},
{
"type": "STRING",
"value": "write"
},
{
"type": "STRING",
"value": "raw"
},
{
"type": "STRING",
"value": "sh"
},
{
"type": "STRING",
"value": "bash"
},
{
"type": "STRING",
"value": "fish"
},
{
"type": "STRING",
"value": "zsh"
},
{
"type": "STRING",
"value": "random"
},
{
"type": "STRING",
"value": "random_boolean"
},
{
"type": "STRING",
"value": "random_float"
},
{
"type": "STRING",
"value": "random_string"
},
{
"type": "STRING",
"value": "random_integer"
},
{
"type": "STRING",
"value": "length"
},
{
"type": "STRING",
"value": "sort"
},
{
"type": "STRING",
"value": "transform"
},
{
"type": "STRING",
"value": "filter"
},
{
"type": "STRING",
"value": "to_csv"
},
{
"type": "STRING",
"value": "from_csv"
},
{
"type": "STRING",
"value": "to_json"
},
{
"type": "STRING",
"value": "from_json"
},
{
"type": "STRING",
"value": "help"
}
]
},
"select": { "select": {
"type": "PREC_RIGHT", "type": "PREC_RIGHT",
"value": 0, "value": 0,
@ -1008,8 +898,36 @@
"value": "select" "value": "select"
}, },
{ {
"type": "SYMBOL", "type": "STRING",
"name": "identifier" "value": "<"
},
{
"type": "REPEAT",
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": ","
},
{
"type": "BLANK"
}
]
}
]
}
},
{
"type": "STRING",
"value": ">"
}, },
{ {
"type": "STRING", "type": "STRING",
@ -1017,7 +935,7 @@
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "identifier" "name": "expression"
}, },
{ {
"type": "CHOICE", "type": "CHOICE",
@ -1027,11 +945,15 @@
"members": [ "members": [
{ {
"type": "STRING", "type": "STRING",
"value": "where" "value": "{"
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "expression" "name": "item"
},
{
"type": "STRING",
"value": "}"
} }
] ]
}, },
@ -1115,6 +1037,38 @@
"value": "}" "value": "}"
} }
] ]
},
"assert": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "assert"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "expression"
}
}
]
},
"assert_equal": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "assert"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "expression"
}
}
]
} }
}, },
"extras": [ "extras": [

View File

@ -220,10 +220,6 @@
{ {
"type": "identifier", "type": "identifier",
"named": true "named": true
},
{
"type": "tool",
"named": true
} }
] ]
} }
@ -443,6 +439,10 @@
{ {
"type": "identifier", "type": "identifier",
"named": true "named": true
},
{
"type": "item",
"named": true
} }
] ]
} }
@ -529,11 +529,6 @@
] ]
} }
}, },
{
"type": "tool",
"named": true,
"fields": {}
},
{ {
"type": "transform", "type": "transform",
"named": true, "named": true,
@ -703,18 +698,10 @@
"type": "assert", "type": "assert",
"named": false "named": false
}, },
{
"type": "assert_equal",
"named": false
},
{ {
"type": "async", "type": "async",
"named": false "named": false
}, },
{
"type": "bash",
"named": false
},
{ {
"type": "else", "type": "else",
"named": false "named": false
@ -735,10 +722,6 @@
"type": "find", "type": "find",
"named": false "named": false
}, },
{
"type": "fish",
"named": false
},
{ {
"type": "float", "type": "float",
"named": true "named": true
@ -751,22 +734,10 @@
"type": "from", "type": "from",
"named": false "named": false
}, },
{
"type": "from_csv",
"named": false
},
{
"type": "from_json",
"named": false
},
{ {
"type": "function", "type": "function",
"named": false "named": false
}, },
{
"type": "help",
"named": false
},
{ {
"type": "identifier", "type": "identifier",
"named": true "named": true
@ -791,42 +762,6 @@
"type": "into", "type": "into",
"named": false "named": false
}, },
{
"type": "length",
"named": false
},
{
"type": "output",
"named": false
},
{
"type": "random",
"named": false
},
{
"type": "random_boolean",
"named": false
},
{
"type": "random_float",
"named": false
},
{
"type": "random_integer",
"named": false
},
{
"type": "random_string",
"named": false
},
{
"type": "raw",
"named": false
},
{
"type": "read",
"named": false
},
{ {
"type": "remove", "type": "remove",
"named": false "named": false
@ -835,14 +770,6 @@
"type": "select", "type": "select",
"named": false "named": false
}, },
{
"type": "sh",
"named": false
},
{
"type": "sort",
"named": false
},
{ {
"type": "string", "type": "string",
"named": true "named": true
@ -851,14 +778,6 @@
"type": "table", "type": "table",
"named": false "named": false
}, },
{
"type": "to_csv",
"named": false
},
{
"type": "to_json",
"named": false
},
{ {
"type": "transform", "type": "transform",
"named": false "named": false
@ -875,14 +794,6 @@
"type": "while", "type": "while",
"named": false "named": false
}, },
{
"type": "write",
"named": false
},
{
"type": "zsh",
"named": false
},
{ {
"type": "{", "type": "{",
"named": false "named": false

File diff suppressed because it is too large Load Diff