Merge branch '0.3.6-std' into 0.3.6
This commit is contained in:
commit
6484b1b307
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -468,7 +468,6 @@ dependencies = [
|
|||||||
"comfy-table",
|
"comfy-table",
|
||||||
"csv",
|
"csv",
|
||||||
"git2",
|
"git2",
|
||||||
"json",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -909,12 +908,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "json"
|
|
||||||
version = "0.12.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kv-log-macro"
|
name = "kv-log-macro"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -2,14 +2,19 @@
|
|||||||
name = "dust-lang"
|
name = "dust-lang"
|
||||||
description = "Data-Oriented Programming Language"
|
description = "Data-Oriented Programming Language"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
repository = "https://github.com/tree-sitter/tree-sitter-dust"
|
repository = "https://git.jeffa.io/jeff/dust.git"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "dust"
|
name = "dust"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
async-std = { version = "1.12.0", features = ["attributes"] }
|
async-std = { version = "1.12.0", features = ["attributes"] }
|
||||||
@ -17,7 +22,6 @@ clap = { version = "4.4.4", features = ["derive"] }
|
|||||||
comfy-table = "7.0.1"
|
comfy-table = "7.0.1"
|
||||||
csv = "1.2.2"
|
csv = "1.2.2"
|
||||||
git2 = "0.18.1"
|
git2 = "0.18.1"
|
||||||
json = "0.12.4"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rayon = "1.8.0"
|
rayon = "1.8.0"
|
||||||
reqwest = { version = "0.11.20", features = ["blocking", "json"] }
|
reqwest = { version = "0.11.20", features = ["blocking", "json"] }
|
||||||
|
211
README.md
211
README.md
@ -1,6 +1,6 @@
|
|||||||
# Dust
|
# Dust
|
||||||
|
|
||||||
Dust is a programming language and interactive shell. Dust can be used as a replacement for a traditional command line shell, as a scripting language and as a data format. Dust is fast, efficient and easy to learn.
|
Dust is a general purpose programming language that emphasises concurrency and correctness.
|
||||||
|
|
||||||
A basic dust program:
|
A basic dust program:
|
||||||
|
|
||||||
@ -17,30 +17,53 @@ async {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Dust is an interpreted, general purpose language with first class functions. It is *data-oriented*, with extensive tools to manage structured and relational data. Dust also includes built-in tooling to import and export data in a variety of formats, including JSON, TOML, YAML and CSV.
|
You can make *any* block, i.e. `{}`, run its statements in parallel by changing it to `async {}`.
|
||||||
|
|
||||||
|
```dust
|
||||||
|
if (random_boolean) {
|
||||||
|
(output "Do something...")
|
||||||
|
} else async {
|
||||||
|
(output "Do something else instead...")
|
||||||
|
(output "And another thing at the same time...")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
|
- [Benchmarks](#benchmarks)
|
||||||
|
- [Implementation](#implementation)
|
||||||
- [The Dust Programming Language](#the-dust-programming-language)
|
- [The Dust Programming Language](#the-dust-programming-language)
|
||||||
- [Declaring Variables](#declaring-variables)
|
- [Declaring Variables](#declaring-variables)
|
||||||
- [Lists](#lists)
|
- [Lists](#lists)
|
||||||
- [Maps](#maps)
|
- [Maps](#maps)
|
||||||
- [Tables](#tables)
|
- [Loops](#loops)
|
||||||
- [Functions](#functions)
|
- [Functions](#functions)
|
||||||
- [Concurrency](#concurrency)
|
- [Concurrency](#concurrency)
|
||||||
- [Implementation](#implementation)
|
- [Acknowledgements](#acknowledgements)
|
||||||
<!--toc:end-->
|
<!--toc:end-->
|
||||||
|
|
||||||
## 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.
|
- Speed: Dust is built on [Tree Sitter] and [Rust] to prioritize performance and correctness. See [Benchmarks] below.
|
||||||
- Data format: Dust is data-oriented, making it a great language for defining data.
|
- Concurrency: Easily and safely write code that runs in parallel.
|
||||||
- Format conversion: Effortlessly convert between dust and formats like JSON, CSV and TOML.
|
- Safety: Written in safe, stable Rust.
|
||||||
- Structured data: Dust can represent data with more than just strings. Lists, maps and tables are easy to make and manage.
|
- Correctness: Type checking makes it easy to write good code that works.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -58,6 +81,61 @@ You must have the default rust toolchain installed and up-to-date. Install [rust
|
|||||||
|
|
||||||
To build from source, clone the repository and build the parser. To do so, enter the `tree-sitter-dust` directory and run `tree-sitter-generate`. In the project root, run `cargo run` to start the shell. To see other command line options, use `cargo run -- --help`.
|
To build from source, clone the repository and build the parser. To do so, enter the `tree-sitter-dust` directory and run `tree-sitter-generate`. In the project root, run `cargo run` to start the shell. To see other command line options, use `cargo run -- --help`.
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Dust is at a very early development stage but performs strongly in preliminary benchmarks. The examples given were tested using [Hyperfine] on a single-core cloud instance with 1024 MB RAM. Each test was run 1000 times. The test script is shown below. Each test asks the program to read a JSON file and count the objects. Dust is a command line shell, programming language and data manipulation tool so three appropriate targets were chosen for comparison: nushell, NodeJS and jq. The programs produced identical output with the exception that NodeJS printed in color.
|
||||||
|
|
||||||
|
For the first test, a file with four entries was used.
|
||||||
|
|
||||||
|
| Command | Mean [ms] | Min [ms] | Max [ms]
|
||||||
|
|:---|---:|---:|---:|
|
||||||
|
| Dust | 3.1 ± 0.5 | 2.4 | 8.4 |
|
||||||
|
| jq | 33.7 ± 2.2 | 30.0 | 61.8 |
|
||||||
|
| NodeJS | 226.4 ± 13.1 | 197.6 | 346.2 |
|
||||||
|
| Nushell | 51.6 ± 3.7 | 45.4 | 104.3 |
|
||||||
|
|
||||||
|
The second set of data is from the GitHub API, it consists of 100 commits from the jq GitHub repo.
|
||||||
|
|
||||||
|
| Command | Mean [ms] | Min [ms] | Max [ms] |
|
||||||
|
|:---|---:|---:|---:|
|
||||||
|
| Dust | 6.8 ± 0.6 | 5.7 | 12.0 | 2.20 ± 0.40 |
|
||||||
|
| jq | 43.3 ± 3.6 | 37.6 | 81.6 | 13.95 ± 2.49 |
|
||||||
|
| NodeJS | 224.9 ± 12.3 | 194.8 | 298.5 |
|
||||||
|
| Nushell | 59.2 ± 5.7 | 49.7 | 125.0 | 19.11 ± 3.55 |
|
||||||
|
|
||||||
|
This data came from CERN, it is a massive file of 100,000 entries.
|
||||||
|
|
||||||
|
| Command | Mean [ms] | Min [ms] | Max [ms] |
|
||||||
|
|:---|---:|---:|---:|
|
||||||
|
| Dust | 1080.8 ± 38.7 | 975.3 | 1326.6 |
|
||||||
|
| jq | 1305.3 ± 64.3 | 1159.7 | 1925.1 |
|
||||||
|
| NodeJS | 1850.5 ± 72.5 | 1641.9 | 2395.1 |
|
||||||
|
| Nushell | 1850.5 ± 86.2 | 1625.5 | 2400.7 |
|
||||||
|
|
||||||
|
The tests were run after 5 warmup runs and the cache was cleared before each run.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
hyperfine \
|
||||||
|
--shell none \
|
||||||
|
--warmup 5 \
|
||||||
|
--prepare "rm -rf /root/.cache" \
|
||||||
|
--runs 1000 \
|
||||||
|
--parameter-list data_path seaCreatures.json,jq_data.json,dielectron.json \
|
||||||
|
--export-markdown test_output.md \
|
||||||
|
"dust -c '(length (from_json input))' -p {data_path}" \
|
||||||
|
"jq 'length' {data_path}" \
|
||||||
|
"node --eval \"require('node:fs').readFile('{data_path}',(err,data)=>{console.log(JSON.parse(data).length)})\"" \
|
||||||
|
"nu -c 'open {data_path} | length'"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
Dust is formally defined as a Tree Sitter grammar in the tree-sitter-dust directory. Tree sitter generates a parser, written in C, from a set of rules defined in JavaScript. Dust itself is a rust binary that calls the C parser using FFI.
|
||||||
|
|
||||||
|
Tests are written in three places: in the Rust library, in Dust as examples and in the Tree Sitter test format. Generally, features are added by implementing and testing the syntax in the tree-sitter-dust repository, then writing library tests to evaluate the new syntax. Implementation tests run the Dust files in the "examples" directory and should be used to demonstrate and verify that features work together.
|
||||||
|
|
||||||
|
Tree Sitter generates a concrete syntax tree, which Dust traverses to create an abstract syntax tree that can run the Dust code. The CST generation is an extra step but it allows easy testing of the parser, defining the language in one file and makes the syntax easy to modify and expand. Because it uses Tree Sitter, developer-friendly features like syntax highlighting and code navigation are already available in any text editor that supports Tree Sitter.
|
||||||
|
|
||||||
## The Dust Programming Language
|
## The Dust Programming Language
|
||||||
|
|
||||||
Dust is easy to learn. Aside from this guide, the best way to learn Dust is to read the examples and tests to get a better idea of what it can do.
|
Dust is easy to learn. Aside from this guide, the best way to learn Dust is to read the examples and tests to get a better idea of what it can do.
|
||||||
@ -72,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.
|
||||||
@ -91,14 +168,14 @@ Note that strings can be wrapped with any kind of quote: single, double or backt
|
|||||||
|
|
||||||
### Lists
|
### Lists
|
||||||
|
|
||||||
Lists are sequential collections. They can be built by grouping values with square brackets. Commas are optional. Values can be indexed by their position using dot notation with an integer. Dust lists are zero-indexed.
|
Lists are sequential collections. They can be built by grouping values with square brackets. Commas are optional. Values can be indexed by their position using a colon `:` followed by an integer. Dust lists are zero-indexed.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
list = [true 41 "Ok"]
|
list = [true 41 "Ok"]
|
||||||
|
|
||||||
(assert_equal list.0 true)
|
(assert_equal list:0 true)
|
||||||
|
|
||||||
the_answer = list.1 + 1
|
the_answer = list:1 + 1
|
||||||
|
|
||||||
(assert_equal the_answer, 42) # You can also use commas when passing values to
|
(assert_equal the_answer, 42) # You can also use commas when passing values to
|
||||||
# a function.
|
# a function.
|
||||||
@ -106,7 +183,7 @@ the_answer = list.1 + 1
|
|||||||
|
|
||||||
### Maps
|
### Maps
|
||||||
|
|
||||||
Maps are flexible collections with arbitrary key-value pairs, similar to JSON objects. A map is created with a pair of curly braces and its entries are variables declared inside those braces. Map contents can be accessed using dot notation.
|
Maps are flexible collections with arbitrary key-value pairs, similar to JSON objects. A map is created with a pair of curly braces and its entries are variables declared inside those braces. Map contents can be accessed using a colon `:`.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
reminder = {
|
reminder = {
|
||||||
@ -114,7 +191,7 @@ reminder = {
|
|||||||
tags = ["groceries", "home"]
|
tags = ["groceries", "home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
(output reminder.message)
|
(output reminder:message)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Loops
|
### Loops
|
||||||
@ -137,111 +214,43 @@ list = [ 1, 2, 3 ]
|
|||||||
for number in list {
|
for number in list {
|
||||||
(output number + 1)
|
(output number + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
# The original list is left unchanged.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To create a new list, use a **transform** loop, which modifies the values into a new list without changing the original.
|
An **async for** loop will run the loop operations in parallel using a thread pool.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
list = [1 2 3]
|
async for i in [1 2 3 4 5 6 7 8 9 0] {
|
||||||
|
(output i)
|
||||||
new_list = transform number in list {
|
|
||||||
number - 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(output new_list)
|
|
||||||
# Output: [ 0 1 2 ]
|
|
||||||
|
|
||||||
(output list)
|
|
||||||
# Output: [ 1 2 3 ]
|
|
||||||
```
|
|
||||||
|
|
||||||
To filter out some of the values in a list, use a **filter** loop.
|
|
||||||
|
|
||||||
```dust
|
|
||||||
list = filter number in [1 2 3] {
|
|
||||||
number >= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
(output list)
|
|
||||||
# Output: [ 2 3 ]
|
|
||||||
```
|
|
||||||
|
|
||||||
A **find** loop will return a single value, the first item that satisfies the predicate.
|
|
||||||
|
|
||||||
```dust
|
|
||||||
found = find number in [1 2 1] {
|
|
||||||
number != 1
|
|
||||||
}
|
|
||||||
|
|
||||||
(output found)
|
|
||||||
# Output: 2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tables
|
|
||||||
|
|
||||||
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
|
|
||||||
animals = table <name species age> [
|
|
||||||
["rover" "cat" 14]
|
|
||||||
["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. The function body is wrapped in single parentheses. To create a function, use the "function" keyword. The function's arguments are identifiers inside of a set of pointed braces and the function body is enclosed in curly braces. To call a fuction, invoke its variable name inside a set of parentheses. 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.
|
Functions are first-class values in dust, so they are assigned to variables like any other value.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
say_hi = function <> {
|
# This simple function has no arguments and no return type.
|
||||||
|
say_hi = || {
|
||||||
(output "hi")
|
(output "hi")
|
||||||
}
|
}
|
||||||
|
|
||||||
add_one = function <number> {
|
# This function has one argument and will return an integer.
|
||||||
(number + 1)
|
add_one = |number| <int> {
|
||||||
|
number + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
(say_hi)
|
(say_hi)
|
||||||
(assert_equal (add_one 3), 4)
|
(assert_equal 4 (add_one 3))
|
||||||
```
|
```
|
||||||
|
|
||||||
This function simply passes the input to the shell's standard output.
|
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.
|
||||||
|
|
||||||
```dust
|
|
||||||
print = function <input> {
|
|
||||||
(output input)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Concurrency
|
### Concurrency
|
||||||
|
|
||||||
As a language written in Rust, Dust features effortless concurrency anywhere in your code.
|
Dust features effortless concurrency anywhere in your code. Any block of code can be made to run its contents asynchronously. Dust's concurrency is written in safe Rust and uses a thread pool whose size depends on the number of cores available.
|
||||||
|
|
||||||
```dust
|
```dust
|
||||||
|
# An async block will run each statement in its own thread.
|
||||||
async {
|
async {
|
||||||
(output (random_integer))
|
(output (random_integer))
|
||||||
(output (random_float))
|
(output (random_float))
|
||||||
@ -249,7 +258,7 @@ async {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In an **async** block, each statement is run in parallel. In this case, we want to read from a file and assign the data to a variable. It doesn't matter which statement finishes first, the last statement in the block will be used as the assigned value. If one of the statements in an **async** block produces an error, the other statements will stop running if they have not already finished.
|
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 {
|
||||||
@ -258,16 +267,12 @@ data = async {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Implementation
|
## Acknowledgements
|
||||||
|
|
||||||
Dust is formally defined as a Tree Sitter grammar in the tree-sitter-dust directory. Tree sitter generates a parser, written in C, from a set of rules defined in Javascript. Dust itself is a rust binary that calls the C parser using FFI.
|
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.
|
||||||
|
|
||||||
Tests are written in three places: in the Rust library, in Dust as examples and in the Tree Sitter test format. Generally, features are added by implementing and testing the syntax in the tree-sitter-dust repository, then writing library tests to evaluate the new syntax. Implementation tests run the Dust files in the "examples" directory and should be used to demonstrate and verify that features work together.
|
|
||||||
|
|
||||||
Tree Sitter generates a concrete syntax tree, which Dust traverses to create an abstract syntax tree that can run the Dust code. The CST generation is an extra step but it allows easy testing of the parser, defining the language in one file and makes the syntax easy to modify and expand. Because it uses Tree Sitter, developer-friendly features like syntax highlighting and code navigation are already available in any text editor that supports Tree Sitter.
|
|
||||||
|
|
||||||
[Tree Sitter]: https://tree-sitter.github.io/tree-sitter/
|
[Tree Sitter]: https://tree-sitter.github.io/tree-sitter/
|
||||||
[Rust]: https://rust-lang.org
|
[Rust]: https://rust-lang.org
|
||||||
[dnf]: https://dnf.readthedocs.io/en/latest/index.html
|
|
||||||
[evalexpr]: https://github.com/ISibboI/evalexpr
|
[evalexpr]: https://github.com/ISibboI/evalexpr
|
||||||
[rustup]: https://rustup.rs
|
[rustup]: https://rustup.rs
|
||||||
|
[Hyperfine]: https://github.com/sharkdp/hyperfine
|
||||||
|
@ -1,33 +1,19 @@
|
|||||||
(output "This will print first.")
|
(output "This will print first.")
|
||||||
(output "This will print second.")
|
|
||||||
|
|
||||||
create_random_numbers = |count| => {
|
create_random_numbers = |count <int>| {
|
||||||
numbers = [];
|
numbers = [];
|
||||||
|
|
||||||
while (length numbers) < count {
|
while (length numbers) < count {
|
||||||
numbers += (random_integer)
|
numbers += (random_integer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(output "Made " + count + " numbers.")
|
||||||
}
|
}
|
||||||
|
|
||||||
do_a_lot = async {
|
async {
|
||||||
(create_random_numbers 1000)
|
(create_random_numbers 1000)
|
||||||
(output "Made 1000 numbers")
|
|
||||||
}
|
|
||||||
|
|
||||||
do_some = async {
|
|
||||||
(create_random_numbers 100)
|
(create_random_numbers 100)
|
||||||
(output "Made 100 numbers")
|
|
||||||
}
|
|
||||||
|
|
||||||
do_a_little = async {
|
|
||||||
(create_random_numbers 10)
|
(create_random_numbers 10)
|
||||||
(output "Made 10 numbers")
|
|
||||||
}
|
|
||||||
|
|
||||||
await {
|
|
||||||
do_a_lot
|
|
||||||
do_some
|
|
||||||
do_a_little
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(output "This will print last.")
|
(output "This will print last.")
|
||||||
|
11
examples/async_download.ds
Normal file
11
examples/async_download.ds
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
async {
|
||||||
|
cast = (download "https://api.sampleapis.com/futurama/cast")
|
||||||
|
characters = (download "https://api.sampleapis.com/futurama/characters")
|
||||||
|
episodes = (download "https://api.sampleapis.com/futurama/episodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
cast_len = (length (from_json cast))
|
||||||
|
characters_len = (length (from_json characters))
|
||||||
|
episodes_len = (length (from_json episodes))
|
||||||
|
|
||||||
|
(output [cast_len, characters_len, episodes_len])
|
@ -1,44 +1,51 @@
|
|||||||
rooms = ['Library' 'Kitchen']
|
all_cards = {
|
||||||
suspects = ['White' 'Green']
|
rooms = ['Library' 'Kitchen' 'Conservatory']
|
||||||
weapons = ['Rope' 'Lead_Pipe']
|
suspects = ['White' 'Green' 'Scarlett']
|
||||||
cards = [rooms suspects weapons]
|
weapons = ['Rope' 'Lead_Pipe' 'Knife']
|
||||||
|
|
||||||
take_turn = |current_room opponent_card| => {
|
|
||||||
(remove_card opponent_card)
|
|
||||||
(make_guess current_room)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_card = |opponent_card| => {
|
is_ready_to_solve = |cards <map>| <bool> {
|
||||||
for card_list in cards {
|
((length cards:suspects) == 1)
|
||||||
removed = remove card from card_list
|
&& ((length cards:rooms) == 1)
|
||||||
card == opponent_card
|
&& ((length cards:weapons) == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type removed) == 'empty'
|
take_turn = |opponent_card <str>, current_room <str>, cards <map>| <map> {
|
||||||
output 'Card not found.'
|
(remove_card opponent_card cards)
|
||||||
|
(make_guess current_room cards)
|
||||||
|
cards
|
||||||
}
|
}
|
||||||
|
|
||||||
make_guess = |current_room| => {
|
remove_card = |opponent_card <str>, cards <map>| {
|
||||||
if ((length suspects) == 1)
|
cards:rooms -= opponent_card
|
||||||
&& ((length rooms) == 1)
|
cards:suspects -= opponent_card
|
||||||
&& ((length weapons) == 1)
|
cards:weapons -= opponent_card
|
||||||
{
|
}
|
||||||
|
|
||||||
|
make_guess = |current_room <str>, cards <map>| {
|
||||||
|
if (is_ready_to_solve cards) {
|
||||||
(output 'It was '
|
(output 'It was '
|
||||||
+ suspects:0
|
+ cards:suspects:0
|
||||||
+ ' in the '
|
+ ' in the '
|
||||||
+ rooms:0
|
+ cards:rooms:0
|
||||||
+ ' with the '
|
+ ' with the '
|
||||||
+ weapons:0
|
+ cards:weapons:0
|
||||||
+ '!')
|
+ '!')
|
||||||
} else {
|
} else {
|
||||||
(output 'I accuse '
|
(output 'I accuse '
|
||||||
+ (random suspects)
|
+ (random cards:suspects)
|
||||||
+ ' in the '
|
+ ' in the '
|
||||||
+ current_room
|
+ current_room
|
||||||
+ ' with the '
|
+ ' with the '
|
||||||
+ (random weapons)
|
+ (random cards:weapons)
|
||||||
+ '!')
|
+ '.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(make_guess 'Library')
|
(take_turn 'Rope' 'Kitchen'
|
||||||
|
(take_turn 'Library' 'Kitchen'
|
||||||
|
(take_turn 'Conservatory' 'Kitchen'
|
||||||
|
(take_turn 'White' 'Kitchen'
|
||||||
|
(take_turn 'Green' 'Kitchen'
|
||||||
|
(take_turn 'Knife' 'Kitchen' all_cards))))))
|
||||||
|
|
||||||
|
9
examples/download.ds
Normal file
9
examples/download.ds
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
cast = (download "https://api.sampleapis.com/futurama/cast")
|
||||||
|
characters = (download "https://api.sampleapis.com/futurama/characters")
|
||||||
|
episodes = (download "https://api.sampleapis.com/futurama/episodes")
|
||||||
|
|
||||||
|
cast_len = (length (from_json cast))
|
||||||
|
characters_len = (length (from_json characters))
|
||||||
|
episodes_len = (length (from_json episodes))
|
||||||
|
|
||||||
|
(output [cast_len, characters_len, episodes_len])
|
@ -1,21 +0,0 @@
|
|||||||
numbers = (from_json input)
|
|
||||||
flip_count = 0
|
|
||||||
checksum = 0
|
|
||||||
|
|
||||||
while numbers.0 != 1 {
|
|
||||||
(reverse numbers 0 numbers.0)
|
|
||||||
|
|
||||||
if flip_count % 2 == 0 {
|
|
||||||
checksum += flip_count
|
|
||||||
} else {
|
|
||||||
checksum -= flip_count
|
|
||||||
}
|
|
||||||
|
|
||||||
checksum += flip_count * 1
|
|
||||||
|
|
||||||
flip_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
(output numbers)
|
|
||||||
(output flip_count)
|
|
||||||
(output checksum)
|
|
@ -1,8 +1,10 @@
|
|||||||
raw_data = (download "https://api.sampleapis.com/futurama/cast")
|
raw_data = (download "https://api.sampleapis.com/futurama/cast")
|
||||||
cast_data = (from_json raw_data)
|
cast_data = (from_json raw_data)
|
||||||
|
|
||||||
names = transform cast_member in cast_data {
|
names = []
|
||||||
cast_member.name
|
|
||||||
|
for cast_member in cast_data {
|
||||||
|
names += cast_member:name
|
||||||
}
|
}
|
||||||
|
|
||||||
(assert_equal "Billy West", names.0)
|
(assert_equal "Billy West", names:0)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
fib = |i| => {
|
fib = |i <int>| <int> {
|
||||||
if i <= 1 {
|
if i <= 1 {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
filter item in (from_json (read 'examples/assets/jq_data.json')) {
|
|
||||||
(length item.commit.committer.name) <= 10
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
list = [1 2 1 3]
|
|
||||||
|
|
||||||
found = find i in list {
|
|
||||||
i == 3
|
|
||||||
}
|
|
||||||
|
|
||||||
(assert_equal 3 found)
|
|
@ -5,13 +5,13 @@ while count <= 15 {
|
|||||||
divides_by_5 = count % 5 == 0
|
divides_by_5 = count % 5 == 0
|
||||||
|
|
||||||
if divides_by_3 && divides_by_5 {
|
if divides_by_3 && divides_by_5 {
|
||||||
output 'fizzbuzz'
|
(output 'fizzbuzz')
|
||||||
} else if divides_by_3 {
|
} else if divides_by_3 {
|
||||||
output 'fizz'
|
(output 'fizz')
|
||||||
} else if divides_by_5 {
|
} else if divides_by_5 {
|
||||||
output 'buzz'
|
(output 'buzz')
|
||||||
} else {
|
} else {
|
||||||
output count
|
(output count)
|
||||||
}
|
}
|
||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
data = (from_json (read 'examples/assets/jq_data.json'))
|
data = (from_json (read 'examples/assets/jq_data.json'))
|
||||||
|
|
||||||
transform commit_data in data {
|
new_data = [];
|
||||||
{
|
|
||||||
|
for commit_data in data {
|
||||||
|
new_data += {
|
||||||
message = commit_data.commit.message
|
message = commit_data.commit.message
|
||||||
name = commit_data.commit.committer.name
|
name = commit_data.commit.committer.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_data
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
numbers = [1, 2, 3]
|
numbers = [1, 2, 3]
|
||||||
|
|
||||||
x = numbers.{0}
|
x = numbers:0
|
||||||
y = numbers.{1}
|
y = numbers:1
|
||||||
z = numbers.{2}
|
z = numbers:2
|
||||||
|
|
||||||
(assert_equal x + y, z)
|
(assert_equal x + y, z)
|
||||||
|
@ -5,7 +5,7 @@ dictionary = {
|
|||||||
|
|
||||||
(output
|
(output
|
||||||
'Dust is '
|
'Dust is '
|
||||||
+ dictionary.dust
|
+ dictionary:dust
|
||||||
+ '! The answer is '
|
+ '! The answer is '
|
||||||
+ dictionary.answer
|
+ dictionary:answer
|
||||||
)
|
)
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
list = [1 2 1 3]
|
|
||||||
|
|
||||||
removed = remove i from list {
|
|
||||||
i == 3
|
|
||||||
}
|
|
||||||
|
|
||||||
(assert_equal 3 removed)
|
|
||||||
(assert_equal [1 2 1] list)
|
|
@ -8,11 +8,11 @@ data = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for creature in sea_creatures {
|
for creature in sea_creatures {
|
||||||
data.creatures += creature.name
|
data:creatures += creature:name
|
||||||
data.total_clams += creature.clams
|
data:total_clams += creature:clams
|
||||||
|
|
||||||
if creature.type == 'dolphin' {
|
if creature:type == 'dolphin' {
|
||||||
data.dolphin_clams += creature.clams
|
data:dolphin_clams += creature:clams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ test_table = table |text bool| [
|
|||||||
["a", true]
|
["a", true]
|
||||||
]
|
]
|
||||||
|
|
||||||
test_select = select |text bool| from my_table
|
test_select = select |text bool| from my_table;
|
||||||
|
|
||||||
(assert_equal test_select, test_table)
|
(assert_equal test_select, test_table)
|
||||||
|
|
||||||
test_table = table |text number bool| [
|
test_table = table |number bool| [
|
||||||
[1, true]
|
[1, true]
|
||||||
[3, true]
|
[3, true]
|
||||||
]
|
]
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
data = (from_json (read "examples/assets/jq_data.json"))
|
|
||||||
|
|
||||||
transform item in data {
|
|
||||||
item:commit:committer:name
|
|
||||||
}
|
|
15
examples/yield.ds
Normal file
15
examples/yield.ds
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
1 -> (output)
|
||||||
|
|
||||||
|
add_one = |numbers <list>| <list> {
|
||||||
|
new_numbers = []
|
||||||
|
|
||||||
|
for number in numbers {
|
||||||
|
new_numbers += number + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
new_numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
foo = [1, 2, 3] -> (add_one)
|
||||||
|
|
||||||
|
(assert_equal [2 3 4] foo)
|
@ -1,11 +1,12 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Value};
|
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Type, 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,
|
||||||
}
|
}
|
||||||
@ -19,10 +20,23 @@ pub enum AssignmentOperator {
|
|||||||
|
|
||||||
impl AbstractTree for Assignment {
|
impl AbstractTree for Assignment {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
let identifier_node = node.child(0).unwrap();
|
Error::expect_syntax_node(source, "assignment", node)?;
|
||||||
|
|
||||||
|
let identifier_node = node.child_by_field_name("identifier").unwrap();
|
||||||
let identifier = Identifier::from_syntax_node(source, identifier_node)?;
|
let identifier = Identifier::from_syntax_node(source, identifier_node)?;
|
||||||
|
|
||||||
let operator_node = node.child(1).unwrap().child(0).unwrap();
|
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)?)
|
||||||
|
} 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,
|
||||||
@ -37,42 +51,46 @@ impl AbstractTree for Assignment {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let statement_node = node.child(2).unwrap();
|
let statement_node = node.child_by_field_name("statement").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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
let key = self.identifier.inner().clone();
|
let key = self.identifier.inner();
|
||||||
let value = self.statement.run(source, context)?;
|
let value = self.statement.run(source, context)?;
|
||||||
let mut context = context.variables_mut();
|
|
||||||
|
|
||||||
let new_value = match self.operator {
|
let new_value = match self.operator {
|
||||||
AssignmentOperator::PlusEqual => {
|
AssignmentOperator::PlusEqual => {
|
||||||
if let Some(mut previous_value) = context.get(&key).cloned() {
|
if let Some(mut previous_value) = context.variables()?.get(key).cloned() {
|
||||||
previous_value += value;
|
previous_value += value;
|
||||||
previous_value
|
previous_value
|
||||||
} else {
|
} else {
|
||||||
Value::Empty
|
return Err(Error::VariableIdentifierNotFound(key.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssignmentOperator::MinusEqual => {
|
AssignmentOperator::MinusEqual => {
|
||||||
if let Some(mut previous_value) = context.get(&key).cloned() {
|
if let Some(mut previous_value) = context.variables()?.get(key).cloned() {
|
||||||
previous_value -= value;
|
previous_value -= value;
|
||||||
previous_value
|
previous_value
|
||||||
} else {
|
} else {
|
||||||
Value::Empty
|
return Err(Error::VariableIdentifierNotFound(key.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssignmentOperator::Equal => value,
|
AssignmentOperator::Equal => value,
|
||||||
};
|
};
|
||||||
|
|
||||||
context.insert(key, new_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)
|
Ok(Value::Empty)
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,84 @@
|
|||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Result, Statement};
|
use crate::{AbstractTree, Error, Map, Result, Statement, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
|
is_async: bool,
|
||||||
statements: Vec<Statement>,
|
statements: Vec<Statement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Block {
|
impl AbstractTree for Block {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
debug_assert_eq!("block", node.kind());
|
Error::expect_syntax_node(source, "block", node)?;
|
||||||
|
|
||||||
let statement_count = node.child_count();
|
let first_child = node.child(0).unwrap();
|
||||||
|
let is_async = first_child.kind() == "async";
|
||||||
|
|
||||||
|
let statement_count = if is_async {
|
||||||
|
node.child_count() - 3
|
||||||
|
} else {
|
||||||
|
node.child_count() - 2
|
||||||
|
};
|
||||||
let mut statements = Vec::with_capacity(statement_count);
|
let mut statements = Vec::with_capacity(statement_count);
|
||||||
|
|
||||||
for index in 0..statement_count {
|
for index in 1..node.child_count() - 1 {
|
||||||
let child_node = node.child(index).unwrap();
|
let child_node = node.child(index).unwrap();
|
||||||
|
|
||||||
if child_node.kind() == "statement" {
|
if child_node.is_named() {
|
||||||
let statement = Statement::from_syntax_node(source, child_node)?;
|
let statement = Statement::from_syntax_node(source, child_node)?;
|
||||||
statements.push(statement);
|
statements.push(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Block { statements })
|
Ok(Block {
|
||||||
|
is_async,
|
||||||
|
statements,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut crate::Map) -> crate::Result<crate::Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
for statement in &self.statements[0..self.statements.len() - 1] {
|
if self.is_async {
|
||||||
statement.run(source, context)?;
|
let statements = &self.statements;
|
||||||
|
let final_result = RwLock::new(Ok(Value::Empty));
|
||||||
|
|
||||||
|
statements
|
||||||
|
.into_par_iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map_first(|(index, statement)| {
|
||||||
|
if let Statement::Return(expression) = statement {
|
||||||
|
return Some(expression.run(source, &mut context.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_statement = self.statements.last().unwrap();
|
let result = statement.run(source, &mut context.clone());
|
||||||
let final_value = final_statement.run(source, context)?;
|
|
||||||
|
|
||||||
Ok(final_value)
|
if result.is_err() {
|
||||||
|
Some(result)
|
||||||
|
} else if index == statements.len() - 1 {
|
||||||
|
let _ = final_result.write().unwrap().as_mut().map(|_| result);
|
||||||
|
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(final_result.into_inner().unwrap())
|
||||||
|
} else {
|
||||||
|
let mut prev_result = None;
|
||||||
|
|
||||||
|
for statement in &self.statements {
|
||||||
|
if let Statement::Return(expression) = statement {
|
||||||
|
return expression.run(source, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_result = Some(statement.run(source, context));
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_result.unwrap_or(Ok(Value::Empty))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use reqwest::blocking::get;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Error, Expression, List, Map, Result, Table, Value, ValueType};
|
use crate::{AbstractTree, Error, Expression, List, Map, Result, Table, Type, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum BuiltInFunction {
|
pub enum BuiltInFunction {
|
||||||
@ -19,6 +19,7 @@ pub enum BuiltInFunction {
|
|||||||
Assert(Vec<Expression>),
|
Assert(Vec<Expression>),
|
||||||
AssertEqual(Vec<Expression>),
|
AssertEqual(Vec<Expression>),
|
||||||
Download(Expression),
|
Download(Expression),
|
||||||
|
Context,
|
||||||
Help(Option<Expression>),
|
Help(Option<Expression>),
|
||||||
Length(Expression),
|
Length(Expression),
|
||||||
Output(Vec<Expression>),
|
Output(Vec<Expression>),
|
||||||
@ -30,7 +31,7 @@ pub enum BuiltInFunction {
|
|||||||
Append(Vec<Expression>),
|
Append(Vec<Expression>),
|
||||||
Metadata(Expression),
|
Metadata(Expression),
|
||||||
Move(Vec<Expression>),
|
Move(Vec<Expression>),
|
||||||
Read(Expression),
|
Read(Option<Expression>),
|
||||||
Remove(Expression),
|
Remove(Expression),
|
||||||
Write(Vec<Expression>),
|
Write(Vec<Expression>),
|
||||||
|
|
||||||
@ -93,6 +94,7 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
|
|
||||||
BuiltInFunction::AssertEqual(expressions)
|
BuiltInFunction::AssertEqual(expressions)
|
||||||
}
|
}
|
||||||
|
"context" => BuiltInFunction::Context,
|
||||||
"download" => {
|
"download" => {
|
||||||
let expression_node = node.child(1).unwrap();
|
let expression_node = node.child(1).unwrap();
|
||||||
let expression = Expression::from_syntax_node(source, expression_node)?;
|
let expression = Expression::from_syntax_node(source, expression_node)?;
|
||||||
@ -153,8 +155,11 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
BuiltInFunction::Move(expressions)
|
BuiltInFunction::Move(expressions)
|
||||||
}
|
}
|
||||||
"read" => {
|
"read" => {
|
||||||
let expression_node = node.child(1).unwrap();
|
let expression = if let Some(node) = node.child(1) {
|
||||||
let expression = Expression::from_syntax_node(source, expression_node)?;
|
Some(Expression::from_syntax_node(source, node)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
BuiltInFunction::Read(expression)
|
BuiltInFunction::Read(expression)
|
||||||
}
|
}
|
||||||
@ -306,6 +311,7 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
|
|
||||||
Ok(Value::Empty)
|
Ok(Value::Empty)
|
||||||
}
|
}
|
||||||
|
BuiltInFunction::Context => Ok(Value::Map(context.clone())),
|
||||||
BuiltInFunction::Download(expression) => {
|
BuiltInFunction::Download(expression) => {
|
||||||
let value = expression.run(source, context)?;
|
let value = expression.run(source, context)?;
|
||||||
let url = value.as_string()?;
|
let url = value.as_string()?;
|
||||||
@ -317,7 +323,7 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
let value = expression.run(source, context)?;
|
let value = expression.run(source, context)?;
|
||||||
let length = match value {
|
let length = match value {
|
||||||
Value::List(list) => list.items().len(),
|
Value::List(list) => list.items().len(),
|
||||||
Value::Map(map) => map.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(),
|
||||||
_ => {
|
_ => {
|
||||||
@ -361,9 +367,9 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
BuiltInFunction::Type(expression) => {
|
BuiltInFunction::Type(expression) => {
|
||||||
let run_expression = expression.run(source, context);
|
let run_expression = expression.run(source, context);
|
||||||
let value_type = if let Ok(value) = run_expression {
|
let value_type = if let Ok(value) = run_expression {
|
||||||
value.value_type()
|
value.r#type()
|
||||||
} else if let Err(Error::VariableIdentifierNotFound(_)) = run_expression {
|
} else if let Err(Error::VariableIdentifierNotFound(_)) = run_expression {
|
||||||
ValueType::Empty
|
Type::Any
|
||||||
} else {
|
} else {
|
||||||
return run_expression;
|
return run_expression;
|
||||||
};
|
};
|
||||||
@ -405,7 +411,7 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
let metadata_output = Map::new();
|
let metadata_output = Map::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut metadata_variables = metadata_output.variables_mut();
|
let mut metadata_variables = metadata_output.variables_mut()?;
|
||||||
|
|
||||||
metadata_variables.insert("type".to_string(), Value::String(file_type));
|
metadata_variables.insert("type".to_string(), Value::String(file_type));
|
||||||
metadata_variables.insert("size".to_string(), Value::Integer(size));
|
metadata_variables.insert("size".to_string(), Value::Integer(size));
|
||||||
@ -428,8 +434,13 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
Ok(Value::Empty)
|
Ok(Value::Empty)
|
||||||
}
|
}
|
||||||
BuiltInFunction::Read(expression) => {
|
BuiltInFunction::Read(expression) => {
|
||||||
|
let path = if let Some(expression) = expression {
|
||||||
let path_value = expression.run(source, context)?;
|
let path_value = expression.run(source, context)?;
|
||||||
let path = PathBuf::from(path_value.as_string()?);
|
|
||||||
|
PathBuf::from(path_value.as_string()?)
|
||||||
|
} else {
|
||||||
|
PathBuf::from(".")
|
||||||
|
};
|
||||||
let content = if path.is_dir() {
|
let content = if path.is_dir() {
|
||||||
let dir = read_dir(&path)?;
|
let dir = read_dir(&path)?;
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
@ -572,11 +583,8 @@ impl AbstractTree for BuiltInFunction {
|
|||||||
let value = expressions[0].run(source, context)?;
|
let value = expressions[0].run(source, context)?;
|
||||||
let list = value.as_list()?.items();
|
let list = value.as_list()?.items();
|
||||||
|
|
||||||
if list.len() < 2 {
|
if list.len() == 1 {
|
||||||
return Err(Error::ExpectedMinLengthList {
|
return Ok(list.first().cloned().unwrap());
|
||||||
minimum_len: 2,
|
|
||||||
actual_len: list.len(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = 0..list.len();
|
let range = 0..list.len();
|
||||||
|
@ -3,7 +3,7 @@ use tree_sitter::Node;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
value_node::ValueNode, AbstractTree, BuiltInFunction, Error, Identifier, Index, Map, Result,
|
value_node::ValueNode, AbstractTree, BuiltInFunction, Error, Identifier, Index, Map, Result,
|
||||||
Sublist, Value,
|
Value, Yield,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{function_call::FunctionCall, logic::Logic, math::Math};
|
use super::{function_call::FunctionCall, logic::Logic, math::Math};
|
||||||
@ -12,63 +12,58 @@ use super::{function_call::FunctionCall, logic::Logic, math::Math};
|
|||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Value(ValueNode),
|
Value(ValueNode),
|
||||||
Identifier(Identifier),
|
Identifier(Identifier),
|
||||||
Sublist(Box<Sublist>),
|
|
||||||
Index(Box<Index>),
|
Index(Box<Index>),
|
||||||
Math(Box<Math>),
|
Math(Box<Math>),
|
||||||
Logic(Box<Logic>),
|
Logic(Box<Logic>),
|
||||||
FunctionCall(FunctionCall),
|
FunctionCall(Box<FunctionCall>),
|
||||||
Tool(Box<BuiltInFunction>),
|
Tool(Box<BuiltInFunction>),
|
||||||
|
Yield(Box<Yield>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Expression {
|
impl AbstractTree for Expression {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
debug_assert_eq!("expression", node.kind());
|
Error::expect_syntax_node(source, "expression", node)?;
|
||||||
|
|
||||||
|
let child = if node.child(0).unwrap().is_named() {
|
||||||
|
node.child(0).unwrap()
|
||||||
|
} else {
|
||||||
|
node.child(1).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
for index in 0..node.child_count() {
|
|
||||||
let child = node.child(index).unwrap();
|
|
||||||
let expression = match child.kind() {
|
let expression = match child.kind() {
|
||||||
"value" => Expression::Value(ValueNode::from_syntax_node(source, child)?),
|
"value" => Expression::Value(ValueNode::from_syntax_node(source, child)?),
|
||||||
"identifier" => {
|
"identifier" => Expression::Identifier(Identifier::from_syntax_node(source, child)?),
|
||||||
Expression::Identifier(Identifier::from_syntax_node(source, child)?)
|
|
||||||
}
|
|
||||||
"sublist" => {
|
|
||||||
Expression::Sublist(Box::new(Sublist::from_syntax_node(source, child)?))
|
|
||||||
}
|
|
||||||
"index" => Expression::Index(Box::new(Index::from_syntax_node(source, child)?)),
|
"index" => Expression::Index(Box::new(Index::from_syntax_node(source, child)?)),
|
||||||
"math" => Expression::Math(Box::new(Math::from_syntax_node(source, child)?)),
|
"math" => Expression::Math(Box::new(Math::from_syntax_node(source, child)?)),
|
||||||
"logic" => Expression::Logic(Box::new(Logic::from_syntax_node(source, child)?)),
|
"logic" => Expression::Logic(Box::new(Logic::from_syntax_node(source, child)?)),
|
||||||
"function_call" => {
|
"function_call" => {
|
||||||
Expression::FunctionCall(FunctionCall::from_syntax_node(source, child)?)
|
Expression::FunctionCall(Box::new(FunctionCall::from_syntax_node(source, child)?))
|
||||||
}
|
}
|
||||||
"tool" => {
|
"tool" => Expression::Tool(Box::new(BuiltInFunction::from_syntax_node(source, child)?)),
|
||||||
Expression::Tool(Box::new(BuiltInFunction::from_syntax_node(source, child)?))
|
"yield" => Expression::Yield(Box::new(Yield::from_syntax_node(source, child)?)),
|
||||||
}
|
_ => {
|
||||||
_ => continue,
|
return Err(Error::UnexpectedSyntaxNode {
|
||||||
};
|
expected: "value, identifier, index, math, logic, function_call or yield",
|
||||||
|
|
||||||
return Ok(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = node.child(0).unwrap();
|
|
||||||
|
|
||||||
Err(Error::UnexpectedSyntaxNode {
|
|
||||||
expected: "value, identifier, sublist, index, math or function_call",
|
|
||||||
actual: child.kind(),
|
actual: child.kind(),
|
||||||
location: child.start_position(),
|
location: child.start_position(),
|
||||||
relevant_source: source[child.byte_range()].to_string(),
|
relevant_source: source[child.byte_range()].to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(expression)
|
||||||
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
match self {
|
match self {
|
||||||
Expression::Value(value_node) => value_node.run(source, context),
|
Expression::Value(value_node) => value_node.run(source, context),
|
||||||
Expression::Identifier(identifier) => identifier.run(source, context),
|
Expression::Identifier(identifier) => identifier.run(source, context),
|
||||||
Expression::Sublist(sublist) => sublist.run(source, context),
|
|
||||||
Expression::Math(math) => math.run(source, context),
|
Expression::Math(math) => math.run(source, context),
|
||||||
Expression::Logic(logic) => logic.run(source, context),
|
Expression::Logic(logic) => logic.run(source, context),
|
||||||
Expression::FunctionCall(function_call) => function_call.run(source, context),
|
Expression::FunctionCall(function_call) => function_call.run(source, context),
|
||||||
Expression::Tool(tool) => tool.run(source, context),
|
Expression::Tool(tool) => tool.run(source, context),
|
||||||
Expression::Index(index) => index.run(source, context),
|
Expression::Index(index) => index.run(source, context),
|
||||||
|
Expression::Yield(r#yield) => r#yield.run(source, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ impl AbstractTree for Filter {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let item_id_node = node.child(1).unwrap();
|
let item_id_node = node.child_by_field_name("item_id").unwrap();
|
||||||
let item_id = Identifier::from_syntax_node(source, item_id_node)?;
|
let item_id = Identifier::from_syntax_node(source, item_id_node)?;
|
||||||
|
|
||||||
let collection_node = node.child(3).unwrap();
|
let collection_node = node.child_by_field_name("collection").unwrap();
|
||||||
let collection = Expression::from_syntax_node(source, collection_node)?;
|
let collection = Expression::from_syntax_node(source, collection_node)?;
|
||||||
|
|
||||||
let predicate_node = node.child(5).unwrap();
|
let predicate_node = node.child_by_field_name("predicate").unwrap();
|
||||||
let predicate = Block::from_syntax_node(source, predicate_node)?;
|
let predicate = Block::from_syntax_node(source, predicate_node)?;
|
||||||
|
|
||||||
Ok(Filter {
|
Ok(Filter {
|
||||||
@ -45,6 +45,7 @@ impl AbstractTree for Filter {
|
|||||||
Some(expression) => Some(expression.run(source, context)?.as_integer()? as usize),
|
Some(expression) => Some(expression.run(source, context)?.as_integer()? as usize),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
let loop_context = Map::clone_from(context)?;
|
||||||
|
|
||||||
values.par_iter().try_for_each(|value| {
|
values.par_iter().try_for_each(|value| {
|
||||||
if let Some(max) = count {
|
if let Some(max) = count {
|
||||||
@ -53,11 +54,16 @@ impl AbstractTree for Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut context = Map::new();
|
let mut iter_context = loop_context.clone();
|
||||||
|
|
||||||
context.variables_mut().insert(key.clone(), value.clone());
|
iter_context
|
||||||
|
.variables_mut()?
|
||||||
|
.insert(key.clone(), value.clone());
|
||||||
|
|
||||||
let should_include = self.predicate.run(source, &mut context)?.as_boolean()?;
|
let should_include = self
|
||||||
|
.predicate
|
||||||
|
.run(source, &mut iter_context)?
|
||||||
|
.as_boolean()?;
|
||||||
|
|
||||||
if should_include {
|
if should_include {
|
||||||
new_values.items_mut().push(value.clone());
|
new_values.items_mut().push(value.clone());
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Block, Expression, Identifier, Map, Result, Value};
|
use crate::{AbstractTree, Block, Error, Expression, Identifier, Map, Result, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Find {
|
pub struct Find {
|
||||||
@ -29,21 +30,41 @@ impl AbstractTree for Find {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
let value = self.expression.run(source, context)?;
|
let expression_run = self.expression.run(source, context)?;
|
||||||
let values = value.as_list()?.items();
|
let list = expression_run.as_list()?.items();
|
||||||
let key = self.identifier.inner();
|
let key = self.identifier.inner();
|
||||||
let mut context = context.clone();
|
|
||||||
|
|
||||||
for value in values.iter() {
|
let find_result = list.par_iter().find_map_first(|value| {
|
||||||
context.variables_mut().insert(key.clone(), value.clone());
|
let loop_context = Map::clone_from(context).unwrap();
|
||||||
|
|
||||||
let should_return = self.item.run(source, &mut context)?.as_boolean()?;
|
loop_context
|
||||||
|
.variables_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert(key.clone(), (*value).clone());
|
||||||
|
|
||||||
|
let run_result = self.item.run(source, &mut loop_context.clone());
|
||||||
|
|
||||||
|
if let Ok(run_result_value) = run_result {
|
||||||
|
if let Ok(should_return) = run_result_value.as_boolean() {
|
||||||
if should_return {
|
if should_return {
|
||||||
return Ok(value.clone());
|
Some(Ok(value.clone()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Some(Err(Error::ExpectedBoolean {
|
||||||
|
actual: value.clone(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Some(run_result)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(result) = find_result {
|
||||||
|
result
|
||||||
|
} else {
|
||||||
Ok(Value::Empty)
|
Ok(Value::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -7,19 +7,22 @@ use crate::{AbstractTree, Block, Error, Expression, Identifier, Map, Result, Val
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct For {
|
pub struct For {
|
||||||
is_async: bool,
|
is_async: bool,
|
||||||
identifier: Identifier,
|
item_id: Identifier,
|
||||||
expression: Expression,
|
collection: Expression,
|
||||||
item: Block,
|
block: Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for For {
|
impl AbstractTree for For {
|
||||||
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, "for", node)?;
|
||||||
|
|
||||||
let for_node = node.child(0).unwrap();
|
let for_node = node.child(0).unwrap();
|
||||||
let is_async = match for_node.kind() {
|
let is_async = match for_node.kind() {
|
||||||
"for" => false,
|
"for" => false,
|
||||||
|
"async for" => true,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnexpectedSyntaxNode {
|
return Err(Error::UnexpectedSyntaxNode {
|
||||||
expected: "for",
|
expected: "for or async for",
|
||||||
actual: for_node.kind(),
|
actual: for_node.kind(),
|
||||||
location: for_node.start_position(),
|
location: for_node.start_position(),
|
||||||
relevant_source: source[for_node.byte_range()].to_string(),
|
relevant_source: source[for_node.byte_range()].to_string(),
|
||||||
@ -38,32 +41,36 @@ impl AbstractTree for For {
|
|||||||
|
|
||||||
Ok(For {
|
Ok(For {
|
||||||
is_async,
|
is_async,
|
||||||
identifier,
|
item_id: identifier,
|
||||||
expression,
|
collection: expression,
|
||||||
item,
|
block: item,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
let expression_run = self.expression.run(source, context)?;
|
let expression_run = self.collection.run(source, context)?;
|
||||||
let values = expression_run.as_list()?.items();
|
let values = expression_run.as_list()?.items();
|
||||||
let key = self.identifier.inner();
|
let key = self.item_id.inner();
|
||||||
|
|
||||||
if self.is_async {
|
if self.is_async {
|
||||||
values.par_iter().try_for_each(|value| {
|
values.par_iter().try_for_each(|value| {
|
||||||
let mut iter_context = Map::new();
|
let mut iter_context = Map::clone_from(context)?;
|
||||||
|
|
||||||
iter_context
|
iter_context
|
||||||
.variables_mut()
|
.variables_mut()?
|
||||||
.insert(key.clone(), value.clone());
|
.insert(key.clone(), value.clone());
|
||||||
|
|
||||||
self.item.run(source, &mut iter_context).map(|_value| ())
|
self.block.run(source, &mut iter_context).map(|_value| ())
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
for value in values.iter() {
|
let loop_context = Map::clone_from(context)?;
|
||||||
context.variables_mut().insert(key.clone(), value.clone());
|
|
||||||
|
|
||||||
self.item.run(source, context)?;
|
for value in values.iter() {
|
||||||
|
loop_context
|
||||||
|
.variables_mut()?
|
||||||
|
.insert(key.clone(), value.clone());
|
||||||
|
|
||||||
|
self.block.run(source, &mut loop_context.clone())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@ use tree_sitter::Node;
|
|||||||
|
|
||||||
use crate::{AbstractTree, BuiltInFunction, Error, Map, Result, Value};
|
use crate::{AbstractTree, BuiltInFunction, Error, Map, Result, Value};
|
||||||
|
|
||||||
use super::{expression::Expression, identifier::Identifier};
|
use super::expression::Expression;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum FunctionCall {
|
pub enum FunctionCall {
|
||||||
BuiltIn(Box<BuiltInFunction>),
|
BuiltIn(Box<BuiltInFunction>),
|
||||||
ContextDefined {
|
ContextDefined {
|
||||||
name: Identifier,
|
name: Expression,
|
||||||
arguments: Vec<Expression>,
|
arguments: Vec<Expression>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -18,14 +18,14 @@ impl AbstractTree for FunctionCall {
|
|||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
debug_assert_eq!("function_call", node.kind());
|
debug_assert_eq!("function_call", node.kind());
|
||||||
|
|
||||||
let function_node = node.child(0).unwrap();
|
let function_node = node.child(1).unwrap();
|
||||||
let mut arguments = Vec::new();
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
for index in 1..node.child_count() {
|
for index in 2..node.child_count() - 1 {
|
||||||
let child = node.child(index).unwrap();
|
let node = node.child(index).unwrap();
|
||||||
|
|
||||||
if child.is_named() {
|
if node.is_named() {
|
||||||
let expression = Expression::from_syntax_node(source, child)?;
|
let expression = Expression::from_syntax_node(source, node)?;
|
||||||
|
|
||||||
arguments.push(expression);
|
arguments.push(expression);
|
||||||
}
|
}
|
||||||
@ -36,12 +36,9 @@ impl AbstractTree for FunctionCall {
|
|||||||
|
|
||||||
FunctionCall::BuiltIn(Box::new(function))
|
FunctionCall::BuiltIn(Box::new(function))
|
||||||
} else {
|
} else {
|
||||||
let identifier = Identifier::from_syntax_node(source, function_node)?;
|
let name = Expression::from_syntax_node(source, function_node)?;
|
||||||
|
|
||||||
FunctionCall::ContextDefined {
|
FunctionCall::ContextDefined { name, arguments }
|
||||||
name: identifier,
|
|
||||||
arguments,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(function_call)
|
Ok(function_call)
|
||||||
@ -54,23 +51,30 @@ impl AbstractTree for FunctionCall {
|
|||||||
FunctionCall::ContextDefined { name, arguments } => (name, arguments),
|
FunctionCall::ContextDefined { name, arguments } => (name, arguments),
|
||||||
};
|
};
|
||||||
|
|
||||||
let definition = if let Some(value) = context.variables().get(name.inner()) {
|
let function = if let Expression::Identifier(identifier) = name {
|
||||||
value.as_function().cloned()?
|
if let Some(value) = context.variables()?.get(identifier.inner()) {
|
||||||
|
value.as_function().cloned()
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::FunctionIdentifierNotFound(name.clone()));
|
return Err(Error::FunctionIdentifierNotFound(identifier.clone()));
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
let name_run = name.run(source, context)?;
|
||||||
|
|
||||||
if let Some(parameters) = definition.identifiers() {
|
name_run.as_function().cloned()
|
||||||
let parameter_expression_pairs = parameters.iter().zip(arguments.iter());
|
}?;
|
||||||
|
|
||||||
for (identifier, expression) in parameter_expression_pairs {
|
let mut function_context = Map::clone_from(context)?;
|
||||||
|
let parameter_expression_pairs = function.parameters().iter().zip(arguments.iter());
|
||||||
|
|
||||||
|
for ((identifier, r#type), expression) in parameter_expression_pairs {
|
||||||
let key = identifier.clone().take_inner();
|
let key = identifier.clone().take_inner();
|
||||||
let value = expression.run(source, context)?;
|
let value = expression.run(source, context)?;
|
||||||
|
|
||||||
function_context.variables_mut().insert(key, value);
|
r#type.check(&value)?;
|
||||||
}
|
|
||||||
|
function_context.variables_mut()?.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.body().run(source, &mut function_context)
|
function.run(source, &mut function_context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ impl Identifier {
|
|||||||
|
|
||||||
impl AbstractTree for Identifier {
|
impl AbstractTree for Identifier {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
debug_assert_eq!("identifier", node.kind());
|
Error::expect_syntax_node(source, "identifier", node)?;
|
||||||
|
|
||||||
let identifier = &source[node.byte_range()];
|
let identifier = &source[node.byte_range()];
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ impl AbstractTree for Identifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, _source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, _source: &str, context: &mut Map) -> Result<Value> {
|
||||||
if let Some(value) = context.variables().get(&self.0) {
|
if let Some(value) = context.variables()?.get(&self.0) {
|
||||||
Ok(value.clone())
|
Ok(value.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::VariableIdentifierNotFound(self.inner().clone()))
|
Err(Error::VariableIdentifierNotFound(self.inner().clone()))
|
||||||
|
79
src/abstract_tree/identifier_assignment.rs
Normal file
79
src/abstract_tree/identifier_assignment.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tree_sitter::Node;
|
||||||
|
|
||||||
|
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct IdentifierAssignment {
|
||||||
|
identifier: Identifier,
|
||||||
|
operator: AssignmentOperator,
|
||||||
|
statement: Statement,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub enum AssignmentOperator {
|
||||||
|
Equal,
|
||||||
|
PlusEqual,
|
||||||
|
MinusEqual,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AbstractTree for IdentifierAssignment {
|
||||||
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
|
let identifier_node = node.child(0).unwrap();
|
||||||
|
let identifier = Identifier::from_syntax_node(source, identifier_node)?;
|
||||||
|
|
||||||
|
let operator_node = node.child(1).unwrap().child(0).unwrap();
|
||||||
|
let operator = match operator_node.kind() {
|
||||||
|
"=" => AssignmentOperator::Equal,
|
||||||
|
"+=" => AssignmentOperator::PlusEqual,
|
||||||
|
"-=" => AssignmentOperator::MinusEqual,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedSyntaxNode {
|
||||||
|
expected: "=, += or -=",
|
||||||
|
actual: operator_node.kind(),
|
||||||
|
location: operator_node.start_position(),
|
||||||
|
relevant_source: source[operator_node.byte_range()].to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let statement_node = node.child(2).unwrap();
|
||||||
|
let statement = Statement::from_syntax_node(source, statement_node)?;
|
||||||
|
|
||||||
|
Ok(IdentifierAssignment {
|
||||||
|
identifier,
|
||||||
|
operator,
|
||||||
|
statement,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
|
let key = self.identifier.inner().clone();
|
||||||
|
let value = self.statement.run(source, context)?;
|
||||||
|
let mut context = context.variables_mut();
|
||||||
|
|
||||||
|
let new_value = match self.operator {
|
||||||
|
AssignmentOperator::PlusEqual => {
|
||||||
|
if let Some(mut previous_value) = context.get(&key).cloned() {
|
||||||
|
previous_value += value;
|
||||||
|
previous_value
|
||||||
|
} else {
|
||||||
|
Value::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignmentOperator::MinusEqual => {
|
||||||
|
if let Some(mut previous_value) = context.get(&key).cloned() {
|
||||||
|
previous_value -= value;
|
||||||
|
previous_value
|
||||||
|
} else {
|
||||||
|
Value::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignmentOperator::Equal => value,
|
||||||
|
};
|
||||||
|
|
||||||
|
context.insert(key, new_value);
|
||||||
|
|
||||||
|
Ok(Value::Empty)
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Error, Expression, List, Result, Value};
|
use crate::{AbstractTree, Error, Expression, List, Map, Result, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Index {
|
pub struct Index {
|
||||||
collection: Expression,
|
pub collection: Expression,
|
||||||
index: Expression,
|
pub index: Expression,
|
||||||
index_end: Option<Expression>,
|
pub index_end: Option<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Index {
|
impl AbstractTree for Index {
|
||||||
@ -32,10 +32,10 @@ impl AbstractTree for Index {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut crate::Map) -> crate::Result<crate::Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
let value = self.collection.run(source, context)?;
|
let collection = self.collection.run(source, context)?;
|
||||||
|
|
||||||
match value {
|
match collection {
|
||||||
Value::List(list) => {
|
Value::List(list) => {
|
||||||
let index = self.index.run(source, context)?.as_integer()? as usize;
|
let index = self.index.run(source, context)?.as_integer()? as usize;
|
||||||
|
|
||||||
@ -50,8 +50,17 @@ impl AbstractTree for Index {
|
|||||||
|
|
||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
Value::Map(mut map) => {
|
Value::Map(map) => {
|
||||||
let value = self.index.run(source, &mut map)?;
|
let value = if let Expression::Identifier(identifier) = &self.index {
|
||||||
|
let key = identifier.inner();
|
||||||
|
|
||||||
|
map.variables()?.get(key).cloned().unwrap_or(Value::Empty)
|
||||||
|
} else {
|
||||||
|
let value = self.index.run(source, context)?;
|
||||||
|
let key = value.as_string()?;
|
||||||
|
|
||||||
|
map.variables()?.get(key).cloned().unwrap_or(Value::Empty)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
@ -61,7 +70,34 @@ impl AbstractTree for Index {
|
|||||||
|
|
||||||
Ok(Value::String(item.to_string()))
|
Ok(Value::String(item.to_string()))
|
||||||
}
|
}
|
||||||
_ => Err(Error::ExpectedCollection { actual: value }),
|
_ => Err(Error::ExpectedCollection { actual: collection }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::evaluate;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_list_index() {
|
||||||
|
let test = evaluate("x = [1 [2] 3] x:1:0").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(Value::Integer(2), test);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_map_index() {
|
||||||
|
let test = evaluate("x = {y = {z = 2}} x:y:z").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(Value::Integer(2), test);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_complex_index() {
|
||||||
|
let test = evaluate("x = [1 2 3]; y = || <int> { 0 } x:((y))").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(Value::Integer(1), test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
93
src/abstract_tree/index_assignment.rs
Normal file
93
src/abstract_tree/index_assignment.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tree_sitter::Node;
|
||||||
|
|
||||||
|
use crate::{AbstractTree, Error, Index, Map, Result, Statement, Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct IndexAssignment {
|
||||||
|
index: Index,
|
||||||
|
operator: AssignmentOperator,
|
||||||
|
statement: Statement,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub enum AssignmentOperator {
|
||||||
|
Equal,
|
||||||
|
PlusEqual,
|
||||||
|
MinusEqual,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AbstractTree for IndexAssignment {
|
||||||
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
|
Error::expect_syntax_node(source, "index_assignment", node)?;
|
||||||
|
|
||||||
|
let index_node = node.child(0).unwrap();
|
||||||
|
let index = Index::from_syntax_node(source, index_node)?;
|
||||||
|
|
||||||
|
let operator_node = node.child(1).unwrap().child(0).unwrap();
|
||||||
|
let operator = match operator_node.kind() {
|
||||||
|
"=" => AssignmentOperator::Equal,
|
||||||
|
"+=" => AssignmentOperator::PlusEqual,
|
||||||
|
"-=" => AssignmentOperator::MinusEqual,
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedSyntaxNode {
|
||||||
|
expected: "=, += or -=",
|
||||||
|
actual: operator_node.kind(),
|
||||||
|
location: operator_node.start_position(),
|
||||||
|
relevant_source: source[operator_node.byte_range()].to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let statement_node = node.child(2).unwrap();
|
||||||
|
let statement = Statement::from_syntax_node(source, statement_node)?;
|
||||||
|
|
||||||
|
Ok(IndexAssignment {
|
||||||
|
index,
|
||||||
|
operator,
|
||||||
|
statement,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, source: &str, context: &mut 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 {
|
||||||
|
identifier.inner()
|
||||||
|
} else {
|
||||||
|
return Err(Error::VariableIdentifierNotFound(
|
||||||
|
self.index.index.run(source, context)?.to_string(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = self.statement.run(source, &mut context.clone())?;
|
||||||
|
|
||||||
|
let new_value = match self.operator {
|
||||||
|
AssignmentOperator::PlusEqual => {
|
||||||
|
if let Some(mut previous_value) = index_context.variables()?.get(index_key).cloned()
|
||||||
|
{
|
||||||
|
previous_value += value;
|
||||||
|
previous_value
|
||||||
|
} else {
|
||||||
|
Value::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignmentOperator::MinusEqual => {
|
||||||
|
if let Some(mut previous_value) = index_context.variables()?.get(index_key).cloned()
|
||||||
|
{
|
||||||
|
previous_value -= value;
|
||||||
|
previous_value
|
||||||
|
} else {
|
||||||
|
Value::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignmentOperator::Equal => value,
|
||||||
|
};
|
||||||
|
|
||||||
|
index_context
|
||||||
|
.variables_mut()?
|
||||||
|
.insert(index_key.clone(), new_value);
|
||||||
|
|
||||||
|
Ok(Value::Empty)
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,7 @@ impl AbstractTree for Insert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context
|
context
|
||||||
.variables_mut()
|
.variables_mut()?
|
||||||
.insert(table_name, Value::Table(table));
|
.insert(table_name, Value::Table(table));
|
||||||
|
|
||||||
Ok(Value::Empty)
|
Ok(Value::Empty)
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
//! examples.
|
//! examples.
|
||||||
|
|
||||||
pub mod assignment;
|
pub mod assignment;
|
||||||
pub mod r#await;
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod built_in_function;
|
pub mod built_in_function;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
@ -18,6 +17,7 @@ pub mod function_call;
|
|||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod if_else;
|
pub mod if_else;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
|
pub mod index_assignment;
|
||||||
pub mod insert;
|
pub mod insert;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
pub mod r#match;
|
pub mod r#match;
|
||||||
@ -25,16 +25,18 @@ pub mod math;
|
|||||||
pub mod remove;
|
pub mod remove;
|
||||||
pub mod select;
|
pub mod select;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod sublist;
|
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
pub mod r#type;
|
||||||
|
pub mod r#use;
|
||||||
pub mod value_node;
|
pub mod value_node;
|
||||||
pub mod r#while;
|
pub mod r#while;
|
||||||
|
pub mod r#yield;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
assignment::*, block::*, built_in_function::*, expression::*, filter::*, find::*,
|
assignment::*, block::*, built_in_function::*, expression::*, filter::*, find::*,
|
||||||
function_call::*, identifier::*, if_else::*, index::*, insert::*, logic::*, math::*,
|
function_call::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment,
|
||||||
r#await::*, r#await::*, r#for::*, r#match::*, r#while::*, remove::*, select::*, statement::*,
|
insert::*, logic::*, math::*, r#for::*, r#match::*, r#type::*, r#use::*, r#while::*,
|
||||||
sublist::*, transform::*, value_node::*,
|
r#yield::*, remove::*, select::*, statement::*, transform::*, value_node::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
@ -55,6 +57,6 @@ pub trait AbstractTree: Sized {
|
|||||||
/// node's byte range.
|
/// node's byte range.
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self>;
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self>;
|
||||||
|
|
||||||
/// Execute dust code by traversing the tree
|
/// Execute dust code by traversing the tree.
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value>;
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value>;
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,72 @@
|
|||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{AbstractTree, Block, Expression, Identifier, Map, Result, Value};
|
use crate::{AbstractTree, Block, Error, Expression, Identifier, Map, Result, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Remove {
|
pub struct Remove {
|
||||||
identifier: Identifier,
|
item_id: Identifier,
|
||||||
expression: Expression,
|
collection: Expression,
|
||||||
item: Block,
|
predicate: Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Remove {
|
impl AbstractTree for Remove {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
let identifier_node = node.child(1).unwrap();
|
let identifier_node = node.child(1).unwrap();
|
||||||
let identifier = Identifier::from_syntax_node(source, identifier_node)?;
|
let item_id = Identifier::from_syntax_node(source, identifier_node)?;
|
||||||
|
|
||||||
let expression_node = node.child(3).unwrap();
|
let expression_node = node.child(3).unwrap();
|
||||||
let expression = Expression::from_syntax_node(source, expression_node)?;
|
let collection = Expression::from_syntax_node(source, expression_node)?;
|
||||||
|
|
||||||
let item_node = node.child(4).unwrap();
|
let block_node = node.child(4).unwrap();
|
||||||
let item = Block::from_syntax_node(source, item_node)?;
|
let predicate = Block::from_syntax_node(source, block_node)?;
|
||||||
|
|
||||||
Ok(Remove {
|
Ok(Remove {
|
||||||
identifier,
|
item_id,
|
||||||
expression,
|
collection,
|
||||||
item,
|
predicate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
let expression_run = self.expression.run(source, context)?;
|
let value = self.collection.run(source, context)?;
|
||||||
let values = expression_run.into_inner_list()?;
|
let values = value.as_list()?;
|
||||||
let key = self.identifier.inner();
|
let key = self.item_id.inner();
|
||||||
let mut sub_context = context.clone();
|
let should_remove_index = RwLock::new(None);
|
||||||
let mut should_remove_index = None;
|
|
||||||
|
|
||||||
for (index, value) in values.items().iter().enumerate() {
|
values
|
||||||
sub_context
|
.items()
|
||||||
.variables_mut()
|
.par_iter()
|
||||||
|
.enumerate()
|
||||||
|
.try_for_each(|(index, value)| {
|
||||||
|
if should_remove_index.read()?.is_some() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let iter_context = Map::clone_from(context)?;
|
||||||
|
|
||||||
|
iter_context
|
||||||
|
.variables_mut()?
|
||||||
.insert(key.clone(), value.clone());
|
.insert(key.clone(), value.clone());
|
||||||
|
|
||||||
let should_remove = self.item.run(source, &mut sub_context)?.as_boolean()?;
|
let should_remove = self
|
||||||
|
.predicate
|
||||||
|
.run(source, &mut iter_context.clone())?
|
||||||
|
.as_boolean()?;
|
||||||
|
|
||||||
if should_remove {
|
if should_remove {
|
||||||
should_remove_index = Some(index);
|
let _ = should_remove_index.write()?.insert(index);
|
||||||
|
|
||||||
match &self.expression {
|
|
||||||
Expression::Identifier(identifier) => {
|
|
||||||
sub_context
|
|
||||||
.variables_mut()
|
|
||||||
.insert(identifier.inner().clone(), Value::List(values.clone()));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(index) = should_remove_index {
|
Ok::<(), Error>(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let index = should_remove_index.read()?;
|
||||||
|
|
||||||
|
if let Some(index) = *index {
|
||||||
Ok(values.items_mut().remove(index))
|
Ok(values.items_mut().remove(index))
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Empty)
|
Ok(Value::Empty)
|
||||||
|
@ -7,7 +7,7 @@ use crate::{AbstractTree, Block, Expression, Identifier, Map, Result, Table, Val
|
|||||||
pub struct Select {
|
pub struct Select {
|
||||||
identifiers: Vec<Identifier>,
|
identifiers: Vec<Identifier>,
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
block: Option<Block>,
|
predicate: Option<Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Select {
|
impl AbstractTree for Select {
|
||||||
@ -15,6 +15,8 @@ impl AbstractTree for Select {
|
|||||||
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();
|
||||||
|
|
||||||
for index in 1..identifier_list.child_count() - 1 {
|
for index in 1..identifier_list.child_count() - 1 {
|
||||||
let node = identifier_list.child(index).unwrap();
|
let node = identifier_list.child(index).unwrap();
|
||||||
|
|
||||||
@ -27,8 +29,10 @@ impl AbstractTree for Select {
|
|||||||
let expression_node = node.child(3).unwrap();
|
let expression_node = node.child(3).unwrap();
|
||||||
let expression = Expression::from_syntax_node(source, expression_node)?;
|
let expression = Expression::from_syntax_node(source, expression_node)?;
|
||||||
|
|
||||||
let block = if let Some(block_node) = node.child(4) {
|
let final_node = node.child(child_count - 1).unwrap();
|
||||||
Some(Block::from_syntax_node(source, block_node)?)
|
|
||||||
|
let predicate = if final_node.kind() == "block" {
|
||||||
|
Some(Block::from_syntax_node(source, final_node)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -36,7 +40,7 @@ impl AbstractTree for Select {
|
|||||||
Ok(Select {
|
Ok(Select {
|
||||||
identifiers,
|
identifiers,
|
||||||
expression,
|
expression,
|
||||||
block,
|
predicate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +51,7 @@ impl AbstractTree for Select {
|
|||||||
self.identifiers
|
self.identifiers
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|identifierier| identifierier.take_inner())
|
.map(|identifier| identifier.take_inner())
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
old_table.headers().clone()
|
old_table.headers().clone()
|
||||||
@ -56,13 +60,13 @@ impl AbstractTree for Select {
|
|||||||
|
|
||||||
for row in old_table.rows() {
|
for row in old_table.rows() {
|
||||||
let mut new_row = Vec::new();
|
let mut new_row = Vec::new();
|
||||||
let mut row_context = Map::new();
|
let row_context = Map::new();
|
||||||
|
|
||||||
for (i, value) in row.iter().enumerate() {
|
for (i, value) in row.iter().enumerate() {
|
||||||
let column_name = old_table.headers().get(i).unwrap();
|
let column_name = old_table.headers().get(i).unwrap();
|
||||||
|
|
||||||
row_context
|
row_context
|
||||||
.variables_mut()
|
.variables_mut()?
|
||||||
.insert(column_name.clone(), value.clone());
|
.insert(column_name.clone(), value.clone());
|
||||||
|
|
||||||
let new_table_column_index =
|
let new_table_column_index =
|
||||||
@ -86,8 +90,10 @@ impl AbstractTree for Select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(where_clause) = &self.block {
|
if let Some(where_clause) = &self.predicate {
|
||||||
let should_include = where_clause.run(source, &mut row_context)?.as_boolean()?;
|
let should_include = where_clause
|
||||||
|
.run(source, &mut row_context.clone())?
|
||||||
|
.as_boolean()?;
|
||||||
|
|
||||||
if should_include {
|
if should_include {
|
||||||
new_table.insert(new_row)?;
|
new_table.insert(new_row)?;
|
||||||
|
@ -2,35 +2,34 @@ use serde::{Deserialize, Serialize};
|
|||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractTree, Assignment, Await, Error, Expression, Filter, Find, For, IfElse, Insert, Map,
|
AbstractTree, Assignment, Block, Error, Expression, Filter, Find, For, IfElse, IndexAssignment,
|
||||||
Match, Remove, Result, Select, Transform, Value, While,
|
Insert, Map, Match, Remove, Result, Select, Transform, Use, Value, While,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Abstract representation of a statement.
|
/// Abstract representation of a statement.
|
||||||
///
|
|
||||||
/// A statement may evaluate to an Empty value when run. If a Statement is an
|
|
||||||
/// Expression, it will always return a non-empty value when run.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Assignment(Box<Assignment>),
|
Assignment(Box<Assignment>),
|
||||||
Await(Await),
|
Return(Expression),
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
IfElse(Box<IfElse>),
|
IfElse(Box<IfElse>),
|
||||||
Match(Match),
|
Match(Match),
|
||||||
While(Box<While>),
|
While(Box<While>),
|
||||||
Async(Box<Await>),
|
Block(Box<Block>),
|
||||||
For(Box<For>),
|
For(Box<For>),
|
||||||
Transform(Box<Transform>),
|
Transform(Box<Transform>),
|
||||||
Filter(Box<Filter>),
|
Filter(Box<Filter>),
|
||||||
Find(Box<Find>),
|
Find(Box<Find>),
|
||||||
Remove(Box<Remove>),
|
Remove(Box<Remove>),
|
||||||
|
Use(Use),
|
||||||
Select(Box<Select>),
|
Select(Box<Select>),
|
||||||
Insert(Box<Insert>),
|
Insert(Box<Insert>),
|
||||||
|
IndexAssignment(Box<IndexAssignment>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Statement {
|
impl AbstractTree for Statement {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
debug_assert_eq!("statement", node.kind());
|
Error::expect_syntax_node(source, "statement", node)?;
|
||||||
|
|
||||||
let child = node.child(0).unwrap();
|
let child = node.child(0).unwrap();
|
||||||
|
|
||||||
@ -38,7 +37,11 @@ impl AbstractTree for Statement {
|
|||||||
"assignment" => Ok(Statement::Assignment(Box::new(
|
"assignment" => Ok(Statement::Assignment(Box::new(
|
||||||
Assignment::from_syntax_node(source, child)?,
|
Assignment::from_syntax_node(source, child)?,
|
||||||
))),
|
))),
|
||||||
"await" => Ok(Statement::Await(Await::from_syntax_node(source, child)?)),
|
"return" => {
|
||||||
|
let expression_node = child.child(1).unwrap();
|
||||||
|
|
||||||
|
Ok(Statement::Return(Expression::from_syntax_node(source, expression_node)?))
|
||||||
|
},
|
||||||
"expression" => Ok(Self::Expression(Expression::from_syntax_node(
|
"expression" => Ok(Self::Expression(Expression::from_syntax_node(
|
||||||
source, child,
|
source, child,
|
||||||
)?)),
|
)?)),
|
||||||
@ -51,7 +54,7 @@ impl AbstractTree for Statement {
|
|||||||
"while" => Ok(Statement::While(Box::new(While::from_syntax_node(
|
"while" => Ok(Statement::While(Box::new(While::from_syntax_node(
|
||||||
source, child,
|
source, child,
|
||||||
)?))),
|
)?))),
|
||||||
"async" => Ok(Statement::Async(Box::new(Await::from_syntax_node(
|
"block" => Ok(Statement::Block(Box::new(Block::from_syntax_node(
|
||||||
source, child,
|
source, child,
|
||||||
)?))),
|
)?))),
|
||||||
"for" => Ok(Statement::For(Box::new(For::from_syntax_node(
|
"for" => Ok(Statement::For(Box::new(For::from_syntax_node(
|
||||||
@ -72,11 +75,15 @@ impl AbstractTree for Statement {
|
|||||||
"select" => Ok(Statement::Select(Box::new(Select::from_syntax_node(
|
"select" => Ok(Statement::Select(Box::new(Select::from_syntax_node(
|
||||||
source, child,
|
source, child,
|
||||||
)?))),
|
)?))),
|
||||||
|
"use" => Ok(Statement::Use(Use::from_syntax_node(source, child)?)),
|
||||||
"insert" => Ok(Statement::Insert(Box::new(Insert::from_syntax_node(
|
"insert" => Ok(Statement::Insert(Box::new(Insert::from_syntax_node(
|
||||||
source, child,
|
source, child,
|
||||||
)?))),
|
)?))),
|
||||||
|
"index_assignment" => Ok(Statement::IndexAssignment(Box::new(IndexAssignment::from_syntax_node(
|
||||||
|
source, child,
|
||||||
|
)?))),
|
||||||
_ => Err(Error::UnexpectedSyntaxNode {
|
_ => Err(Error::UnexpectedSyntaxNode {
|
||||||
expected: "assignment, expression, if...else, while, for, transform, filter, tool, async, find, remove, select or insert",
|
expected: "assignment, expression, if...else, while, for, transform, filter, tool, async, find, remove, select, insert, index_assignment or yield",
|
||||||
actual: child.kind(),
|
actual: child.kind(),
|
||||||
location: child.start_position(),
|
location: child.start_position(),
|
||||||
relevant_source: source[child.byte_range()].to_string(),
|
relevant_source: source[child.byte_range()].to_string(),
|
||||||
@ -87,19 +94,21 @@ impl AbstractTree for Statement {
|
|||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Assignment(assignment) => assignment.run(source, context),
|
Statement::Assignment(assignment) => assignment.run(source, context),
|
||||||
Statement::Await(r#await) => r#await.run(source, context),
|
Statement::Return(expression) => expression.run(source, context),
|
||||||
Statement::Expression(expression) => expression.run(source, context),
|
Statement::Expression(expression) => expression.run(source, context),
|
||||||
Statement::IfElse(if_else) => if_else.run(source, context),
|
Statement::IfElse(if_else) => if_else.run(source, context),
|
||||||
Statement::Match(r#match) => r#match.run(source, context),
|
Statement::Match(r#match) => r#match.run(source, context),
|
||||||
Statement::While(r#while) => r#while.run(source, context),
|
Statement::While(r#while) => r#while.run(source, context),
|
||||||
Statement::Async(run) => run.run(source, context),
|
Statement::Block(block) => block.run(source, context),
|
||||||
Statement::For(r#for) => r#for.run(source, context),
|
Statement::For(r#for) => r#for.run(source, context),
|
||||||
Statement::Transform(transform) => transform.run(source, context),
|
Statement::Transform(transform) => transform.run(source, context),
|
||||||
Statement::Filter(filter) => filter.run(source, context),
|
Statement::Filter(filter) => filter.run(source, context),
|
||||||
Statement::Find(find) => find.run(source, context),
|
Statement::Find(find) => find.run(source, context),
|
||||||
Statement::Remove(remove) => remove.run(source, context),
|
Statement::Remove(remove) => remove.run(source, context),
|
||||||
|
Statement::Use(run) => run.run(source, context),
|
||||||
Statement::Select(select) => select.run(source, context),
|
Statement::Select(select) => select.run(source, context),
|
||||||
Statement::Insert(insert) => insert.run(source, context),
|
Statement::Insert(insert) => insert.run(source, context),
|
||||||
|
Statement::IndexAssignment(index_assignment) => index_assignment.run(source, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{AbstractTree, Expression, List, Value};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub struct Sublist {
|
|
||||||
list: Expression,
|
|
||||||
start: Expression,
|
|
||||||
end: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractTree for Sublist {
|
|
||||||
fn from_syntax_node(source: &str, node: tree_sitter::Node) -> crate::Result<Self> {
|
|
||||||
let list_node = node.child(0).unwrap();
|
|
||||||
let list = Expression::from_syntax_node(source, list_node)?;
|
|
||||||
|
|
||||||
let start_node = node.child(2).unwrap();
|
|
||||||
let start = Expression::from_syntax_node(source, start_node)?;
|
|
||||||
|
|
||||||
let end_node = node.child(4).unwrap();
|
|
||||||
let end = Expression::from_syntax_node(source, end_node)?;
|
|
||||||
|
|
||||||
Ok(Sublist { list, start, end })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut crate::Map) -> crate::Result<crate::Value> {
|
|
||||||
let value = self.list.run(source, context)?;
|
|
||||||
let list = value.as_list()?.items();
|
|
||||||
let start = self.start.run(source, context)?.as_integer()? as usize;
|
|
||||||
let end = self.end.run(source, context)?.as_integer()? as usize;
|
|
||||||
let sublist = &list[start..=end];
|
|
||||||
|
|
||||||
Ok(Value::List(List::with_items(sublist.to_vec())))
|
|
||||||
}
|
|
||||||
}
|
|
@ -36,18 +36,16 @@ impl AbstractTree for Transform {
|
|||||||
let new_values = values
|
let new_values = values
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|value| {
|
.map(|value| {
|
||||||
let mut iter_context = Map::new();
|
let iter_context = Map::clone_from(context).unwrap();
|
||||||
|
|
||||||
iter_context
|
iter_context
|
||||||
.variables_mut()
|
.variables_mut()
|
||||||
|
.unwrap()
|
||||||
.insert(key.clone(), value.clone());
|
.insert(key.clone(), value.clone());
|
||||||
|
|
||||||
let item_run = self.item.run(source, &mut iter_context);
|
self.item
|
||||||
|
.run(source, &mut iter_context.clone())
|
||||||
match item_run {
|
.unwrap_or_default()
|
||||||
Ok(value) => value,
|
|
||||||
Err(_) => Value::Empty,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
|
117
src/abstract_tree/type.rs
Normal file
117
src/abstract_tree/type.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,32 @@
|
|||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
|
||||||
|
|
||||||
use crate::{evaluate_with_context, AbstractTree, Result, Value, ValueNode, VariableMap};
|
use crate::{evaluate_with_context, AbstractTree, Error, Map, Result, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Use {
|
pub struct Use {
|
||||||
path: ValueNode,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for Use {
|
impl AbstractTree for Use {
|
||||||
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
fn from_syntax_node(source: &str, node: tree_sitter::Node) -> crate::Result<Self> {
|
||||||
let path_node = node.child(1).unwrap();
|
Error::expect_syntax_node(source, "use", node)?;
|
||||||
let value_node = ValueNode::from_syntax_node(source, path_node)?;
|
|
||||||
|
|
||||||
Ok(Use { path: value_node })
|
let string_node = node.child(1).unwrap();
|
||||||
|
let path = source[string_node.start_byte() + 1..string_node.end_byte() - 1].to_string();
|
||||||
|
|
||||||
|
println!("{path}");
|
||||||
|
|
||||||
|
Ok(Use { path })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut VariableMap) -> Result<Value> {
|
fn run(&self, _source: &str, _context: &mut Map) -> Result<Value> {
|
||||||
let run_node = self.path.run(source, context)?;
|
let file_contents = read_to_string(&self.path)?;
|
||||||
let path = run_node.as_string()?;
|
let mut file_context = Map::new();
|
||||||
let file_contents = read_to_string(path)?;
|
|
||||||
let mut temp_context = VariableMap::new();
|
|
||||||
let eval_result = evaluate_with_context(&file_contents, &mut temp_context)?;
|
|
||||||
|
|
||||||
while let Some((key, value)) = temp_context.inner_mut().pop_first() {
|
evaluate_with_context(&file_contents, &mut file_context)?;
|
||||||
context.set_value(key, value)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(eval_result)
|
Ok(Value::Map(file_context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
use std::{collections::BTreeMap, ops::Range};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tree_sitter::Node;
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractTree, Block, Error, Expression, Function, Identifier, List, Map, Result, Statement,
|
AbstractTree, Error, Expression, Function, Identifier, List, Map, Result, Statement, Table,
|
||||||
Table, Value, ValueType,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct ValueNode {
|
pub enum ValueNode {
|
||||||
value_type: ValueType,
|
Boolean(String),
|
||||||
start_byte: usize,
|
Float(String),
|
||||||
end_byte: usize,
|
Integer(String),
|
||||||
}
|
String(String),
|
||||||
|
List(Vec<Expression>),
|
||||||
impl ValueNode {
|
Empty,
|
||||||
pub fn new(value_type: ValueType, start_byte: usize, end_byte: usize) -> Self {
|
Map(BTreeMap<String, Statement>),
|
||||||
Self {
|
Table {
|
||||||
value_type,
|
column_names: Vec<Identifier>,
|
||||||
start_byte,
|
rows: Box<Expression>,
|
||||||
end_byte,
|
},
|
||||||
}
|
Function(Function),
|
||||||
}
|
|
||||||
|
|
||||||
pub fn byte_range(&self) -> Range<usize> {
|
|
||||||
self.start_byte..self.end_byte
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractTree for ValueNode {
|
impl AbstractTree for ValueNode {
|
||||||
@ -34,12 +29,15 @@ impl AbstractTree for ValueNode {
|
|||||||
debug_assert_eq!("value", node.kind());
|
debug_assert_eq!("value", node.kind());
|
||||||
|
|
||||||
let child = node.child(0).unwrap();
|
let child = node.child(0).unwrap();
|
||||||
let value_type = match child.kind() {
|
let value_node = match child.kind() {
|
||||||
"integer" => ValueType::Integer,
|
"boolean" => ValueNode::Boolean(source[child.byte_range()].to_string()),
|
||||||
"float" => ValueType::Float,
|
"float" => ValueNode::Float(source[child.byte_range()].to_string()),
|
||||||
"string" => ValueType::String,
|
"integer" => ValueNode::Integer(source[child.byte_range()].to_string()),
|
||||||
"boolean" => ValueType::Boolean,
|
"string" => {
|
||||||
"empty" => ValueType::Empty,
|
let without_quotes = child.start_byte() + 1..child.end_byte() - 1;
|
||||||
|
|
||||||
|
ValueNode::String(source[without_quotes].to_string())
|
||||||
|
}
|
||||||
"list" => {
|
"list" => {
|
||||||
let mut expressions = Vec::new();
|
let mut expressions = Vec::new();
|
||||||
|
|
||||||
@ -52,7 +50,7 @@ impl AbstractTree for ValueNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType::List(expressions)
|
ValueNode::List(expressions)
|
||||||
}
|
}
|
||||||
"table" => {
|
"table" => {
|
||||||
let identifier_list_node = child.child(1).unwrap();
|
let identifier_list_node = child.child(1).unwrap();
|
||||||
@ -72,7 +70,7 @@ impl AbstractTree for ValueNode {
|
|||||||
let expression_node = child.child(2).unwrap();
|
let expression_node = child.child(2).unwrap();
|
||||||
let expression = Expression::from_syntax_node(source, expression_node)?;
|
let expression = Expression::from_syntax_node(source, expression_node)?;
|
||||||
|
|
||||||
ValueType::Table {
|
ValueNode::Table {
|
||||||
column_names,
|
column_names,
|
||||||
rows: Box::new(expression),
|
rows: Box::new(expression),
|
||||||
}
|
}
|
||||||
@ -97,37 +95,17 @@ impl AbstractTree for ValueNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType::Map(child_nodes)
|
ValueNode::Map(child_nodes)
|
||||||
}
|
}
|
||||||
"function" => {
|
"function" => ValueNode::Function(Function::from_syntax_node(source, child)?),
|
||||||
let parameters_node = child.child_by_field_name("parameters");
|
_ => {
|
||||||
let parameters = if let Some(node) = parameters_node {
|
return Err(Error::UnexpectedSyntaxNode {
|
||||||
let mut parameter_list = Vec::new();
|
expected:
|
||||||
|
"string, integer, float, boolean, list, table, map, function or empty",
|
||||||
for index in 0..node.child_count() {
|
actual: child.kind(),
|
||||||
let child_node = node.child(index).unwrap();
|
location: child.start_position(),
|
||||||
|
relevant_source: source[child.byte_range()].to_string(),
|
||||||
if child_node.is_named() {
|
})
|
||||||
let parameter = Identifier::from_syntax_node(source, child_node)?;
|
|
||||||
|
|
||||||
parameter_list.push(parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(parameter_list)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let body_node = child.child_by_field_name("body").unwrap();
|
|
||||||
let body = Block::from_syntax_node(source, body_node)?;
|
|
||||||
|
|
||||||
ValueType::Function(Function::new(parameters, body))
|
|
||||||
}
|
|
||||||
"future" => {
|
|
||||||
let block_node = child.child(1).unwrap();
|
|
||||||
let block = Block::from_syntax_node(source, block_node)?;
|
|
||||||
|
|
||||||
ValueType::Future(block)
|
|
||||||
}
|
}
|
||||||
_ => return Err(Error::UnexpectedSyntaxNode {
|
_ => return Err(Error::UnexpectedSyntaxNode {
|
||||||
expected:
|
expected:
|
||||||
@ -138,29 +116,19 @@ impl AbstractTree for ValueNode {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ValueNode {
|
Ok(value_node)
|
||||||
value_type,
|
|
||||||
start_byte: child.start_byte(),
|
|
||||||
end_byte: child.end_byte(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
|
||||||
let value_source = &source[self.byte_range()];
|
let value = match self {
|
||||||
let value = match &self.value_type {
|
ValueNode::Boolean(value_source) => Value::Boolean(value_source.parse().unwrap()),
|
||||||
ValueType::Any => todo!(),
|
ValueNode::Float(value_source) => Value::Float(value_source.parse().unwrap()),
|
||||||
ValueType::String => {
|
ValueNode::Integer(value_source) => Value::Integer(value_source.parse().unwrap()),
|
||||||
let without_quotes = &value_source[1..value_source.len() - 1];
|
ValueNode::String(value_source) => Value::String(value_source.parse().unwrap()),
|
||||||
|
ValueNode::List(expressions) => {
|
||||||
|
let mut values = Vec::with_capacity(expressions.len());
|
||||||
|
|
||||||
Value::String(without_quotes.to_string())
|
for node in expressions {
|
||||||
}
|
|
||||||
ValueType::Float => Value::Float(value_source.parse().unwrap()),
|
|
||||||
ValueType::Integer => Value::Integer(value_source.parse().unwrap()),
|
|
||||||
ValueType::Boolean => Value::Boolean(value_source.parse().unwrap()),
|
|
||||||
ValueType::List(nodes) => {
|
|
||||||
let mut values = Vec::with_capacity(nodes.len());
|
|
||||||
|
|
||||||
for node in nodes {
|
|
||||||
let value = node.run(source, context)?;
|
let value = node.run(source, context)?;
|
||||||
|
|
||||||
values.push(value);
|
values.push(value);
|
||||||
@ -168,19 +136,23 @@ impl AbstractTree for ValueNode {
|
|||||||
|
|
||||||
Value::List(List::with_items(values))
|
Value::List(List::with_items(values))
|
||||||
}
|
}
|
||||||
ValueType::Empty => Value::Empty,
|
ValueNode::Empty => Value::Empty,
|
||||||
ValueType::Map(nodes) => {
|
ValueNode::Map(key_statement_pairs) => {
|
||||||
let map = Map::new();
|
let map = Map::new();
|
||||||
|
|
||||||
for (key, node) in nodes {
|
{
|
||||||
let value = node.run(source, context)?;
|
let mut variables = map.variables_mut()?;
|
||||||
|
|
||||||
map.variables_mut().insert(key.clone(), value);
|
for (key, statement) in key_statement_pairs {
|
||||||
|
let value = statement.run(source, context)?;
|
||||||
|
|
||||||
|
variables.insert(key.clone(), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Map(map)
|
Value::Map(map)
|
||||||
}
|
}
|
||||||
ValueType::Table {
|
ValueNode::Table {
|
||||||
column_names,
|
column_names,
|
||||||
rows: row_expression,
|
rows: row_expression,
|
||||||
} => {
|
} => {
|
||||||
@ -206,8 +178,7 @@ impl AbstractTree for ValueNode {
|
|||||||
|
|
||||||
Value::Table(table)
|
Value::Table(table)
|
||||||
}
|
}
|
||||||
ValueType::Function(function) => Value::Function(function.clone()),
|
ValueNode::Function(function) => Value::Function(function.clone()),
|
||||||
ValueType::Future(block) => Value::Future(block.clone()),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
|
47
src/abstract_tree/yield.rs
Normal file
47
src/abstract_tree/yield.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tree_sitter::Node;
|
||||||
|
|
||||||
|
use crate::{AbstractTree, BuiltInFunction, Expression, FunctionCall, Result, Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct Yield {
|
||||||
|
call: FunctionCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AbstractTree for Yield {
|
||||||
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
|
let input_node = node.child(0).unwrap();
|
||||||
|
let input = Expression::from_syntax_node(source, input_node)?;
|
||||||
|
|
||||||
|
let function_node = node.child(3).unwrap();
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
|
arguments.push(input);
|
||||||
|
|
||||||
|
for index in 4..node.child_count() - 1 {
|
||||||
|
let node = node.child(index).unwrap();
|
||||||
|
|
||||||
|
if node.is_named() {
|
||||||
|
let expression = Expression::from_syntax_node(source, node)?;
|
||||||
|
|
||||||
|
arguments.push(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let call = if function_node.kind() == "built_in_function" {
|
||||||
|
let function = BuiltInFunction::from_syntax_node(source, function_node)?;
|
||||||
|
|
||||||
|
FunctionCall::BuiltIn(Box::new(function))
|
||||||
|
} else {
|
||||||
|
let name = Expression::from_syntax_node(source, function_node)?;
|
||||||
|
|
||||||
|
FunctionCall::ContextDefined { name, arguments }
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Yield { call })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, source: &str, context: &mut crate::Map) -> Result<Value> {
|
||||||
|
self.call.run(source, context)
|
||||||
|
}
|
||||||
|
}
|
103
src/error.rs
103
src/error.rs
@ -3,9 +3,11 @@
|
|||||||
//! To deal with errors from dependencies, either create a new error variant
|
//! To deal with errors from dependencies, either create a new error variant
|
||||||
//! or use the ToolFailure variant if the error can only occur inside a tool.
|
//! or use the ToolFailure variant if the error can only occur inside a tool.
|
||||||
|
|
||||||
use crate::{value::Value, Identifier};
|
use tree_sitter::{Node, Point};
|
||||||
|
|
||||||
use std::{fmt, io, time, string::FromUtf8Error, num::ParseFloatError};
|
use crate::{value::Value, Identifier, Type};
|
||||||
|
|
||||||
|
use std::{fmt, io, num::ParseFloatError, string::FromUtf8Error, sync::PoisonError, time};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
@ -14,10 +16,15 @@ pub enum Error {
|
|||||||
UnexpectedSyntaxNode {
|
UnexpectedSyntaxNode {
|
||||||
expected: &'static str,
|
expected: &'static str,
|
||||||
actual: &'static str,
|
actual: &'static str,
|
||||||
location: tree_sitter::Point,
|
location: Point,
|
||||||
relevant_source: String,
|
relevant_source: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
TypeCheck {
|
||||||
|
expected: Type,
|
||||||
|
actual: Value,
|
||||||
|
},
|
||||||
|
|
||||||
/// The 'assert' macro did not resolve successfully.
|
/// The 'assert' macro did not resolve successfully.
|
||||||
AssertEqualFailed {
|
AssertEqualFailed {
|
||||||
expected: Value,
|
expected: Value,
|
||||||
@ -57,7 +64,7 @@ pub enum Error {
|
|||||||
actual: Value,
|
actual: Value,
|
||||||
},
|
},
|
||||||
|
|
||||||
ExpectedInt {
|
ExpectedInteger {
|
||||||
actual: Value,
|
actual: Value,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -125,9 +132,33 @@ pub enum Error {
|
|||||||
|
|
||||||
/// A custom error explained by its message.
|
/// A custom error explained by its message.
|
||||||
CustomMessage(String),
|
CustomMessage(String),
|
||||||
|
|
||||||
|
/// Invalid user input.
|
||||||
|
Syntax {
|
||||||
|
source: String,
|
||||||
|
location: Point,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
pub fn expect_syntax_node(source: &str, expected: &'static str, actual: Node) -> Result<()> {
|
||||||
|
if expected == actual.kind() {
|
||||||
|
Ok(())
|
||||||
|
} else if actual.is_error() {
|
||||||
|
Err(Error::Syntax {
|
||||||
|
source: source[actual.byte_range()].to_string(),
|
||||||
|
location: actual.start_position(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::UnexpectedSyntaxNode {
|
||||||
|
expected,
|
||||||
|
actual: actual.kind(),
|
||||||
|
location: actual.start_position(),
|
||||||
|
relevant_source: source[actual.byte_range()].to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expect_tool_argument_amount(
|
pub fn expect_tool_argument_amount(
|
||||||
tool_name: &'static str,
|
tool_name: &'static str,
|
||||||
expected: usize,
|
expected: usize,
|
||||||
@ -145,6 +176,12 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> From<PoisonError<T>> for Error {
|
||||||
|
fn from(value: PoisonError<T>) -> Self {
|
||||||
|
Error::ToolFailure(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<FromUtf8Error> for Error {
|
impl From<FromUtf8Error> for Error {
|
||||||
fn from(value: FromUtf8Error) -> Self {
|
fn from(value: FromUtf8Error) -> Self {
|
||||||
Error::ToolFailure(value.to_string())
|
Error::ToolFailure(value.to_string())
|
||||||
@ -163,12 +200,6 @@ impl From<csv::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<json::Error> for Error {
|
|
||||||
fn from(value: json::Error) -> Self {
|
|
||||||
Error::ToolFailure(value.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(value: std::io::Error) -> Self {
|
fn from(value: std::io::Error) -> Self {
|
||||||
Error::ToolFailure(value.to_string())
|
Error::ToolFailure(value.to_string())
|
||||||
@ -228,7 +259,7 @@ impl fmt::Display for Error {
|
|||||||
} else {
|
} else {
|
||||||
write!(f, " {actual}.")
|
write!(f, " {actual}.")
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
AssertFailed => write!(
|
AssertFailed => write!(
|
||||||
f,
|
f,
|
||||||
"Assertion failed. A false value was passed to \"assert\"."
|
"Assertion failed. A false value was passed to \"assert\"."
|
||||||
@ -255,24 +286,20 @@ 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 Value::String, but got {:?}.", actual)
|
write!(f, "Expected a 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 Value::Boolean, but got {:?}.", actual)
|
write!(f, "Expected a boolean, but got {:?}.", actual)
|
||||||
}
|
}
|
||||||
ExpectedList { actual } => write!(f, "Expected a Value::List, but got {:?}.", actual),
|
ExpectedList { actual } => write!(f, "Expected a list, but got {:?}.", actual),
|
||||||
ExpectedMinLengthList {
|
ExpectedMinLengthList {
|
||||||
minimum_len,
|
minimum_len,
|
||||||
actual_len,
|
actual_len,
|
||||||
@ -285,14 +312,14 @@ impl fmt::Display for Error {
|
|||||||
actual,
|
actual,
|
||||||
} => write!(
|
} => write!(
|
||||||
f,
|
f,
|
||||||
"Expected a Value::List of len {}, but got {:?}.",
|
"Expected a list of len {}, but got {:?}.",
|
||||||
expected_len, actual
|
expected_len, actual
|
||||||
),
|
),
|
||||||
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
|
ExpectedEmpty { actual } => write!(f, "Expected an empty value, but got {:?}.", actual),
|
||||||
ExpectedMap { actual } => write!(f, "Expected a Value::Map, but got {:?}.", actual),
|
ExpectedMap { actual } => write!(f, "Expected a map, but got {:?}.", actual),
|
||||||
ExpectedTable { actual } => write!(f, "Expected a Value::Table, but got {:?}.", actual),
|
ExpectedTable { actual } => write!(f, "Expected a table, but got {:?}.", actual),
|
||||||
ExpectedFunction { actual } => {
|
ExpectedFunction { actual } => {
|
||||||
write!(f, "Expected Value::Function, but got {:?}.", actual)
|
write!(f, "Expected function, but got {:?}.", actual)
|
||||||
}
|
}
|
||||||
ExpectedCollection { actual } => {
|
ExpectedCollection { actual } => {
|
||||||
write!(
|
write!(
|
||||||
@ -318,11 +345,21 @@ impl fmt::Display for Error {
|
|||||||
relevant_source,
|
relevant_source,
|
||||||
} => write!(
|
} => write!(
|
||||||
f,
|
f,
|
||||||
"Expected syntax node {expected}, but got {actual} at {location}. Code: {relevant_source} ",
|
"Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ",
|
||||||
|
),
|
||||||
|
WrongColumnAmount { expected, actual } => write!(
|
||||||
|
f,
|
||||||
|
"Wrong column amount. Expected {expected} but got {actual}."
|
||||||
),
|
),
|
||||||
WrongColumnAmount { expected, actual } => write!(f, "Wrong column amount. Expected {expected} but got {actual}."),
|
|
||||||
ToolFailure(message) => write!(f, "{message}"),
|
ToolFailure(message) => write!(f, "{message}"),
|
||||||
CustomMessage(message) => write!(f, "{message}"),
|
CustomMessage(message) => write!(f, "{message}"),
|
||||||
|
Syntax { source, location } => {
|
||||||
|
write!(f, "Syntax error at {location}, this is not valid: {source}")
|
||||||
|
}
|
||||||
|
TypeCheck { expected, actual } => write!(
|
||||||
|
f,
|
||||||
|
"Type check error. Expected a {expected} but got {actual}."
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
|
|
||||||
use tree_sitter::{Parser, Tree as TSTree};
|
use tree_sitter::{Parser, Tree as TSTree};
|
||||||
|
|
||||||
use crate::{language, AbstractTree, Block, Map, Result, Value};
|
use crate::{language, AbstractTree, Map, Result, Statement, Value};
|
||||||
|
|
||||||
/// Evaluate the given source code.
|
/// Evaluate the given source code.
|
||||||
///
|
///
|
||||||
@ -33,9 +33,13 @@ pub fn evaluate(source: &str) -> Result<Value> {
|
|||||||
/// # use dust_lang::*;
|
/// # use dust_lang::*;
|
||||||
/// let mut context = Map::new();
|
/// let mut context = Map::new();
|
||||||
///
|
///
|
||||||
/// context.set_value("one".into(), 1.into());
|
/// {
|
||||||
/// context.set_value("two".into(), 2.into());
|
/// let mut variables = context.variables_mut().unwrap();
|
||||||
/// context.set_value("three".into(), 3.into());
|
///
|
||||||
|
/// variables.insert("one".into(), 1.into());
|
||||||
|
/// variables.insert("two".into(), 2.into());
|
||||||
|
/// variables.insert("three".into(), 3.into());
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// let dust_code = "four = 4 one + two + three + four";
|
/// let dust_code = "four = 4 one + two + three + four";
|
||||||
///
|
///
|
||||||
@ -55,10 +59,10 @@ pub fn evaluate_with_context(source: &str, context: &mut Map) -> Result<Value> {
|
|||||||
///
|
///
|
||||||
/// The Evaluator turns a tree sitter concrete syntax tree into a vector of
|
/// The Evaluator turns a tree sitter concrete syntax tree into a vector of
|
||||||
/// abstract trees called [Item][]s that can be run to execute the source code.
|
/// abstract trees called [Item][]s that can be run to execute the source code.
|
||||||
pub struct Evaluator<'context, 'code> {
|
pub struct Evaluator<'c, 's> {
|
||||||
_parser: Parser,
|
_parser: Parser,
|
||||||
context: &'context mut Map,
|
context: &'c mut Map,
|
||||||
source: &'code str,
|
source: &'s str,
|
||||||
syntax_tree: TSTree,
|
syntax_tree: TSTree,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +72,8 @@ impl Debug for Evaluator<'_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'context, 'code> Evaluator<'context, 'code> {
|
impl<'c, 's> Evaluator<'c, 's> {
|
||||||
fn new(mut parser: Parser, context: &'context mut Map, source: &'code str) -> Self {
|
pub fn new(mut parser: Parser, context: &'c mut Map, source: &'s str) -> Self {
|
||||||
let syntax_tree = parser.parse(source, None).unwrap();
|
let syntax_tree = parser.parse(source, None).unwrap();
|
||||||
|
|
||||||
Evaluator {
|
Evaluator {
|
||||||
@ -80,17 +84,23 @@ impl<'context, 'code> Evaluator<'context, 'code> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self) -> Result<Value> {
|
pub fn run(self) -> Result<Value> {
|
||||||
let mut cursor = self.syntax_tree.walk();
|
let root_node = self.syntax_tree.root_node();
|
||||||
let root_node = cursor.node();
|
|
||||||
let mut prev_result = Ok(Value::Empty);
|
|
||||||
|
|
||||||
for block_node in root_node.children(&mut cursor) {
|
let mut prev_result = Value::Empty;
|
||||||
let block = Block::from_syntax_node(self.source, block_node)?;
|
|
||||||
prev_result = block.run(self.source, self.context);
|
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)?;
|
||||||
|
|
||||||
|
prev_result = statement.run(self.source, self.context)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_result
|
Ok(prev_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syntax_tree(&self) -> String {
|
||||||
|
self.syntax_tree.root_node().to_sexp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,10 +159,12 @@ mod tests {
|
|||||||
fn evaluate_map() {
|
fn evaluate_map() {
|
||||||
let map = Map::new();
|
let map = Map::new();
|
||||||
|
|
||||||
map.variables_mut()
|
{
|
||||||
.insert("x".to_string(), Value::Integer(1));
|
let mut variables = map.variables_mut().unwrap();
|
||||||
map.variables_mut()
|
|
||||||
.insert("foo".to_string(), Value::String("bar".to_string()));
|
variables.insert("x".to_string(), Value::Integer(1));
|
||||||
|
variables.insert("foo".to_string(), Value::String("bar".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(evaluate("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map)));
|
assert_eq!(evaluate("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map)));
|
||||||
}
|
}
|
||||||
@ -221,7 +233,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_if_else_else_if_else_if_else_if_else() {
|
fn evaluate_if_else_if_else_if_else_if_else() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate(
|
evaluate(
|
||||||
"
|
"
|
||||||
@ -247,7 +259,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
evaluate(
|
evaluate(
|
||||||
"
|
"
|
||||||
foobar = |message| => message
|
foobar = |message <str>| <str> { message }
|
||||||
(foobar 'Hiya')
|
(foobar 'Hiya')
|
||||||
",
|
",
|
||||||
),
|
),
|
||||||
|
@ -8,7 +8,7 @@ pub use crate::{
|
|||||||
abstract_tree::*,
|
abstract_tree::*,
|
||||||
error::*,
|
error::*,
|
||||||
evaluator::*,
|
evaluator::*,
|
||||||
value::{function::Function, list::List, map::Map, table::Table, value_type::ValueType, Value},
|
value::{function::Function, list::List, map::Map, table::Table, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod abstract_tree;
|
mod abstract_tree;
|
||||||
|
30
src/main.rs
30
src/main.rs
@ -1,5 +1,4 @@
|
|||||||
//! Command line interface for the dust programming language.
|
//! Command line interface for the dust programming language.
|
||||||
use async_std::fs::read_to_string;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rustyline::{
|
use rustyline::{
|
||||||
completion::FilenameCompleter,
|
completion::FilenameCompleter,
|
||||||
@ -9,10 +8,11 @@ use rustyline::{
|
|||||||
history::DefaultHistory,
|
history::DefaultHistory,
|
||||||
Completer, Context, Editor, Helper, Validator,
|
Completer, Context, Editor, Helper, Validator,
|
||||||
};
|
};
|
||||||
|
use tree_sitter::Parser as TSParser;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, fs::read_to_string};
|
||||||
|
|
||||||
use dust_lang::{evaluate_with_context, Map, Value};
|
use dust_lang::{evaluate_with_context, language, Evaluator, Map, Value};
|
||||||
|
|
||||||
/// Command-line arguments to be parsed.
|
/// Command-line arguments to be parsed.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -30,12 +30,15 @@ struct Args {
|
|||||||
#[arg(short = 'p', long)]
|
#[arg(short = 'p', long)]
|
||||||
input_path: Option<String>,
|
input_path: Option<String>,
|
||||||
|
|
||||||
|
/// A path to file whose contents will be assigned to the "input" variable.
|
||||||
|
#[arg(short = 't', long = "tree")]
|
||||||
|
show_syntax_tree: bool,
|
||||||
|
|
||||||
/// Location of the file to run.
|
/// Location of the file to run.
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::main]
|
fn main() {
|
||||||
async fn main() {
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
if args.path.is_none() && args.command.is_none() {
|
if args.path.is_none() && args.command.is_none() {
|
||||||
@ -43,7 +46,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let source = if let Some(path) = &args.path {
|
let source = if let Some(path) = &args.path {
|
||||||
read_to_string(path).await.unwrap()
|
read_to_string(path).unwrap()
|
||||||
} else if let Some(command) = &args.command {
|
} else if let Some(command) = &args.command {
|
||||||
command.clone()
|
command.clone()
|
||||||
} else {
|
} else {
|
||||||
@ -55,18 +58,29 @@ async fn main() {
|
|||||||
if let Some(input) = args.input {
|
if let Some(input) = args.input {
|
||||||
context
|
context
|
||||||
.variables_mut()
|
.variables_mut()
|
||||||
|
.unwrap()
|
||||||
.insert("input".to_string(), Value::String(input));
|
.insert("input".to_string(), Value::String(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = args.input_path {
|
if let Some(path) = args.input_path {
|
||||||
let file_contents = read_to_string(path).await.unwrap();
|
let file_contents = read_to_string(path).unwrap();
|
||||||
|
|
||||||
context
|
context
|
||||||
.variables_mut()
|
.variables_mut()
|
||||||
|
.unwrap()
|
||||||
.insert("input".to_string(), Value::String(file_contents));
|
.insert("input".to_string(), Value::String(file_contents));
|
||||||
}
|
}
|
||||||
|
|
||||||
let eval_result = evaluate_with_context(&source, &mut context);
|
let mut parser = TSParser::new();
|
||||||
|
parser.set_language(language()).unwrap();
|
||||||
|
|
||||||
|
let evaluator = Evaluator::new(parser, &mut context, &source);
|
||||||
|
|
||||||
|
if args.show_syntax_tree {
|
||||||
|
println!("{}", evaluator.syntax_tree());
|
||||||
|
}
|
||||||
|
|
||||||
|
let eval_result = evaluator.run();
|
||||||
|
|
||||||
match eval_result {
|
match eval_result {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
|
@ -1,29 +1,81 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tree_sitter::Node;
|
||||||
|
|
||||||
use crate::{Block, Identifier};
|
use crate::{AbstractTree, Block, Error, Identifier, Map, Result, Type, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
parameters: Option<Vec<Identifier>>,
|
parameters: Vec<(Identifier, Type)>,
|
||||||
body: Box<Block>,
|
return_type: Option<Type>,
|
||||||
|
body: Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(parameters: Option<Vec<Identifier>>, body: Block) -> Self {
|
pub fn parameters(&self) -> &Vec<(Identifier, Type)> {
|
||||||
Function {
|
|
||||||
parameters,
|
|
||||||
body: Box::new(body),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn identifiers(&self) -> &Option<Vec<Identifier>> {
|
|
||||||
&self.parameters
|
&self.parameters
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn body(&self) -> &Block {
|
impl AbstractTree for Function {
|
||||||
&self.body
|
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
|
||||||
|
Error::expect_syntax_node(source, "function", node)?;
|
||||||
|
|
||||||
|
let child_count = node.child_count();
|
||||||
|
let mut parameters = Vec::new();
|
||||||
|
|
||||||
|
for index in 1..child_count - 2 {
|
||||||
|
let parameter_node = {
|
||||||
|
let child = node.child(index).unwrap();
|
||||||
|
|
||||||
|
if child.is_named() {
|
||||||
|
child
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, source: &str, context: &mut 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +83,8 @@ impl Display for Function {
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"function < {:?} > {{ {:?} }}", // TODO: Correct this output
|
"Function {{ parameters: {:?}, return_type: {:?}, body: {:?} }}",
|
||||||
self.parameters, self.body
|
self.parameters, self.return_type, self.body
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{value::Value, List, Table};
|
use crate::{value::Value, List, Result, Table};
|
||||||
|
|
||||||
/// A collection dust variables comprised of key-value pairs.
|
/// A collection dust variables comprised of key-value pairs.
|
||||||
///
|
///
|
||||||
@ -25,34 +25,24 @@ impl Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_from(other: &Self) -> Self {
|
pub fn clone_from(other: &Self) -> Result<Self> {
|
||||||
let mut new_map = BTreeMap::new();
|
let mut new_map = BTreeMap::new();
|
||||||
|
|
||||||
for (key, value) in other.variables().iter() {
|
for (key, value) in other.variables()?.iter() {
|
||||||
new_map.insert(key.clone(), value.clone());
|
new_map.insert(key.clone(), value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map {
|
Ok(Map {
|
||||||
variables: Arc::new(RwLock::new(new_map)),
|
variables: Arc::new(RwLock::new(new_map)),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variables(&self) -> RwLockReadGuard<BTreeMap<String, Value>> {
|
pub fn variables(&self) -> Result<RwLockReadGuard<BTreeMap<String, Value>>> {
|
||||||
self.variables.read().unwrap()
|
Ok(self.variables.read()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variables_mut(&self) -> RwLockWriteGuard<BTreeMap<String, Value>> {
|
pub fn variables_mut(&self) -> Result<RwLockWriteGuard<BTreeMap<String, Value>>> {
|
||||||
self.variables.write().unwrap()
|
Ok(self.variables.write()?)
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of stored variables.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.variables.read().unwrap().len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the length is zero.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.variables.read().unwrap().is_empty()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,12 +94,12 @@ impl Display for Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Table> for Map {
|
impl From<&Table> for Result<Map> {
|
||||||
fn from(value: &Table) -> Self {
|
fn from(value: &Table) -> Result<Map> {
|
||||||
let map = Map::new();
|
let map = Map::new();
|
||||||
|
|
||||||
for (row_index, row) in value.rows().iter().enumerate() {
|
for (row_index, row) in value.rows().iter().enumerate() {
|
||||||
map.variables_mut()
|
map.variables_mut()?
|
||||||
.insert(
|
.insert(
|
||||||
row_index.to_string(),
|
row_index.to_string(),
|
||||||
Value::List(List::with_items(row.clone())),
|
Value::List(List::with_items(row.clone())),
|
||||||
@ -117,7 +107,7 @@ impl From<&Table> for Map {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
map
|
Ok(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
140
src/value/mod.rs
140
src/value/mod.rs
@ -1,10 +1,9 @@
|
|||||||
//! Types that represent runtime values.
|
//! Types that represent runtime values.
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
Block, Function, List, Map, Table, ValueType,
|
Function, List, Map, Table, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use json::JsonValue;
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{MapAccess, SeqAccess, Visitor},
|
de::{MapAccess, SeqAccess, Visitor},
|
||||||
ser::SerializeTuple,
|
ser::SerializeTuple,
|
||||||
@ -23,11 +22,10 @@ pub mod function;
|
|||||||
pub mod list;
|
pub mod list;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
pub mod table;
|
pub mod table;
|
||||||
pub mod value_type;
|
|
||||||
|
|
||||||
/// Whale value representation.
|
/// Dust value representation.
|
||||||
///
|
///
|
||||||
/// Every whale variable has a key and a Value. Variables are represented by
|
/// Every dust variable has a key and a Value. Variables are represented by
|
||||||
/// storing them in a VariableMap. This means the map of variables is itself a
|
/// storing them in a VariableMap. This means the map of variables is itself a
|
||||||
/// value that can be treated as any other.
|
/// value that can be treated as any other.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
@ -46,8 +44,18 @@ pub enum Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn value_type(&self) -> ValueType {
|
pub fn r#type(&self) -> Type {
|
||||||
ValueType::from(self)
|
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 is_table(&self) -> bool {
|
pub fn is_table(&self) -> bool {
|
||||||
@ -104,7 +112,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::ExpectedInt {
|
value => Err(Error::ExpectedInteger {
|
||||||
actual: value.clone(),
|
actual: value.clone(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -208,7 +216,7 @@ impl Value {
|
|||||||
match self {
|
match self {
|
||||||
Value::Table(table) => Ok(table.clone()),
|
Value::Table(table) => Ok(table.clone()),
|
||||||
Value::List(list) => Ok(Table::from(list)),
|
Value::List(list) => Ok(Table::from(list)),
|
||||||
Value::Map(map) => Ok(Table::from(map)),
|
Value::Map(map) => Result::from(map),
|
||||||
value => Err(Error::ExpectedTable {
|
value => Err(Error::ExpectedTable {
|
||||||
actual: value.clone(),
|
actual: value.clone(),
|
||||||
}),
|
}),
|
||||||
@ -220,19 +228,20 @@ impl Add for Value {
|
|||||||
type Output = Result<Value>;
|
type Output = Result<Value>;
|
||||||
|
|
||||||
fn add(self, other: Self) -> Self::Output {
|
fn add(self, other: Self) -> Self::Output {
|
||||||
match (self.as_integer(), other.as_integer()) {
|
if let (Ok(left), Ok(right)) = (self.as_integer(), other.as_integer()) {
|
||||||
(Ok(left), Ok(right)) => return Ok(Value::Integer(left + right)),
|
return Ok(Value::Integer(left + right));
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match (self.as_number(), other.as_number()) {
|
if let (Ok(left), Ok(right)) = (self.as_number(), other.as_number()) {
|
||||||
(Ok(left), Ok(right)) => return Ok(Value::Float(left + right)),
|
return Ok(Value::Float(left + right));
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match (self.as_string(), other.as_string()) {
|
if let (Ok(left), Ok(right)) = (self.as_string(), other.as_string()) {
|
||||||
(Ok(left), Ok(right)) => return Ok(Value::String(left.to_string() + right)),
|
return Ok(Value::String(left.to_string() + right));
|
||||||
_ => {}
|
}
|
||||||
|
|
||||||
|
if self.is_string() || other.is_string() {
|
||||||
|
return Ok(Value::String(self.to_string() + &other.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let non_number_or_string = if !self.is_number() == !self.is_string() {
|
let non_number_or_string = if !self.is_number() == !self.is_string() {
|
||||||
@ -335,6 +344,17 @@ impl SubAssign for Value {
|
|||||||
(Value::Integer(left), Value::Integer(right)) => *left -= right,
|
(Value::Integer(left), Value::Integer(right)) => *left -= right,
|
||||||
(Value::Float(left), Value::Float(right)) => *left -= right,
|
(Value::Float(left), Value::Float(right)) => *left -= right,
|
||||||
(Value::Float(left), Value::Integer(right)) => *left -= right as f64,
|
(Value::Float(left), Value::Integer(right)) => *left -= right as f64,
|
||||||
|
(Value::List(list), value) => {
|
||||||
|
let index_to_remove = list
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(i, list_value)| if list_value == &value { Some(i) } else { None });
|
||||||
|
|
||||||
|
if let Some(index) = index_to_remove {
|
||||||
|
list.items_mut().remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,82 +520,6 @@ impl From<()> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<JsonValue> for Value {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(json_value: JsonValue) -> Result<Self> {
|
|
||||||
use JsonValue::*;
|
|
||||||
|
|
||||||
match json_value {
|
|
||||||
Null => Ok(Value::Empty),
|
|
||||||
Short(short) => Ok(Value::String(short.to_string())),
|
|
||||||
String(string) => Ok(Value::String(string)),
|
|
||||||
Number(number) => Ok(Value::Float(f64::from(number))),
|
|
||||||
Boolean(boolean) => Ok(Value::Boolean(boolean)),
|
|
||||||
Object(object) => {
|
|
||||||
let map = Map::new();
|
|
||||||
|
|
||||||
for (key, node_value) in object.iter() {
|
|
||||||
let value = Value::try_from(node_value)?;
|
|
||||||
|
|
||||||
map.variables_mut().insert(key.to_string(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::Map(map))
|
|
||||||
}
|
|
||||||
Array(array) => {
|
|
||||||
let mut values = Vec::new();
|
|
||||||
|
|
||||||
for json_value in array {
|
|
||||||
let value = Value::try_from(json_value)?;
|
|
||||||
|
|
||||||
values.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::List(List::with_items(values)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&JsonValue> for Value {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(json_value: &JsonValue) -> Result<Self> {
|
|
||||||
use JsonValue::*;
|
|
||||||
|
|
||||||
match json_value {
|
|
||||||
Null => Ok(Value::Empty),
|
|
||||||
Short(short) => Ok(Value::String(short.to_string())),
|
|
||||||
String(string) => Ok(Value::String(string.clone())),
|
|
||||||
Number(number) => Ok(Value::Float(f64::from(*number))),
|
|
||||||
Boolean(boolean) => Ok(Value::Boolean(*boolean)),
|
|
||||||
Object(object) => {
|
|
||||||
let map = Map::new();
|
|
||||||
|
|
||||||
for (key, node_value) in object.iter() {
|
|
||||||
let value = Value::try_from(node_value)?;
|
|
||||||
|
|
||||||
map.variables_mut().insert(key.to_string(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::Map(map))
|
|
||||||
}
|
|
||||||
Array(array) => {
|
|
||||||
let mut values = Vec::new();
|
|
||||||
|
|
||||||
for json_value in array {
|
|
||||||
let value = Value::try_from(json_value)?;
|
|
||||||
|
|
||||||
values.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::List(List::with_items(values)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Value> for String {
|
impl TryFrom<Value> for String {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
@ -607,7 +551,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::ExpectedInt { actual: value })
|
Err(Error::ExpectedInteger { actual: value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -846,8 +790,12 @@ impl<'de> Visitor<'de> for ValueVisitor {
|
|||||||
{
|
{
|
||||||
let map = Map::new();
|
let map = Map::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut variables = map.variables_mut().unwrap();
|
||||||
|
|
||||||
while let Some((key, value)) = access.next_entry()? {
|
while let Some((key, value)) = access.next_entry()? {
|
||||||
map.variables_mut().insert(key, value);
|
variables.insert(key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Map(map))
|
Ok(Value::Map(map))
|
||||||
|
@ -158,7 +158,7 @@ impl Display for Table {
|
|||||||
|
|
||||||
string
|
string
|
||||||
}
|
}
|
||||||
Value::Map(map) => format!("Map ({} items)", map.len()),
|
Value::Map(map) => format!("Map ({} items)", map.variables().unwrap().len()),
|
||||||
Value::Table(table) => format!("Table ({} items)", table.len()),
|
Value::Table(table) => format!("Table ({} items)", table.len()),
|
||||||
Value::Function(_) => "Function".to_string(),
|
Value::Function(_) => "Function".to_string(),
|
||||||
Value::Empty => "Empty".to_string(),
|
Value::Empty => "Empty".to_string(),
|
||||||
@ -233,7 +233,7 @@ impl From<&Value> for Table {
|
|||||||
}
|
}
|
||||||
Value::List(list) => Self::from(list),
|
Value::List(list) => Self::from(list),
|
||||||
Value::Empty => Table::new(Vec::with_capacity(0)),
|
Value::Empty => Table::new(Vec::with_capacity(0)),
|
||||||
Value::Map(map) => Self::from(map),
|
Value::Map(map) => Result::<Table>::from(map).unwrap(),
|
||||||
Value::Table(table) => table.clone(),
|
Value::Table(table) => table.clone(),
|
||||||
Value::Function(function) => {
|
Value::Function(function) => {
|
||||||
let mut table = Table::new(vec!["function".to_string()]);
|
let mut table = Table::new(vec!["function".to_string()]);
|
||||||
@ -287,39 +287,35 @@ impl From<&mut List> for Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Map> for Table {
|
impl From<Map> for Result<Table> {
|
||||||
fn from(map: Map) -> Self {
|
fn from(map: Map) -> Self {
|
||||||
let variables = map.variables();
|
let variables = map.variables()?;
|
||||||
let keys = variables.keys().cloned().collect();
|
let keys = variables.keys().cloned().collect();
|
||||||
let values = variables.values().cloned().collect();
|
let values = variables.values().cloned().collect();
|
||||||
let mut table = Table::new(keys);
|
let mut table = Table::new(keys);
|
||||||
|
|
||||||
table
|
table.insert(values)?;
|
||||||
.insert(values)
|
|
||||||
.expect("Failed to create Table from Map. This is a no-op.");
|
|
||||||
|
|
||||||
table
|
Ok(table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Map> for Table {
|
impl From<&Map> for Result<Table> {
|
||||||
fn from(map: &Map) -> Self {
|
fn from(map: &Map) -> Self {
|
||||||
let variables = map.variables();
|
let variables = map.variables()?;
|
||||||
let keys = variables.keys().cloned().collect();
|
let keys = variables.keys().cloned().collect();
|
||||||
let values = variables.values().cloned().collect();
|
let values = variables.values().cloned().collect();
|
||||||
let mut table = Table::new(keys);
|
let mut table = Table::new(keys);
|
||||||
|
|
||||||
table
|
table.insert(values)?;
|
||||||
.insert(values)
|
|
||||||
.expect("Failed to create Table from Map. This is a no-op.");
|
|
||||||
|
|
||||||
table
|
Ok(table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&mut Map> for Table {
|
impl From<&mut Map> for Result<Table> {
|
||||||
fn from(map: &mut Map) -> Self {
|
fn from(map: &mut Map) -> Self {
|
||||||
let variables = map.variables();
|
let variables = map.variables()?;
|
||||||
let keys = variables.keys().cloned().collect();
|
let keys = variables.keys().cloned().collect();
|
||||||
let values = variables.values().cloned().collect();
|
let values = variables.values().cloned().collect();
|
||||||
let mut table = Table::new(keys);
|
let mut table = Table::new(keys);
|
||||||
@ -328,7 +324,7 @@ impl From<&mut Map> for Table {
|
|||||||
.insert(values)
|
.insert(values)
|
||||||
.expect("Failed to create Table from Map. This is a no-op.");
|
.expect("Failed to create Table from Map. This is a no-op.");
|
||||||
|
|
||||||
table
|
Ok(table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
std/list_functions.ds
Normal file
23
std/list_functions.ds
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
find = |items <list> function <fn>| <any> {
|
||||||
|
for i in items {
|
||||||
|
if (function i) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map = |items <list> function <fn>| <list> {
|
||||||
|
new_list = []
|
||||||
|
|
||||||
|
for i in items {
|
||||||
|
new_list += (function i)
|
||||||
|
}
|
||||||
|
|
||||||
|
new_list
|
||||||
|
}
|
||||||
|
|
||||||
|
foobar <int> = [0 1 2]
|
||||||
|
-> (map |i <int>| <int> { i - 1 })
|
||||||
|
-> (find |i <int>| <bool> { i == -1 })
|
||||||
|
|
||||||
|
foobar
|
@ -2,6 +2,21 @@ use std::fs::read_to_string;
|
|||||||
|
|
||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn r#async() {
|
||||||
|
let file_contents = read_to_string("examples/async.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn async_download() {
|
||||||
|
let file_contents = read_to_string("examples/async_download.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn clue_solver() {
|
fn clue_solver() {
|
||||||
let file_contents = read_to_string("examples/clue_solver.ds").unwrap();
|
let file_contents = read_to_string("examples/clue_solver.ds").unwrap();
|
||||||
@ -24,13 +39,6 @@ fn fibonacci() {
|
|||||||
evaluate(&file_contents).unwrap();
|
evaluate(&file_contents).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn find_loop() {
|
|
||||||
let file_contents = read_to_string("examples/find_loop.ds").unwrap();
|
|
||||||
|
|
||||||
evaluate(&file_contents).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fizzbuzz() {
|
fn fizzbuzz() {
|
||||||
let file_contents = read_to_string("examples/fizzbuzz.ds").unwrap();
|
let file_contents = read_to_string("examples/fizzbuzz.ds").unwrap();
|
||||||
@ -53,8 +61,43 @@ fn hello_world() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_loop() {
|
fn jq_data() {
|
||||||
let file_contents = read_to_string("examples/remove_loop.ds").unwrap();
|
let file_contents = read_to_string("examples/jq_data.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list() {
|
||||||
|
let file_contents = read_to_string("examples/list.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map() {
|
||||||
|
let file_contents = read_to_string("examples/map.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn random() {
|
||||||
|
let file_contents = read_to_string("examples/random.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sea_creatures() {
|
||||||
|
let file_contents = read_to_string("examples/sea_creatures.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select() {
|
||||||
|
let file_contents = read_to_string("examples/select.ds").unwrap();
|
||||||
|
|
||||||
evaluate(&file_contents).unwrap();
|
evaluate(&file_contents).unwrap();
|
||||||
}
|
}
|
||||||
@ -73,13 +116,6 @@ fn table() {
|
|||||||
evaluate(&file_contents).unwrap();
|
evaluate(&file_contents).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transform_loop() {
|
|
||||||
let file_contents = read_to_string("examples/transform_loop.ds").unwrap();
|
|
||||||
|
|
||||||
evaluate(&file_contents).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variables() {
|
fn variables() {
|
||||||
let file_contents = read_to_string("examples/variables.ds").unwrap();
|
let file_contents = read_to_string("examples/variables.ds").unwrap();
|
||||||
@ -93,3 +129,10 @@ fn while_loop() {
|
|||||||
|
|
||||||
evaluate(&file_contents).unwrap();
|
evaluate(&file_contents).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn r#yield() {
|
||||||
|
let file_contents = read_to_string("examples/yield.ds").unwrap();
|
||||||
|
|
||||||
|
evaluate(&file_contents).unwrap();
|
||||||
|
}
|
||||||
|
79
tree-sitter-dust/corpus/assignment.txt
Normal file
79
tree-sitter-dust/corpus/assignment.txt
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
================================================================================
|
||||||
|
Simple Assignment
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
x = y
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(assignment
|
||||||
|
(identifier)
|
||||||
|
(assignment_operator)
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(identifier))))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Simple Assignment with Type
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
x <int> = y
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(assignment
|
||||||
|
(identifier)
|
||||||
|
(type)
|
||||||
|
(assignment_operator)
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(identifier))))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Map Item Assignment
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
x:y = 1
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(index_assignment
|
||||||
|
(index
|
||||||
|
(expression
|
||||||
|
(identifier))
|
||||||
|
(expression
|
||||||
|
(identifier)))
|
||||||
|
(assignment_operator)
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
List Item Assignment
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
x:9 = 'foobar'
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(index_assignment
|
||||||
|
(index
|
||||||
|
(expression
|
||||||
|
(identifier))
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer))))
|
||||||
|
(assignment_operator)
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(string)))))))
|
71
tree-sitter-dust/corpus/async.txt
Normal file
71
tree-sitter-dust/corpus/async.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
================================================================================
|
||||||
|
Simple Async Statements
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
async { (output 'Whaddup') }
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(function_call
|
||||||
|
(built_in_function
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(string))))))))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Complex Async Statements
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
async {
|
||||||
|
if 1 % 2 == 0 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
'foobar'
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(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)))))))
|
@ -1,32 +1,29 @@
|
|||||||
==================
|
================================================================================
|
||||||
Simple Function Call
|
Simple Function Call
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
(output 'hi')
|
(output 'hi')
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(function_call
|
(function_call
|
||||||
(built_in_function
|
(built_in_function
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string)))))))))
|
(string))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Nested Function Call
|
Nested Function Call
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
(assert_equal (random_integer) 4)
|
(assert_equal (random_integer) 4)
|
||||||
assert_equal random_integer 4
|
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(function_call
|
(function_call
|
||||||
@ -36,14 +33,4 @@ assert_equal random_integer 4
|
|||||||
(built_in_function)))
|
(built_in_function)))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))
|
(integer))))))))
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(function_call
|
|
||||||
(built_in_function
|
|
||||||
(expression
|
|
||||||
(function_call
|
|
||||||
(built_in_function
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))))))))
|
|
||||||
|
@ -1,53 +1,45 @@
|
|||||||
==================
|
================================================================================
|
||||||
Full Line Comments
|
Full Line Comments
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
not_a_comment
|
not_a_comment
|
||||||
# comment
|
# comment
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier))))
|
(identifier))))
|
||||||
(comment))
|
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Partial Line Comments
|
Partial Line Comments
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
not_a_comment # comment
|
not_a_comment # comment
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier))))
|
(identifier))))
|
||||||
(comment))
|
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Multiline Comments
|
Multiline Comments
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
# comment #
|
# comment #
|
||||||
not_a_comment #
|
not_a_comment #
|
||||||
# comment # "not a comment"
|
# comment # "not a comment"
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(comment)
|
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier)))
|
(identifier)))
|
||||||
(comment)
|
|
||||||
(comment)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))
|
(string)))))
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
==================
|
|
||||||
Simple Filter Loop
|
|
||||||
==================
|
|
||||||
|
|
||||||
filter i in [1, 2, 3] {
|
|
||||||
i <= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(filter
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))))))))))
|
|
||||||
|
|
||||||
==================
|
|
||||||
Nested Filter Loop
|
|
||||||
==================
|
|
||||||
|
|
||||||
filter i in big_list {
|
|
||||||
filter j in i {
|
|
||||||
i != 42
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(filter
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(filter
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))))))))))
|
|
@ -1,119 +0,0 @@
|
|||||||
================================================================================
|
|
||||||
Simple Find Loop
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
find i in [1, 2, 3] {
|
|
||||||
i <= 3
|
|
||||||
}
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(find
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))))))))))
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
Nested Find Loop
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
find i in ["one", "two", "three"] {
|
|
||||||
found = find j in i {
|
|
||||||
i == "e"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type found) != 'empty' {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(find
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
|
||||||
(find
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))))))))))
|
|
||||||
(statement
|
|
||||||
(if_else
|
|
||||||
(if
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(function_call
|
|
||||||
(built_in_function
|
|
||||||
(expression
|
|
||||||
(identifier)))))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(boolean))))))
|
|
||||||
(else
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(boolean)))))))))))))
|
|
@ -2,12 +2,13 @@
|
|||||||
Simple For Loop
|
Simple For Loop
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
for i in [1, 2, 3] output i
|
for i in [1, 2, 3] {
|
||||||
|
(output i)
|
||||||
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(for
|
(for
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -29,18 +30,21 @@ for i in [1, 2, 3] output i
|
|||||||
(function_call
|
(function_call
|
||||||
(built_in_function
|
(built_in_function
|
||||||
(expression
|
(expression
|
||||||
(identifier)))))))))))
|
(identifier))))))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Nested For Loop
|
Nested For Loop
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
for list in list_of_lists for item in list output item
|
for list in list_of_lists {
|
||||||
|
for item in list {
|
||||||
|
(output item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(for
|
(for
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -58,4 +62,4 @@ for list in list_of_lists for item in list output item
|
|||||||
(function_call
|
(function_call
|
||||||
(built_in_function
|
(built_in_function
|
||||||
(expression
|
(expression
|
||||||
(identifier))))))))))))))
|
(identifier)))))))))))))
|
||||||
|
@ -2,32 +2,31 @@
|
|||||||
Simple Function
|
Simple Function
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
=> "Hiya"
|
|| <str> { "Hiya" }
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(function
|
(function
|
||||||
|
(type)
|
||||||
(block
|
(block
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string)))))))))))
|
(string))))))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Function Assignment
|
Function Assignment
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
x = => "Hiya"
|
x = || <str> { "Hiya" }
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(assignment
|
(assignment
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -36,11 +35,12 @@ x = => "Hiya"
|
|||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(function
|
(function
|
||||||
|
(type)
|
||||||
(block
|
(block
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string)))))))))))))
|
(string))))))))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Function Call
|
Function Call
|
||||||
@ -51,20 +51,20 @@ Function Call
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(function_call
|
(function_call
|
||||||
(identifier)
|
(expression
|
||||||
|
(identifier))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))
|
(string)))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Complex Function
|
Complex Function
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
|message number| => {
|
|message <str> number <int>| {
|
||||||
(output message)
|
(output message)
|
||||||
(output number)
|
(output number)
|
||||||
}
|
}
|
||||||
@ -72,14 +72,16 @@ Complex Function
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(function
|
(function
|
||||||
(identifier_list
|
(parameter
|
||||||
(identifier)
|
(identifier)
|
||||||
(identifier))
|
(type))
|
||||||
|
(parameter
|
||||||
|
(identifier)
|
||||||
|
(type))
|
||||||
(block
|
(block
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
@ -92,7 +94,7 @@ Complex Function
|
|||||||
(function_call
|
(function_call
|
||||||
(built_in_function
|
(built_in_function
|
||||||
(expression
|
(expression
|
||||||
(identifier)))))))))))))
|
(identifier))))))))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Complex Function Call
|
Complex Function Call
|
||||||
@ -110,11 +112,11 @@ Complex Function Call
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(function_call
|
(function_call
|
||||||
(identifier)
|
(expression
|
||||||
|
(identifier))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string)))
|
(string)))
|
||||||
@ -124,20 +126,13 @@ Complex Function Call
|
|||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(map
|
(map
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))
|
(integer))))
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))))))))))
|
(integer)))))))))))
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
==================
|
================================================================================
|
||||||
Simple Identifiers
|
Simple Identifiers
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
x
|
x
|
||||||
_y
|
_y
|
||||||
__xyz__
|
__xyz__
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier)))
|
(identifier)))
|
||||||
@ -18,4 +17,4 @@ __xyz__
|
|||||||
(identifier)))
|
(identifier)))
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier)))))
|
(identifier))))
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
==================
|
================================================================================
|
||||||
Simple If
|
Simple If
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
if true { "True" }
|
if true { "True" }
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(if_else
|
(if_else
|
||||||
(if
|
(if
|
||||||
@ -18,18 +17,17 @@ if true { "True" }
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))
|
(string)))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Complex If
|
Complex If
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
if 1 == 1 && 2 == 2 && 3 == 3 "True"
|
if 1 == 1 && 2 == 2 && 3 == 3 { "True" }
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(if_else
|
(if_else
|
||||||
(if
|
(if
|
||||||
@ -70,22 +68,23 @@ if 1 == 1 && 2 == 2 && 3 == 3 "True"
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))
|
(string)))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Nested If
|
Nested If
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
if true
|
if true {
|
||||||
if 42 == 12
|
if 42 == 12 {
|
||||||
'hiya'
|
'hiya'
|
||||||
else
|
} else {
|
||||||
'bye'
|
'bye'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(if_else
|
(if_else
|
||||||
(if
|
(if
|
||||||
@ -115,18 +114,17 @@ if true
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))))))
|
(string)))))))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
If Else
|
If Else
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
if false "True" else "False"
|
if false { "True" } else { "False" }
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(if_else
|
(if_else
|
||||||
(if
|
(if
|
||||||
@ -143,11 +141,11 @@ if false "True" else "False"
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))
|
(string)))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
If Else If
|
If Else If
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
if 1 == 1 {
|
if 1 == 1 {
|
||||||
"math is fun"
|
"math is fun"
|
||||||
@ -155,10 +153,9 @@ if 1 == 1 {
|
|||||||
"math is broken"
|
"math is broken"
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(if_else
|
(if_else
|
||||||
(if
|
(if
|
||||||
@ -190,25 +187,25 @@ if 1 == 1 {
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))
|
(string)))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
If Else Else If Else
|
If Else Else If Else
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
if false
|
if false {
|
||||||
"no"
|
"no"
|
||||||
else if false
|
} else if false {
|
||||||
"no"
|
"no"
|
||||||
else if 1 + 1 == 9
|
} else if 1 + 1 == 9 {
|
||||||
"not the answer"
|
"not the answer"
|
||||||
else
|
} else {
|
||||||
"42"
|
"42"
|
||||||
|
}
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(if_else
|
(if_else
|
||||||
(if
|
(if
|
||||||
@ -255,4 +252,4 @@ else
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))
|
(string)))))))))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
==================
|
================================================================================
|
||||||
Simple Indexes
|
Simple Indexes
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
dust_data:1:name
|
dust_data:1:name
|
||||||
|
|
||||||
@ -8,10 +8,9 @@ creature:total_clams
|
|||||||
|
|
||||||
foobar:1:42
|
foobar:1:42
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(index
|
(index
|
||||||
@ -43,18 +42,17 @@ foobar:1:42
|
|||||||
(integer)))))
|
(integer)))))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))))
|
(integer)))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Nested Indexes
|
Nested Indexes
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
[['answers' 'foobar'], 42, 666]:0:1:0..2
|
[['answers' 'foobar'], 42, 666]:0:1:0..2
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(index
|
(index
|
||||||
@ -91,4 +89,26 @@ Nested Indexes
|
|||||||
(integer)))
|
(integer)))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))))
|
(integer)))))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Function Call Index
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
x:(y):0
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(index
|
||||||
|
(expression
|
||||||
|
(index
|
||||||
|
(expression
|
||||||
|
(identifier))
|
||||||
|
(expression
|
||||||
|
(identifier))))
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))))))
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
==================
|
================================================================================
|
||||||
List Declaration
|
List Declaration
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
['answer', 42]
|
['answer', 42]
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -17,18 +16,17 @@ List Declaration
|
|||||||
(string)))
|
(string)))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))))
|
(integer))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
List Nesting
|
List Nesting
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
['answers', [42, [666]]]
|
['answers', [42, [666]]]
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -47,4 +45,4 @@ List Nesting
|
|||||||
(list
|
(list
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))))))))))
|
(integer))))))))))))))
|
||||||
|
@ -7,20 +7,15 @@ map { answer = 42 }
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(map
|
(map
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))))))))
|
(integer)))))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Nested Maps
|
Nested Maps
|
||||||
@ -39,7 +34,6 @@ x = map {
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(assignment
|
(assignment
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -48,46 +42,28 @@ x = map {
|
|||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(map
|
(map
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(map
|
(map
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))
|
(string))))
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(map
|
(map
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))))))))))))
|
(string))))))))))))
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
(identifier)
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))))))))))
|
(integer)))))))))))
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
==================
|
================================================================================
|
||||||
\==
|
\==
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
3 == 1 + 1 + 1
|
3 == 1 + 1 + 1
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(logic
|
(logic
|
||||||
@ -29,18 +28,17 @@
|
|||||||
(math_operator)
|
(math_operator)
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))))))
|
(integer)))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
&&
|
&&
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
4 + 2 == 42 && true
|
4 + 2 == 42 && true
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(logic
|
(logic
|
||||||
@ -62,18 +60,17 @@
|
|||||||
(logic_operator)
|
(logic_operator)
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(boolean))))))))))
|
(boolean)))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
\||
|
\||
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
4 + 2 == 42 || true
|
4 + 2 == 42 || true
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(logic
|
(logic
|
||||||
@ -95,4 +92,4 @@
|
|||||||
(logic_operator)
|
(logic_operator)
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(boolean))))))))))
|
(boolean)))))))))
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
==================
|
|
||||||
Simple Reduce Loop
|
|
||||||
==================
|
|
||||||
|
|
||||||
reduce i to acc in [1, 2, 3] {
|
|
||||||
acc += i
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(reduce
|
|
||||||
(identifier)
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(identifier))))))))))
|
|
||||||
|
|
||||||
==================
|
|
||||||
Nested Reduce Loop
|
|
||||||
==================
|
|
||||||
|
|
||||||
reduce i to acc in ["one", "two", "three"] {
|
|
||||||
acc += i
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(reduce
|
|
||||||
(identifier)
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(identifier))))))))))
|
|
@ -1,73 +0,0 @@
|
|||||||
==================
|
|
||||||
Simple Remove
|
|
||||||
==================
|
|
||||||
|
|
||||||
remove i from [1, 2, 3] {
|
|
||||||
i <= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(remove
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))))))))))
|
|
||||||
|
|
||||||
==================
|
|
||||||
Nested Remove
|
|
||||||
==================
|
|
||||||
|
|
||||||
remove i from big_list {
|
|
||||||
remove j from i {
|
|
||||||
j != 42
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(remove
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(remove
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(logic
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(logic_operator)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))))))))))
|
|
@ -1,15 +1,14 @@
|
|||||||
==================
|
================================================================================
|
||||||
Simple Statements
|
Simple Statements
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
1
|
1
|
||||||
"one";
|
"one";
|
||||||
x
|
x
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -20,19 +19,18 @@ x
|
|||||||
(string))))
|
(string))))
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(identifier)))))
|
(identifier))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Simple Assignment
|
Simple Assignment
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
x = 1;
|
x = 1;
|
||||||
y = "one"
|
y = "one"
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(assignment
|
(assignment
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -48,11 +46,11 @@ y = "one"
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))
|
(string)))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Complex Assignment
|
Complex Assignment
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
x = if 1 + 1 == 2 {
|
x = if 1 + 1 == 2 {
|
||||||
'yo'
|
'yo'
|
||||||
@ -60,10 +58,9 @@ x = if 1 + 1 == 2 {
|
|||||||
'no'
|
'no'
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(assignment
|
(assignment
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -96,18 +93,17 @@ x = if 1 + 1 == 2 {
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))))
|
(string)))))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Expression Precedence
|
Expression Precedence
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
x = 3 == 1 + 2 + 2
|
x = 3 == 1 + 2 + 2
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(assignment
|
(assignment
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -133,4 +129,4 @@ x = 3 == 1 + 2 + 2
|
|||||||
(math_operator)
|
(math_operator)
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))))))))
|
(integer)))))))))))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
==================
|
================================================================================
|
||||||
Table Declaration
|
Table Declaration
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
table |messages numbers| [
|
table |messages numbers| [
|
||||||
['hiya' 42]
|
['hiya' 42]
|
||||||
@ -8,10 +8,9 @@ table |messages numbers| [
|
|||||||
['bar' 99.99]
|
['bar' 99.99]
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -48,20 +47,19 @@ table |messages numbers| [
|
|||||||
(string)))
|
(string)))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(float)))))))))))))))
|
(float))))))))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Table Access
|
Table Access
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
select |number| from foobar {
|
select |number| from foobar {
|
||||||
text == 'answer'
|
text == 'answer'
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(select
|
(select
|
||||||
(identifier_list
|
(identifier_list
|
||||||
@ -77,20 +75,19 @@ select |number| from foobar {
|
|||||||
(logic_operator)
|
(logic_operator)
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string)))))))))))
|
(string))))))))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Table Insert
|
Table Insert
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
insert into foobar [
|
insert into foobar [
|
||||||
['bob was here', 0]
|
['bob was here', 0]
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(insert
|
(insert
|
||||||
(identifier)
|
(identifier)
|
||||||
@ -105,4 +102,4 @@ insert into foobar [
|
|||||||
(string)))
|
(string)))
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer)))))))))))))
|
(integer))))))))))))
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
================================================================================
|
|
||||||
Transform Loop
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
transform i in [1, 2, 3] {
|
|
||||||
(output i)
|
|
||||||
}
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(transform
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer)))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(integer))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(function_call
|
|
||||||
(built_in_function
|
|
||||||
(expression
|
|
||||||
(identifier)))))))))))
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
Nested Transform Loop
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
transform i in [['one'] ['two'] ['three']] {
|
|
||||||
transform j in i {
|
|
||||||
j += 'foobar'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(root
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(transform
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string))))))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string))))))
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(list
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string)))))))))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(transform
|
|
||||||
(identifier)
|
|
||||||
(expression
|
|
||||||
(identifier))
|
|
||||||
(block
|
|
||||||
(statement
|
|
||||||
(assignment
|
|
||||||
(identifier)
|
|
||||||
(assignment_operator)
|
|
||||||
(statement
|
|
||||||
(expression
|
|
||||||
(value
|
|
||||||
(string))))))))))))))
|
|
@ -1,14 +1,13 @@
|
|||||||
==================
|
================================================================================
|
||||||
Booleans
|
Booleans
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
true
|
true
|
||||||
false
|
false
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -16,19 +15,18 @@ false
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(boolean))))))
|
(boolean)))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Integers
|
Integers
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
1 2 3
|
1 2 3
|
||||||
456 7
|
456 7
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -48,18 +46,17 @@ Integers
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))
|
(integer)))))
|
||||||
|
|
||||||
==================
|
================================================================================
|
||||||
Strings
|
Strings
|
||||||
==================
|
================================================================================
|
||||||
|
|
||||||
"one" 'two' "three" `four` 'five'
|
"one" 'two' "three" `four` 'five'
|
||||||
|
|
||||||
---
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
@ -79,4 +76,4 @@ Strings
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))
|
(string)))))
|
||||||
|
@ -9,7 +9,6 @@ while true {
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(while
|
(while
|
||||||
(expression
|
(expression
|
||||||
@ -22,7 +21,7 @@ while true {
|
|||||||
(built_in_function
|
(built_in_function
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(string))))))))))))
|
(string)))))))))))
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Nested While Loop
|
Nested While Loop
|
||||||
@ -37,7 +36,6 @@ while (true) {
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
(root
|
(root
|
||||||
(block
|
|
||||||
(statement
|
(statement
|
||||||
(while
|
(while
|
||||||
(expression
|
(expression
|
||||||
@ -62,4 +60,4 @@ while (true) {
|
|||||||
(statement
|
(statement
|
||||||
(expression
|
(expression
|
||||||
(value
|
(value
|
||||||
(integer))))))))))))))
|
(integer)))))))))))))
|
||||||
|
41
tree-sitter-dust/corpus/yield.txt
Normal file
41
tree-sitter-dust/corpus/yield.txt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
================================================================================
|
||||||
|
Simple Yield
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
1 -> (output)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(yield
|
||||||
|
(expression
|
||||||
|
(value
|
||||||
|
(integer)))
|
||||||
|
(built_in_function)))))
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Yield Chain
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
x -> (foo) -> (bar) -> (abc)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(root
|
||||||
|
(statement
|
||||||
|
(expression
|
||||||
|
(yield
|
||||||
|
(expression
|
||||||
|
(yield
|
||||||
|
(expression
|
||||||
|
(yield
|
||||||
|
(expression
|
||||||
|
(identifier))
|
||||||
|
(expression
|
||||||
|
(identifier))))
|
||||||
|
(expression
|
||||||
|
(identifier))))
|
||||||
|
(expression
|
||||||
|
(identifier))))))
|
@ -3,37 +3,33 @@ module.exports = grammar({
|
|||||||
|
|
||||||
word: $ => $.identifier,
|
word: $ => $.identifier,
|
||||||
|
|
||||||
extras: $ => [ /\s/, $.comment ],
|
extras: $ => [ /\s/, $._comment ],
|
||||||
|
|
||||||
conflicts: $ => [
|
|
||||||
[$.block],
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
root: $ => $.block,
|
root: $ => prec(1, repeat1($.statement)),
|
||||||
|
|
||||||
comment: $ => /[#][^#\n]*[#|\n]/,
|
_comment: $ => /[#][^#\n]*[#|\n]/,
|
||||||
|
|
||||||
block: $ => prec.right(choice(
|
block: $ => seq(
|
||||||
|
optional('async'),
|
||||||
|
'{',
|
||||||
repeat1($.statement),
|
repeat1($.statement),
|
||||||
seq('{', repeat1($.statement), '}'),
|
'}',
|
||||||
)),
|
),
|
||||||
|
|
||||||
statement: $ => prec.right(seq(
|
statement: $ => prec.left(seq(
|
||||||
choice(
|
choice(
|
||||||
$.assignment,
|
$.assignment,
|
||||||
$.await,
|
$.block,
|
||||||
$.expression,
|
$.expression,
|
||||||
$.filter,
|
|
||||||
$.find,
|
|
||||||
$.for,
|
$.for,
|
||||||
$.if_else,
|
$.if_else,
|
||||||
|
$.index_assignment,
|
||||||
$.insert,
|
$.insert,
|
||||||
$.match,
|
$.match,
|
||||||
$.reduce,
|
$.return,
|
||||||
$.remove,
|
|
||||||
$.select,
|
$.select,
|
||||||
$.transform,
|
$.use,
|
||||||
$.while,
|
$.while,
|
||||||
),
|
),
|
||||||
optional(';'),
|
optional(';'),
|
||||||
@ -44,16 +40,20 @@ module.exports = grammar({
|
|||||||
seq('(', $._expression_kind, ')'),
|
seq('(', $._expression_kind, ')'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
_expression_kind: $ => prec.right(1, choice(
|
_expression_kind: $ => prec.right(choice(
|
||||||
$.function_call,
|
$.function_call,
|
||||||
$.identifier,
|
$.identifier,
|
||||||
$.index,
|
$.index,
|
||||||
$.logic,
|
$.logic,
|
||||||
$.math,
|
$.math,
|
||||||
$.value,
|
$.value,
|
||||||
|
$.yield,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
_expression_list: $ => repeat1(prec.right(seq($.expression, optional(',')))),
|
_expression_list: $ => repeat1(prec.right(seq(
|
||||||
|
$.expression,
|
||||||
|
optional(','),
|
||||||
|
))),
|
||||||
|
|
||||||
identifier: $ => /[_a-zA-Z]+[_a-zA-Z0-9]?/,
|
identifier: $ => /[_a-zA-Z]+[_a-zA-Z0-9]?/,
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ module.exports = grammar({
|
|||||||
'}',
|
'}',
|
||||||
),
|
),
|
||||||
|
|
||||||
index: $ => prec.left(seq(
|
index: $ => prec.left(1, seq(
|
||||||
$.expression,
|
$.expression,
|
||||||
':',
|
':',
|
||||||
$.expression,
|
$.expression,
|
||||||
@ -155,16 +155,23 @@ module.exports = grammar({
|
|||||||
),
|
),
|
||||||
|
|
||||||
assignment: $ => seq(
|
assignment: $ => seq(
|
||||||
$.identifier,
|
field('identifier', $.identifier),
|
||||||
|
optional(field('type', $.type)),
|
||||||
|
field('assignment_operator', $.assignment_operator),
|
||||||
|
field('statement', $.statement),
|
||||||
|
),
|
||||||
|
|
||||||
|
index_assignment: $ => seq(
|
||||||
|
$.index,
|
||||||
$.assignment_operator,
|
$.assignment_operator,
|
||||||
$.statement,
|
$.statement,
|
||||||
),
|
),
|
||||||
|
|
||||||
assignment_operator: $ => choice(
|
assignment_operator: $ => prec.right(choice(
|
||||||
"=",
|
"=",
|
||||||
"+=",
|
"+=",
|
||||||
"-=",
|
"-=",
|
||||||
),
|
)),
|
||||||
|
|
||||||
if_else: $ => prec.right(seq(
|
if_else: $ => prec.right(seq(
|
||||||
$.if,
|
$.if,
|
||||||
@ -206,50 +213,10 @@ module.exports = grammar({
|
|||||||
),
|
),
|
||||||
|
|
||||||
for: $ => seq(
|
for: $ => seq(
|
||||||
|
choice(
|
||||||
'for',
|
'for',
|
||||||
$.identifier,
|
'async for',
|
||||||
'in',
|
|
||||||
$.expression,
|
|
||||||
$.block,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
transform: $ => seq(
|
|
||||||
'transform',
|
|
||||||
$.identifier,
|
|
||||||
'in',
|
|
||||||
$.expression,
|
|
||||||
$.block,
|
|
||||||
),
|
|
||||||
|
|
||||||
filter: $ => seq(
|
|
||||||
'filter',
|
|
||||||
field('count', optional($.expression)),
|
|
||||||
field('statement_id', $.identifier),
|
|
||||||
'in',
|
|
||||||
field('collection', $.expression),
|
|
||||||
field('predicate', $.block),
|
|
||||||
),
|
|
||||||
|
|
||||||
find: $ => seq(
|
|
||||||
'find',
|
|
||||||
$.identifier,
|
|
||||||
'in',
|
|
||||||
$.expression,
|
|
||||||
$.block,
|
|
||||||
),
|
|
||||||
|
|
||||||
remove: $ => seq(
|
|
||||||
'remove',
|
|
||||||
$.identifier,
|
|
||||||
'from',
|
|
||||||
$.expression,
|
|
||||||
$.block,
|
|
||||||
),
|
|
||||||
|
|
||||||
reduce: $ => seq(
|
|
||||||
'reduce',
|
|
||||||
$.identifier,
|
|
||||||
'to',
|
|
||||||
$.identifier,
|
$.identifier,
|
||||||
'in',
|
'in',
|
||||||
$.expression,
|
$.expression,
|
||||||
@ -285,19 +252,56 @@ module.exports = grammar({
|
|||||||
$.expression,
|
$.expression,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
function: $ => seq(
|
return: $ => seq(
|
||||||
field('parameters', optional($.identifier_list)),
|
'return',
|
||||||
'=>',
|
$.expression,
|
||||||
field('body', $.block),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
function_call: $ => choice(
|
use: $ => seq(
|
||||||
|
'use',
|
||||||
|
$.string,
|
||||||
|
),
|
||||||
|
|
||||||
|
type: $ => seq(
|
||||||
|
'<',
|
||||||
|
choice(
|
||||||
|
'any',
|
||||||
|
'bool',
|
||||||
|
'fn',
|
||||||
|
'int',
|
||||||
|
'list',
|
||||||
|
'map',
|
||||||
|
'str',
|
||||||
|
'table',
|
||||||
|
),
|
||||||
|
'>',
|
||||||
|
),
|
||||||
|
|
||||||
|
function: $ => seq(
|
||||||
|
'|',
|
||||||
|
repeat($.parameter),
|
||||||
|
'|',
|
||||||
|
optional($.type),
|
||||||
|
$.block,
|
||||||
|
),
|
||||||
|
|
||||||
|
parameter: $ => seq(
|
||||||
|
$.identifier,
|
||||||
|
$.type,
|
||||||
|
optional(','),
|
||||||
|
),
|
||||||
|
|
||||||
|
function_call: $ => prec.right(1, seq(
|
||||||
|
'(',
|
||||||
|
choice(
|
||||||
$.built_in_function,
|
$.built_in_function,
|
||||||
$._context_defined_function,
|
$._context_defined_function,
|
||||||
),
|
),
|
||||||
|
')',
|
||||||
|
)),
|
||||||
|
|
||||||
_context_defined_function: $ => prec.right(seq(
|
_context_defined_function: $ => prec.right(1, seq(
|
||||||
$.identifier,
|
$.expression,
|
||||||
optional($._expression_list),
|
optional($._expression_list),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
@ -306,10 +310,22 @@ module.exports = grammar({
|
|||||||
optional($._expression_list),
|
optional($._expression_list),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
|
yield: $ => prec.left(seq(
|
||||||
|
$.expression,
|
||||||
|
'->',
|
||||||
|
'(',
|
||||||
|
choice(
|
||||||
|
$.built_in_function,
|
||||||
|
$._context_defined_function,
|
||||||
|
),
|
||||||
|
')',
|
||||||
|
)),
|
||||||
|
|
||||||
_built_in_function_name: $ => choice(
|
_built_in_function_name: $ => choice(
|
||||||
// General
|
// General
|
||||||
'assert',
|
'assert',
|
||||||
'assert_equal',
|
'assert_equal',
|
||||||
|
'context',
|
||||||
'download',
|
'download',
|
||||||
'help',
|
'help',
|
||||||
'length',
|
'length',
|
||||||
@ -352,4 +368,3 @@ module.exports = grammar({
|
|||||||
'reverse',
|
'reverse',
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user