1
0

Begin changes for new type definitions

This commit is contained in:
Jeff 2023-11-29 19:23:42 -05:00
parent 5ffb797b5f
commit bc2615a1ed
46 changed files with 11666 additions and 11898 deletions

407
grammar.js Normal file
View File

@ -0,0 +1,407 @@
module.exports = grammar({
name: "dust",
word: $ => $.identifier,
extras: $ => [/\s/, $._comment],
rules: {
root: $ =>
prec(1, repeat1($.statement)),
_comment: $ => /[#][^#\n]*[#|\n]/,
block: $ =>
seq(
optional("async"),
"{",
repeat1($.statement),
"}",
),
statement: $ =>
prec.left(
seq(
choice(
$.assignment,
$.block,
$.expression,
$.for,
$.if_else,
$.index_assignment,
$.match,
$.return,
$.use,
$.while,
),
optional(";"),
),
),
expression: $ =>
prec.right(
choice(
$._expression_kind,
seq(
"(",
$._expression_kind,
")",
),
),
),
_expression_kind: $ =>
prec.right(
choice(
$.function_call,
$.identifier,
$.index,
$.logic,
$.math,
$.value,
$.yield,
),
),
_expression_list: $ =>
repeat1(
prec.right(
seq(
$.expression,
optional(","),
),
),
),
identifier: $ =>
/[_a-zA-Z]+[_a-zA-Z0-9]?/,
value: $ =>
choice(
$.integer,
$.float,
$.string,
$.boolean,
$.list,
$.function,
$.table,
$.map,
),
integer: $ =>
token(
prec.left(
seq(
optional("-"),
repeat1(
choice(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
),
),
),
),
),
float: $ =>
token(
prec.left(
seq(
optional("-"),
repeat1(
choice(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
),
),
".",
repeat1(
choice(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
),
),
),
),
),
string: $ =>
/("[^"]*?")|('[^']*?')|(`[^`]*?`)/,
boolean: $ =>
choice("true", "false"),
list: $ =>
seq(
"[",
repeat(
prec.left(
seq(
$.expression,
optional(","),
),
),
),
"]",
),
map: $ =>
seq(
"{",
repeat(
seq(
$.identifier,
"=",
$.statement,
optional(","),
),
),
"}",
),
index: $ =>
prec.left(
1,
seq(
$.expression,
":",
$.expression,
optional(
seq("..", $.expression),
),
),
),
math: $ =>
prec.left(
seq(
$.expression,
$.math_operator,
$.expression,
),
),
math_operator: $ =>
choice("+", "-", "*", "/", "%"),
logic: $ =>
prec.right(
seq(
$.expression,
$.logic_operator,
$.expression,
),
),
logic_operator: $ =>
choice(
"==",
"!=",
"&&",
"||",
">",
"<",
">=",
"<=",
),
assignment: $ =>
seq(
field(
"identifier",
$.identifier,
),
optional(
field(
"type",
$.type_definition,
),
),
field(
"assignment_operator",
$.assignment_operator,
),
field("statement", $.statement),
),
index_assignment: $ =>
seq(
$.index,
$.assignment_operator,
$.statement,
),
assignment_operator: $ =>
prec.right(
choice("=", "+=", "-="),
),
if_else: $ =>
prec.right(
seq(
$.if,
repeat($.else_if),
optional($.else),
),
),
if: $ =>
seq("if", $.expression, $.block),
else_if: $ =>
seq(
"else if",
$.expression,
$.block,
),
else: $ => seq("else", $.block),
match: $ =>
prec.right(
seq(
"match",
$.expression,
repeat1(
seq(
$.expression,
"=>",
$.block,
),
),
),
),
while: $ =>
seq(
"while",
$.expression,
$.block,
),
for: $ =>
seq(
choice("for", "async for"),
$.identifier,
"in",
$.expression,
$.block,
),
identifier_list: $ =>
prec.right(
choice(
seq(
"|",
repeat(
seq(
$.identifier,
optional(","),
),
),
"|",
),
),
),
table: $ =>
prec.right(
seq(
"table",
$.identifier_list,
$.expression,
),
),
return: $ =>
seq("return", $.expression),
use: $ => seq("use", $.string),
type_definition: $ =>
seq("<", $.type, ">"),
type: $ =>
prec.right(
choice(
"any",
"bool",
seq(
"fn",
repeat(
seq(
$.type,
optional(","),
),
),
optional(seq("->", $.type)),
),
"int",
seq("list", $.type),
"map",
"num",
"str",
),
),
function: $ =>
seq(
"|",
repeat(
seq(
$.identifier,
optional(","),
),
),
"|",
$.block,
),
function_call: $ =>
prec.right(
1,
seq(
"(",
$.expression,
optional($._expression_list),
")",
),
),
yield: $ =>
prec.left(
seq(
$.expression,
"->",
"(",
$.expression,
optional($._expression_list),
")",
),
),
},
});

View File

@ -1,12 +1,12 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Type, Value};
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Assignment {
identifier: Identifier,
r#type: Option<Type>,
r#type: Option<TypeDefinition>,
operator: AssignmentOperator,
statement: Statement,
}
@ -27,7 +27,7 @@ impl AbstractTree for Assignment {
let type_node = node.child_by_field_name("type");
let r#type = if let Some(type_node) = type_node {
Some(Type::from_syntax_node(source, type_node)?)
Some(TypeDefinition::from_syntax_node(source, type_node)?)
} else {
None
};
@ -62,7 +62,7 @@ impl AbstractTree for Assignment {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let key = self.identifier.inner();
let value = self.statement.run(source, context)?;
@ -86,12 +86,12 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => value,
};
if let Some(r#type) = &self.r#type {
r#type.check(&new_value)?;
}
context.variables_mut()?.insert(key.clone(), new_value);
Ok(Value::Empty)
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Empty)
}
}

View File

