1
0

Compare commits

..

No commits in common. "f0635bf330a20bc7fc6e9afebf9d10c68ab3d313" and "8db95b237cd3ecaf2a296982732ae8c3fcdc1412" have entirely different histories.

11 changed files with 9291 additions and 9710 deletions

View File

@ -28,7 +28,7 @@ if (random_boolean) {
} }
``` ```
Dust is an interpreted, strictly typed language with first class functions. It emphasises concurrency by allowing any group of statements to be executed in parallel. Dust includes built-in tooling to import and export data in a variety of formats, including JSON, TOML, YAML and CSV. Dust is an interpreted, dynamically typed language with first class functions. It emphasises concurrency by allowing any group of statements to be executed in parallel. It is *data-oriented*, with extensive tools to manage structured and relational data. Dust includes built-in tooling to import and export data in a variety of formats, including JSON, TOML, YAML and CSV.
<!--toc:start--> <!--toc:start-->
- [Dust](#dust) - [Dust](#dust)
@ -50,10 +50,10 @@ Dust is an interpreted, strictly typed language with first class functions. It e
## Features ## Features
- Simplicity: Dust is designed to be easy to learn. - Simplicity: Dust is designed to be easy to learn.
- Speed: Dust is built on [Tree Sitter] and [Rust] to prioritize performance and correctness. See [Benchmarks] below. - Speed: Dust is built on [Tree Sitter] and [Rust] to prioritize performance and correctness.
- Concurrency: Easily and safely write code that runs in parallel. - Data format: Dust is data-oriented, making it a great language for defining data.
- Safety: Written in safe, stable Rust. - Format conversion: Effortlessly convert between dust and formats like JSON, CSV and TOML.
- Correctness: Type checking makes it easy to write good code that works. - Structured data: Dust can represent data with more than just strings. Lists, maps and tables are easy to make and manage.
## Usage ## Usage

View File

@ -1,12 +1,11 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node; use tree_sitter::Node;
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Type, Value}; use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Assignment { pub struct Assignment {
identifier: Identifier, identifier: Identifier,
r#type: Option<Type>,
operator: AssignmentOperator, operator: AssignmentOperator,
statement: Statement, statement: Statement,
} }
@ -22,21 +21,10 @@ impl AbstractTree for Assignment {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> { fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "assignment", node)?; Error::expect_syntax_node(source, "assignment", node)?;
let identifier_node = node.child_by_field_name("identifier").unwrap(); let identifier_node = node.child(0).unwrap();
let identifier = Identifier::from_syntax_node(source, identifier_node)?; let identifier = Identifier::from_syntax_node(source, identifier_node)?;
let type_node = node.child_by_field_name("type"); let operator_node = node.child(1).unwrap().child(0).unwrap();
let r#type = if let Some(type_node) = type_node {
Some(Type::from_syntax_node(source, type_node)?)
} else {
None
};
let operator_node = node
.child_by_field_name("assignment_operator")
.unwrap()
.child(0)
.unwrap();
let operator = match operator_node.kind() { let operator = match operator_node.kind() {
"=" => AssignmentOperator::Equal, "=" => AssignmentOperator::Equal,
"+=" => AssignmentOperator::PlusEqual, "+=" => AssignmentOperator::PlusEqual,
@ -51,12 +39,11 @@ impl AbstractTree for Assignment {
} }
}; };
let statement_node = node.child_by_field_name("statement").unwrap(); let statement_node = node.child(2).unwrap();
let statement = Statement::from_syntax_node(source, statement_node)?; let statement = Statement::from_syntax_node(source, statement_node)?;
Ok(Assignment { Ok(Assignment {
identifier, identifier,
r#type,
operator, operator,
statement, statement,
}) })
@ -86,28 +73,6 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => value, AssignmentOperator::Equal => value,
}; };
let expected_type = self.r#type.as_ref().unwrap_or(&Type::Any);
match (expected_type, new_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) => {}
(Type::Boolean, _) => return Err(Error::ExpectedBoolean { actual: new_value }),
(Type::Float, _) => return Err(Error::ExpectedFloat { actual: new_value }),
(Type::Function, _) => return Err(Error::ExpectedFunction { actual: new_value }),
(Type::Integer, _) => return Err(Error::ExpectedInteger { actual: new_value }),
(Type::List, _) => return Err(Error::ExpectedList { actual: new_value }),
(Type::Map, _) => return Err(Error::ExpectedMap { actual: new_value }),
(Type::String, _) => return Err(Error::ExpectedString { actual: new_value }),
(Type::Table, _) => return Err(Error::ExpectedTable { actual: new_value }),
}
context.variables_mut()?.insert(key, new_value); context.variables_mut()?.insert(key, new_value);
Ok(Value::Empty) Ok(Value::Empty)

