Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
6484b1b307 | |||
2c3c26a0bc | |||
e5691f8b7e | |||
32b54c402f | |||
69d7b4b1db | |||
2178c67499 | |||
bc1b88a5fa |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
target/
|
target/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
tree_sitter_dust/src/
|
||||||
|
60
README.md
60
README.md
@ -28,7 +28,17 @@ 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 enforces strict type checking to make sure your code is correct. Dust does *not* have a null type.
|
||||||
|
|
||||||
|
```dust
|
||||||
|
fib = |i <int>| <int> {
|
||||||
|
if i <= 1 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
(fib i - 1) + (fib i - 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
<!--toc:start-->
|
<!--toc:start-->
|
||||||
- [Dust](#dust)
|
- [Dust](#dust)
|
||||||
@ -42,9 +52,9 @@ Dust is an interpreted, strictly typed language with first class functions. It e
|
|||||||
- [Lists](#lists)
|
- [Lists](#lists)
|
||||||
- [Maps](#maps)
|
- [Maps](#maps)
|
||||||
- [Loops](#loops)
|
- [Loops](#loops)
|
||||||
- [Tables](#tables)
|
|
||||||
- [Functions](#functions)
|
- [Functions](#functions)
|
||||||
- [Concurrency](#concurrency)
|
- [Concurrency](#concurrency)
|
||||||
|
- [Acknowledgements](#acknowledgements)
|
||||||
<!--toc:end-->
|
<!--toc:end-->
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@ -140,7 +150,6 @@ Variables have two parts: a key and a value. The key is always a string. The val
|
|||||||
- boolean
|
- boolean
|
||||||
- list
|
- list
|
||||||
- map
|
- map
|
||||||
- table
|
|
||||||
- function
|
- function
|
||||||
|
|
||||||
Here are some examples of variables in dust.
|
Here are some examples of variables in dust.
|
||||||
@ -207,56 +216,31 @@ for number in list {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tables
|
An **async for** loop will run the loop operations in parallel using a thread pool.
|
||||||
|
|
||||||
Tables are strict collections, each row must have a value for each column. If a value is "missing" it should be set to an appropriate value for that type. For example, a string can be empty and a number can be set to zero. Dust table declarations consist of a list of column names, which are identifiers enclosed in pointed braces, followed by a list of rows.
|
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
animals = table <name species age> [
|
async for i in [1 2 3 4 5 6 7 8 9 0] {
|
||||||
["rover" "cat" 14]
|
(output i)
|
||||||
["spot" "snake" 9]
|
|
||||||
["bob" "giraffe" 2]
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Querying a table is similar to SQL.
|
|
||||||
|
|
||||||
```dust
|
|
||||||
names = select name from animals
|
|
||||||
youngins = select species from animals {
|
|
||||||
age <= 10
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The keywords `table` and `insert` make sure that all of the memory used to hold the rows is allocated at once, so it is good practice to group your rows together instead of using a call for each row.
|
|
||||||
|
|
||||||
```dust
|
|
||||||
insert into animals [
|
|
||||||
["eliza" "ostrich" 4]
|
|
||||||
["pat" "white rhino" 7]
|
|
||||||
["jim" "walrus" 9]
|
|
||||||
]
|
|
||||||
|
|
||||||
(assert_equal 6 (length animals))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Functions
|
### Functions
|
||||||
|
|
||||||
Functions are first-class values in dust, so they are assigned to variables like any other value.
|
Functions are first-class values in dust, so they are assigned to variables like any other value.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
# This simple function has no arguments.
|
# This simple function has no arguments and no return type.
|
||||||
say_hi = || => {
|
say_hi = || {
|
||||||
(output "hi")
|
(output "hi")
|
||||||
}
|
}
|
||||||
|
|
||||||
# This function has one argument and will return a value.
|
# This function has one argument and will return an integer.
|
||||||
add_one = |number| => {
|
add_one = |number| <int> {
|
||||||
number + 1
|
number + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
(say_hi)
|
(say_hi)
|
||||||
(assert_equal (add_one 3), 4)
|
(assert_equal 4 (add_one 3))
|
||||||
```
|
```
|
||||||
|
|
||||||
You don't need commas when listing arguments and you don't need to add whitespace inside the function body but doing so may make your code easier to read.
|
You don't need commas when listing arguments and you don't need to add whitespace inside the function body but doing so may make your code easier to read.
|
||||||
@ -274,6 +258,8 @@ async {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the final statement in an async block creates a value, the block will return that value just like in a normal block.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
data = async {
|
data = async {
|
||||||
(output "Reading a file...")
|
(output "Reading a file...")
|
||||||
@ -281,7 +267,7 @@ data = async {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
Dust began as a fork of [evalexpr]. Some of the original code is still in place but the project has dramatically changed and no longer uses any of its parsing or interpreting.
|
Dust began as a fork of [evalexpr]. Some of the original code is still in place but the project has dramatically changed and no longer uses any of its parsing or interpreting.
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
cast = (download "https://api.sampleapis.com/futurama/cast")
|
async {
|
||||||
characters = (download "https://api.sampleapis.com/futurama/characters")
|
cast = (download "https://api.sampleapis.com/futurama/cast")
|
||||||
episodes = (download "https://api.sampleapis.com/futurama/episodes")
|
characters = (download "https://api.sampleapis.com/futurama/characters")
|
||||||
|
episodes = (download "https://api.sampleapis.com/futurama/episodes")
|
||||||
|
}
|
||||||
|
|
||||||
cast_len = (length (from_json cast))
|
cast_len = (length (from_json cast))
|
||||||
characters_len = (length (from_json characters))
|
characters_len = (length (from_json characters))
|
||||||
|
53
src/abstract_tree/await.rs
Normal file
53
src/abstract_tree/await.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use rayon::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tree_sitter::Node;
|
||||||
|
|
||||||
|
use crate::{AbstractTree, Expression, Map, Result, Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct Await {
|
||||||
|
expressions: Vec<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AbstractTree for Await {
|
||||||
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
|
debug_assert_eq!("await", node.kind());
|
||||||
|
|
||||||
|
let mut expressions = Vec::new();
|
||||||
|
|
||||||
|
for index in 2..node.child_count() - 1 {
|
||||||
|
let child = node.child(index).unwrap();
|
||||||
|
|
||||||
|
if child.is_named() {
|
||||||
|
let expression = Expression::from_syntax_node(source, child)?;
|
||||||
|
|
||||||
|
expressions.push(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Await { expressions })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
|
let expressions = &self.expressions;
|
||||||
|
|
||||||
|
expressions
|
||||||
|
.into_par_iter()
|
||||||
|
.find_map_first(|expression| {
|
||||||
|
let mut context = context.clone();
|
||||||
|
let value = if let Ok(value) = expression.run(source, &mut context) {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let run_result = match value {
|
||||||
|
Value::Future(block) => block.run(source, &mut context),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(run_result)
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok(Value::Empty))
|
||||||
|
}
|
||||||
|
}
|
@ -326,11 +326,11 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
Value::Map(map) => map.variables()?.len(),
|
Value::Map(map) => map.variables()?.len(),
|
||||||
Value::Table(table) => table.len(),
|
Value::Table(table) => table.len(),
|
||||||
Value::String(string) => string.chars().count(),
|
Value::String(string) => string.chars().count(),
|
||||||
Value::Function(_) => todo!(),
|
_ => {
|
||||||
Value::Float(_) => todo!(),
|
return Err(Error::ExpectedCollection {
|
||||||
Value::Integer(_) => todo!(),
|
actual: value.clone(),
|
||||||
Value::Boolean(_) => todo!(),
|
});
|
||||||
Value::Empty => todo!(),
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Value::Integer(length as i64))
|
Ok(Value::Integer(length as i64))
|
||||||
|
@ -45,8 +45,9 @@ impl AbstractTree for FunctionCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
|
let mut function_context = Map::clone_from(context);
|
||||||
let (name, arguments) = match self {
|
let (name, arguments) = match self {
|
||||||
FunctionCall::BuiltIn(function) => return function.run(source, context),
|
FunctionCall::BuiltIn(function) => return function.run(source, &mut function_context),
|
||||||
FunctionCall::ContextDefined { name, arguments } => (name, arguments),
|
FunctionCall::ContextDefined { name, arguments } => (name, arguments),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ pub struct Select {
|
|||||||
|
|
||||||
impl AbstractTree for Select {
|
impl AbstractTree for Select {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
let child_count = node.child_count();
|
|
||||||
let mut identifiers = Vec::new();
|
let mut identifiers = Vec::new();
|
||||||
|
let identifier_list = node.child(1).unwrap();
|
||||||
|
|
||||||
let identifier_list = node.child(1).unwrap();
|
let identifier_list = node.child(1).unwrap();
|
||||||
|
|
||||||
|
@ -107,6 +107,13 @@ impl AbstractTree for ValueNode {
|
|||||||
relevant_source: source[child.byte_range()].to_string(),
|
relevant_source: source[child.byte_range()].to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
_ => return Err(Error::UnexpectedSyntaxNode {
|
||||||
|
expected:
|
||||||
|
"string, integer, float, boolean, list, table, map, function, future or empty",
|
||||||
|
actual: child.kind(),
|
||||||
|
location: child.start_position(),
|
||||||
|
relevant_source: source[child.byte_range()].to_string(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(value_node)
|
Ok(value_node)
|
||||||
|
@ -30,14 +30,15 @@ pub mod table;
|
|||||||
/// value that can be treated as any other.
|
/// value that can be treated as any other.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
Boolean(bool),
|
||||||
|
Float(f64),
|
||||||
|
Function(Function),
|
||||||
|
Future(Block),
|
||||||
|
Integer(i64),
|
||||||
List(List),
|
List(List),
|
||||||
Map(Map),
|
Map(Map),
|
||||||
Table(Table),
|
|
||||||
Function(Function),
|
|
||||||
String(String),
|
String(String),
|
||||||
Float(f64),
|
Table(Table),
|
||||||
Integer(i64),
|
|
||||||
Boolean(bool),
|
|
||||||
#[default]
|
#[default]
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
@ -411,6 +412,8 @@ impl Ord for Value {
|
|||||||
(Value::Table(_), _) => Ordering::Greater,
|
(Value::Table(_), _) => Ordering::Greater,
|
||||||
(Value::Function(left), Value::Function(right)) => left.cmp(right),
|
(Value::Function(left), Value::Function(right)) => left.cmp(right),
|
||||||
(Value::Function(_), _) => Ordering::Greater,
|
(Value::Function(_), _) => Ordering::Greater,
|
||||||
|
(Value::Future(left), Value::Future(right)) => left.cmp(right),
|
||||||
|
(Value::Future(_), _) => Ordering::Greater,
|
||||||
(Value::Empty, Value::Empty) => Ordering::Equal,
|
(Value::Empty, Value::Empty) => Ordering::Equal,
|
||||||
(Value::Empty, _) => Ordering::Less,
|
(Value::Empty, _) => Ordering::Less,
|
||||||
}
|
}
|
||||||
@ -441,6 +444,7 @@ impl Serialize for Value {
|
|||||||
Value::Map(inner) => inner.serialize(serializer),
|
Value::Map(inner) => inner.serialize(serializer),
|
||||||
Value::Table(inner) => inner.serialize(serializer),
|
Value::Table(inner) => inner.serialize(serializer),
|
||||||
Value::Function(inner) => inner.serialize(serializer),
|
Value::Function(inner) => inner.serialize(serializer),
|
||||||
|
Value::Future(inner) => inner.serialize(serializer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,6 +467,7 @@ impl Display for Value {
|
|||||||
Value::Map(map) => write!(f, "{map}"),
|
Value::Map(map) => write!(f, "{map}"),
|
||||||
Value::Table(table) => write!(f, "{table}"),
|
Value::Table(table) => write!(f, "{table}"),
|
||||||
Value::Function(function) => write!(f, "{function}"),
|
Value::Function(function) => write!(f, "{function}"),
|
||||||
|
Value::Future(block) => write!(f, "{block:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,6 +242,13 @@ impl From<&Value> for Table {
|
|||||||
.insert(vec![Value::Function(function.clone())])
|
.insert(vec![Value::Function(function.clone())])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
table
|
||||||
|
}
|
||||||
|
Value::Future(block) => {
|
||||||
|
let mut table = Table::new(vec!["future".to_string()]);
|
||||||
|
|
||||||
|
table.insert(vec![Value::Future(block.clone())]).unwrap();
|
||||||
|
|
||||||
table
|
table
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
151
src/value/value_type.rs
Normal file
151
src/value/value_type.rs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{value_node::ValueNode, Block, Expression, Function, Identifier, Statement, Value};
|
||||||
|
|
||||||
|
/// The type of a `Value`.
|
||||||
|
#[derive(Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
|
pub enum ValueType {
|
||||||
|
Any,
|
||||||
|
String,
|
||||||
|
Float,
|
||||||
|
Integer,
|
||||||
|
Boolean,
|
||||||
|
List(Vec<Expression>),
|
||||||
|
Empty,
|
||||||
|
Map(BTreeMap<String, Statement>),
|
||||||
|
Table {
|
||||||
|
column_names: Vec<Identifier>,
|
||||||
|
rows: Box<Expression>,
|
||||||
|
},
|
||||||
|
Function(Function),
|
||||||
|
Future(Block),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ValueType {}
|
||||||
|
|
||||||
|
impl PartialEq for ValueType {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(ValueType::Any, _) => true,
|
||||||
|
(_, ValueType::Any) => true,
|
||||||
|
(ValueType::String, ValueType::String) => true,
|
||||||
|
(ValueType::Float, ValueType::Float) => true,
|
||||||
|
(ValueType::Integer, ValueType::Integer) => true,
|
||||||
|
(ValueType::Boolean, ValueType::Boolean) => true,
|
||||||
|
(ValueType::List(left), ValueType::List(right)) => left == right,
|
||||||
|
(ValueType::Empty, ValueType::Empty) => true,
|
||||||
|
(ValueType::Map(left), ValueType::Map(right)) => left == right,
|
||||||
|
(
|
||||||
|
ValueType::Table {
|
||||||
|
column_names: left_columns,
|
||||||
|
rows: left_rows,
|
||||||
|
},
|
||||||
|
ValueType::Table {
|
||||||
|
column_names: right_columns,
|
||||||
|
rows: right_rows,
|
||||||
|
},
|
||||||
|
) => left_columns == right_columns && left_rows == right_rows,
|
||||||
|
(ValueType::Function(left), ValueType::Function(right)) => left == right,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ValueType {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match &self {
|
||||||
|
ValueType::Any => write!(f, "any"),
|
||||||
|
ValueType::String => write!(f, "string"),
|
||||||
|
ValueType::Float => write!(f, "float"),
|
||||||
|
ValueType::Integer => write!(f, "integer"),
|
||||||
|
ValueType::Boolean => write!(f, "boolean"),
|
||||||
|
ValueType::List(list) => {
|
||||||
|
write!(f, "(")?;
|
||||||
|
for (index, item) in list.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{item:?}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
ValueType::Empty => write!(f, "empty"),
|
||||||
|
ValueType::Map(_map) => write!(f, "map"),
|
||||||
|
ValueType::Table {
|
||||||
|
column_names: _,
|
||||||
|
rows: _,
|
||||||
|
} => {
|
||||||
|
write!(f, "table")
|
||||||
|
}
|
||||||
|
ValueType::Function(function) => write!(f, "{function}"),
|
||||||
|
ValueType::Future(_) => write!(f, "future"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ValueType {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Value> for ValueType {
|
||||||
|
fn from(value: &Value) -> Self {
|
||||||
|
match value {
|
||||||
|
Value::String(_) => ValueType::String,
|
||||||
|
Value::Float(_) => ValueType::Float,
|
||||||
|
Value::Integer(_) => ValueType::Integer,
|
||||||
|
Value::Boolean(_) => ValueType::Boolean,
|
||||||
|
Value::Empty => ValueType::Empty,
|
||||||
|
Value::List(list) => {
|
||||||
|
let value_nodes = list
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
.map(|value| Expression::Value(ValueNode::new(value.value_type(), 0, 0)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
ValueType::List(value_nodes)
|
||||||
|
}
|
||||||
|
Value::Map(map) => {
|
||||||
|
let mut value_nodes = BTreeMap::new();
|
||||||
|
|
||||||
|
for (key, value) in map.variables().iter() {
|
||||||
|
let value_type = value.value_type();
|
||||||
|
let value_node = ValueNode::new(value_type, 0, 0);
|
||||||
|
let statement = Statement::Expression(Expression::Value(value_node));
|
||||||
|
|
||||||
|
value_nodes.insert(key.to_string(), statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType::Map(value_nodes)
|
||||||
|
}
|
||||||
|
Value::Table(table) => ValueType::Table {
|
||||||
|
column_names: table
|
||||||
|
.headers()
|
||||||
|
.iter()
|
||||||
|
.map(|column_name| Identifier::new(column_name.clone()))
|
||||||
|
.collect(),
|
||||||
|
rows: Box::new(Expression::Value(ValueNode::new(
|
||||||
|
ValueType::List(Vec::with_capacity(0)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
Value::Function(function) => ValueType::Function(function.clone()),
|
||||||
|
Value::Future(block) => ValueType::Future(block.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&mut Value> for ValueType {
|
||||||
|
fn from(value: &mut Value) -> Self {
|
||||||
|
From::<&Value>::from(value)
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,23 @@
|
|||||||
find = |list function| => {
|
find = |items <list> function <fn>| <any> {
|
||||||
for i in list {
|
for i in items {
|
||||||
if (function i) {
|
if (function i) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
map = |list function| => {
|
map = |items <list> function <fn>| <list> {
|
||||||
new_list = []
|
new_list = []
|
||||||
|
|
||||||
for i in list {
|
for i in items {
|
||||||
new_list += (function i)
|
new_list += (function i)
|
||||||
}
|
}
|
||||||
|
|
||||||
new_list
|
new_list
|
||||||
}
|
}
|
||||||
|
|
||||||
[0 1 2] -> (map |i| => { i - 1})
|
foobar <int> = [0 1 2]
|
||||||
-> (find |i| => { i == 1 })
|
-> (map |i <int>| <int> { i - 1 })
|
||||||
|
-> (find |i <int>| <bool> { i == -1 })
|
||||||
|
|
||||||
|
foobar
|
||||||
|
@ -102,6 +102,13 @@ fn select() {
|
|||||||
evaluate(&file_contents).unwrap();
|
evaluate(&file_contents).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select() {
|
||||||
|
let file_contents = read_to_string("examples/select.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn table() {
|
fn table() {
|
||||||
let file_contents = read_to_string("examples/table.ds").unwrap();
|
let file_contents = read_to_string("examples/table.ds").unwrap();
|
||||||
|
@ -103,7 +103,7 @@ Complex Function Call
|
|||||||
(foobar
|
(foobar
|
||||||
"hi"
|
"hi"
|
||||||
42
|
42
|
||||||
{
|
map {
|
||||||
x = 1
|
x = 1
|
||||||
y = 2
|
y = 2
|
||||||
}
|
}
|
||||||
|
95
tree-sitter-dust/corpus/futures.txt
Normal file
95
tree-sitter-dust/corpus/futures.txt
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
================================================================================
|
||||||
|
Simple Future
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
async { (output 'Whaddup') }
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(future
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(function_call
|
||||||
|
(built_in_function
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(string))))))))))))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Complex Future
|
||||||
|
================================================================================
|
||||||
|
async {
|
||||||
|
if 1 % 2 == 0 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
'foobar'
|
||||||
|
}
|
||||||
|
|
||||||
|
async { 123 }
|
||||||
|
|
||||||
|
'foo'
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(future
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(if_else
|
||||||
|
(if
|
||||||
|
(expression
|
||||||
|
(logic
|
||||||
|
(expression
|
||||||
|
(math
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))
|
||||||
|
(math_operator)
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))))
|
||||||
|
(logic_operator)
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))))
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(boolean))))))
|
||||||
|
(else
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(boolean))))))))
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(string)))))))))
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(future
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))))))))
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(string))))))
|
@ -2,7 +2,7 @@
|
|||||||
Simple Map
|
Simple Map
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
{ answer = 42 }
|
map { answer = 42 }
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -21,10 +21,10 @@ Simple Map
|
|||||||
Nested Maps
|
Nested Maps
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
x = {
|
x = map {
|
||||||
y = {
|
y = map {
|
||||||
foo = 'bar'
|
foo = 'bar'
|
||||||
z = {
|
z = map {
|
||||||
message = 'hiya'
|
message = 'hiya'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ module.exports = grammar({
|
|||||||
$.function,
|
$.function,
|
||||||
$.table,
|
$.table,
|
||||||
$.map,
|
$.map,
|
||||||
|
$.future,
|
||||||
),
|
),
|
||||||
|
|
||||||
integer: $ => token(prec.left(seq(
|
integer: $ => token(prec.left(seq(
|
||||||
@ -91,18 +92,24 @@ module.exports = grammar({
|
|||||||
|
|
||||||
list: $ => seq(
|
list: $ => seq(
|
||||||
'[',
|
'[',
|
||||||
repeat(prec.left(seq($.expression, optional(',')))),
|
repeat(prec.right(seq($.expression, optional(',')))),
|
||||||
']',
|
']',
|
||||||
),
|
),
|
||||||
|
|
||||||
map: $ => seq(
|
map: $ => seq(
|
||||||
|
'map',
|
||||||
|
$.block,
|
||||||
|
),
|
||||||
|
|
||||||
|
future: $ => seq(
|
||||||
|
'async',
|
||||||
|
$.block,
|
||||||
|
),
|
||||||
|
|
||||||
|
await: $ => seq(
|
||||||
|
'await',
|
||||||
'{',
|
'{',
|
||||||
repeat(seq(
|
$._expression_list,
|
||||||
$.identifier,
|
|
||||||
"=",
|
|
||||||
$.statement,
|
|
||||||
optional(',')
|
|
||||||
)),
|
|
||||||
'}',
|
'}',
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -361,4 +368,3 @@ module.exports = grammar({
|
|||||||
'reverse',
|
'reverse',
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,224 +0,0 @@
|
|||||||
#ifndef TREE_SITTER_PARSER_H_
|
|
||||||
#define TREE_SITTER_PARSER_H_
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define ts_builtin_sym_error ((TSSymbol)-1)
|
|
||||||
#define ts_builtin_sym_end 0
|
|
||||||
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
|
|
||||||
|
|
||||||
typedef uint16_t TSStateId;
|
|
||||||
|
|
||||||
#ifndef TREE_SITTER_API_H_
|
|
||||||
typedef uint16_t TSSymbol;
|
|
||||||
typedef uint16_t TSFieldId;
|
|
||||||
typedef struct TSLanguage TSLanguage;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
TSFieldId field_id;
|
|
||||||
uint8_t child_index;
|
|
||||||
bool inherited;
|
|
||||||
} TSFieldMapEntry;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t index;
|
|
||||||
uint16_t length;
|
|
||||||
} TSFieldMapSlice;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool visible;
|
|
||||||
bool named;
|
|
||||||
bool supertype;
|
|
||||||
} TSSymbolMetadata;
|
|
||||||
|
|
||||||
typedef struct TSLexer TSLexer;
|
|
||||||
|
|
||||||
struct TSLexer {
|
|
||||||
int32_t lookahead;
|
|
||||||
TSSymbol result_symbol;
|
|
||||||
void (*advance)(TSLexer *, bool);
|
|
||||||
void (*mark_end)(TSLexer *);
|
|
||||||
uint32_t (*get_column)(TSLexer *);
|
|
||||||
bool (*is_at_included_range_start)(const TSLexer *);
|
|
||||||
bool (*eof)(const TSLexer *);
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
TSParseActionTypeShift,
|
|
||||||
TSParseActionTypeReduce,
|
|
||||||
TSParseActionTypeAccept,
|
|
||||||
TSParseActionTypeRecover,
|
|
||||||
} TSParseActionType;
|
|
||||||
|
|
||||||
typedef union {
|
|
||||||
struct {
|
|
||||||
uint8_t type;
|
|
||||||
TSStateId state;
|
|
||||||
bool extra;
|
|
||||||
bool repetition;
|
|
||||||
} shift;
|
|
||||||
struct {
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t child_count;
|
|
||||||
TSSymbol symbol;
|
|
||||||
int16_t dynamic_precedence;
|
|
||||||
uint16_t production_id;
|
|
||||||
} reduce;
|
|
||||||
uint8_t type;
|
|
||||||
} TSParseAction;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t lex_state;
|
|
||||||
uint16_t external_lex_state;
|
|
||||||
} TSLexMode;
|
|
||||||
|
|
||||||
typedef union {
|
|
||||||
TSParseAction action;
|
|
||||||
struct {
|
|
||||||
uint8_t count;
|
|
||||||
bool reusable;
|
|
||||||
} entry;
|
|
||||||
} TSParseActionEntry;
|
|
||||||
|
|
||||||
struct TSLanguage {
|
|
||||||
uint32_t version;
|
|
||||||
uint32_t symbol_count;
|
|
||||||
uint32_t alias_count;
|
|
||||||
uint32_t token_count;
|
|
||||||
uint32_t external_token_count;
|
|
||||||
uint32_t state_count;
|
|
||||||
uint32_t large_state_count;
|
|
||||||
uint32_t production_id_count;
|
|
||||||
uint32_t field_count;
|
|
||||||
uint16_t max_alias_sequence_length;
|
|
||||||
const uint16_t *parse_table;
|
|
||||||
const uint16_t *small_parse_table;
|
|
||||||
const uint32_t *small_parse_table_map;
|
|
||||||
const TSParseActionEntry *parse_actions;
|
|
||||||
const char * const *symbol_names;
|
|
||||||
const char * const *field_names;
|
|
||||||
const TSFieldMapSlice *field_map_slices;
|
|
||||||
const TSFieldMapEntry *field_map_entries;
|
|
||||||
const TSSymbolMetadata *symbol_metadata;
|
|
||||||
const TSSymbol *public_symbol_map;
|
|
||||||
const uint16_t *alias_map;
|
|
||||||
const TSSymbol *alias_sequences;
|
|
||||||
const TSLexMode *lex_modes;
|
|
||||||
bool (*lex_fn)(TSLexer *, TSStateId);
|
|
||||||
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
|
|
||||||
TSSymbol keyword_capture_token;
|
|
||||||
struct {
|
|
||||||
const bool *states;
|
|
||||||
const TSSymbol *symbol_map;
|
|
||||||
void *(*create)(void);
|
|
||||||
void (*destroy)(void *);
|
|
||||||
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
|
|
||||||
unsigned (*serialize)(void *, char *);
|
|
||||||
void (*deserialize)(void *, const char *, unsigned);
|
|
||||||
} external_scanner;
|
|
||||||
const TSStateId *primary_state_ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Lexer Macros
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define START_LEXER() \
|
|
||||||
bool result = false; \
|
|
||||||
bool skip = false; \
|
|
||||||
bool eof = false; \
|
|
||||||
int32_t lookahead; \
|
|
||||||
goto start; \
|
|
||||||
next_state: \
|
|
||||||
lexer->advance(lexer, skip); \
|
|
||||||
start: \
|
|
||||||
skip = false; \
|
|
||||||
lookahead = lexer->lookahead;
|
|
||||||
|
|
||||||
#define ADVANCE(state_value) \
|
|
||||||
{ \
|
|
||||||
state = state_value; \
|
|
||||||
goto next_state; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SKIP(state_value) \
|
|
||||||
{ \
|
|
||||||
skip = true; \
|
|
||||||
state = state_value; \
|
|
||||||
goto next_state; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ACCEPT_TOKEN(symbol_value) \
|
|
||||||
result = true; \
|
|
||||||
lexer->result_symbol = symbol_value; \
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
|
|
||||||
#define END_STATE() return result;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse Table Macros
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define SMALL_STATE(id) id - LARGE_STATE_COUNT
|
|
||||||
|
|
||||||
#define STATE(id) id
|
|
||||||
|
|
||||||
#define ACTIONS(id) id
|
|
||||||
|
|
||||||
#define SHIFT(state_value) \
|
|
||||||
{{ \
|
|
||||||
.shift = { \
|
|
||||||
.type = TSParseActionTypeShift, \
|
|
||||||
.state = state_value \
|
|
||||||
} \
|
|
||||||
}}
|
|
||||||
|
|
||||||
#define SHIFT_REPEAT(state_value) \
|
|
||||||
{{ \
|
|
||||||
.shift = { \
|
|
||||||
.type = TSParseActionTypeShift, \
|
|
||||||
.state = state_value, \
|
|
||||||
.repetition = true \
|
|
||||||
} \
|
|
||||||
}}
|
|
||||||
|
|
||||||
#define SHIFT_EXTRA() \
|
|
||||||
{{ \
|
|
||||||
.shift = { \
|
|
||||||
.type = TSParseActionTypeShift, \
|
|
||||||
.extra = true \
|
|
||||||
} \
|
|
||||||
}}
|
|
||||||
|
|
||||||
#define REDUCE(symbol_val, child_count_val, ...) \
|
|
||||||
{{ \
|
|
||||||
.reduce = { \
|
|
||||||
.type = TSParseActionTypeReduce, \
|
|
||||||
.symbol = symbol_val, \
|
|
||||||
.child_count = child_count_val, \
|
|
||||||
__VA_ARGS__ \
|
|
||||||
}, \
|
|
||||||
}}
|
|
||||||
|
|
||||||
#define RECOVER() \
|
|
||||||
{{ \
|
|
||||||
.type = TSParseActionTypeRecover \
|
|
||||||
}}
|
|
||||||
|
|
||||||
#define ACCEPT_INPUT() \
|
|
||||||
{{ \
|
|
||||||
.type = TSParseActionTypeAccept \
|
|
||||||
}}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // TREE_SITTER_PARSER_H_
|
|
Loading…
Reference in New Issue
Block a user