@ -4,7 +4,7 @@ use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Map, Result, Statement, Value};
use crate::{AbstractTree, Error, Map, Result, Statement, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Block {
@ -41,7 +41,7 @@ impl AbstractTree for Block {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
if self.is_async {
let statements = &self.statements;
let final_result = RwLock::new(Ok(Value::Empty));
@ -81,4 +81,8 @@ impl AbstractTree for Block {
prev_result.unwrap_or(Ok(Value::Empty))
}
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.statements.last().unwrap().expected_type(context)
}
}

View File

@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{
value_node::ValueNode, AbstractTree, Error, Identifier, Index, Map, Result, Value, Yield,
value_node::ValueNode, AbstractTree, Error, Identifier, Index, Map, Result, TypeDefinition,
Value, Yield,
};
use super::{function_call::FunctionCall, logic::Logic, math::Math};
@ -51,7 +52,7 @@ impl AbstractTree for Expression {
Ok(expression)
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
match self {
Expression::Value(value_node) => value_node.run(source, context),
Expression::Identifier(identifier) => identifier.run(source, context),
@ -62,4 +63,16 @@ impl AbstractTree for Expression {
Expression::Yield(r#yield) => r#yield.run(source, context),
}
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
match self {
Expression::Value(value_node) => value_node.expected_type(context),
Expression::Identifier(identifier) => identifier.expected_type(context),
Expression::Math(math) => math.expected_type(context),
Expression::Logic(logic) => logic.expected_type(context),
Expression::FunctionCall(function_call) => function_call.expected_type(context),
Expression::Index(index) => index.expected_type(context),
Expression::Yield(r#yield) => r#yield.expected_type(context),
}
}
}

View File

@ -1,77 +0,0 @@
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Block, Error, Expression, Identifier, List, Map, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Filter {
count: Option<Expression>,
item_id: Identifier,
collection: Expression,
predicate: Block,
}
impl AbstractTree for Filter {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
let count = match node.child_by_field_name("count") {
Some(node) => Some(Expression::from_syntax_node(source, node)?),
None => None,
};
let item_id_node = node.child_by_field_name("item_id").unwrap();
let item_id = Identifier::from_syntax_node(source, item_id_node)?;
let collection_node = node.child_by_field_name("collection").unwrap();
let collection = Expression::from_syntax_node(source, collection_node)?;
let predicate_node = node.child_by_field_name("predicate").unwrap();
let predicate = Block::from_syntax_node(source, predicate_node)?;
Ok(Filter {
count,
item_id,
collection,
predicate,
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
let value = self.collection.run(source, context)?;
let values = value.as_list()?.items();
let key = self.item_id.inner();
let new_values = List::new();
let count = match &self.count {
Some(expression) => Some(expression.run(source, context)?.as_integer()? as usize),
None => None,
};
let loop_context = Map::clone_from(context)?;
values.par_iter().try_for_each(|value| {
if let Some(max) = count {
if new_values.items().len() == max {
return Ok(());
}
}
let mut iter_context = loop_context.clone();
iter_context
.variables_mut()?
.insert(key.clone(), value.clone());
let should_include = self
.predicate
.run(source, &mut iter_context)?
.as_boolean()?;
if should_include {
new_values.items_mut().push(value.clone());
}
Ok::<(), Error>(())
})?;
Ok(Value::List(new_values))
}
}

View File

@ -2,7 +2,9 @@ use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Block, Error, Expression, Identifier, Map, Result, Value};
use crate::{
AbstractTree, Block, Error, Expression, Identifier, Map, Result, TypeDefinition, Value,
};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct For {
@ -47,7 +49,7 @@ impl AbstractTree for For {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let expression_run = self.collection.run(source, context)?;
let values = expression_run.as_list()?.items();
let key = self.item_id.inner();
@ -76,4 +78,8 @@ impl AbstractTree for For {
Ok(Value::Empty)
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Empty)
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Map, Result, Value, BUILT_IN_FUNCTIONS};
use crate::{AbstractTree, Error, Map, Result, TypeDefinition, Value, BUILT_IN_FUNCTIONS};
use super::expression::Expression;
@ -45,7 +45,7 @@ impl AbstractTree for FunctionCall {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let function = if let Expression::Identifier(identifier) = &self.function {
let key = identifier.inner();
@ -77,15 +77,17 @@ impl AbstractTree for FunctionCall {
let mut function_context = Map::clone_from(context)?;
let parameter_expression_pairs = function.parameters().iter().zip(self.arguments.iter());
for ((identifier, r#type), expression) in parameter_expression_pairs {
for (identifier, expression) in parameter_expression_pairs {
let key = identifier.clone().take_inner();
let value = expression.run(source, context)?;
r#type.check(&value)?;
function_context.variables_mut()?.insert(key, value);
}
function.run(source, &mut function_context)
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.function.expected_type(context)
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Map, Result, Value};
use crate::{AbstractTree, Error, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Identifier(String);
@ -29,11 +29,19 @@ impl AbstractTree for Identifier {
Ok(Identifier(identifier.to_string()))
}
fn run(&self, _source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, _source: &str, context: &Map) -> Result<Value> {
if let Some(value) = context.variables()?.get(&self.0) {
Ok(value.clone())
} else {
Err(Error::VariableIdentifierNotFound(self.inner().clone()))
}
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
if let Some(value) = context.variables()?.get(&self.0) {
Ok(value.r#type(context)?)
} else {
Ok(TypeDefinition::Empty)
}
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Block, Expression, Map, Result, Value};
use crate::{AbstractTree, Block, Expression, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct IfElse {
@ -55,7 +55,7 @@ impl AbstractTree for IfElse {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let if_boolean = self.if_expression.run(source, context)?.as_boolean()?;
if if_boolean {
@ -80,4 +80,8 @@ impl AbstractTree for IfElse {
}
}
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.if_block.expected_type(context)
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Expression, List, Map, Result, Value};
use crate::{AbstractTree, Error, Expression, List, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Index {
@ -32,7 +32,7 @@ impl AbstractTree for Index {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let collection = self.collection.run(source, context)?;
match collection {
@ -73,6 +73,10 @@ impl AbstractTree for Index {
_ => Err(Error::ExpectedCollection { actual: collection }),
}
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.collection.expected_type(context)
}
}
#[cfg(test)]

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Index, Map, Result, Statement, Value};
use crate::{AbstractTree, Error, Index, Map, Result, Statement, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct IndexAssignment {
@ -49,7 +49,7 @@ impl AbstractTree for IndexAssignment {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let index_collection = self.index.collection.run(source, context)?;
let index_context = index_collection.as_map().unwrap_or(&context);
let index_key = if let crate::Expression::Identifier(identifier) = &self.index.index {
@ -90,4 +90,8 @@ impl AbstractTree for IndexAssignment {
Ok(Value::Empty)
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Empty)
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Expression, Map, Result, Value};
use crate::{AbstractTree, Error, Expression, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Logic {
@ -45,7 +45,7 @@ impl AbstractTree for Logic {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let left = self.left.run(source, context)?;
let right = self.right.run(source, context)?;
let result = match self.operator {
@ -73,6 +73,10 @@ impl AbstractTree for Logic {
Ok(Value::Boolean(result))
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Boolean)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]

View File

@ -6,7 +6,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Map, Result, Value};
use crate::{AbstractTree, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Match {}
@ -16,7 +16,11 @@ impl AbstractTree for Match {
todo!()
}
fn run(&self, _source: &str, _context: &mut Map) -> Result<Value> {
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
todo!()
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
todo!()
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Expression, Map, Result, Value};
use crate::{AbstractTree, Error, Expression, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Math {
@ -42,7 +42,7 @@ impl AbstractTree for Math {
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let left = self.left.run(source, context)?;
let right = self.right.run(source, context)?;
let value = match self.operator {
@ -55,6 +55,10 @@ impl AbstractTree for Math {
Ok(value)
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Number)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]

View File

@ -9,38 +9,67 @@
pub mod assignment;
pub mod block;
pub mod expression;
pub mod filter;
pub mod find;
pub mod r#for;
pub mod function_call;
pub mod identifier;
pub mod if_else;
pub mod index;
pub mod index_assignment;
pub mod insert;
pub mod logic;
pub mod r#match;
pub mod math;
pub mod remove;
pub mod select;
pub mod statement;
pub mod transform;
pub mod r#type;
pub mod type_defintion;
pub mod r#use;
pub mod value_node;
pub mod r#while;
pub mod r#yield;
pub use {
assignment::*, block::*, expression::*, filter::*, find::*, function_call::*, identifier::*,
if_else::*, index::*, index_assignment::IndexAssignment, insert::*, logic::*, math::*,
r#for::*, r#match::*, r#type::*, r#use::*, r#while::*, r#yield::*, remove::*, select::*,
statement::*, transform::*, value_node::*,
assignment::*, block::*, expression::*, function_call::*, identifier::*, if_else::*, index::*,
index_assignment::IndexAssignment, logic::*, math::*, r#for::*, r#match::*, r#use::*,
r#while::*, r#yield::*, statement::*, type_defintion::*, value_node::*,
};
use tree_sitter::Node;
use crate::{Map, Result, Value};
use crate::{Error, Map, Result, Value};
pub struct Root {
statements: Vec<Statement>,
}
impl AbstractTree for Root {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "root", node)?;
let statement_count = node.child_count();
let mut statements = Vec::with_capacity(statement_count);
for index in 0..statement_count {
let statement_node = node.child(index).unwrap();
let statement = Statement::from_syntax_node(source, statement_node)?;
statements.push(statement);
}
Ok(Root { statements })
}
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let mut value = Value::Empty;
for statement in &self.statements {
value = statement.run(source, context)?;
}
Ok(value)
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.statements.last().unwrap().expected_type(context)
}
}
/// This trait is implemented by the Evaluator's internal types to form an
/// executable tree that resolves to a single value.
@ -57,5 +86,7 @@ pub trait AbstractTree: Sized {
fn from_syntax_node(source: &str, node: Node) -> Result<Self>;
/// Execute dust code by traversing the tree.
fn run(&self, source: &str, context: &mut Map) -> Result<Value>;
fn run(&self, source: &str, context: &Map) -> Result<Value>;
fn expected_type(&self, context: &Map) -> Result<TypeDefinition>;
}

View File

@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{
AbstractTree, Assignment, Block, Error, Expression, Filter, Find, For, IfElse, IndexAssignment,
Insert, Map, Match, Remove, Result, Select, Transform, Use, Value, While,
AbstractTree, Assignment, Block, Error, Expression, For, IfElse, IndexAssignment, Map, Match,
Result, TypeDefinition, Use, Value, While,
};
/// Abstract representation of a statement.
@ -17,13 +17,7 @@ pub enum Statement {
While(Box<While>),
Block(Box<Block>),
For(Box<For>),
Transform(Box<Transform>),
Filter(Box<Filter>),
Find(Box<Find>),
Remove(Box<Remove>),
Use(Use),
Select(Box<Select>),
Insert(Box<Insert>),
IndexAssignment(Box<IndexAssignment>),
}
@ -60,25 +54,7 @@ impl AbstractTree for Statement {
"for" => Ok(Statement::For(Box::new(For::from_syntax_node(
source, child,
)?))),
"transform" => Ok(Statement::Transform(Box::new(Transform::from_syntax_node(
source, child,
)?))),
"filter" => Ok(Statement::Filter(Box::new(Filter::from_syntax_node(
source, child,
)?))),
"find" => Ok(Statement::Find(Box::new(Find::from_syntax_node(
source, child,
)?))),
"remove" => Ok(Statement::Remove(Box::new(Remove::from_syntax_node(
source, child,
)?))),
"select" => Ok(Statement::Select(Box::new(Select::from_syntax_node(
source, child,
)?))),
"use" => Ok(Statement::Use(Use::from_syntax_node(source, child)?)),
"insert" => Ok(Statement::Insert(Box::new(Insert::from_syntax_node(
source, child,
)?))),
"index_assignment" => Ok(Statement::IndexAssignment(Box::new(IndexAssignment::from_syntax_node(
source, child,
)?))),
@ -91,7 +67,7 @@ impl AbstractTree for Statement {
}
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
match self {
Statement::Assignment(assignment) => assignment.run(source, context),
Statement::Return(expression) => expression.run(source, context),
@ -101,14 +77,23 @@ impl AbstractTree for Statement {
Statement::While(r#while) => r#while.run(source, context),
Statement::Block(block) => block.run(source, context),
Statement::For(r#for) => r#for.run(source, context),
Statement::Transform(transform) => transform.run(source, context),
Statement::Filter(filter) => filter.run(source, context),
Statement::Find(find) => find.run(source, context),
Statement::Remove(remove) => remove.run(source, context),
Statement::Use(run) => run.run(source, context),
Statement::Select(select) => select.run(source, context),
Statement::Insert(insert) => insert.run(source, context),
Statement::IndexAssignment(index_assignment) => index_assignment.run(source, context),
}
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
match self {
Statement::Assignment(assignment) => assignment.expected_type(context),
Statement::Return(expression) => expression.expected_type(context),
Statement::Expression(expression) => expression.expected_type(context),
Statement::IfElse(if_else) => if_else.expected_type(context),
Statement::Match(r#match) => r#match.expected_type(context),
Statement::While(r#while) => r#while.expected_type(context),
Statement::Block(block) => block.expected_type(context),
Statement::For(r#for) => r#for.expected_type(context),
Statement::Use(r#use) => r#use.expected_type(context),
Statement::IndexAssignment(index_assignment) => index_assignment.expected_type(context),
}
}
}

View File

@ -1,117 +0,0 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Map, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Type {
Any,
Boolean,
Float,
Function,
Integer,
List,
Map,
String,
Table,
}
impl Type {
pub fn check(&self, value: &Value) -> Result<()> {
match (self, value.r#type()) {
(Type::Any, _)
| (Type::Boolean, Type::Boolean)
| (Type::Float, Type::Float)
| (Type::Function, Type::Function)
| (Type::Integer, Type::Integer)
| (Type::List, Type::List)
| (Type::Map, Type::Map)
| (Type::String, Type::String)
| (Type::Table, Type::Table) => Ok(()),
(Type::Boolean, _) => Err(Error::TypeCheck {
expected: Type::Boolean,
actual: value.clone(),
}),
(Type::Float, _) => Err(Error::TypeCheck {
expected: Type::Float,
actual: value.clone(),
}),
(Type::Function, _) => Err(Error::TypeCheck {
expected: Type::Function,
actual: value.clone(),
}),
(Type::Integer, _) => Err(Error::TypeCheck {
expected: Type::Integer,
actual: value.clone(),
}),
(Type::List, _) => Err(Error::TypeCheck {
expected: Type::List,
actual: value.clone(),
}),
(Type::Map, _) => Err(Error::TypeCheck {
expected: Type::Map,
actual: value.clone(),
}),
(Type::String, _) => Err(Error::TypeCheck {
expected: Type::String,
actual: value.clone(),
}),
(Type::Table, _) => Err(Error::TypeCheck {
expected: Type::Table,
actual: value.clone(),
}),
}
}
}
impl AbstractTree for Type {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "type", node)?;
let range_without_punctuation = node.start_byte() + 1..node.end_byte() - 1;
let r#type = match &source[range_without_punctuation] {
"any" => Type::Any,
"bool" => Type::Boolean,
"float" => Type::Float,
"fn" => Type::Function,
"int" => Type::Integer,
"list" => Type::List,
"map" => Type::Map,
"str" => Type::String,
"table" => Type::Table,
_ => {
return Err(Error::UnexpectedSyntaxNode {
expected: "any, bool, float, fn, int, list, map, str or table",
actual: node.kind(),
location: node.start_position(),
relevant_source: source[node.byte_range()].to_string(),
})
}
};
Ok(r#type)
}
fn run(&self, _source: &str, _context: &mut Map) -> Result<Value> {
Ok(Value::Empty)
}
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Type::Any => write!(f, "any"),
Type::Boolean => write!(f, "bool"),
Type::Float => write!(f, "float"),
Type::Function => write!(f, "function"),
Type::Integer => write!(f, "integer"),
Type::List => write!(f, "list"),
Type::Map => write!(f, "map"),
Type::String => write!(f, "string"),
Type::Table => write!(f, "table"),
}
}
}

View File

@ -0,0 +1,170 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Map, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum TypeDefinition {
Any,
Boolean,
Empty,
Float,
Function {
parameter_types: Vec<TypeDefinition>,
return_type: Box<TypeDefinition>,
},
Integer,
List(Box<TypeDefinition>),
Map,
Number,
String,
Table,
}
impl TypeDefinition {
// pub fn check(&self, value: &Value) -> Result<()> {
// match (self, value.r#type()?) {
// (Type::Any, _)
// | (Type::Boolean, Type::Boolean)
// | (Type::Empty, Type::Empty)
// | (Type::Float, Type::Float)
// | (Type::Integer, Type::Integer)
// | (Type::Map, Type::Map)
// | (Type::Number, Type::Number)
// | (Type::Number, Type::Integer)
// | (Type::Number, Type::Float)
// | (Type::Integer, Type::Number)
// | (Type::Float, Type::Number)
// | (Type::String, Type::String)
// | (Type::Table, Type::Table) => Ok(()),
// (Type::List(expected), Type::List(actual)) => {
// if expected != &actual {
// Err(Error::TypeCheck {
// expected: Type::List(expected.clone()),
// actual: value.clone(),
// })
// } else {
// Ok(())
// }
// }
// (
// Type::Function {
// parameter_types: left_parameters,
// return_type: left_return,
// },
// Type::Function {
// parameter_types: right_parameters,
// return_type: right_return,
// },
// ) => {
// if left_parameters != &right_parameters || left_return != &right_return {
// Err(Error::TypeCheck {
// expected: Type::Function {
// parameter_types: left_parameters.clone(),
// return_type: left_return.clone(),
// },
// actual: value.clone(),
// })
// } else {
// Ok(())
// }
// }
// (Type::Boolean, _) => Err(Error::TypeCheck {
// expected: Type::Boolean,
// actual: value.clone(),
// }),
// (Type::Empty, _) => Err(Error::TypeCheck {
// expected: Type::Empty,
// actual: value.clone(),
// }),
// (Type::Float, _) => Err(Error::TypeCheck {
// expected: Type::Float,
// actual: value.clone(),
// }),
// (expected, _) => Err(Error::TypeCheck {
// expected: expected.clone(),
// actual: value.clone(),
// }),
// (Type::Integer, _) => Err(Error::TypeCheck {
// expected: Type::Integer,
// actual: value.clone(),
// }),
// (expected, _) => Err(Error::TypeCheck {
// expected: expected.clone(),
// actual: value.clone(),
// }),
// (Type::Map, _) => Err(Error::TypeCheck {
// expected: Type::Map,
// actual: value.clone(),
// }),
// (Type::String, _) => Err(Error::TypeCheck {
// expected: Type::String,
// actual: value.clone(),
// }),
// (Type::Table, _) => Err(Error::TypeCheck {
// expected: Type::Table,
// actual: value.clone(),
// }),
// }
// }
}
impl AbstractTree for TypeDefinition {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "type_definition", node)?;
let type_node = node.child(1).unwrap();
let type_symbol = &source[type_node.byte_range()];
let r#type = match type_symbol {
"any" => TypeDefinition::Any,
"bool" => TypeDefinition::Boolean,
"float" => TypeDefinition::Float,
"fn" => {
todo!()
}
"int" => TypeDefinition::Integer,
"map" => TypeDefinition::Map,
"str" => TypeDefinition::String,
"table" => TypeDefinition::Table,
_ => {
return Err(Error::UnexpectedSyntaxNode {
expected: "any, bool, float, fn, int, list, map, str or table",
actual: type_node.kind(),
location: type_node.start_position(),
relevant_source: source[type_node.byte_range()].to_string(),
})
}
};
Ok(r#type)
}
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
Ok(Value::Empty)
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Empty)
}
}
impl Display for TypeDefinition {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TypeDefinition::Any => write!(f, "any"),
TypeDefinition::Boolean => write!(f, "bool"),
TypeDefinition::Empty => write!(f, "empty"),
TypeDefinition::Float => write!(f, "float"),
TypeDefinition::Function { .. } => write!(f, "function"),
TypeDefinition::Integer => write!(f, "integer"),
TypeDefinition::List(_) => write!(f, "list"),
TypeDefinition::Map => write!(f, "map"),
TypeDefinition::Number => write!(f, "number"),
TypeDefinition::String => write!(f, "string"),
TypeDefinition::Table => write!(f, "table"),
}
}
}

View File

@ -2,7 +2,7 @@ use std::fs::read_to_string;
use serde::{Deserialize, Serialize};
use crate::{evaluate_with_context, AbstractTree, Error, Map, Result, Value};
use crate::{evaluate_with_context, AbstractTree, Error, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Use {
@ -21,7 +21,7 @@ impl AbstractTree for Use {
Ok(Use { path })
}
fn run(&self, _source: &str, _context: &mut Map) -> Result<Value> {
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
let file_contents = read_to_string(&self.path)?;
let mut file_context = Map::new();
@ -29,4 +29,8 @@ impl AbstractTree for Use {
Ok(Value::Map(file_context))
}
fn expected_type(&self, _context: &Map) -> Result<TypeDefinition> {
Ok(TypeDefinition::Map)
}
}

View File

@ -5,7 +5,7 @@ use tree_sitter::Node;
use crate::{
AbstractTree, Error, Expression, Function, Identifier, List, Map, Result, Statement, Table,
Value,
TypeDefinition, Value,
};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -112,7 +112,7 @@ impl AbstractTree for ValueNode {
Ok(value_node)
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let value = match self {
ValueNode::Boolean(value_source) => Value::Boolean(value_source.parse().unwrap()),
ValueNode::Float(value_source) => Value::Float(value_source.parse().unwrap()),
@ -176,4 +176,31 @@ impl AbstractTree for ValueNode {
Ok(value)
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
let r#type = match self {
ValueNode::Boolean(_) => TypeDefinition::Boolean,
ValueNode::Float(_) => TypeDefinition::Float,
ValueNode::Integer(_) => TypeDefinition::Integer,
ValueNode::String(_) => TypeDefinition::String,
ValueNode::List(expressions) => {
let first_expression_type = if let Some(first) = expressions.first() {
first.expected_type(context)?
} else {
TypeDefinition::Empty
};
TypeDefinition::List(Box::new(first_expression_type))
}
ValueNode::Empty => TypeDefinition::Any,
ValueNode::Map(_) => TypeDefinition::Map,
ValueNode::Table {
column_names: _,
rows: _,
} => TypeDefinition::Table,
ValueNode::Function(function) => function.expected_type(context)?,
};
Ok(r#type)
}
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Block, Expression, Map, Result, Value};
use crate::{AbstractTree, Block, Expression, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct While {
@ -22,13 +22,17 @@ impl AbstractTree for While {
Ok(While { expression, block })
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
while self.expression.run(source, context)?.as_boolean()? {
self.block.run(source, context)?;
}
Ok(Value::Empty)
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.block.expected_type(context)
}
}
#[cfg(test)]

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Expression, FunctionCall, Result, Value};
use crate::{AbstractTree, Expression, FunctionCall, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Yield {
@ -35,7 +35,11 @@ impl AbstractTree for Yield {
Ok(Yield { call })
}
fn run(&self, source: &str, context: &mut crate::Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
self.call.run(source, context)
}
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.call.expected_type(context)
}
}

View File

@ -5,7 +5,7 @@
use tree_sitter::{Node, Point};
use crate::{value::Value, Identifier, Type};
use crate::{value::Value, Identifier, TypeDefinition};
use std::{fmt, io, num::ParseFloatError, string::FromUtf8Error, sync::PoisonError, time};
@ -21,7 +21,7 @@ pub enum Error {
},
TypeCheck {
expected: Type,
expected: TypeDefinition,
actual: Value,
},

View File

@ -4,7 +4,7 @@
//! functions or by constructing your own Evaluator.
use tree_sitter::{Parser, Tree as TSTree};
use crate::{language, AbstractTree, Map, Result, Statement, Value};
use crate::{language, AbstractTree, Map, Result, Root, Value};
/// Evaluate the given source code.
///
@ -50,41 +50,34 @@ pub fn evaluate_with_context(source: &str, context: &mut Map) -> Result<Value> {
let mut parser = Parser::new();
parser.set_language(language()).unwrap();
Evaluator::new(parser, context, source).run()
Interpreter::parse(parser, context, source)?.run()
}
/// A source code interpreter for the Dust language.
pub struct Evaluator<'c, 's> {
pub struct Interpreter<'c, 's> {
_parser: Parser,
context: &'c mut Map,
source: &'s str,
syntax_tree: TSTree,
abstract_tree: Root,
}
impl<'c, 's> Evaluator<'c, 's> {
pub fn new(mut parser: Parser, context: &'c mut Map, source: &'s str) -> Self {
impl<'c, 's> Interpreter<'c, 's> {
pub fn parse(mut parser: Parser, context: &'c mut Map, source: &'s str) -> Result<Self> {
let syntax_tree = parser.parse(source, None).unwrap();
let abstract_tree = Root::from_syntax_node(source, syntax_tree.root_node())?;
Evaluator {
Ok(Interpreter {
_parser: parser,
context,
source,
syntax_tree,
}
abstract_tree,
})
}
pub fn run(self) -> Result<Value> {
let root_node = self.syntax_tree.root_node();
let mut value = Value::Empty;
for index in 0..root_node.child_count() {
let statement_node = root_node.child(index).unwrap();
let statement = Statement::from_syntax_node(self.source, statement_node)?;
value = statement.run(self.source, self.context)?;
}
Ok(value)
pub fn run(&mut self) -> Result<Value> {
self.abstract_tree.run(self.source, self.context)
}
pub fn syntax_tree(&self) -> String {
@ -247,7 +240,7 @@ mod tests {
assert_eq!(
evaluate(
"
foobar = |message <str>| <str> { message }
foobar <fn str -> str> = |message| { message }
(foobar 'Hiya')
",
),

View File

@ -8,14 +8,14 @@ pub use crate::{
abstract_tree::*,
built_in_functions::{BuiltInFunction, BUILT_IN_FUNCTIONS},
error::*,
evaluator::*,
evaluate::*,
value::{function::Function, list::List, map::Map, table::Table, Value},
};
mod abstract_tree;
pub mod built_in_functions;
mod error;
mod evaluator;
mod evaluate;
mod value;
use tree_sitter::Language;

View File

@ -12,7 +12,7 @@ use tree_sitter::Parser as TSParser;
use std::{borrow::Cow, fs::read_to_string};
use dust_lang::{evaluate_with_context, language, Evaluator, Map, Value};
use dust_lang::{evaluate_with_context, language, Interpreter, Map, Value};
/// Command-line arguments to be parsed.
#[derive(Parser, Debug)]
@ -30,10 +30,14 @@ struct Args {
#[arg(short = 'p', long)]
input_path: Option<String>,
/// A path to file whose contents will be assigned to the "input" variable.
/// Show the syntax tree.
#[arg(short = 't', long = "tree")]
show_syntax_tree: bool,
/// Launch in interactive mode.
#[arg(short, long)]
interactive: bool,
/// Location of the file to run.
path: Option<String>,
}
@ -74,13 +78,21 @@ fn main() {
let mut parser = TSParser::new();
parser.set_language(language()).unwrap();
let evaluator = Evaluator::new(parser, &mut context, &source);
let mut interpreter = Interpreter::parse(parser, &mut context, &source).unwrap();
if args.show_syntax_tree {
println!("{}", evaluator.syntax_tree());
if args.interactive {
loop {
let result = interpreter.run();
println!("{result:?}")
}
}
let eval_result = evaluator.run();
if args.show_syntax_tree {
println!("{}", interpreter.syntax_tree());
}
let eval_result = interpreter.run();
match eval_result {
Ok(value) => {

View File

@ -3,19 +3,22 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Block, Error, Identifier, Map, Result, Type, Value};
use crate::{AbstractTree, Block, Error, Identifier, Map, Result, TypeDefinition, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Function {
parameters: Vec<(Identifier, Type)>,
return_type: Option<Type>,
parameters: Vec<Identifier>,
body: Block,
}
impl Function {
pub fn parameters(&self) -> &Vec<(Identifier, Type)> {
pub fn parameters(&self) -> &Vec<Identifier> {
&self.parameters
}
pub fn body(&self) -> &Block {
&self.body
}
}
impl AbstractTree for Function {
@ -25,57 +28,29 @@ impl AbstractTree for Function {
let child_count = node.child_count();
let mut parameters = Vec::new();
for index in 1..child_count - 2 {
let parameter_node = {
for index in 0..child_count {
let child = node.child(index).unwrap();
if child.is_named() {
child
} else {
continue;
let identifier = Identifier::from_syntax_node(source, child)?;
parameters.push(identifier);
}
};
Error::expect_syntax_node(source, "parameter", parameter_node)?;
let identifier_node = parameter_node.child(0).unwrap();
let identifier = Identifier::from_syntax_node(source, identifier_node)?;
let type_node = parameter_node.child(1).unwrap();
let r#type = Type::from_syntax_node(source, type_node)?;
parameters.push((identifier, r#type))
}
let return_type_node = node.child(child_count - 2).unwrap();
let return_type = if return_type_node.is_named() {
Some(Type::from_syntax_node(source, return_type_node)?)
} else {
None
};
let body_node = node.child(child_count - 1).unwrap();
let body = Block::from_syntax_node(source, body_node)?;
Ok(Function {
parameters,
return_type,
body,
})
Ok(Function { parameters, body })
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
fn run(&self, source: &str, context: &Map) -> Result<Value> {
let return_value = self.body.run(source, context)?;
if let Some(r#type) = &self.return_type {
r#type.check(&return_value)?;
} else if !return_value.is_empty() {
return Err(Error::ExpectedEmpty {
actual: return_value.clone(),
});
Ok(return_value)
}
Ok(return_value)
fn expected_type(&self, context: &Map) -> Result<TypeDefinition> {
self.body.expected_type(context)
}
}
@ -83,8 +58,8 @@ impl Display for Function {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"Function {{ parameters: {:?}, return_type: {:?}, body: {:?} }}",
self.parameters, self.return_type, self.body
"Function {{ parameters: {:?}, body: {:?} }}",
self.parameters, self.body
)
}
}

View File

@ -1,7 +1,7 @@
//! Types that represent runtime values.
use crate::{
error::{Error, Result},
Function, List, Map, Table, Type,
AbstractTree, Function, List, Map, Table, TypeDefinition,
};
use serde::{
@ -43,18 +43,41 @@ pub enum Value {
}
impl Value {
pub fn r#type(&self) -> Type {
match self {
Value::List(_) => Type::List,
Value::Map(_) => Type::Map,
Value::Table(_) => Type::Table,
Value::Function(_) => Type::Function,
Value::String(_) => Type::String,
Value::Float(_) => Type::Float,
Value::Integer(_) => Type::Integer,
Value::Boolean(_) => Type::Boolean,
Value::Empty => Type::Any,
pub fn r#type(&self, context: &Map) -> Result<TypeDefinition> {
let r#type = match self {
Value::List(list) => {
let first_item_type = if let Some(first) = list.items().first() {
first.r#type(context)?
} else {
TypeDefinition::Empty
};
TypeDefinition::List(Box::new(first_item_type))
}
Value::Map(_) => TypeDefinition::Map,
Value::Table(_) => TypeDefinition::Table,
Value::Function(function) => {
let parameter_types = Vec::new();
for identifier in function.parameters() {
let _type = identifier.expected_type(context)?;
}
let return_type = function.body().expected_type(context)?;
TypeDefinition::Function {
parameter_types,
return_type: Box::new(return_type),
}
}
Value::String(_) => TypeDefinition::String,
Value::Float(_) => TypeDefinition::Float,
Value::Integer(_) => TypeDefinition::Integer,
Value::Boolean(_) => TypeDefinition::Boolean,
Value::Empty => TypeDefinition::Empty,
};
Ok(r#type)
}
pub fn is_table(&self) -> bool {

View File

@ -0,0 +1 @@
src/

View File

@ -0,0 +1,5 @@
{
"arrowParens": "avoid",
"printWidth": 40,
"singleQuote": true
}

View File

@ -27,7 +27,8 @@ x <int> = y
(statement
(assignment
(identifier)
(type)
(type_definition
(type))
(assignment_operator)
(statement
(expression

View File

@ -12,10 +12,11 @@ async { (output 'Whaddup') }
(statement
(expression
(function_call
(built_in_function
(expression
(identifier))
(expression
(value
(string))))))))))
(string)))))))))
================================================================================
Complex Async Statements

View File

@ -10,10 +10,11 @@ Simple Function Call
(statement
(expression
(function_call
(built_in_function
(expression
(identifier))
(expression
(value
(string))))))))
(string)))))))
================================================================================
Nested Function Call
@ -27,10 +28,10 @@ Nested Function Call
(statement
(expression
(function_call
(built_in_function
(expression
(function_call
(built_in_function)))
(identifier))
(expression
(identifier))
(expression
(value
(integer))))))))
(integer)))))))

View File

@ -28,9 +28,10 @@ for i in [1, 2, 3] {
(statement
(expression
(function_call
(built_in_function
(expression
(identifier))))))))))
(identifier))
(expression
(identifier)))))))))
================================================================================
Nested For Loop
@ -60,6 +61,7 @@ for list in list_of_lists {
(statement
(expression
(function_call
(built_in_function
(expression
(identifier)))))))))))))
(identifier))
(expression
(identifier))))))))))))