View File

@ -5,7 +5,6 @@ use crate::{AbstractTree, Error, Map, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Type { pub enum Type {
Any,
Boolean, Boolean,
Float, Float,
Function, Function,
@ -20,10 +19,7 @@ impl AbstractTree for Type {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> { fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
Error::expect_syntax_node(source, "type", node)?; Error::expect_syntax_node(source, "type", node)?;
let range_without_punctuation = node.start_byte() + 1..node.end_byte() - 1; let r#type = match &source[node.byte_range()] {
let r#type = match &source[range_without_punctuation] {
"any" => Type::Any,
"bool" => Type::Boolean, "bool" => Type::Boolean,
"float" => Type::Float, "float" => Type::Float,
"fn" => Type::Function, "fn" => Type::Function,
@ -46,6 +42,15 @@ impl AbstractTree for Type {
} }
fn run(&self, _source: &str, _context: &mut Map) -> Result<Value> { fn run(&self, _source: &str, _context: &mut Map) -> Result<Value> {
Ok(Value::Empty) match self {
Type::Boolean => Ok(Value::String("bool".to_string())),
Type::Float => Ok(Value::String("float".to_string())),
Type::Function => Ok(Value::String("fn".to_string())),
Type::Integer => Ok(Value::String("int".to_string())),
Type::List => Ok(Value::String("list".to_string())),
Type::Map => Ok(Value::String("map".to_string())),
Type::String => Ok(Value::String("string".to_string())),
Type::Table => Ok(Value::String("table".to_string())),
}
} }
} }

View File

@ -59,7 +59,7 @@ pub enum Error {
actual: Value, actual: Value,
}, },
ExpectedInteger { ExpectedInt {
actual: Value, actual: Value,
}, },
@ -270,20 +270,24 @@ impl fmt::Display for Error {
"{identifier} expected a minimum of {minimum} arguments, but got {actual}.", "{identifier} expected a minimum of {minimum} arguments, but got {actual}.",
), ),
ExpectedString { actual } => { ExpectedString { actual } => {
write!(f, "Expected a string but got {:?}.", actual) write!(f, "Expected a Value::String, but got {:?}.", actual)
}
ExpectedInteger { actual } => write!(f, "Expected an integer, but got {:?}.", actual),
ExpectedFloat { actual } => write!(f, "Expected a float, but got {:?}.", actual),
ExpectedNumber { actual } => {
write!(f, "Expected a float or integer but got {:?}.", actual)
}
ExpectedNumberOrString { actual } => {
write!(f, "Expected a number or string, but got {:?}.", actual)
} }
ExpectedInt { actual } => write!(f, "Expected a Value::Int, but got {:?}.", actual),
ExpectedFloat { actual } => write!(f, "Expected a Value::Float, but got {:?}.", actual),
ExpectedNumber { actual } => write!(
f,
"Expected a Value::Float or Value::Int, but got {:?}.",
actual
),
ExpectedNumberOrString { actual } => write!(
f,
"Expected a Value::Number or a Value::String, but got {:?}.",
actual
),
ExpectedBoolean { actual } => { ExpectedBoolean { actual } => {
write!(f, "Expected a boolean, but got {:?}.", actual) write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
} }
ExpectedList { actual } => write!(f, "Expected a list, but got {:?}.", actual), ExpectedList { actual } => write!(f, "Expected a Value::List, but got {:?}.", actual),
ExpectedMinLengthList { ExpectedMinLengthList {
minimum_len, minimum_len,
actual_len, actual_len,
@ -296,14 +300,14 @@ impl fmt::Display for Error {
actual, actual,
} => write!( } => write!(
f, f,
"Expected a list of len {}, but got {:?}.", "Expected a Value::List of len {}, but got {:?}.",
expected_len, actual expected_len, actual
), ),
ExpectedEmpty { actual } => write!(f, "Expected an empty value, but got {:?}.", actual), ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
ExpectedMap { actual } => write!(f, "Expected a map, but got {:?}.", actual), ExpectedMap { actual } => write!(f, "Expected a Value::Map, but got {:?}.", actual),
ExpectedTable { actual } => write!(f, "Expected a table, but got {:?}.", actual), ExpectedTable { actual } => write!(f, "Expected a Value::Table, but got {:?}.", actual),
ExpectedFunction { actual } => { ExpectedFunction { actual } => {
write!(f, "Expected function, but got {:?}.", actual) write!(f, "Expected Value::Function, but got {:?}.", actual)
} }
ExpectedCollection { actual } => { ExpectedCollection { actual } => {
write!( write!(

View File

@ -52,9 +52,9 @@ impl Value {
Value::Function(_) => Type::Function, Value::Function(_) => Type::Function,
Value::String(_) => Type::String, Value::String(_) => Type::String,
Value::Float(_) => Type::Float, Value::Float(_) => Type::Float,
Value::Integer(_) => Type::Integer, Value::Integer(_) => todo!(),
Value::Boolean(_) => Type::Boolean, Value::Boolean(_) => todo!(),
Value::Empty => Type::Any, Value::Empty => todo!(),
} }
} }
@ -116,7 +116,7 @@ impl Value {
pub fn as_integer(&self) -> Result<i64> { pub fn as_integer(&self) -> Result<i64> {
match self { match self {
Value::Integer(i) => Ok(*i), Value::Integer(i) => Ok(*i),
value => Err(Error::ExpectedInteger { value => Err(Error::ExpectedInt {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -540,7 +540,7 @@ impl TryFrom<Value> for i64 {
if let Value::Integer(value) = value { if let Value::Integer(value) = value {
Ok(value) Ok(value)
} else { } else {
Err(Error::ExpectedInteger { actual: value }) Err(Error::ExpectedInt { actual: value })
} }
} }
} }

View File

@ -15,24 +15,6 @@ x = y
(expression (expression
(identifier)))))) (identifier))))))
================================================================================
Simple Assignment with Type
================================================================================
x <int> = y
--------------------------------------------------------------------------------
(root
(statement
(assignment
(identifier)
(type)
(assignment_operator)
(statement
(expression
(identifier))))))
================================================================================ ================================================================================
Map Item Assignment Map Item Assignment
================================================================================ ================================================================================

