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