View File

@ -2,7 +2,7 @@
Simple Function
================================================================================
|| <str> { "Hiya" }
|| { "Hiya" }
--------------------------------------------------------------------------------
@ -11,7 +11,6 @@ Simple Function
(expression
(value
(function
(type)
(block
(statement
(expression
@ -22,7 +21,7 @@ Simple Function
Function Assignment
================================================================================
x = || <str> { "Hiya" }
x <fn num -> str> = |number| { "Hiya" }
--------------------------------------------------------------------------------
@ -30,12 +29,16 @@ x = || <str> { "Hiya" }
(statement
(assignment
(identifier)
(type_definition
(type
(type)
(type)))
(assignment_operator)
(statement
(expression
(value
(function
(type)
(identifier)
(block
(statement
(expression
@ -64,7 +67,7 @@ Function Call
Complex Function
================================================================================
|message <str> number <int>| {
|message number| {
(output message)
(output number)
}
@ -76,25 +79,23 @@ Complex Function
(expression
(value
(function
(parameter
(identifier)
(type))
(parameter
(identifier)
(type))
(block
(statement
(expression
(function_call
(built_in_function
(expression
(identifier))))))
(identifier))
(expression
(identifier)))))
(statement
(expression
(function_call
(built_in_function
(expression
(identifier))))))))))))
(identifier))
(expression
(identifier)))))))))))
================================================================================
Complex Function Call