View File

@ -62,7 +62,7 @@ Function Call
Complex Function Complex Function
================================================================================ ================================================================================
|message <str> number <int>| => { |message:str number:int| => {
(output message) (output message)
(output number) (output number)
} }
@ -75,9 +75,9 @@ Complex Function
(value (value
(function (function
(identifier) (identifier)
(type) (type_definition)
(identifier) (identifier)
(type) (type_definition)
(block (block
(statement (statement
(expression (expression

View File

@ -44,7 +44,7 @@ module.exports = grammar({
seq('(', $._expression_kind, ')'), seq('(', $._expression_kind, ')'),
)), )),
_expression_kind: $ => prec.right(choice( _expression_kind: $ => choice(
$.function_call, $.function_call,
$.identifier, $.identifier,
$.index, $.index,
@ -52,7 +52,7 @@ module.exports = grammar({
$.math, $.math,
$.value, $.value,
$.yield, $.yield,
)), ),
_expression_list: $ => repeat1(prec.right(seq( _expression_list: $ => repeat1(prec.right(seq(
$.expression, $.expression,
@ -152,10 +152,9 @@ module.exports = grammar({
), ),
assignment: $ => seq( assignment: $ => seq(
field('identifier', $.identifier), $.identifier,
optional(field('type', $.type)), $.assignment_operator,
field('assignment_operator', $.assignment_operator), $.statement,
field('statement', $.statement),
), ),
index_assignment: $ => seq( index_assignment: $ => seq(
@ -259,10 +258,7 @@ module.exports = grammar({
$.string, $.string,
), ),
type: $ => seq( type_definition: $ => choice(
'<',
choice(
'any',
'bool', 'bool',
'fn', 'fn',
'int', 'int',
@ -271,15 +267,14 @@ module.exports = grammar({
'str', 'str',
'table', 'table',
), ),
'>',
),
function: $ => seq( function: $ => seq(
optional(seq( optional(seq(
'|', '|',
field('parameter', repeat(seq( field('parameter', repeat(seq(
$.identifier, $.identifier,
$.type, ':',
$.type_definition,
optional(',') optional(',')
))), ))),
'|', '|',

View File

@ -154,9 +154,6 @@
} }
}, },
"_expression_kind": { "_expression_kind": {
"type": "PREC_RIGHT",
"value": 0,
"content": {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
{ {
@ -188,7 +185,6 @@
"name": "yield" "name": "yield"
} }
] ]
}
}, },
"_expression_list": { "_expression_list": {
"type": "REPEAT1", "type": "REPEAT1",
@ -708,45 +704,17 @@
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
"type": "FIELD",
"name": "identifier",
"content": {
"type": "SYMBOL", "type": "SYMBOL",
"name": "identifier" "name": "identifier"
}
}, },
{ {
"type": "CHOICE",
"members": [
{
"type": "FIELD",
"name": "type",
"content": {
"type": "SYMBOL",
"name": "type"
}
},
{
"type": "BLANK"
}
]
},
{
"type": "FIELD",
"name": "assignment_operator",
"content": {
"type": "SYMBOL", "type": "SYMBOL",
"name": "assignment_operator" "name": "assignment_operator"
}
}, },
{ {
"type": "FIELD",
"name": "statement",
"content": {
"type": "SYMBOL", "type": "SYMBOL",
"name": "statement" "name": "statement"
} }
}
] ]
}, },
"index_assignment": { "index_assignment": {
@ -1105,20 +1073,9 @@
} }
] ]
}, },
"type": { "type_definition": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "<"
},
{
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
{
"type": "STRING",
"value": "any"
},
{ {
"type": "STRING", "type": "STRING",
"value": "bool" "value": "bool"
@ -1149,12 +1106,6 @@
} }
] ]
}, },
{
"type": "STRING",
"value": ">"
}
]
},
"function": { "function": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@ -1180,9 +1131,13 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "identifier" "name": "identifier"
}, },
{
"type": "STRING",
"value": ":"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "type" "name": "type_definition"
}, },
{ {
"type": "CHOICE", "type": "CHOICE",

View File

@ -2,47 +2,24 @@
{ {
"type": "assignment", "type": "assignment",
"named": true, "named": true,
"fields": { "fields": {},
"assignment_operator": { "children": {
"multiple": false, "multiple": true,
"required": true, "required": true,
"types": [ "types": [
{ {
"type": "assignment_operator", "type": "assignment_operator",
"named": true "named": true
}
]
}, },
"identifier": {
"multiple": false,
"required": true,
"types": [
{ {
"type": "identifier", "type": "identifier",
"named": true "named": true
}
]
}, },
"statement": {
"multiple": false,
"required": true,
"types": [
{ {
"type": "statement", "type": "statement",
"named": true "named": true
} }
] ]
},
"type": {
"multiple": false,
"required": false,
"types": [
{
"type": "type",
"named": true
}
]
}
} }
}, },
{ {
@ -203,12 +180,16 @@
"type": ",", "type": ",",
"named": false "named": false
}, },
{
"type": ":",
"named": false
},
{ {
"type": "identifier", "type": "identifier",
"named": true "named": true
}, },
{ {
"type": "type", "type": "type_definition",
"named": true "named": true
} }
] ]
@ -581,7 +562,7 @@
} }
}, },
{ {
"type": "type", "type": "type_definition",
"named": true, "named": true,
"fields": {} "fields": {}
}, },
@ -781,10 +762,6 @@
"type": "]", "type": "]",
"named": false "named": false
}, },
{
"type": "any",
"named": false
},
{ {
"type": "append", "type": "append",
"named": false "named": false

File diff suppressed because it is too large Load Diff