init
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/node_modules/
|
||||
/target/
|
||||
Generated
+215
@@ -0,0 +1,215 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2038684e0058edba0d17302619f62eabce4a8e11c6ac59506996a8d79848851d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"streaming-iterator",
|
||||
"tree-sitter-language",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-language"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca0d1bf6fdd806e43ae5198f82f527056d359def39e54e67a0f478ac09dac081"
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-lscript"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
"tree-sitter-language",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "tree-sitter-lscript"
|
||||
description = "LScript grammar for Tree-sitter"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
readme = "README.md"
|
||||
keywords = ["incremental", "parsing", "tree-sitter", "lscript"]
|
||||
categories = ["parsing", "text-editors"]
|
||||
edition = "2024"
|
||||
autoexamples = false
|
||||
build = "bindings/rust/build.rs"
|
||||
include = ["bindings/rust/*", "docs/*", "grammar.js", "queries/*", "src/*", "tree-sitter.json"]
|
||||
|
||||
[lib]
|
||||
path = "bindings/rust/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
tree-sitter-language = "0.1"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.2"
|
||||
|
||||
[dev-dependencies]
|
||||
tree-sitter = "0.27"
|
||||
@@ -0,0 +1,36 @@
|
||||
# tree-sitter-lscript
|
||||
|
||||
Tree-sitter grammar and editor queries for LScript.
|
||||
|
||||
This package provides a lossless, error-tolerant concrete syntax tree for
|
||||
LScript editors and source tools. Syntax fixtures and tests are included here.
|
||||
|
||||
The public named-node and field contract is documented in
|
||||
[docs/cst.md](docs/cst.md).
|
||||
|
||||
## Development
|
||||
|
||||
Tree-sitter 0.27 and Node.js are required.
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run generate
|
||||
npm test
|
||||
npm run test:fixtures
|
||||
npm run test:queries
|
||||
cargo test
|
||||
```
|
||||
|
||||
`test:fixtures` requires every syntax-valid fixture to parse without an `ERROR`
|
||||
or `MISSING` node. It also checks syntax-invalid fixtures and fails if a new one
|
||||
is accepted without recovery. Three exceptions are listed explicitly by the
|
||||
script: the adjacency-only `=-` and `!=` lexer errors, and recovery after a
|
||||
repeated result operator. These need lexical or recovery handling rather than
|
||||
a looser grammar rule.
|
||||
|
||||
The future-reserved word `context` is not yet emitted as a Tree-sitter keyword
|
||||
because it has no grammar production. It should move into lexical handling with
|
||||
the two adjacency checks above.
|
||||
|
||||
Generated parser sources under `src/` are committed so consumers do not need
|
||||
Node.js or the Tree-sitter CLI.
|
||||
@@ -0,0 +1,15 @@
|
||||
fn main() {
|
||||
let source_directory = std::path::Path::new("src");
|
||||
let parser = source_directory.join("parser.c");
|
||||
let scanner = source_directory.join("scanner.c");
|
||||
|
||||
cc::Build::new()
|
||||
.std("c11")
|
||||
.include(source_directory)
|
||||
.file(&parser)
|
||||
.file(&scanner)
|
||||
.compile("tree-sitter-lscript");
|
||||
|
||||
println!("cargo:rerun-if-changed={}", parser.display());
|
||||
println!("cargo:rerun-if-changed={}", scanner.display());
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
//! LScript language support for the Tree-sitter parsing library.
|
||||
|
||||
use tree_sitter_language::LanguageFn;
|
||||
|
||||
unsafe extern "C" {
|
||||
fn tree_sitter_lscript() -> *const ();
|
||||
}
|
||||
|
||||
/// The Tree-sitter language function for LScript.
|
||||
pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_lscript) };
|
||||
|
||||
/// Generated descriptions of the grammar's named node types.
|
||||
pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
|
||||
|
||||
/// The default syntax-highlighting query.
|
||||
pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm");
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn parse(source: &str) -> tree_sitter::Tree {
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser
|
||||
.set_language(&super::LANGUAGE.into())
|
||||
.expect("generated LScript grammar must load");
|
||||
parser
|
||||
.parse(source, None)
|
||||
.expect("parser must produce a tree")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loads_and_parses_lscript() {
|
||||
let tree = parse("answer :: 42\n");
|
||||
assert!(!tree.root_node().has_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exposes_stable_expression_fields_without_precedence_wrappers() {
|
||||
let source = "answer := left + right * -value\n";
|
||||
let tree = parse(source);
|
||||
let declaration = tree.root_node().named_child(0).unwrap();
|
||||
let addition = declaration.child_by_field_name("value").unwrap();
|
||||
|
||||
assert_eq!(addition.kind(), "binary_expression");
|
||||
assert_eq!(
|
||||
addition
|
||||
.child_by_field_name("operator")
|
||||
.unwrap()
|
||||
.utf8_text(source.as_bytes())
|
||||
.unwrap(),
|
||||
"+"
|
||||
);
|
||||
assert_eq!(
|
||||
addition.child_by_field_name("left").unwrap().kind(),
|
||||
"identifier"
|
||||
);
|
||||
|
||||
let multiplication = addition.child_by_field_name("right").unwrap();
|
||||
assert_eq!(multiplication.kind(), "binary_expression");
|
||||
let negation = multiplication.child_by_field_name("right").unwrap();
|
||||
assert_eq!(negation.kind(), "unary_expression");
|
||||
assert_eq!(
|
||||
negation.child_by_field_name("operand").unwrap().kind(),
|
||||
"identifier"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exposes_meaningful_type_constructor_fields() {
|
||||
let source = "Transform :: []{i32?} -> [str]i32 ! Error\n";
|
||||
let tree = parse(source);
|
||||
let declaration = tree.root_node().named_child(0).unwrap();
|
||||
let definition = declaration.child_by_field_name("value").unwrap();
|
||||
let function = definition.named_child(0).unwrap();
|
||||
|
||||
assert_eq!(function.kind(), "function_type");
|
||||
assert_eq!(
|
||||
function
|
||||
.child_by_field_name("effect")
|
||||
.unwrap()
|
||||
.utf8_text(source.as_bytes())
|
||||
.unwrap(),
|
||||
"->"
|
||||
);
|
||||
assert_eq!(
|
||||
function.child_by_field_name("input").unwrap().kind(),
|
||||
"array_type"
|
||||
);
|
||||
let result = function.child_by_field_name("result").unwrap();
|
||||
assert_eq!(result.kind(), "result_type");
|
||||
assert_eq!(
|
||||
result.child_by_field_name("value").unwrap().kind(),
|
||||
"map_type"
|
||||
);
|
||||
assert_eq!(
|
||||
result.child_by_field_name("error").unwrap().kind(),
|
||||
"identifier"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exposes_typed_function_headers() {
|
||||
let source =
|
||||
"named :: @ value: i32 => i32 { value }\nadd :: @ (a: i32, b: i32) { a + b }\n";
|
||||
let tree = parse(source);
|
||||
assert!(
|
||||
!tree.root_node().has_error(),
|
||||
"{}",
|
||||
tree.root_node().to_sexp()
|
||||
);
|
||||
|
||||
let named = tree.root_node().named_child(0).unwrap();
|
||||
let function = named.child_by_field_name("value").unwrap();
|
||||
assert_eq!(function.kind(), "function_literal");
|
||||
let parameter = function.child_by_field_name("parameter").unwrap();
|
||||
assert_eq!(parameter.kind(), "typed_parameter");
|
||||
assert_eq!(
|
||||
parameter
|
||||
.child_by_field_name("pattern")
|
||||
.unwrap()
|
||||
.utf8_text(source.as_bytes())
|
||||
.unwrap(),
|
||||
"value"
|
||||
);
|
||||
assert_eq!(
|
||||
function
|
||||
.child_by_field_name("effect")
|
||||
.unwrap()
|
||||
.utf8_text(source.as_bytes())
|
||||
.unwrap(),
|
||||
"=>"
|
||||
);
|
||||
assert_eq!(
|
||||
function.child_by_field_name("result").unwrap().kind(),
|
||||
"primitive_type"
|
||||
);
|
||||
assert_eq!(
|
||||
function.child_by_field_name("body").unwrap().kind(),
|
||||
"block"
|
||||
);
|
||||
|
||||
let tuple_declaration = tree.root_node().named_child(1).unwrap();
|
||||
let tuple_function = tuple_declaration.child_by_field_name("value").unwrap();
|
||||
let tuple_input = tuple_function.child_by_field_name("parameter").unwrap();
|
||||
assert_eq!(tuple_input.kind(), "typed_tuple_pattern");
|
||||
assert!(tuple_function.child_by_field_name("parameters").is_none());
|
||||
assert_eq!(tuple_input.named_child_count(), 2);
|
||||
for index in 0..2 {
|
||||
assert_eq!(
|
||||
tuple_input.named_child(index).unwrap().kind(),
|
||||
"typed_tuple_component"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_removed_function_literal_forms() {
|
||||
for source in [
|
||||
"old: i32 -> i32: value@ value\n",
|
||||
"old: i32 -> i32:\n@ value: i32 { value }\n",
|
||||
"old :: @ value\n",
|
||||
"old :: @ (value:i32) { value }\n",
|
||||
"old :: @ (value:i32,) { value }\n",
|
||||
"old :: @ (\nvalue:i32\n) -> i32 { value }\n",
|
||||
] {
|
||||
assert!(parse(source).root_node().has_error(), "accepted {source}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn distinguishes_compact_ranges_floats_and_word_prefixes() {
|
||||
let source = "value := (item.value, 0..<limit)->slice\n\
|
||||
trailing_dot := 1.\n\
|
||||
anchor := value\n\
|
||||
android := value\n\
|
||||
order: []Item = []\n\
|
||||
ready := first\n\
|
||||
and second\n\
|
||||
or fallback\n";
|
||||
let tree = parse(source);
|
||||
|
||||
assert!(
|
||||
!tree.root_node().has_error(),
|
||||
"{}",
|
||||
tree.root_node().to_sexp()
|
||||
);
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
# Concrete syntax tree contract
|
||||
|
||||
This document describes the named nodes and fields intended for consumers such
|
||||
as editor integrations, formatters, and structural search. Rules prefixed with `_`
|
||||
and rules listed as Tree-sitter supertypes are implementation details and do not
|
||||
appear as named wrapper nodes in ordinary trees.
|
||||
|
||||
## Source and declarations
|
||||
|
||||
`source_file` contains declarations, imports, publication declarations, and
|
||||
standalone expressions directly. The choice-only `module_item`, `expression`,
|
||||
and `value_expression` rules are hidden supertypes.
|
||||
|
||||
`declaration` uses these fields:
|
||||
|
||||
- `name` for a named declaration;
|
||||
- `pattern` for a destructuring declaration;
|
||||
- `type` for an explicit type;
|
||||
- `value` for an initializer.
|
||||
|
||||
The punctuation distinguishes immutable, mutable, typed, and deferred forms.
|
||||
Consumers should inspect that punctuation rather than infer the form from the
|
||||
set of fields alone.
|
||||
|
||||
## Expressions
|
||||
|
||||
Only expressions that actually contain an operator produce an operator node:
|
||||
|
||||
| Node | Fields |
|
||||
| --- | --- |
|
||||
| `assignment_expression` | `left`, `operator`, `right` |
|
||||
| `range_expression` | `left`, `operator`, `right` |
|
||||
| `binary_expression` | `left`, `operator`, `right` |
|
||||
| `unary_expression` | `operator`, `operand` |
|
||||
| `postfix_expression` | `value`, repeated `operator`; member and index operators additionally expose `member` or `index` |
|
||||
| `pipeline_expression` | `input`, followed by stage nodes |
|
||||
|
||||
Precedence-only rules are hidden. For example, `a + b * -c` has one outer and
|
||||
one inner `binary_expression`, then a `unary_expression`; it does not contain a
|
||||
wrapper for every precedence level.
|
||||
|
||||
Literal leaves are named. Boolean and nil values use `boolean_literal` and
|
||||
`nil_literal`; numeric, string, character, and unit literals retain their
|
||||
specific nodes. `default_expression` is also named.
|
||||
|
||||
`function_literal` takes one input and owns the `body` field. Direct binding
|
||||
appears as `parameter: (typed_parameter)`; tuple destructuring appears as
|
||||
`parameter: (typed_tuple_pattern)`. A `typed_tuple_pattern` contains zero or at
|
||||
least two `typed_tuple_component` nodes. These bind components of one tuple
|
||||
input, not separate function arguments. `()` matches unit; singleton tuple
|
||||
patterns are invalid. Typed bindings expose `pattern` and `type`; a header may
|
||||
also expose `effect` (`->` or `=>`) and `result`.
|
||||
|
||||
## Types
|
||||
|
||||
Meaningful type constructors are named directly:
|
||||
|
||||
| Node | Fields |
|
||||
| --- | --- |
|
||||
| `function_type` | `input`, `effect`, `result` |
|
||||
| `result_type` | `value`, `error` |
|
||||
| `optional_type` | `value` |
|
||||
| `array_type` | `element` |
|
||||
| `map_type` | `key`, `value` |
|
||||
| `grouped_type` | `value` |
|
||||
| `type_application` | `constructor` followed by its type arguments |
|
||||
|
||||
Primitive, unit, tuple, product, sum, and bits types retain their corresponding
|
||||
named nodes. Internal primary/container/result categories are hidden.
|
||||
|
||||
## Trivia and physical lines
|
||||
|
||||
Comments are named extra nodes. Spaces, tabs, and structural newlines remain in
|
||||
the source gaps between nodes, so a formatter must retain the original source
|
||||
alongside the tree. Leading-operator and pipeline continuations use composite
|
||||
tokens containing their newline; this prevents a scope separator from stealing
|
||||
a valid continuation and preserves the language's physical-line rules.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Adding nodes or fields is compatible. Removing or renaming a named node or
|
||||
field, changing its meaning, or changing which source construct owns it is a
|
||||
breaking CST change. Until the package reaches 1.0, such changes require corpus
|
||||
snapshot updates and a version change, and must be called out in release notes.
|
||||
|
||||
The Tree-sitter tree is designed for source tooling and error recovery; it does
|
||||
not perform semantic validation.
|
||||
+692
@@ -0,0 +1,692 @@
|
||||
/// <reference types="tree-sitter-cli/dsl" />
|
||||
// @ts-check
|
||||
|
||||
const PREC = {
|
||||
ASSIGNMENT: 1,
|
||||
RANGE: 2,
|
||||
OR: 3,
|
||||
AND: 4,
|
||||
NOT: 5,
|
||||
COMPARE: 6,
|
||||
BIT_OR: 7,
|
||||
BIT_XOR: 8,
|
||||
BIT_AND: 9,
|
||||
ADD: 10,
|
||||
MULTIPLY: 11,
|
||||
PREFIX: 12,
|
||||
POSTFIX: 13,
|
||||
};
|
||||
|
||||
const RESERVED_WORDS = [
|
||||
"and", "as", "bits", "break", "continue", "default", "distinct", "else",
|
||||
"false", "for", "if", "in", "loop", "match", "nil", "not", "or", "pub",
|
||||
"return", "true", "use",
|
||||
"bit", "chr", "f32", "f64", "i8", "i16", "i32", "i64", "nvr", "str",
|
||||
"u8", "u16", "u32", "u64",
|
||||
];
|
||||
|
||||
module.exports = grammar({
|
||||
name: "lscript",
|
||||
|
||||
externals: $ => [
|
||||
$._and_continuation,
|
||||
$._or_continuation,
|
||||
$._trailing_dot_float,
|
||||
],
|
||||
|
||||
extras: $ => [/[ \t]/, $.comment],
|
||||
word: $ => $.identifier,
|
||||
reserved: {
|
||||
global: _ => RESERVED_WORDS,
|
||||
},
|
||||
|
||||
supertypes: $ => [
|
||||
$.module_item,
|
||||
$.expression,
|
||||
$.value_expression,
|
||||
$.type_expression,
|
||||
$.primary_expression,
|
||||
$.literal,
|
||||
$.type_name,
|
||||
$.binding_pattern,
|
||||
$.pattern_atom,
|
||||
$.pipeline_stage,
|
||||
$.inline_pipeline_stage,
|
||||
],
|
||||
|
||||
conflicts: $ => [
|
||||
[$.product_pattern_field, $.primary_expression],
|
||||
[$.pattern_atom, $.literal],
|
||||
[$.pattern_atom, $.primary_expression],
|
||||
[$.signed_numeric_literal],
|
||||
[$.source_file, $._scope_separator],
|
||||
[$.qualified_name],
|
||||
[$.block, $._scope_separator],
|
||||
[$.match_expression, $._scope_separator],
|
||||
[$.product_constructor, $._scope_separator],
|
||||
[$.tuple_type],
|
||||
[$.type_application],
|
||||
[$.tuple_pattern],
|
||||
[$.product_pattern],
|
||||
[$.array_pattern],
|
||||
[$.tuple_value],
|
||||
[$.collection_literal],
|
||||
[$.block, $.product_pattern],
|
||||
[$.array_pattern, $.collection_literal],
|
||||
[$.typed_tuple_pattern],
|
||||
[$.unit_pattern, $.unit_literal],
|
||||
[$.sum_pattern, $.sum_constructor],
|
||||
[$.bits_pattern, $.bits_constructor],
|
||||
[$.primary_expression, $.qualified_name],
|
||||
[$.array_pattern_entry, $.primary_expression],
|
||||
[$.continue_expression],
|
||||
[$.unit_type, $.unit_literal],
|
||||
[$.unit_type, $.unit_pattern, $.unit_literal],
|
||||
[$.product_pattern_field, $.primary_expression, $.type_name],
|
||||
[$.pattern_atom, $.primary_expression, $.type_name],
|
||||
[$.type_name, $.qualified_name],
|
||||
[$.primary_expression, $.type_name],
|
||||
[$.type_expression, $.function_type],
|
||||
[$.array_type, $.collection_literal],
|
||||
],
|
||||
|
||||
rules: {
|
||||
source_file: $ => seq(
|
||||
repeat($._newline),
|
||||
optional(seq(
|
||||
$.module_item,
|
||||
repeat(seq($._scope_separator, $.module_item)),
|
||||
repeat($._newline),
|
||||
)),
|
||||
),
|
||||
|
||||
module_item: $ => choice(
|
||||
$.use_declaration,
|
||||
$.pub_declaration,
|
||||
$.expression,
|
||||
),
|
||||
|
||||
use_declaration: $ => seq(
|
||||
"use",
|
||||
choice(
|
||||
seq($.module_path, optional(seq("as", field("alias", $.identifier)))),
|
||||
seq($.module_path, ".", "{", listBody($, $.import_member), "}"),
|
||||
),
|
||||
),
|
||||
|
||||
import_member: $ => seq(
|
||||
field("name", $.identifier),
|
||||
optional(seq("as", field("alias", $.identifier))),
|
||||
),
|
||||
|
||||
module_path: $ => choice(
|
||||
$.type_name,
|
||||
seq(choice(".", ".."), $.type_name),
|
||||
),
|
||||
|
||||
pub_declaration: $ => seq("pub", "{", scopeBody($, $.identifier), "}"),
|
||||
|
||||
expression: $ => choice(
|
||||
$.declaration,
|
||||
$.value_expression,
|
||||
),
|
||||
|
||||
value_expression: $ => choice(
|
||||
$.function_literal,
|
||||
$.return_expression,
|
||||
$.break_expression,
|
||||
$.continue_expression,
|
||||
$.pipeline_expression,
|
||||
$._ordinary_expression,
|
||||
),
|
||||
|
||||
declaration: $ => choice(
|
||||
seq(
|
||||
field("name", $.identifier),
|
||||
"::",
|
||||
field("value", choice($.type_definition, prec(1, $.value_expression))),
|
||||
),
|
||||
seq(
|
||||
field("name", $.identifier),
|
||||
":=",
|
||||
field("value", $.value_expression),
|
||||
),
|
||||
seq(
|
||||
field("name", $.identifier),
|
||||
":",
|
||||
field("type", $.type_expression),
|
||||
choice(
|
||||
seq(":", field("value", $.value_expression)),
|
||||
optional(seq("=", field("value", $.value_expression))),
|
||||
),
|
||||
),
|
||||
seq(
|
||||
field("pattern", choice($.tuple_pattern, $.product_pattern)),
|
||||
choice("::", ":="),
|
||||
field("value", $.value_expression),
|
||||
),
|
||||
),
|
||||
|
||||
type_definition: $ => seq(optional("distinct"), $.type_expression),
|
||||
|
||||
type_expression: $ => choice(
|
||||
$.function_type,
|
||||
$._result_type,
|
||||
),
|
||||
|
||||
function_type: $ => prec.right(seq(
|
||||
field("input", $._result_type),
|
||||
field("effect", choice("->", "=>")),
|
||||
field("result", $.type_expression),
|
||||
)),
|
||||
|
||||
_result_type: $ => choice(
|
||||
$.result_type,
|
||||
$.optional_type,
|
||||
$._container_type,
|
||||
),
|
||||
|
||||
result_type: $ => seq(
|
||||
field("value", $._container_type),
|
||||
"!",
|
||||
field("error", $._container_type),
|
||||
),
|
||||
|
||||
optional_type: $ => seq(
|
||||
field("value", $._container_type),
|
||||
repeat1("?"),
|
||||
),
|
||||
|
||||
_container_type: $ => choice(
|
||||
$.array_type,
|
||||
$.map_type,
|
||||
$._primary_type,
|
||||
),
|
||||
|
||||
array_type: $ => prec.right(seq(
|
||||
"[",
|
||||
"]",
|
||||
field("element", $._container_type),
|
||||
)),
|
||||
|
||||
map_type: $ => prec.right(seq(
|
||||
"[",
|
||||
field("key", $.type_expression),
|
||||
"]",
|
||||
field("value", $._container_type),
|
||||
)),
|
||||
|
||||
_primary_type: $ => choice(
|
||||
$.primitive_type,
|
||||
$.type_name,
|
||||
$.unit_type,
|
||||
$.tuple_type,
|
||||
$.product_type,
|
||||
$.sum_type,
|
||||
$.bits_type,
|
||||
$.type_application,
|
||||
$.grouped_type,
|
||||
),
|
||||
|
||||
grouped_type: $ => seq("{", field("value", $.type_expression), "}"),
|
||||
|
||||
unit_type: _ => seq("(", ")"),
|
||||
|
||||
tuple_type: $ => seq(
|
||||
"(",
|
||||
listBodyAtLeastTwo($, $.type_expression),
|
||||
")",
|
||||
),
|
||||
|
||||
type_application: $ => seq(
|
||||
field("constructor", $.type_name),
|
||||
"<",
|
||||
listBody($, $.type_expression),
|
||||
">",
|
||||
),
|
||||
|
||||
product_type: $ => seq("&", "{", scopeBody($, $.product_field), "}"),
|
||||
|
||||
product_field: $ => seq(
|
||||
field("name", $.identifier),
|
||||
":",
|
||||
field("type", $.type_expression),
|
||||
choice(":", "="),
|
||||
optional(field("default", $.expression)),
|
||||
),
|
||||
|
||||
sum_type: $ => seq(
|
||||
"|",
|
||||
optional($.integer_type),
|
||||
"{",
|
||||
scopeBody($, $.sum_variant),
|
||||
"}",
|
||||
),
|
||||
|
||||
sum_variant: $ => seq(
|
||||
field("name", $.identifier),
|
||||
optional(choice(
|
||||
seq(":", field("payload", $.type_expression)),
|
||||
seq("::", field("discriminant", $.signed_integer_literal)),
|
||||
)),
|
||||
),
|
||||
|
||||
bits_type: $ => seq(
|
||||
"bits",
|
||||
"|",
|
||||
$.unsigned_integer_type,
|
||||
"{",
|
||||
scopeBody($, $.bits_member),
|
||||
"}",
|
||||
),
|
||||
|
||||
bits_member: $ => seq($.identifier, "::", $.integer_literal),
|
||||
|
||||
primitive_type: _ => choice(
|
||||
"f32", "f64", "bit", "chr", "str", "nvr",
|
||||
"i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64",
|
||||
),
|
||||
|
||||
integer_type: _ => choice("i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"),
|
||||
unsigned_integer_type: _ => choice("u8", "u16", "u32", "u64"),
|
||||
|
||||
function_literal: $ => choice(
|
||||
prec(1, seq(
|
||||
"@",
|
||||
field("parameter", $.typed_parameter),
|
||||
optional(seq(
|
||||
field("effect", choice("->", "=>")),
|
||||
field("result", $.type_expression),
|
||||
)),
|
||||
field("body", $.block),
|
||||
)),
|
||||
prec(1, seq(
|
||||
"@",
|
||||
field("parameter", $.typed_tuple_pattern),
|
||||
optional(seq(
|
||||
field("effect", choice("->", "=>")),
|
||||
field("result", $.type_expression),
|
||||
)),
|
||||
field("body", $.block),
|
||||
)),
|
||||
prec(1, seq("@", field("body", $.block))),
|
||||
),
|
||||
|
||||
typed_tuple_pattern: $ => seq("(", optional(listBodyAtLeastTwo($, $.typed_tuple_component)), ")"),
|
||||
|
||||
typed_parameter: $ => seq(
|
||||
field("pattern", $.binding_pattern),
|
||||
":",
|
||||
field("type", $._result_type),
|
||||
),
|
||||
|
||||
typed_tuple_component: $ => seq(
|
||||
field("pattern", $.binding_pattern),
|
||||
":",
|
||||
field("type", $.type_expression),
|
||||
),
|
||||
|
||||
return_expression: $ => prec.right(seq("return", optional($.expression))),
|
||||
break_expression: $ => prec.right(seq("break", optional($.label), optional($.expression))),
|
||||
continue_expression: $ => seq("continue", optional($.label)),
|
||||
|
||||
label: $ => seq("`", $.identifier),
|
||||
|
||||
if_expression: $ => prec.right(seq(
|
||||
"if",
|
||||
field("condition", $.expression),
|
||||
field("consequence", $.block),
|
||||
optional(seq("else", field("alternative", choice($.block, $.if_expression)))),
|
||||
)),
|
||||
|
||||
match_expression: $ => seq(
|
||||
"match",
|
||||
field("value", $.expression),
|
||||
"{",
|
||||
scopeBody($, $.match_arm),
|
||||
"}",
|
||||
),
|
||||
|
||||
match_arm: $ => seq(
|
||||
field("pattern", $.pattern),
|
||||
optional(seq("if", field("guard", $.expression))),
|
||||
"=>",
|
||||
field("body", $.expression),
|
||||
),
|
||||
|
||||
loop_expression: $ => seq("loop", optional($.label), $.block),
|
||||
|
||||
for_expression: $ => seq(
|
||||
"for",
|
||||
field("pattern", $.binding_pattern),
|
||||
"in",
|
||||
field("value", $.expression),
|
||||
optional($.label),
|
||||
field("body", $.block),
|
||||
),
|
||||
|
||||
block: $ => seq("{", scopeBody($, $.expression), "}"),
|
||||
|
||||
pattern: $ => prec.left(seq($.pattern_atom, repeat(seq("or", $.pattern_atom)))),
|
||||
|
||||
binding_pattern: $ => choice(
|
||||
"_",
|
||||
$.identifier,
|
||||
$.unit_pattern,
|
||||
$.tuple_pattern,
|
||||
$.product_pattern,
|
||||
),
|
||||
|
||||
pattern_atom: $ => choice(
|
||||
"_",
|
||||
$.identifier,
|
||||
$.signed_numeric_literal,
|
||||
$.string_literal,
|
||||
$.character_literal,
|
||||
$.boolean_literal,
|
||||
$.nil_literal,
|
||||
$.sum_pattern,
|
||||
$.bits_pattern,
|
||||
$.unit_pattern,
|
||||
$.tuple_pattern,
|
||||
$.product_pattern,
|
||||
$.array_pattern,
|
||||
),
|
||||
|
||||
unit_pattern: _ => seq("(", ")"),
|
||||
tuple_pattern: $ => seq("(", listBodyAtLeastTwo($, $.pattern), ")"),
|
||||
|
||||
product_pattern: $ => seq("{", listBody($, $.product_pattern_field), "}"),
|
||||
product_pattern_field: $ => seq($.identifier, optional(seq("as", $.identifier))),
|
||||
|
||||
sum_pattern: $ => seq(
|
||||
"|",
|
||||
optional($.type_name),
|
||||
".",
|
||||
field("variant", $.identifier),
|
||||
optional($.pattern_atom),
|
||||
),
|
||||
|
||||
bits_pattern: $ => seq(
|
||||
"|",
|
||||
optional($.type_name),
|
||||
".",
|
||||
"{",
|
||||
listBody($, $.identifier),
|
||||
"}",
|
||||
),
|
||||
|
||||
array_pattern: $ => seq("[", listBody($, $.array_pattern_entry), "]"),
|
||||
array_pattern_entry: $ => choice($.pattern, seq("...", choice($.identifier, "_"))),
|
||||
|
||||
pipeline_expression: $ => prec.dynamic(1, prec.left(choice(
|
||||
seq(field("input", choice($._prefix_expression, $.function_literal)), $.inline_pipeline_stage, repeat($.pipeline_stage)),
|
||||
seq(field("input", choice($._non_assignment_expression, $.function_literal)), $.continuation_pipeline_stage, repeat($.pipeline_stage)),
|
||||
))),
|
||||
|
||||
pipeline_stage: $ => choice($.inline_pipeline_stage, $.continuation_pipeline_stage),
|
||||
inline_pipeline_stage: $ => choice($.direct_pipeline_stage, $.aggregate_pipeline_stage),
|
||||
continuation_pipeline_stage: $ => choice(
|
||||
seq(
|
||||
$._direct_pipeline_continuation,
|
||||
field("callable", $.pipeline_callable),
|
||||
repeat(choice("?", "!")),
|
||||
),
|
||||
seq(
|
||||
$._aggregate_pipeline_continuation,
|
||||
field("callable", $.aggregate_pipeline_callable),
|
||||
field("arguments", $.aggregate_tail),
|
||||
repeat(choice("?", "!")),
|
||||
),
|
||||
),
|
||||
|
||||
direct_pipeline_stage: $ => seq(
|
||||
"->",
|
||||
field("callable", $.pipeline_callable),
|
||||
repeat(choice("?", "!")),
|
||||
),
|
||||
|
||||
aggregate_pipeline_stage: $ => seq(
|
||||
"|>",
|
||||
field("callable", $.aggregate_pipeline_callable),
|
||||
field("arguments", $.aggregate_tail),
|
||||
repeat(choice("?", "!")),
|
||||
),
|
||||
|
||||
aggregate_tail: $ => choice(
|
||||
seq("(", listBodyOneOrMore($, $.expression), ")"),
|
||||
seq("[", optional(listBody($, $.array_tail_entry)), "]"),
|
||||
),
|
||||
|
||||
array_tail_entry: $ => choice($.expression, seq("...", $.expression)),
|
||||
|
||||
pipeline_callable: $ => seq(
|
||||
$.primary_expression,
|
||||
repeat(choice(
|
||||
seq(".", $.identifier),
|
||||
seq("[", $.expression, "]"),
|
||||
)),
|
||||
),
|
||||
|
||||
aggregate_pipeline_callable: $ => seq(
|
||||
$.primary_expression,
|
||||
repeat(seq(".", $.identifier)),
|
||||
),
|
||||
|
||||
_ordinary_expression: $ => choice(
|
||||
$.assignment_expression,
|
||||
$._non_assignment_expression,
|
||||
),
|
||||
|
||||
_non_assignment_expression: $ => choice(
|
||||
$.range_expression,
|
||||
$._binary_expression,
|
||||
),
|
||||
|
||||
_binary_expression: $ => choice(
|
||||
$.binary_expression,
|
||||
$._prefix_expression,
|
||||
),
|
||||
|
||||
_prefix_expression: $ => choice(
|
||||
$.unary_expression,
|
||||
$.postfix_expression,
|
||||
$.primary_expression,
|
||||
),
|
||||
|
||||
assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq(
|
||||
field("left", $._non_assignment_expression),
|
||||
field("operator", choice("=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=")),
|
||||
field("right", choice(
|
||||
$.function_literal,
|
||||
$.return_expression,
|
||||
$.break_expression,
|
||||
$.pipeline_expression,
|
||||
$._non_assignment_expression,
|
||||
)),
|
||||
)),
|
||||
|
||||
range_expression: $ => prec.left(PREC.RANGE, seq(
|
||||
field("left", $._binary_expression),
|
||||
field("operator", choice("..<", "..=")),
|
||||
field("right", $._binary_expression),
|
||||
)),
|
||||
|
||||
binary_expression: $ => choice(
|
||||
binaryExpression($, "or", PREC.OR, $._or_continuation, true),
|
||||
binaryExpression($, "and", PREC.AND, $._and_continuation, true),
|
||||
binaryExpression($, choice("<", "<=", ">", ">=", "=="), PREC.COMPARE, $._comparison_continuation, true),
|
||||
binaryExpression($, "|", PREC.BIT_OR, null),
|
||||
binaryExpression($, "^", PREC.BIT_XOR),
|
||||
binaryExpression($, "&", PREC.BIT_AND, null),
|
||||
binaryExpression($, choice("+", "-"), PREC.ADD, "+"),
|
||||
binaryExpression($, choice("*", "/", "%"), PREC.MULTIPLY),
|
||||
),
|
||||
|
||||
unary_expression: $ => prec.right(PREC.PREFIX, seq(
|
||||
field("operator", choice("not", "!", "-", "~")),
|
||||
field("operand", $._prefix_expression),
|
||||
)),
|
||||
|
||||
postfix_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field("value", $.primary_expression),
|
||||
repeat1(field("operator", choice(
|
||||
seq(".", field("member", $.identifier)),
|
||||
seq("[", field("index", $.expression), "]"),
|
||||
"?",
|
||||
"!",
|
||||
))),
|
||||
)),
|
||||
|
||||
primary_expression: $ => choice(
|
||||
$.literal,
|
||||
$.identifier,
|
||||
$.block,
|
||||
$.if_expression,
|
||||
$.match_expression,
|
||||
$.loop_expression,
|
||||
$.for_expression,
|
||||
$.tuple_value,
|
||||
$.collection_literal,
|
||||
$.product_constructor,
|
||||
$.sum_constructor,
|
||||
$.bits_constructor,
|
||||
$.default_expression,
|
||||
),
|
||||
|
||||
tuple_value: $ => seq("(", listBodyAtLeastTwo($, $.expression), ")"),
|
||||
|
||||
collection_literal: $ => seq("[", listBody($, $.collection_entry), "]"),
|
||||
collection_entry: $ => choice(
|
||||
seq($.expression, optional(seq("=>", $.expression))),
|
||||
seq("...", $.expression),
|
||||
),
|
||||
|
||||
product_constructor: $ => seq(
|
||||
"&",
|
||||
optional($.type_name),
|
||||
".",
|
||||
"{",
|
||||
scopeBody($, $.product_entry),
|
||||
"}",
|
||||
),
|
||||
|
||||
product_entry: $ => choice(
|
||||
seq($.identifier, choice(":", "="), $.expression),
|
||||
seq("...", $.expression),
|
||||
),
|
||||
|
||||
sum_constructor: $ => seq("|", optional($.type_name), ".", $.identifier),
|
||||
bits_constructor: $ => seq("|", optional($.type_name), ".", "{", listBody($, $.identifier), "}"),
|
||||
|
||||
literal: $ => choice(
|
||||
$.signed_numeric_literal,
|
||||
$.string_literal,
|
||||
$.character_literal,
|
||||
$.boolean_literal,
|
||||
$.nil_literal,
|
||||
$.unit_literal,
|
||||
),
|
||||
|
||||
boolean_literal: _ => choice("true", "false"),
|
||||
nil_literal: _ => "nil",
|
||||
default_expression: _ => "default",
|
||||
unit_literal: _ => seq("(", ")"),
|
||||
signed_numeric_literal: $ => seq(optional("-"), choice(
|
||||
$.integer_literal,
|
||||
$.float_literal,
|
||||
alias($._trailing_dot_float, $.float_literal),
|
||||
)),
|
||||
signed_integer_literal: $ => seq(optional("-"), $.integer_literal),
|
||||
|
||||
integer_literal: _ => token(choice(
|
||||
/0[bB][01](?:_?[01])*/,
|
||||
/0[oO][0-7](?:_?[0-7])*/,
|
||||
/0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*/,
|
||||
/[0-9](?:_?[0-9])*/,
|
||||
)),
|
||||
|
||||
float_literal: _ => token(choice(
|
||||
/[0-9](?:_?[0-9])*\.[0-9](?:_?[0-9])*(?:[eE][+-]?[0-9](?:_?[0-9])*)?/,
|
||||
/\.[0-9](?:_?[0-9])*(?:[eE][+-]?[0-9](?:_?[0-9])*)?/,
|
||||
/[0-9](?:_?[0-9])*[eE][+-]?[0-9](?:_?[0-9])*/,
|
||||
)),
|
||||
|
||||
string_literal: _ => token(/'(?:[^'\\\r\n]|\\(?:[\\'nrt0]|u\{[0-9a-fA-F]{1,6}\}))*'/),
|
||||
character_literal: _ => token(/c'(?:[^'\\\r\n]|\\(?:[\\'nrt0]|u\{[0-9a-fA-F]{1,6}\}))'/),
|
||||
|
||||
type_name: $ => choice($.identifier, $.qualified_name),
|
||||
qualified_name: $ => seq($.identifier, repeat1(seq(".", $.identifier))),
|
||||
identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/,
|
||||
comment: _ => token(seq("#", /[^\r\n]*/)),
|
||||
|
||||
_direct_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "->")),
|
||||
_aggregate_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "|>")),
|
||||
_comparison_continuation: _ => token(seq(/\r?\n[ \t]*/, choice("<=", ">=", "==", "<", ">"))),
|
||||
_newline: _ => /\r?\n/,
|
||||
_newlines: $ => repeat1($._newline),
|
||||
_scope_separator: $ => choice($._newlines, ";"),
|
||||
},
|
||||
});
|
||||
|
||||
function scopeBody($, rule) {
|
||||
return seq(
|
||||
repeat($._newline),
|
||||
optional(seq(
|
||||
rule,
|
||||
repeat(seq($._scope_separator, rule)),
|
||||
repeat($._newline),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
function listBody($, rule) {
|
||||
return seq(
|
||||
repeat($._newline),
|
||||
optional(seq(
|
||||
rule,
|
||||
repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)),
|
||||
optional(","),
|
||||
repeat($._newline),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
function listBodyOneOrMore($, rule) {
|
||||
return seq(
|
||||
repeat($._newline),
|
||||
rule,
|
||||
repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)),
|
||||
optional(","),
|
||||
repeat($._newline),
|
||||
);
|
||||
}
|
||||
|
||||
function listBodyAtLeastTwo($, rule) {
|
||||
return seq(
|
||||
repeat($._newline),
|
||||
rule,
|
||||
choice(seq(",", repeat($._newline)), $._newlines),
|
||||
rule,
|
||||
repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)),
|
||||
optional(","),
|
||||
repeat($._newline),
|
||||
);
|
||||
}
|
||||
|
||||
function binaryExpression($, operator, precedence, continuationOperator = operator, continuationIsToken = false) {
|
||||
const operatorRule = continuationOperator === null
|
||||
? seq(operator, optional($._newline))
|
||||
: choice(
|
||||
seq(operator, optional($._newline)),
|
||||
continuationIsToken
|
||||
? continuationOperator
|
||||
: token(seq(/\r?\n[ \t]*/, continuationOperator)),
|
||||
);
|
||||
|
||||
return prec.left(precedence, seq(
|
||||
field("left", $._binary_expression),
|
||||
field("operator", operatorRule),
|
||||
field("right", $._binary_expression),
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "tree-sitter-lscript",
|
||||
"version": "0.1.0",
|
||||
"description": "LScript grammar for Tree-sitter",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"keywords": [
|
||||
"incremental",
|
||||
"parsing",
|
||||
"tree-sitter",
|
||||
"lscript"
|
||||
],
|
||||
"files": [
|
||||
"grammar.js",
|
||||
"tree-sitter.json",
|
||||
"docs/*",
|
||||
"queries/*",
|
||||
"scripts/*",
|
||||
"test/**",
|
||||
"src/**"
|
||||
],
|
||||
"devDependencies": {
|
||||
"tree-sitter-cli": "0.27.0"
|
||||
},
|
||||
"scripts": {
|
||||
"generate": "tree-sitter generate",
|
||||
"test": "tree-sitter test",
|
||||
"test:fixtures": "sh ./scripts/check-fixtures.sh",
|
||||
"test:queries": "tree-sitter query -p . --quiet queries/highlights.scm test/fixtures/valid/*.ls"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
[
|
||||
"and"
|
||||
"as"
|
||||
"bits"
|
||||
"break"
|
||||
"continue"
|
||||
"distinct"
|
||||
"else"
|
||||
"for"
|
||||
"if"
|
||||
"in"
|
||||
"loop"
|
||||
"match"
|
||||
"not"
|
||||
"or"
|
||||
"pub"
|
||||
"return"
|
||||
"use"
|
||||
] @keyword
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
(identifier) @variable
|
||||
(comment) @comment
|
||||
(default_expression) @keyword
|
||||
(boolean_literal) @constant.builtin
|
||||
(nil_literal) @constant.builtin
|
||||
(string_literal) @string
|
||||
(character_literal) @character
|
||||
(integer_literal) @number
|
||||
(float_literal) @number.float
|
||||
(label (identifier) @label)
|
||||
|
||||
(declaration name: (identifier) @variable.definition)
|
||||
(typed_parameter pattern: (identifier) @variable.parameter)
|
||||
(typed_tuple_component pattern: (identifier) @variable.parameter)
|
||||
(product_field name: (identifier) @property)
|
||||
(product_entry (identifier) @property)
|
||||
(sum_variant name: (identifier) @constructor)
|
||||
(bits_member (identifier) @constant)
|
||||
|
||||
[
|
||||
"=" "+=" "-=" "*=" "/=" "%=" "&=" "^=" "|="
|
||||
"==" "<" "<=" ">" ">="
|
||||
"+" "-" "*" "/" "%" "&" "|" "^" "~" "!"
|
||||
"->" "=>" "|>" "..<" "..=" "@"
|
||||
] @operator
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
tool_directory=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
tree_sitter=${TREE_SITTER:-"$tool_directory/node_modules/.bin/tree-sitter"}
|
||||
cache_directory=${TMPDIR:-/tmp}/tree-sitter-lscript-cache
|
||||
|
||||
cd "$tool_directory"
|
||||
|
||||
XDG_CACHE_HOME="$cache_directory" "$tree_sitter" parse \
|
||||
-p . --quiet --stat "$tool_directory"/test/fixtures/valid/*.ls
|
||||
|
||||
unexpected_success=0
|
||||
for fixture in "$tool_directory"/test/fixtures/invalid/*.ls; do
|
||||
if XDG_CACHE_HOME="$cache_directory" "$tree_sitter" parse \
|
||||
-p . --quiet "$fixture" >/dev/null 2>&1; then
|
||||
case "$(basename -- "$fixture")" in
|
||||
adjacent_equal_minus.ls|bang_inequality.ls|repeated_result.ls)
|
||||
;;
|
||||
*)
|
||||
echo "invalid fixture parsed without recovery errors: $fixture" >&2
|
||||
unexpected_success=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$unexpected_success" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Known recovery exceptions: adjacent_equal_minus, bang_inequality, repeated_result"
|
||||
+5632
File diff suppressed because it is too large
Load Diff
+2798
File diff suppressed because it is too large
Load Diff
+45982
File diff suppressed because it is too large
Load Diff
+129
@@ -0,0 +1,129 @@
|
||||
#include "tree_sitter/parser.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum TokenType {
|
||||
AND_CONTINUATION,
|
||||
OR_CONTINUATION,
|
||||
TRAILING_DOT_FLOAT,
|
||||
};
|
||||
|
||||
static bool is_identifier_character(int32_t character) {
|
||||
return (character >= 'a' && character <= 'z') ||
|
||||
(character >= 'A' && character <= 'Z') ||
|
||||
(character >= '0' && character <= '9') || character == '_';
|
||||
}
|
||||
|
||||
static bool scan_word_continuation(TSLexer *lexer, enum TokenType symbol,
|
||||
const char *word) {
|
||||
for (const char *character = word; *character != '\0'; character++) {
|
||||
if (lexer->lookahead != *character) {
|
||||
return false;
|
||||
}
|
||||
lexer->advance(lexer, false);
|
||||
}
|
||||
if (is_identifier_character(lexer->lookahead)) {
|
||||
return false;
|
||||
}
|
||||
lexer->mark_end(lexer);
|
||||
lexer->result_symbol = symbol;
|
||||
return true;
|
||||
}
|
||||
|
||||
void *tree_sitter_lscript_external_scanner_create(void) { return NULL; }
|
||||
|
||||
void tree_sitter_lscript_external_scanner_destroy(void *payload) {
|
||||
(void)payload;
|
||||
}
|
||||
|
||||
unsigned tree_sitter_lscript_external_scanner_serialize(void *payload,
|
||||
char *buffer) {
|
||||
(void)payload;
|
||||
(void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tree_sitter_lscript_external_scanner_deserialize(void *payload,
|
||||
const char *buffer,
|
||||
unsigned length) {
|
||||
(void)payload;
|
||||
(void)buffer;
|
||||
(void)length;
|
||||
}
|
||||
|
||||
bool tree_sitter_lscript_external_scanner_scan(void *payload, TSLexer *lexer,
|
||||
const bool *valid_symbols) {
|
||||
(void)payload;
|
||||
|
||||
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
|
||||
lexer->advance(lexer, true);
|
||||
}
|
||||
|
||||
if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
|
||||
if (lexer->lookahead == '\r') {
|
||||
lexer->advance(lexer, false);
|
||||
if (lexer->lookahead != '\n') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lexer->advance(lexer, false);
|
||||
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
|
||||
lexer->advance(lexer, false);
|
||||
}
|
||||
if (valid_symbols[AND_CONTINUATION] && lexer->lookahead == 'a') {
|
||||
return scan_word_continuation(lexer, AND_CONTINUATION, "and");
|
||||
}
|
||||
if (valid_symbols[OR_CONTINUATION] && lexer->lookahead == 'o') {
|
||||
return scan_word_continuation(lexer, OR_CONTINUATION, "or");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!valid_symbols[TRAILING_DOT_FLOAT]) {
|
||||
return false;
|
||||
}
|
||||
if (lexer->lookahead < '0' || lexer->lookahead > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool previous_was_digit = false;
|
||||
while ((lexer->lookahead >= '0' && lexer->lookahead <= '9') ||
|
||||
lexer->lookahead == '_') {
|
||||
if (lexer->lookahead == '_' && !previous_was_digit) {
|
||||
return false;
|
||||
}
|
||||
previous_was_digit = lexer->lookahead != '_';
|
||||
lexer->advance(lexer, false);
|
||||
}
|
||||
if (!previous_was_digit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lexer->lookahead != '.') {
|
||||
return false;
|
||||
}
|
||||
lexer->advance(lexer, false);
|
||||
if (lexer->lookahead == '.' ||
|
||||
(lexer->lookahead >= '0' && lexer->lookahead <= '9')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lexer->mark_end(lexer);
|
||||
if (lexer->lookahead == 'e' || lexer->lookahead == 'E') {
|
||||
lexer->advance(lexer, false);
|
||||
if (lexer->lookahead == '+' || lexer->lookahead == '-') {
|
||||
lexer->advance(lexer, false);
|
||||
}
|
||||
if (lexer->lookahead < '0' || lexer->lookahead > '9') {
|
||||
return false;
|
||||
}
|
||||
while ((lexer->lookahead >= '0' && lexer->lookahead <= '9') ||
|
||||
lexer->lookahead == '_') {
|
||||
lexer->advance(lexer, false);
|
||||
}
|
||||
lexer->mark_end(lexer);
|
||||
}
|
||||
|
||||
lexer->result_symbol = TRAILING_DOT_FLOAT;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef TREE_SITTER_ALLOC_H_
|
||||
#define TREE_SITTER_ALLOC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Allow clients to override allocation functions
|
||||
#ifdef TREE_SITTER_REUSE_ALLOCATOR
|
||||
|
||||
extern void *(*ts_current_malloc)(size_t size);
|
||||
extern void *(*ts_current_calloc)(size_t count, size_t size);
|
||||
extern void *(*ts_current_realloc)(void *ptr, size_t size);
|
||||
extern void (*ts_current_free)(void *ptr);
|
||||
|
||||
#ifndef ts_malloc
|
||||
#define ts_malloc ts_current_malloc
|
||||
#endif
|
||||
#ifndef ts_calloc
|
||||
#define ts_calloc ts_current_calloc
|
||||
#endif
|
||||
#ifndef ts_realloc
|
||||
#define ts_realloc ts_current_realloc
|
||||
#endif
|
||||
#ifndef ts_free
|
||||
#define ts_free ts_current_free
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ts_malloc
|
||||
#define ts_malloc malloc
|
||||
#endif
|
||||
#ifndef ts_calloc
|
||||
#define ts_calloc calloc
|
||||
#endif
|
||||
#ifndef ts_realloc
|
||||
#define ts_realloc realloc
|
||||
#endif
|
||||
#ifndef ts_free
|
||||
#define ts_free free
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_ALLOC_H_
|
||||
@@ -0,0 +1,336 @@
|
||||
#ifndef TREE_SITTER_ARRAY_H_
|
||||
#define TREE_SITTER_ARRAY_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "./alloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4101)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
|
||||
#define Array(T) \
|
||||
struct { \
|
||||
T *contents; \
|
||||
uint32_t size; \
|
||||
uint32_t capacity; \
|
||||
}
|
||||
|
||||
/// Initialize an array.
|
||||
#define array_init(self) \
|
||||
((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL)
|
||||
|
||||
/// Create an empty array.
|
||||
#define array_new() \
|
||||
{ NULL, 0, 0 }
|
||||
|
||||
/// Get a pointer to the element at a given `index` in the array.
|
||||
#define array_get(self, _index) \
|
||||
(assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index])
|
||||
|
||||
/// Get a pointer to the first element in the array.
|
||||
#define array_front(self) array_get(self, 0)
|
||||
|
||||
/// Get a pointer to the last element in the array.
|
||||
#define array_back(self) array_get(self, (self)->size - 1)
|
||||
|
||||
/// Clear the array, setting its size to zero. Note that this does not free any
|
||||
/// memory allocated for the array's contents.
|
||||
#define array_clear(self) ((self)->size = 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define _array__cast(self, expr) (decltype((self)->contents))(expr)
|
||||
#else
|
||||
#define _array__cast(self, expr) (expr)
|
||||
#endif
|
||||
|
||||
/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is
|
||||
/// less than the array's current capacity, this function has no effect.
|
||||
#define array_reserve(self, new_capacity) \
|
||||
((self)->contents = _array__cast(self, _array__reserve( \
|
||||
(void *)(self)->contents, &(self)->capacity, \
|
||||
array_elem_size(self), new_capacity)) \
|
||||
)
|
||||
|
||||
/// Free any memory allocated for this array. Note that this does not free any
|
||||
/// memory allocated for the array's contents.
|
||||
#define array_delete(self) \
|
||||
do { \
|
||||
if ((self)->contents) ts_free((self)->contents); \
|
||||
(self)->contents = NULL; \
|
||||
(self)->size = 0; \
|
||||
(self)->capacity = 0; \
|
||||
} while (0)
|
||||
|
||||
/// Push a new `element` onto the end of the array.
|
||||
#define array_push(self, element) \
|
||||
do { \
|
||||
(self)->contents = _array__cast(self, _array__grow( \
|
||||
(void *)(self)->contents, (self)->size, &(self)->capacity, \
|
||||
1, array_elem_size(self) \
|
||||
)); \
|
||||
(self)->contents[(self)->size++] = (element); \
|
||||
} while(0)
|
||||
|
||||
/// Increase the array's size by `count` elements.
|
||||
/// New elements are zero-initialized.
|
||||
#define array_grow_by(self, count) \
|
||||
do { \
|
||||
if ((count) == 0) break; \
|
||||
(self)->contents = _array__cast(self, _array__grow( \
|
||||
(self)->contents, (self)->size, &(self)->capacity, \
|
||||
count, array_elem_size(self) \
|
||||
)); \
|
||||
memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \
|
||||
(self)->size += (count); \
|
||||
} while (0)
|
||||
|
||||
/// Append all elements from one array to the end of another.
|
||||
#define array_push_all(self, other) \
|
||||
array_extend((self), (other)->size, (other)->contents)
|
||||
|
||||
/// Append `count` elements to the end of the array, reading their values from the
|
||||
/// `contents` pointer.
|
||||
#define array_extend(self, count, other_contents) \
|
||||
((self)->contents = _array__cast(self, _array__splice( \
|
||||
(void*)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), (self)->size, 0, count, other_contents \
|
||||
)))
|
||||
|
||||
/// Remove `old_count` elements from the array starting at the given `index`. At
|
||||
/// the same index, insert `new_count` new elements, reading their values from the
|
||||
/// `new_contents` pointer.
|
||||
#define array_splice(self, _index, old_count, new_count, new_contents) \
|
||||
((self)->contents = _array__cast(self, _array__splice( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), _index, old_count, new_count, new_contents \
|
||||
)))
|
||||
|
||||
/// Insert one `element` into the array at the given `index`.
|
||||
#define array_insert(self, _index, element) \
|
||||
((self)->contents = _array__cast(self, _array__splice( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), _index, 0, 1, &(element) \
|
||||
)))
|
||||
|
||||
/// Remove one element from the array at the given `index`.
|
||||
#define array_erase(self, _index) \
|
||||
_array__erase((void *)(self)->contents, &(self)->size, array_elem_size(self), _index)
|
||||
|
||||
/// Pop the last element off the array, returning the element by value.
|
||||
#define array_pop(self) ((self)->contents[--(self)->size])
|
||||
|
||||
/// Assign the contents of one array to another, reallocating if necessary.
|
||||
#define array_assign(self, other) \
|
||||
((self)->contents = _array__cast(self, _array__assign( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
(const void *)(other)->contents, (other)->size, array_elem_size(self) \
|
||||
)))
|
||||
|
||||
/// Swap one array with another
|
||||
#define array_swap(self, other) \
|
||||
do { \
|
||||
void *_array_swap_tmp = (void *)(self)->contents; \
|
||||
(self)->contents = (other)->contents; \
|
||||
(other)->contents = _array__cast(other, _array_swap_tmp); \
|
||||
_array__swap(&(self)->size, &(self)->capacity, \
|
||||
&(other)->size, &(other)->capacity); \
|
||||
} while (0)
|
||||
|
||||
/// Get the size of the array contents
|
||||
#define array_elem_size(self) (sizeof *(self)->contents)
|
||||
|
||||
/// Search a sorted array for a given `needle` value, using the given `compare`
|
||||
/// callback to determine the order.
|
||||
///
|
||||
/// If an existing element is found to be equal to `needle`, then the `index`
|
||||
/// out-parameter is set to the existing value's index, and the `exists`
|
||||
/// out-parameter is set to true. Otherwise, `index` is set to an index where
|
||||
/// `needle` should be inserted in order to preserve the sorting, and `exists`
|
||||
/// is set to false.
|
||||
#define array_search_sorted_with(self, compare, needle, _index, _exists) \
|
||||
_array__search_sorted(self, 0, compare, , needle, _index, _exists)
|
||||
|
||||
/// Search a sorted array for a given `needle` value, using integer comparisons
|
||||
/// of a given struct field (specified with a leading dot) to determine the order.
|
||||
///
|
||||
/// See also `array_search_sorted_with`.
|
||||
#define array_search_sorted_by(self, field, needle, _index, _exists) \
|
||||
_array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists)
|
||||
|
||||
/// Insert a given `value` into a sorted array, using the given `compare`
|
||||
/// callback to determine the order.
|
||||
#define array_insert_sorted_with(self, compare, value) \
|
||||
do { \
|
||||
unsigned _index, _exists; \
|
||||
array_search_sorted_with(self, compare, &(value), &_index, &_exists); \
|
||||
if (!_exists) array_insert(self, _index, value); \
|
||||
} while (0)
|
||||
|
||||
/// Insert a given `value` into a sorted array, using integer comparisons of
|
||||
/// a given struct field (specified with a leading dot) to determine the order.
|
||||
///
|
||||
/// See also `array_search_sorted_by`.
|
||||
#define array_insert_sorted_by(self, field, value) \
|
||||
do { \
|
||||
unsigned _index, _exists; \
|
||||
array_search_sorted_by(self, field, (value) field, &_index, &_exists); \
|
||||
if (!_exists) array_insert(self, _index, value); \
|
||||
} while (0)
|
||||
|
||||
// Private
|
||||
|
||||
// Pointers to individual `Array` fields (rather than the entire `Array` itself)
|
||||
// are passed to the various `_array__*` functions below to address strict aliasing
|
||||
// violations that arises when the _entire_ `Array` struct is passed as `Array(void)*`.
|
||||
//
|
||||
// The `Array` type itself was not altered as a solution in order to avoid breakage
|
||||
// with existing consumers (in particular, parsers with external scanners).
|
||||
|
||||
/// This is not what you're looking for, see `array_erase`.
|
||||
static inline void _array__erase(void* self_contents, uint32_t *size,
|
||||
size_t element_size, uint32_t index) {
|
||||
assert(index < *size);
|
||||
char *contents = (char *)self_contents;
|
||||
memmove(contents + index * element_size, contents + (index + 1) * element_size,
|
||||
(*size - index - 1) * element_size);
|
||||
(*size)--;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_reserve`.
|
||||
static inline void *_array__reserve(void *contents, uint32_t *capacity,
|
||||
size_t element_size, uint32_t new_capacity) {
|
||||
void *new_contents = contents;
|
||||
if (new_capacity > *capacity) {
|
||||
if (contents) {
|
||||
new_contents = ts_realloc(contents, new_capacity * element_size);
|
||||
} else {
|
||||
new_contents = ts_malloc(new_capacity * element_size);
|
||||
}
|
||||
*capacity = new_capacity;
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_assign`.
|
||||
static inline void *_array__assign(void* self_contents, uint32_t *self_size, uint32_t *self_capacity,
|
||||
const void *other_contents, uint32_t other_size, size_t element_size) {
|
||||
void *new_contents = _array__reserve(self_contents, self_capacity, element_size, other_size);
|
||||
*self_size = other_size;
|
||||
memcpy(new_contents, other_contents, *self_size * element_size);
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_swap`.
|
||||
static inline void _array__swap(uint32_t *self_size, uint32_t *self_capacity,
|
||||
uint32_t *other_size, uint32_t *other_capacity) {
|
||||
uint32_t tmp_size = *self_size;
|
||||
uint32_t tmp_capacity = *self_capacity;
|
||||
*self_size = *other_size;
|
||||
*self_capacity = *other_capacity;
|
||||
*other_size = tmp_size;
|
||||
*other_capacity = tmp_capacity;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_push` or `array_grow_by`.
|
||||
static inline void *_array__grow(void *contents, uint32_t size, uint32_t *capacity,
|
||||
uint32_t count, size_t element_size) {
|
||||
void *new_contents = contents;
|
||||
uint32_t new_size = size + count;
|
||||
if (new_size > *capacity) {
|
||||
uint32_t new_capacity = *capacity * 2;
|
||||
if (new_capacity < 8) new_capacity = 8;
|
||||
if (new_capacity < new_size) new_capacity = new_size;
|
||||
new_contents = _array__reserve(contents, capacity, element_size, new_capacity);
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_splice`.
|
||||
static inline void *_array__splice(void *self_contents, uint32_t *size, uint32_t *capacity,
|
||||
size_t element_size,
|
||||
uint32_t index, uint32_t old_count,
|
||||
uint32_t new_count, const void *elements) {
|
||||
uint32_t new_size = *size + new_count - old_count;
|
||||
uint32_t old_end = index + old_count;
|
||||
uint32_t new_end = index + new_count;
|
||||
assert(old_end <= *size);
|
||||
|
||||
void *new_contents = _array__reserve(self_contents, capacity, element_size, new_size);
|
||||
|
||||
char *contents = (char *)new_contents;
|
||||
if (*size > old_end) {
|
||||
memmove(
|
||||
contents + new_end * element_size,
|
||||
contents + old_end * element_size,
|
||||
(*size - old_end) * element_size
|
||||
);
|
||||
}
|
||||
if (new_count > 0) {
|
||||
if (elements) {
|
||||
memcpy(
|
||||
(contents + index * element_size),
|
||||
elements,
|
||||
new_count * element_size
|
||||
);
|
||||
} else {
|
||||
memset(
|
||||
(contents + index * element_size),
|
||||
0,
|
||||
new_count * element_size
|
||||
);
|
||||
}
|
||||
}
|
||||
*size += new_count - old_count;
|
||||
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// A binary search routine, based on Rust's `std::slice::binary_search_by`.
|
||||
/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`.
|
||||
#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \
|
||||
do { \
|
||||
*(_index) = start; \
|
||||
*(_exists) = false; \
|
||||
uint32_t size = (self)->size - *(_index); \
|
||||
if (size == 0) break; \
|
||||
int comparison; \
|
||||
while (size > 1) { \
|
||||
uint32_t half_size = size / 2; \
|
||||
uint32_t mid_index = *(_index) + half_size; \
|
||||
comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \
|
||||
if (comparison <= 0) *(_index) = mid_index; \
|
||||
size -= half_size; \
|
||||
} \
|
||||
comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \
|
||||
if (comparison == 0) *(_exists) = true; \
|
||||
else if (comparison < 0) *(_index) += 1; \
|
||||
} while (0)
|
||||
|
||||
/// Helper macro for the `_sorted_by` routines below. This takes the left (existing)
|
||||
/// parameter by reference in order to work with the generic sorting function above.
|
||||
#define _compare_int(a, b) ((int)*(a) - (int)(b))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_ARRAY_H_
|
||||
@@ -0,0 +1,286 @@
|
||||
#ifndef TREE_SITTER_PARSER_H_
|
||||
#define TREE_SITTER_PARSER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ts_builtin_sym_error ((TSSymbol)-1)
|
||||
#define ts_builtin_sym_end 0
|
||||
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
|
||||
|
||||
#ifndef TREE_SITTER_API_H_
|
||||
typedef uint16_t TSStateId;
|
||||
typedef uint16_t TSSymbol;
|
||||
typedef uint16_t TSFieldId;
|
||||
typedef struct TSLanguage TSLanguage;
|
||||
typedef struct TSLanguageMetadata {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint8_t patch_version;
|
||||
} TSLanguageMetadata;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
TSFieldId field_id;
|
||||
uint8_t child_index;
|
||||
bool inherited;
|
||||
} TSFieldMapEntry;
|
||||
|
||||
// Used to index the field and supertype maps.
|
||||
typedef struct {
|
||||
uint16_t index;
|
||||
uint16_t length;
|
||||
} TSMapSlice;
|
||||
|
||||
typedef struct {
|
||||
bool visible;
|
||||
bool named;
|
||||
bool supertype;
|
||||
} TSSymbolMetadata;
|
||||
|
||||
typedef struct TSLexer TSLexer;
|
||||
|
||||
struct TSLexer {
|
||||
int32_t lookahead;
|
||||
TSSymbol result_symbol;
|
||||
void (*advance)(TSLexer *, bool);
|
||||
void (*mark_end)(TSLexer *);
|
||||
uint32_t (*get_column)(TSLexer *);
|
||||
bool (*is_at_included_range_start)(const TSLexer *);
|
||||
bool (*eof)(const TSLexer *);
|
||||
void (*log)(const TSLexer *, const char *, ...);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TSParseActionTypeShift,
|
||||
TSParseActionTypeReduce,
|
||||
TSParseActionTypeAccept,
|
||||
TSParseActionTypeRecover,
|
||||
} TSParseActionType;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t type;
|
||||
TSStateId state;
|
||||
bool extra;
|
||||
bool repetition;
|
||||
} shift;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t child_count;
|
||||
TSSymbol symbol;
|
||||
int16_t dynamic_precedence;
|
||||
uint16_t production_id;
|
||||
} reduce;
|
||||
uint8_t type;
|
||||
} TSParseAction;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
} TSLexMode;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
uint16_t reserved_word_set_id;
|
||||
} TSLexerMode;
|
||||
|
||||
typedef union {
|
||||
TSParseAction action;
|
||||
struct {
|
||||
uint8_t count;
|
||||
bool reusable;
|
||||
} entry;
|
||||
} TSParseActionEntry;
|
||||
|
||||
typedef struct {
|
||||
int32_t start;
|
||||
int32_t end;
|
||||
} TSCharacterRange;
|
||||
|
||||
struct TSLanguage {
|
||||
uint32_t abi_version;
|
||||
uint32_t symbol_count;
|
||||
uint32_t alias_count;
|
||||
uint32_t token_count;
|
||||
uint32_t external_token_count;
|
||||
uint32_t state_count;
|
||||
uint32_t large_state_count;
|
||||
uint32_t production_id_count;
|
||||
uint32_t field_count;
|
||||
uint16_t max_alias_sequence_length;
|
||||
const uint16_t *parse_table;
|
||||
const uint16_t *small_parse_table;
|
||||
const uint32_t *small_parse_table_map;
|
||||
const TSParseActionEntry *parse_actions;
|
||||
const char * const *symbol_names;
|
||||
const char * const *field_names;
|
||||
const TSMapSlice *field_map_slices;
|
||||
const TSFieldMapEntry *field_map_entries;
|
||||
const TSSymbolMetadata *symbol_metadata;
|
||||
const TSSymbol *public_symbol_map;
|
||||
const uint16_t *alias_map;
|
||||
const TSSymbol *alias_sequences;
|
||||
const TSLexerMode *lex_modes;
|
||||
bool (*lex_fn)(TSLexer *, TSStateId);
|
||||
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
|
||||
TSSymbol keyword_capture_token;
|
||||
struct {
|
||||
const bool *states;
|
||||
const TSSymbol *symbol_map;
|
||||
void *(*create)(void);
|
||||
void (*destroy)(void *);
|
||||
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
|
||||
unsigned (*serialize)(void *, char *);
|
||||
void (*deserialize)(void *, const char *, unsigned);
|
||||
} external_scanner;
|
||||
const TSStateId *primary_state_ids;
|
||||
const char *name;
|
||||
const TSSymbol *reserved_words;
|
||||
uint16_t max_reserved_word_set_size;
|
||||
uint32_t supertype_count;
|
||||
const TSSymbol *supertype_symbols;
|
||||
const TSMapSlice *supertype_map_slices;
|
||||
const TSSymbol *supertype_map_entries;
|
||||
TSLanguageMetadata metadata;
|
||||
};
|
||||
|
||||
static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) {
|
||||
uint32_t index = 0;
|
||||
uint32_t size = len - index;
|
||||
while (size > 1) {
|
||||
uint32_t half_size = size / 2;
|
||||
uint32_t mid_index = index + half_size;
|
||||
const TSCharacterRange *range = &ranges[mid_index];
|
||||
if (lookahead >= range->start && lookahead <= range->end) {
|
||||
return true;
|
||||
} else if (lookahead > range->end) {
|
||||
index = mid_index;
|
||||
}
|
||||
size -= half_size;
|
||||
}
|
||||
const TSCharacterRange *range = &ranges[index];
|
||||
return (lookahead >= range->start && lookahead <= range->end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexer Macros
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define UNUSED __pragma(warning(suppress : 4101))
|
||||
#else
|
||||
#define UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#define START_LEXER() \
|
||||
bool result = false; \
|
||||
bool skip = false; \
|
||||
UNUSED \
|
||||
bool eof = false; \
|
||||
int32_t lookahead; \
|
||||
goto start; \
|
||||
next_state: \
|
||||
lexer->advance(lexer, skip); \
|
||||
start: \
|
||||
skip = false; \
|
||||
lookahead = lexer->lookahead;
|
||||
|
||||
#define ADVANCE(state_value) \
|
||||
{ \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ADVANCE_MAP(...) \
|
||||
{ \
|
||||
static const uint16_t map[] = { __VA_ARGS__ }; \
|
||||
for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \
|
||||
if (map[i] == lookahead) { \
|
||||
state = map[i + 1]; \
|
||||
goto next_state; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SKIP(state_value) \
|
||||
{ \
|
||||
skip = true; \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ACCEPT_TOKEN(symbol_value) \
|
||||
result = true; \
|
||||
lexer->result_symbol = symbol_value; \
|
||||
lexer->mark_end(lexer);
|
||||
|
||||
#define END_STATE() return result;
|
||||
|
||||
/*
|
||||
* Parse Table Macros
|
||||
*/
|
||||
|
||||
#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
|
||||
|
||||
#define STATE(id) id
|
||||
|
||||
#define ACTIONS(id) id
|
||||
|
||||
#define SHIFT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = (state_value) \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_REPEAT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = (state_value), \
|
||||
.repetition = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_EXTRA() \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.extra = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define REDUCE(symbol_name, children, precedence, prod_id) \
|
||||
{{ \
|
||||
.reduce = { \
|
||||
.type = TSParseActionTypeReduce, \
|
||||
.symbol = symbol_name, \
|
||||
.child_count = children, \
|
||||
.dynamic_precedence = precedence, \
|
||||
.production_id = prod_id \
|
||||
}, \
|
||||
}}
|
||||
|
||||
#define RECOVER() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeRecover \
|
||||
}}
|
||||
|
||||
#define ACCEPT_INPUT() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeAccept \
|
||||
}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_PARSER_H_
|
||||
@@ -0,0 +1,235 @@
|
||||
==================
|
||||
Declarations
|
||||
==================
|
||||
|
||||
immutable :: 1
|
||||
mutable := 2
|
||||
typed: i32: 3
|
||||
deferred: i32
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(declaration
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(integer_literal)))
|
||||
(declaration
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(integer_literal)))
|
||||
(declaration
|
||||
(identifier)
|
||||
(primitive_type)
|
||||
(signed_numeric_literal
|
||||
(integer_literal)))
|
||||
(declaration
|
||||
(identifier)
|
||||
(primitive_type)))
|
||||
|
||||
==================
|
||||
Types and constructors
|
||||
==================
|
||||
|
||||
Point :: &{
|
||||
x: i32:
|
||||
y: i32 = 0
|
||||
}
|
||||
point := &Point.{ x: 1; y = 2 }
|
||||
maybe: []{i32?}: [nil, 1->|.some]
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(declaration
|
||||
(identifier)
|
||||
(type_definition
|
||||
(product_type
|
||||
(product_field
|
||||
(identifier)
|
||||
(primitive_type))
|
||||
(product_field
|
||||
(identifier)
|
||||
(primitive_type)
|
||||
(signed_numeric_literal
|
||||
(integer_literal))))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(product_constructor
|
||||
(identifier)
|
||||
(product_entry
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(integer_literal)))
|
||||
(product_entry
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(integer_literal)))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(array_type
|
||||
(grouped_type
|
||||
(optional_type
|
||||
(primitive_type))))
|
||||
(collection_literal
|
||||
(collection_entry
|
||||
(nil_literal))
|
||||
(collection_entry
|
||||
(pipeline_expression
|
||||
(signed_numeric_literal
|
||||
(integer_literal))
|
||||
(direct_pipeline_stage
|
||||
(pipeline_callable
|
||||
(sum_constructor
|
||||
(identifier)))))))))
|
||||
|
||||
==================
|
||||
Control flow and patterns
|
||||
==================
|
||||
|
||||
describe: []i32 -> str: @ values: []i32 { match values {
|
||||
[] => 'empty'
|
||||
[first, ...rest] if first > 0 => 'positive'
|
||||
_ => 'other'
|
||||
} }
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(declaration
|
||||
(identifier)
|
||||
(function_type
|
||||
(array_type
|
||||
(primitive_type))
|
||||
(primitive_type))
|
||||
(function_literal
|
||||
(typed_parameter
|
||||
(identifier)
|
||||
(array_type
|
||||
(primitive_type)))
|
||||
(block
|
||||
(match_expression
|
||||
(identifier)
|
||||
(match_arm
|
||||
(pattern
|
||||
(array_pattern))
|
||||
(string_literal))
|
||||
(match_arm
|
||||
(pattern
|
||||
(array_pattern
|
||||
(array_pattern_entry
|
||||
(pattern
|
||||
(identifier)))
|
||||
(array_pattern_entry
|
||||
(identifier))))
|
||||
(binary_expression
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(integer_literal)))
|
||||
(string_literal))
|
||||
(match_arm
|
||||
(pattern)
|
||||
(string_literal)))))))
|
||||
|
||||
==================
|
||||
Physical-line continuations
|
||||
==================
|
||||
|
||||
value := 'text' # the newline remains significant
|
||||
->reverse_chrs
|
||||
|
||||
ready :: first
|
||||
and second
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(declaration
|
||||
(identifier)
|
||||
(pipeline_expression
|
||||
(string_literal)
|
||||
(comment)
|
||||
(continuation_pipeline_stage
|
||||
(pipeline_callable
|
||||
(identifier)))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(binary_expression
|
||||
(identifier)
|
||||
(identifier))))
|
||||
==================
|
||||
Compact range after an integer
|
||||
==================
|
||||
|
||||
slice :: (source,0..<limit)
|
||||
trailing_dot :: 1.
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(declaration
|
||||
(identifier)
|
||||
(tuple_value
|
||||
(identifier)
|
||||
(range_expression
|
||||
(signed_numeric_literal
|
||||
(integer_literal))
|
||||
(identifier))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(float_literal))))
|
||||
|
||||
==================
|
||||
Typed function declarations
|
||||
==================
|
||||
|
||||
double :: @ value: i32 { value * 2 }
|
||||
square :: @ value: i32 => i32 { value * value }
|
||||
add :: @ (left: i32, right: i32) { left + right }
|
||||
unit :: @ { () }
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(declaration
|
||||
(identifier)
|
||||
(function_literal
|
||||
(typed_parameter
|
||||
(identifier)
|
||||
(primitive_type))
|
||||
(block
|
||||
(binary_expression
|
||||
(identifier)
|
||||
(signed_numeric_literal
|
||||
(integer_literal))))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(function_literal
|
||||
(typed_parameter
|
||||
(identifier)
|
||||
(primitive_type))
|
||||
(primitive_type)
|
||||
(block
|
||||
(binary_expression
|
||||
(identifier)
|
||||
(identifier)))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(function_literal
|
||||
(typed_tuple_pattern
|
||||
(typed_tuple_component
|
||||
(identifier)
|
||||
(primitive_type))
|
||||
(typed_tuple_component
|
||||
(identifier)
|
||||
(primitive_type)))
|
||||
(block
|
||||
(binary_expression
|
||||
(identifier)
|
||||
(identifier)))))
|
||||
(declaration
|
||||
(identifier)
|
||||
(function_literal
|
||||
(block
|
||||
(unit_literal)))))
|
||||
@@ -0,0 +1,2 @@
|
||||
value := 0
|
||||
value=-1
|
||||
@@ -0,0 +1 @@
|
||||
identity :: @ value { value }
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
text :: 'bad\qescape'
|
||||
+1
@@ -0,0 +1 @@
|
||||
different :: left != right
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
->initialize
|
||||
+1
@@ -0,0 +1 @@
|
||||
Point :: &{ x: i32:, y: i32: }
|
||||
@@ -0,0 +1 @@
|
||||
value: i32:
|
||||
+1
@@ -0,0 +1 @@
|
||||
outer := inner := 1
|
||||
@@ -0,0 +1 @@
|
||||
text :: "double quoted"
|
||||
+1
@@ -0,0 +1 @@
|
||||
values := [1,, 2]
|
||||
@@ -0,0 +1 @@
|
||||
(left, right): (i32, i32): (1, 2)
|
||||
@@ -0,0 +1,2 @@
|
||||
identity :: @ value: i32
|
||||
{ value }
|
||||
+1
@@ -0,0 +1 @@
|
||||
in := 1
|
||||
@@ -0,0 +1 @@
|
||||
answer: () -> i32: @ 42
|
||||
@@ -0,0 +1,2 @@
|
||||
identity: i32 -> i32:
|
||||
@ value: i32 { value }
|
||||
@@ -0,0 +1 @@
|
||||
identity: i32 -> i32: value@ value
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
Choice :: |{
|
||||
one
|
||||
}
|
||||
|
||||
describe :: @ choice: Choice -> str { match choice {
|
||||
|.one =>
|
||||
'one'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{start as {line}} :: value
|
||||
+1
@@ -0,0 +1 @@
|
||||
value :: 1__000
|
||||
+1
@@ -0,0 +1 @@
|
||||
value :: 1.0f
|
||||
@@ -0,0 +1 @@
|
||||
StringIterator :: Iter(str)
|
||||
@@ -0,0 +1,2 @@
|
||||
Error :: |{ failed }
|
||||
Bad :: i32? ! Error
|
||||
@@ -0,0 +1,2 @@
|
||||
Error :: |{ failed }
|
||||
Bad :: i32 ! Error?
|
||||
@@ -0,0 +1 @@
|
||||
value :: (1 + 2)
|
||||
@@ -0,0 +1,3 @@
|
||||
value := 1
|
||||
|
||||
->to_str
|
||||
@@ -0,0 +1,3 @@
|
||||
value := 1
|
||||
# this line terminates the preceding expression
|
||||
->to_str
|
||||
@@ -0,0 +1 @@
|
||||
value :: 1 + 2->to_str
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
FirstError :: |{ failed }
|
||||
SecondError :: |{ failed }
|
||||
Bad :: i32 ! FirstError ! SecondError
|
||||
@@ -0,0 +1,4 @@
|
||||
value := {
|
||||
first;
|
||||
second
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
value := 1
|
||||
value >>= 1
|
||||
+1
@@ -0,0 +1 @@
|
||||
value :: 1 << 2
|
||||
@@ -0,0 +1 @@
|
||||
identity :: @ (value: i32) { value }
|
||||
@@ -0,0 +1 @@
|
||||
identity :: @ (value: i32,) -> i32 { value }
|
||||
+1
@@ -0,0 +1 @@
|
||||
value :: (1,)
|
||||
+1
@@ -0,0 +1 @@
|
||||
value :: if true then 1 else 2
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
value := 1->
|
||||
to_str
|
||||
@@ -0,0 +1 @@
|
||||
value: i32 =
|
||||
+1
@@ -0,0 +1 @@
|
||||
value := { 1; }
|
||||
@@ -0,0 +1 @@
|
||||
iterator: Iter<i32>=
|
||||
@@ -0,0 +1 @@
|
||||
Bad :: (i32,)
|
||||
+1
@@ -0,0 +1 @@
|
||||
value :=
|
||||
+1
@@ -0,0 +1 @@
|
||||
use module.*
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
numbers := [1, 2, 3]
|
||||
more_numbers := [0, ...numbers, 4]
|
||||
|
||||
codes := ['ok' => 200, 'missing' => 404]
|
||||
more_codes := [...codes, 'failed' => 500]
|
||||
|
||||
empty_numbers: []i32: []
|
||||
empty_codes: [str]i32: []
|
||||
optional_numbers: []i32?: nil
|
||||
optional_elements: []{i32?}: [nil, 1->|.some]
|
||||
|
||||
Options :: &{
|
||||
name: str: 'default'
|
||||
count: i32 = 0
|
||||
}
|
||||
|
||||
defaults := &Options.{}
|
||||
overridden := &Options.{ ...defaults; count = 2 }
|
||||
anonymous := &.{ label: 'value'; count = 1 }
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
choose: (bit, i32, i32) -> i32: @ (condition: bit, when_true: i32, when_false: i32) { if condition { when_true } else { when_false } }
|
||||
|
||||
choose_block: (bit, i32, i32) -> i32: @ (condition: bit, when_true: i32, when_false: i32) { if condition {
|
||||
when_true
|
||||
} else if not condition {
|
||||
when_false
|
||||
} else {
|
||||
0
|
||||
} }
|
||||
|
||||
first_positive: []i32 -> i32?: @ values: []i32 {
|
||||
for value in values {
|
||||
if value > 0 {
|
||||
return value->|.some
|
||||
}
|
||||
}
|
||||
nil
|
||||
}
|
||||
|
||||
labelled_search: []i32 -> i32: @ values: []i32 { loop `outer {
|
||||
for value in values `items {
|
||||
if value > 0 {
|
||||
break `outer value
|
||||
}
|
||||
continue `items
|
||||
}
|
||||
break 0
|
||||
} }
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
immutable :: 1
|
||||
mutable := 2
|
||||
typed_immutable: i32: 3
|
||||
typed_mutable: i32 = 4
|
||||
deferred: i32
|
||||
deferred = 5
|
||||
then :: 6
|
||||
|
||||
(left, right) :: (1, 2)
|
||||
(mutable_left, mutable_right) := (3, 4)
|
||||
|
||||
Point :: &{
|
||||
x: i32:
|
||||
y: i32 =
|
||||
}
|
||||
|
||||
point := &Point.{ x: 10; y = 20 }
|
||||
{x as horizontal, y} :: point
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
double: i32 -> i32: @ value: i32 { value * 2 }
|
||||
|
||||
square: i32 => i32: @ value: i32 { value * value }
|
||||
|
||||
add: (i32, i32) -> i32: @ (left: i32, right: i32) { left + right }
|
||||
|
||||
normalize: i32 -> i32: @ value: i32 {
|
||||
adjusted := value + 1
|
||||
adjusted->double
|
||||
}
|
||||
|
||||
apply: (i32, i32 -> i32) -> i32: @ (value, operation): {(i32, i32 -> i32)} { value->operation }
|
||||
|
||||
factory: i32 => i32 -> i32: @ offset: i32 { @ value: i32 { value + offset } }
|
||||
|
||||
initialize: () -> i32: @ () { 1 }
|
||||
|
||||
computed := (4, @ value: i32 { value + 1 })->apply
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
inline_array := [1, 2, 3]
|
||||
|
||||
newline_array := [
|
||||
1
|
||||
2
|
||||
3
|
||||
]
|
||||
|
||||
comma_array := [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
inline_block := { first := 1; second := 2; first + second }
|
||||
|
||||
InlinePoint :: &{ x: i32:; y: i32: }
|
||||
inline_point := &InlinePoint.{ x: 1; y: 2 }
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
decimal :: 1_000_000
|
||||
binary :: 0b1010_0101
|
||||
octal :: 0o755
|
||||
hexadecimal :: 0xCAFE_beef
|
||||
leading_zeroes :: 007
|
||||
|
||||
fraction_without_integer :: .5
|
||||
fraction_without_digits :: 1.
|
||||
scientific :: 1.25e-10
|
||||
scientific_integer :: 1E+4
|
||||
negative_minimum: i8: -128
|
||||
spaced_negative: i8: - 127
|
||||
|
||||
text :: 'can\'t\nstop'
|
||||
letter :: c'é'
|
||||
escaped_letter :: c'\u{1f642}'
|
||||
nul :: c'\0'
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
use math
|
||||
use math as arithmetic
|
||||
use math.{pi, sqrt as square_root}
|
||||
|
||||
ExportedType :: distinct i32
|
||||
exported :: 1
|
||||
circle: f64: pi
|
||||
root: f64: 4.0->square_root
|
||||
qualified_root: f64: 9.0->arithmetic.sqrt
|
||||
|
||||
pub {
|
||||
ExportedType
|
||||
exported
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
ready: bit: true
|
||||
connected: bit: true
|
||||
busy: bit: false
|
||||
|
||||
can_run :: ready
|
||||
and connected
|
||||
and not busy
|
||||
|
||||
one: i32: 1
|
||||
two: i32: 2
|
||||
three: i32: 3
|
||||
sum :: one
|
||||
+ two
|
||||
* three
|
||||
|
||||
eight: i32: 8
|
||||
five: i32: 5
|
||||
difference :: eight -
|
||||
five
|
||||
|
||||
first_mask: u8: 1
|
||||
second_mask: u8: 2
|
||||
mask :: first_mask |
|
||||
second_mask
|
||||
|
||||
is_ordered :: sum
|
||||
> difference
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
Location :: |{
|
||||
unknown
|
||||
known: (f32, f32)
|
||||
}
|
||||
|
||||
describe: Location -> str: @ location: Location { match location {
|
||||
|.unknown => 'unknown'
|
||||
|.known (latitude, longitude) if latitude >= 0.0 => 'known'
|
||||
|.known (_, _) => 'known'
|
||||
} }
|
||||
|
||||
Point :: &{
|
||||
x: i32:
|
||||
y: i32:
|
||||
}
|
||||
|
||||
sum_point: Point -> i32: @ point: Point {
|
||||
{x as horizontal, y as vertical} :: point
|
||||
horizontal + vertical
|
||||
}
|
||||
|
||||
classify_array: []i32 -> str: @ values: []i32 { match values {
|
||||
[] => 'empty'
|
||||
[only] => 'one'
|
||||
[first, ...rest] => 'many'
|
||||
} }
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
Error :: distinct |{
|
||||
failed
|
||||
}
|
||||
|
||||
reversed := 'value'->reverse_chrs
|
||||
sum_text := {1 + 2}->to_str
|
||||
|
||||
continued := 'value'
|
||||
->reverse_chrs
|
||||
->reverse_chrs
|
||||
|
||||
commented := 'value' # an inline comment preserves continuation
|
||||
->reverse_chrs
|
||||
|
||||
computed := 1 + 2
|
||||
->to_str
|
||||
|
||||
fallible: i32 -> i32 ! Error: @ value: i32 { value! }
|
||||
|
||||
propagate: i32 -> i32 ! Error: @ value: i32 {
|
||||
result := {value->fallible}?
|
||||
result!
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
double :: @ value: i32 { value * 2 }
|
||||
add :: @ (a: i32, b: i32) { a + b }
|
||||
square :: @ value: i32 => i32 { value * value }
|
||||
constant :: @ () -> i32 { 42 }
|
||||
unit :: @ { () }
|
||||
|
||||
apply :: @ (value: i32, callback: i32 -> i32) {
|
||||
value->callback
|
||||
}
|
||||
|
||||
sum :: @ (left: i32, right: i32) { left + right }
|
||||
|
||||
multiline :: @ (
|
||||
first: i32
|
||||
second: i32
|
||||
) -> i32 { first + second }
|
||||
|
||||
explicit: i32 -> i32: @ value: i32 { value + 1 }
|
||||
mutable := @ value: i32 { value + 1 }
|
||||
run_callback :: @ callback: {() -> i32} { ()->callback }
|
||||
callback_result := @ () -> i32 { 42 }->run_callback
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
Error :: distinct |{
|
||||
failed
|
||||
}
|
||||
|
||||
OptionalArray :: []u8?
|
||||
OptionalElements :: []{u8?}
|
||||
OptionalMap :: [str]i32?
|
||||
OptionalValues :: [str]{i32?}
|
||||
|
||||
OptionalSuccessResult :: {i32?} ! Error
|
||||
OptionalResult :: {i32 ! Error}?
|
||||
OptionalErrorResult :: i32 ! {Error?}
|
||||
|
||||
Curried :: i32 -> str -> bit
|
||||
PureTransform :: str => str
|
||||
MixedTransform :: i32 => str -> bit
|
||||
ReturningResult :: str -> i32 ! Error
|
||||
|
||||
IntegerRange :: Range<i32>
|
||||
StringIterator :: Iter<str>
|
||||
NestedIterator :: Iter<Iter<str>>
|
||||
TupleIterator :: Iter<(i32, str)>
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"grammars": [
|
||||
{
|
||||
"name": "lscript",
|
||||
"camelcase": "LScript",
|
||||
"scope": "source.lscript",
|
||||
"path": ".",
|
||||
"file-types": [
|
||||
"ls"
|
||||
],
|
||||
"highlights": "queries/highlights.scm"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"version": "0.1.0",
|
||||
"license": "UNLICENSED",
|
||||
"description": "LScript grammar for Tree-sitter"
|
||||
},
|
||||
"bindings": {
|
||||
"c": true,
|
||||
"go": false,
|
||||
"node": false,
|
||||
"python": false,
|
||||
"rust": true,
|
||||
"swift": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user