View File

@ -1,105 +0,0 @@
================================================================================
Table Declaration
================================================================================
table |messages numbers| [
['hiya' 42]
['foo' 57]
['bar' 99.99]
]
--------------------------------------------------------------------------------
(root
(statement
(expression
(value
(table
(identifier_list
(identifier)
(identifier))
(expression
(value
(list
(expression
(value
(list
(expression
(value
(string)))
(expression
(value
(integer))))))
(expression
(value
(list
(expression
(value
(string)))
(expression
(value
(integer))))))
(expression
(value
(list
(expression
(value
(string)))
(expression
(value
(float))))))))))))))
================================================================================
Table Access
================================================================================
select |number| from foobar {
text == 'answer'
}
--------------------------------------------------------------------------------
(root
(statement
(select
(identifier_list
(identifier))
(expression
(identifier))
(block
(statement
(expression
(logic
(expression
(identifier))
(logic_operator)
(expression
(value
(string))))))))))
================================================================================
Table Insert
================================================================================
insert into foobar [
['bob was here', 0]
]
--------------------------------------------------------------------------------
(root
(statement
(insert
(identifier)
(expression
(value
(list
(expression
(value
(list
(expression
(value
(string)))
(expression
(value
(integer))))))))))))

View File

@ -18,10 +18,11 @@ while true {
(statement
(expression
(function_call
(built_in_function
(expression
(identifier))
(expression
(value
(string)))))))))))
(string))))))))))
================================================================================
Nested While Loop

View File

@ -13,7 +13,8 @@ Simple Yield
(expression
(value
(integer)))
(built_in_function)))))
(expression
(identifier))))))
================================================================================
Yield Chain

View File

@ -3,21 +3,25 @@ module.exports = grammar({
word: $ => $.identifier,
extras: $ => [ /\s/, $._comment ],
extras: $ => [/\s/, $._comment],
rules: {
root: $ => prec(1, repeat1($.statement)),
root: $ =>
prec(1, repeat1($.statement)),
_comment: $ => /[#][^#\n]*[#|\n]/,
block: $ => seq(
block: $ =>
seq(
optional('async'),
'{',
repeat1($.statement),
'}',
),
statement: $ => prec.left(seq(
statement: $ =>
prec.left(
seq(
choice(
$.assignment,
$.block,
@ -25,22 +29,30 @@ module.exports = grammar({
$.for,
$.if_else,
$.index_assignment,
$.insert,
$.match,
$.return,
$.select,
$.use,
$.while,
),
optional(';'),
)),
),
),
expression: $ => prec.right(choice(
expression: $ =>
prec.right(
choice(
$._expression_kind,
seq('(', $._expression_kind, ')'),
)),
seq(
'(',
$._expression_kind,
')',
),
),
),
_expression_kind: $ => prec.right(choice(
_expression_kind: $ =>
prec.right(
choice(
$.function_call,
$.identifier,
$.index,
@ -48,16 +60,24 @@ module.exports = grammar({
$.math,
$.value,
$.yield,
)),
),
),
_expression_list: $ => repeat1(prec.right(seq(
_expression_list: $ =>
repeat1(
prec.right(
seq(
$.expression,
optional(','),
))),
),
),
),
identifier: $ => /[_a-zA-Z]+[_a-zA-Z0-9]?/,
identifier: $ =>
/[_a-zA-Z]+[_a-zA-Z0-9]?/,
value: $ => choice(
value: $ =>
choice(
$.integer,
$.float,
$.string,
@ -68,236 +88,320 @@ module.exports = grammar({
$.map,
),
integer: $ => token(prec.left(seq(
integer: $ =>
token(
prec.left(
seq(
optional('-'),
repeat1(
choice('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')
choice(
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'0',
),
),
),
),
),
))),
float: $ => token(prec.left(seq(
float: $ =>
token(
prec.left(
seq(
optional('-'),
repeat1(choice('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')),
repeat1(
choice(
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'0',
),
),
'.',
repeat1(choice('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')),
))),
string: $ => /("[^"]*?")|('[^']*?')|(`[^`]*?`)/,
boolean: $ => choice(
'true',
'false',
repeat1(
choice(
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'0',
),
),
),
),
),
list: $ => seq(
string: $ =>
/("[^"]*?")|('[^']*?')|(`[^`]*?`)/,
boolean: $ =>
choice('true', 'false'),
list: $ =>
seq(
'[',
repeat(prec.left(seq($.expression, optional(',')))),
repeat(
prec.left(
seq(
$.expression,
optional(','),
),
),
),
']',
),
map: $ => seq(
map: $ =>
seq(
'{',
repeat(seq(
repeat(
seq(
$.identifier,
"=",
'=',
$.statement,
optional(',')
)),
optional(','),
),
),
'}',
),
index: $ => prec.left(1, seq(
index: $ =>
prec.left(
1,
seq(
$.expression,
':',
$.expression,
optional(seq(
'..',
$.expression,
)),
)),
optional(
seq('..', $.expression),
),
),
),
math: $ => prec.left(seq(
math: $ =>
prec.left(
seq(
$.expression,
$.math_operator,
$.expression,
)),
math_operator: $ => choice(
'+',
'-',
'*',
'/',
'%',
),
),
logic: $ => prec.right(seq(
math_operator: $ =>
choice('+', '-', '*', '/', '%'),
logic: $ =>
prec.right(
seq(
$.expression,
$.logic_operator,
$.expression,
)),
),
),
logic_operator: $ => choice(
logic_operator: $ =>
choice(
'==',
'!=',
'&&',
'||',
'>',
'<',
">=",
"<=",
'>=',
'<=',
),
assignment: $ => seq(
field('identifier', $.identifier),
optional(field('type', $.type)),
field('assignment_operator', $.assignment_operator),
assignment: $ =>
seq(
field(
'identifier',
$.identifier,
),
optional(
field(
'type',
$.type_definition,
),
),
field(
'assignment_operator',
$.assignment_operator,
),
field('statement', $.statement),
),
index_assignment: $ => seq(
index_assignment: $ =>
seq(
$.index,
$.assignment_operator,
$.statement,
),
assignment_operator: $ => prec.right(choice(
"=",
"+=",
"-=",
)),
assignment_operator: $ =>
prec.right(
choice('=', '+=', '-='),
),
if_else: $ => prec.right(seq(
if_else: $ =>
prec.right(
seq(
$.if,
repeat($.else_if),
optional($.else),
)),
if: $ => seq(
'if',
$.expression,
$.block,
),
),
else_if: $ => seq(
if: $ =>
seq('if', $.expression, $.block),
else_if: $ =>
seq(
'else if',
$.expression,
$.block,
),
else: $ => seq(
'else',
$.block,
),
else: $ => seq('else', $.block),
match: $ => prec.right(seq(
match: $ =>
prec.right(
seq(
'match',
$.expression,
repeat1(seq(
repeat1(
seq(
$.expression,
'=>',
$.block,
)),
)),
),
),
),
),
while: $ => seq(
while: $ =>
seq(
'while',
$.expression,
$.block,
),
for: $ => seq(
choice(
'for',
'async for',
),
for: $ =>
seq(
choice('for', 'async for'),
$.identifier,
'in',
$.expression,
$.block,
),
select: $ => prec.right(seq(
'select',
$.identifier_list,
'from',
$.expression,
optional($.block),
)),
insert: $ => prec.right(seq(
'insert',
'into',
$.identifier,
$.expression,
)),
identifier_list: $ => prec.right(choice(
identifier_list: $ =>
prec.right(
choice(
seq(
'|',
repeat(seq($.identifier, optional(','))),
repeat(
seq(
$.identifier,
optional(','),
),
),
'|',
),
)),
),
),
table: $ => prec.right(seq(
table: $ =>
prec.right(
seq(
'table',
$.identifier_list,
$.expression,
)),
return: $ => seq(
'return',
$.expression,
),
),
use: $ => seq(
'use',
$.string,
),
return: $ =>
seq('return', $.expression),
type: $ => seq(
'<',
use: $ => seq('use', $.string),
type_definition: $ =>
seq('<', $.type, '>'),
type: $ =>
prec.right(
choice(
'any',
'bool',
seq(
'fn',
'int',
'list',
'map',
'str',
'table',
),
'>',
),
function: $ => seq(
'|',
repeat($.parameter),
'|',
optional($.type),
$.block,
),
parameter: $ => seq(
$.identifier,
repeat(
seq(
$.type,
optional(','),
),
),
optional(seq('->', $.type)),
),
'int',
seq('list', $.type),
'map',
'num',
'str',
),
),
function_call: $ => prec.right(1, seq(
function: $ =>
seq(
'|',
repeat(
seq(
$.identifier,
optional(','),
),
),
'|',
$.block,
),
function_call: $ =>
prec.right(
1,
seq(
'(',
$.expression,
optional($._expression_list),
')',
)),
),
),
yield: $ => prec.left(seq(
yield: $ =>
prec.left(
seq(
$.expression,
'->',
'(',
$.expression,
optional($._expression_list),
')',
)),
}
),
),
},
});

View File

@ -0,0 +1,407 @@
module.exports = grammar({
name: "dust",
word: $ => $.identifier,
extras: $ => [/\s/, $._comment],
rules: {
root: $ =>
prec(1, repeat1($.statement)),
_comment: $ => /[#][^#\n]*[#|\n]/,
block: $ =>
seq(
optional("async"),
"{",
repeat1($.statement),
"}",
),
statement: $ =>
prec.left(
seq(
choice(
$.assignment,
$.block,
$.expression,
$.for,
$.if_else,
$.index_assignment,
$.match,
$.return,
$.use,
$.while,
),
optional(";"),
),
),
expression: $ =>
prec.right(
choice(
$._expression_kind,
seq(
"(",
$._expression_kind,
")",
),
),
),
_expression_kind: $ =>
prec.right(
choice(
$.function_call,
$.identifier,
$.index,
$.logic,
$.math,
$.value,
$.yield,
),
),
_expression_list: $ =>
repeat1(
prec.right(
seq(
$.expression,
optional(","),
),
),
),
identifier: $ =>
/[_a-zA-Z]+[_a-zA-Z0-9]?/,
value: $ =>
choice(
$.integer,
$.float,
$.string,
$.boolean,
$.list,
$.function,
$.table,
$.map,
),
integer: $ =>
token(
prec.left(
seq(
optional("-"),
repeat1(
choice(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
),
),
),
),
),
float: $ =>
token(
prec.left(
seq(
optional("-"),
repeat1(
choice(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
),
),
".",
repeat1(
choice(
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
),
),
),
),
),
string: $ =>
/("[^"]*?")|('[^']*?')|(`[^`]*?`)/,
boolean: $ =>
choice("true", "false"),
list: $ =>
seq(
"[",
repeat(
prec.left(
seq(
$.expression,
optional(","),
),
),
),
"]",
),
map: $ =>
seq(
"{",
repeat(
seq(
$.identifier,
"=",
$.statement,
optional(","),
),
),
"}",
),
index: $ =>
prec.left(
1,
seq(
$.expression,
":",
$.expression,
optional(
seq("..", $.expression),
),
),
),
math: $ =>
prec.left(
seq(
$.expression,
$.math_operator,
$.expression,
),
),
math_operator: $ =>
choice("+", "-", "*", "/", "%"),
logic: $ =>
prec.right(
seq(
$.expression,
$.logic_operator,
$.expression,
),
),
logic_operator: $ =>
choice(
"==",
"!=",
"&&",
"||",
">",
"<",
">=",
"<=",
),
assignment: $ =>
seq(
field(
"identifier",
$.identifier,
),
optional(
field(
"type",
$.type_definition,
),
),
field(
"assignment_operator",
$.assignment_operator,
),
field("statement", $.statement),
),
index_assignment: $ =>
seq(
$.index,
$.assignment_operator,
$.statement,
),
assignment_operator: $ =>
prec.right(
choice("=", "+=", "-="),
),
if_else: $ =>
prec.right(
seq(
$.if,
repeat($.else_if),
optional($.else),
),
),
if: $ =>
seq("if", $.expression, $.block),
else_if: $ =>
seq(
"else if",
$.expression,
$.block,
),
else: $ => seq("else", $.block),
match: $ =>
prec.right(
seq(
"match",
$.expression,
repeat1(
seq(
$.expression,
"=>",
$.block,
),
),
),
),
while: $ =>
seq(
"while",
$.expression,
$.block,
),
for: $ =>
seq(
choice("for", "async for"),
$.identifier,
"in",
$.expression,
$.block,
),
identifier_list: $ =>
prec.right(
choice(
seq(
"|",
repeat(
seq(
$.identifier,
optional(","),
),
),
"|",
),
),
),
table: $ =>
prec.right(
seq(
"table",
$.identifier_list,
$.expression,
),
),
return: $ =>
seq("return", $.expression),
use: $ => seq("use", $.string),
type_definition: $ =>
seq("<", $.type, ">"),
type: $ =>
prec.right(
choice(
"any",
"bool",
seq(
"fn",
repeat(
seq(
$.type,
optional(","),
),
),
optional(seq("->", $.type)),
),
"int",
seq("list", $.type),
"map",
"num",
"str",
),
),
function: $ =>
seq(
"|",
repeat(
seq(
$.identifier,
optional(","),
),
),
"|",
$.block,
),
function_call: $ =>
prec.right(
1,
seq(
"(",
$.expression,
optional($._expression_list),
")",
),
),
yield: $ =>
prec.left(
seq(
$.expression,
"->",
"(",
$.expression,
optional($._expression_list),
")",
),
),
},
});

View File

@ -7,11 +7,12 @@
"": {
"name": "tree-sitter-dust",
"version": "1.0.0",
"license": "ISC",
"license": "MIT",
"dependencies": {
"nan": "^2.18.0"
},
"devDependencies": {
"prettier": "3.1.0",
"tree-sitter-cli": "^0.20.8"
}
},
@ -20,6 +21,21 @@
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
},
"node_modules/prettier": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
"integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/tree-sitter-cli": {
"version": "0.20.8",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.20.8.tgz",

View File

@ -18,6 +18,7 @@
"nan": "^2.18.0"
},
"devDependencies": {
"prettier": "3.1.0",
"tree-sitter-cli": "^0.20.8"
},
"main": "bindings/node"

View File

@ -82,10 +82,6 @@
"type": "SYMBOL",
"name": "index_assignment"
},
{
"type": "SYMBOL",
"name": "insert"
},
{
"type": "SYMBOL",
"name": "match"
@ -94,10 +90,6 @@
"type": "SYMBOL",
"name": "return"
},
{
"type": "SYMBOL",
"name": "select"
},
{
"type": "SYMBOL",
"name": "use"
@ -723,7 +715,7 @@
"name": "type",
"content": {
"type": "SYMBOL",
"name": "type"
"name": "type_definition"
}
},
{
@ -954,68 +946,6 @@
}
]
},
"select": {
"type": "PREC_RIGHT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "select"
},
{
"type": "SYMBOL",
"name": "identifier_list"
},
{
"type": "STRING",
"value": "from"
},
{
"type": "SYMBOL",
"name": "expression"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "block"
},
{
"type": "BLANK"
}
]
}
]
}
},
"insert": {
"type": "PREC_RIGHT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "insert"
},
{
"type": "STRING",
"value": "into"
},
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "expression"
}
]
}
},
"identifier_list": {
"type": "PREC_RIGHT",
"value": 0,
@ -1109,7 +1039,7 @@
}
]
},
"type": {
"type_definition": {
"type": "SEQ",
"members": [
{
@ -1117,6 +1047,19 @@
"value": "<"
},
{
"type": "SYMBOL",
"name": "type"
},
{
"type": "STRING",
"value": ">"
}
]
},
"type": {
"type": "PREC_RIGHT",
"value": 0,
"content": {
"type": "CHOICE",
"members": [
{
@ -1127,37 +1070,74 @@
"type": "STRING",
"value": "bool"
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "fn"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "type"
}
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "->"
},
{
"type": "SYMBOL",
"name": "type"
}
]
},
{
"type": "BLANK"
}
]
}
]
},
{
"type": "STRING",
"value": "int"
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "list"
},
{
"type": "SYMBOL",
"name": "type"
}
]
},
{
"type": "STRING",
"value": "map"
},
{
"type": "STRING",
"value": "num"
},
{
"type": "STRING",
"value": "str"
},
{
"type": "STRING",
"value": "table"
}
]
},
{
"type": "STRING",
"value": ">"
}
]
},
"function": {
"type": "SEQ",
@ -1169,43 +1149,12 @@
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "parameter"
}
},
{
"type": "STRING",
"value": "|"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "type"
},
{
"type": "BLANK"
}
]
},
{
"type": "SYMBOL",
"name": "block"
}
]
},
"parameter": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "SYMBOL",
"name": "type"
},
{
"type": "CHOICE",
"members": [
@ -1219,6 +1168,17 @@
]
}
]
}
},
{
"type": "STRING",
"value": "|"
},
{
"type": "SYMBOL",
"name": "block"
}
]
},
"function_call": {
"type": "PREC_RIGHT",

View File

@ -38,7 +38,7 @@
"required": false,
"types": [
{
"type": "type",
"type": "type_definition",
"named": true
}
]
@ -179,11 +179,7 @@
"named": true
},
{
"type": "parameter",
"named": true
},
{
"type": "type",
"type": "identifier",
"named": true
}
]
@ -299,25 +295,6 @@
]
}
},
{
"type": "insert",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "identifier",
"named": true
}
]
}
},
{
"type": "list",
"named": true,
@ -419,25 +396,6 @@
"named": true,
"fields": {}
},
{
"type": "parameter",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "identifier",
"named": true
},
{
"type": "type",
"named": true
}
]
}
},
{
"type": "return",
"named": true,
@ -468,29 +426,6 @@
]
}
},
{
"type": "select",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "block",
"named": true
},
{
"type": "expression",
"named": true
},
{
"type": "identifier_list",
"named": true
}
]
}
},
{
"type": "statement",
"named": true,
@ -523,10 +458,6 @@
"type": "index_assignment",
"named": true
},
{
"type": "insert",
"named": true
},
{
"type": "match",
"named": true
@ -535,10 +466,6 @@
"type": "return",
"named": true
},
{
"type": "select",
"named": true
},
{
"type": "use",
"named": true
@ -572,7 +499,32 @@
{
"type": "type",
"named": true,
"fields": {}
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "type",
"named": true
}
]
}
},
{
"type": "type_definition",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "type",
"named": true
}
]
}
},
{
"type": "use",
@ -806,10 +758,6 @@
"type": "for",
"named": false
},
{
"type": "from",
"named": false
},
{
"type": "identifier",
"named": true
@ -822,10 +770,6 @@
"type": "in",
"named": false
},
{
"type": "insert",
"named": false
},
{
"type": "int",
"named": false
@ -834,10 +778,6 @@
"type": "integer",
"named": true
},
{
"type": "into",
"named": false
},
{
"type": "list",
"named": false
@ -851,11 +791,11 @@
"named": false
},
{
"type": "return",
"type": "num",
"named": false
},
{
"type": "select",
"type": "return",
"named": false
},
{

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `dust -c '(length (from_json input))' -p seaCreatures.json` | 2.9 ± 0.4 | 2.5 | 6.7 | 1.00 |
| `jq 'length' seaCreatures.json` | 36.7 ± 4.4 | 34.7 | 65.0 | 12.59 ± 2.14 |
| `node --eval "require('node:fs').readFile('seaCreatures.json', (err, data)=>{console.log(JSON.parse(data).length)})"` | 241.2 ± 13.3 | 227.7 | 273.2 | 82.63 ± 11.00 |
| `nu -c 'open seaCreatures.json \| length'` | 54.0 ± 3.3 | 50.3 | 69.2 | 18.49 ± 2.51 |
| `dust -c '(length (from_json input))' -p jq_data.json` | 7.9 ± 0.8 | 6.6 | 12.5 | 2.70 ± 0.43 |
| `jq 'length' jq_data.json` | 44.8 ± 0.6 | 43.5 | 47.3 | 15.36 ± 1.87 |
| `node --eval "require('node:fs').readFile('jq_data.json', (err, data)=>{console.log(JSON.parse(data).length)})"` | 245.2 ± 7.1 | 235.4 | 259.7 | 84.00 ± 10.46 |
| `nu -c 'open jq_data.json \| length'` | 65.9 ± 5.0 | 62.0 | 90.5 | 22.57 ± 3.22 |
| `dust -c '(length (from_json input))' -p dielectron.json` | 1079.5 ± 22.7 | 1043.8 | 1121.5 | 369.86 ± 45.46 |
| `jq 'length' dielectron.json` | 1365.0 ± 20.3 | 1318.5 | 1400.1 | 467.67 ± 57.07 |
| `node --eval "require('node:fs').readFile('dielectron.json', (err, data)=>{console.log(JSON.parse(data).length)})"` | 1910.8 ± 47.9 | 1855.9 | 1985.7 | 654.66 ± 80.97 |
| `nu -c 'open dielectron.json \| length'` | 2001.2 ± 65.1 | 1923.2 | 2112.7 | 685.65 ± 85.98 |