Merge pull request #844 from llakala/update-gleam

Update gleam parser
pull/848/head
Wilfred Hughes 2025-06-15 22:16:50 +07:00 committed by GitHub
commit eff3c36c38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 71987 additions and 35517 deletions

@ -278,7 +278,7 @@ sample_files/text_1.txt sample_files/text_2.txt
f8f81946284fc625ac8e9ae441f31e6d - f8f81946284fc625ac8e9ae441f31e6d -
sample_files/todomvc_1.gleam sample_files/todomvc_2.gleam sample_files/todomvc_1.gleam sample_files/todomvc_2.gleam
9fe64f7d77ad99095ac29357554f3d71 - 6b3b8c9f1e813617819b97144cc5cc4a -
sample_files/toml_1.toml sample_files/toml_2.toml sample_files/toml_1.toml sample_files/toml_2.toml
c331bdb54c00b0a5b5a622dbd250042a - c331bdb54c00b0a5b5a622dbd250042a -

@ -19,7 +19,7 @@ pub fn main() {
string.concat(["Listening on localhost:", int.to_string(port), " ✨"]) string.concat(["Listening on localhost:", int.to_string(port), " ✨"])
|> log.info |> log.info
assert Ok(_) = elli.become(web, on_port: port) let assert Ok(_) = elli.become(web, on_port: port)
} }
pub fn start_database_connection_pool() -> pgo.Connection { pub fn start_database_connection_pool() -> pgo.Connection {

@ -20,7 +20,7 @@ pub fn main() {
string.concat(["Listening on localhost:", int.to_string(port), " ✨"]) string.concat(["Listening on localhost:", int.to_string(port), " ✨"])
log.info(log_string) log.info(log_string)
assert Ok(_) = elli.become(web, on_port: port) let assert Ok(_) = elli.become(web, on_port: port)
} }
pub fn start_database_connection_pool() -> pgo.Connection { pub fn start_database_connection_pool() -> pgo.Connection {

@ -0,0 +1,12 @@
---
name: Issue
about: A problem or suggestion
title: ''
labels: ''
assignees: ''
---
> [!IMPORTANT]
> Please ensure you are using the latest commit in this repo.
> Older versions are not supported.

@ -5,19 +5,21 @@ on: [push, pull_request]
jobs: jobs:
test: test:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install Node - name: Install Node
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with:
node-version: "14.x"
- name: Cache npm dependencies - name: Cache npm dependencies
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -42,15 +44,13 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install Node - name: Install Node
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with:
node-version: "14.x"
- name: Cache npm dependencies - name: Cache npm dependencies
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

@ -13,15 +13,13 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install Node - name: Install Node
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with:
node-version: "14.x"
- name: Cache npm dependencies - name: Cache npm dependencies
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

@ -15,12 +15,10 @@ jobs:
- ubuntu-latest - ubuntu-latest
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install Node - name: Install Node
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with:
node-version: "14.x"
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci

@ -1,5 +1,6 @@
node_modules
target
build
*.wasm *.wasm
build
gleam.so*
log.html log.html
node_modules
target

@ -4,30 +4,45 @@ version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.18" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.72" version = "1.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.4.1" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.4" version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -36,24 +51,39 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.25" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "tree-sitter" name = "tree-sitter"
version = "0.20.2" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36be3222512d85a112491ae0cc280a38076022414f00b64582da1b7565ffd82" checksum = "20f4cd3642c47a85052a887d86704f4eac272969f61b686bdd3f772122aabaff"
dependencies = [ dependencies = [
"cc", "cc",
"regex", "regex",
"regex-syntax",
"tree-sitter-language",
] ]
[[package]] [[package]]
name = "tree-sitter-gleam" name = "tree-sitter-gleam"
version = "0.22.1" version = "1.0.0"
dependencies = [ dependencies = [
"cc", "cc",
"tree-sitter", "tree-sitter",
"tree-sitter-language",
] ]
[[package]]
name = "tree-sitter-language"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2545046bd1473dac6c626659cc2567c6c0ff302fc8b84a56c4243378276f7f57"

@ -1,7 +1,7 @@
[package] [package]
name = "tree-sitter-gleam" name = "tree-sitter-gleam"
description = "gleam grammar for the tree-sitter parsing library" description = "gleam grammar for the tree-sitter parsing library"
version = "0.22.1" version = "1.0.0"
keywords = ["incremental", "parsing", "gleam"] keywords = ["incremental", "parsing", "gleam"]
categories = ["parsing", "text-editors"] categories = ["parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter-gleam" repository = "https://github.com/tree-sitter/tree-sitter-gleam"
@ -20,7 +20,10 @@ include = [
path = "bindings/rust/lib.rs" path = "bindings/rust/lib.rs"
[dependencies] [dependencies]
tree-sitter = "~0.20" tree-sitter-language = "0.1.0"
[dev-dependencies]
tree-sitter = "0.23"
[build-dependencies] [build-dependencies]
cc = "1.0" cc = "1.0"

@ -37,6 +37,7 @@ Various Gotchas
--------------- ---------------
There are a few nodes in the generated AST that may be confusing at first: There are a few nodes in the generated AST that may be confusing at first:
- `type` :: A very ambiguous name, but this refers to a concrete type such as - `type` :: A very ambiguous name, but this refers to a concrete type such as
`List(#(String, Int))` `List(#(String, Int))`
- `type_name` :: Refers to essentially the left side of a type declaration and - `type_name` :: Refers to essentially the left side of a type declaration and
@ -52,6 +53,7 @@ There are a few nodes in the generated AST that may be confusing at first:
—there's no way for the parser to know. In this case, it will be parsed to —there's no way for the parser to know. In this case, it will be parsed to
`(function_call function: (field_access ...) ...)` , as I arbitrarily decided `(function_call function: (field_access ...) ...)` , as I arbitrarily decided
to always assume the code is accessing a field on a record. to always assume the code is accessing a field on a record.
- `constant_field_access` :: Recognizes when a reference to a remote function is used as a constant's value. Generally field accesses are indistinguishable from remote function invocations by the parser so `field_access` is the node name used for both (hence this misnomer).
This is not a comprehensive list. If you find a node confusing, search for it This is not a comprehensive list. If you find a node confusing, search for it
in `grammar.js`, as it might have an explanatory comment. Either way, feel free in `grammar.js`, as it might have an explanatory comment. Either way, feel free
@ -66,13 +68,36 @@ To-do List
[open an issue]: https://github.com/J3RN/tree-sitter-gleam/issues/new [open an issue]: https://github.com/J3RN/tree-sitter-gleam/issues/new
Contributing
------------
1. Change files such as `grammar.js` and `queries/highlight.scm`.
2. The grammar needs to be generated from the `grammar.js` file by running `npm run generate`.
3. Add parser feature tests to the relevant file(s) in `test/corpus/`, or make a new one.
4. Run `npm run test` and fix any failing tests.
Policies
--------
### Backwards-Compatibility Policy
Per the conversation in [#55](https://github.com/gleam-lang/tree-sitter-gleam/pull/55), we have decided that from v0.28.0 forward, tree-sitter-gleam will maintain backwards compatibility with the previous two minor versions; meaning that each release will support three versions:
- 0.x.0
- 0.x-1.*
- 0.x-2.*
e.g. The v0.30.0 release of tree-sitter gleam will support the following version of the Gleam language:
- v0.30.0
- v0.29.*
- v0.28.*
Style Style
----- -----
To prevent headaches from stylistic differences, I request that you please To prevent headaches from stylistic differences, I request that you please
follow these style suggestions. 🙏 follow these style suggestions. 🙏
- Remove all non-mandatory trailing whitespace - Remove all non-mandatory trailing whitespace.
- Ensure a final newline is present at the end of all files (this is the default - Ensure a final newline is present at the end of all files (this is the default
in Vim, Emacs) in Vim, Emacs).
- Format JavaScript by running `npm run format` - Format JavaScript by running `npm run format`.

@ -9,7 +9,7 @@
"sources": [ "sources": [
"bindings/node/binding.cc", "bindings/node/binding.cc",
"src/parser.c", "src/parser.c",
# If your language uses an external scanner, add it here. "src/scanner.c"
], ],
"cflags_c": [ "cflags_c": [
"-std=c99", "-std=c99",

@ -10,14 +10,9 @@ fn main() {
let parser_path = src_dir.join("parser.c"); let parser_path = src_dir.join("parser.c");
c_config.file(&parser_path); c_config.file(&parser_path);
// If your language uses an external scanner written in C,
// then include this block of code:
/*
let scanner_path = src_dir.join("scanner.c"); let scanner_path = src_dir.join("scanner.c");
c_config.file(&scanner_path); c_config.file(&scanner_path);
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
*/
c_config.compile("parser"); c_config.compile("parser");
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());

@ -1,13 +1,25 @@
//! This crate provides gleam language support for the [tree-sitter][] parsing library. //! This crate provides Gleam language support for the [tree-sitter][] parsing library.
//! //!
//! Typically, you will use the [language][language func] function to add this language to a //! Typically, you will use the [language][language func] function to add this language to a
//! tree-sitter [Parser][], and then use the parser to parse some code: //! tree-sitter [Parser][], and then use the parser to parse some code:
//! //!
//! ``` //! ```
//! let code = ""; //! use tree_sitter::Parser;
//! let mut parser = tree_sitter::Parser::new(); //!
//! parser.set_language(tree_sitter_gleam::language()).expect("Error loading gleam grammar"); //! let code = r#"
//! import gleam/io
//!
//! pub fn main() {
//! io.println("hello, friend!")
//! }
//! "#;
//! let mut parser = Parser::new();
//! let language = tree_sitter_gleam::LANGUAGE;
//! parser
//! .set_language(&language.into())
//! .expect("Error loading Gleam parser");
//! let tree = parser.parse(code, None).unwrap(); //! let tree = parser.parse(code, None).unwrap();
//! assert!(!tree.root_node().has_error());
//! ``` //! ```
//! //!
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
@ -15,30 +27,28 @@
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
//! [tree-sitter]: https://tree-sitter.github.io/ //! [tree-sitter]: https://tree-sitter.github.io/
use tree_sitter::Language; use tree_sitter_language::LanguageFn;
extern "C" { extern "C" {
fn tree_sitter_gleam() -> Language; fn tree_sitter_gleam() -> *const ();
} }
/// Get the tree-sitter [Language][] for this grammar. /// The tree-sitter [`LanguageFn`] for this grammar.
/// pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_gleam) };
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
pub fn language() -> Language {
unsafe { tree_sitter_gleam() }
}
/// The content of the [`node-types.json`][] file for this grammar. /// The content of the [`node-types.json`][] file for this grammar.
/// ///
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
// Uncomment these to include any queries that this grammar contains /// The syntax highlighting query for this language.
pub const HIGHLIGHT_QUERY: &'static str = include_str!("../../queries/highlights.scm");
// The locals tagging query for this language.
pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); /// The symbol tagging query for this language.
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -46,7 +56,7 @@ mod tests {
fn test_can_load_grammar() { fn test_can_load_grammar() {
let mut parser = tree_sitter::Parser::new(); let mut parser = tree_sitter::Parser::new();
parser parser
.set_language(super::language()) .set_language(&super::LANGUAGE.into())
.expect("Error loading gleam language"); .expect("Error loading Gleam parser");
} }
} }

@ -4,7 +4,6 @@ module.exports = grammar({
name: "gleam", name: "gleam",
externals: ($) => [$.quoted_content], externals: ($) => [$.quoted_content],
extras: ($) => [ extras: ($) => [
";",
NEWLINE, NEWLINE,
/\s/, /\s/,
$.module_comment, $.module_comment,
@ -13,11 +12,6 @@ module.exports = grammar({
], ],
conflicts: ($) => [ conflicts: ($) => [
[$._maybe_record_expression, $._maybe_tuple_expression], [$._maybe_record_expression, $._maybe_tuple_expression],
[
$._maybe_record_expression,
$._maybe_tuple_expression,
$.remote_type_identifier,
],
[ [
$._maybe_record_expression, $._maybe_record_expression,
$._maybe_tuple_expression, $._maybe_tuple_expression,
@ -25,13 +19,16 @@ module.exports = grammar({
], ],
[$.case_subjects], [$.case_subjects],
[$.source_file], [$.source_file],
[$._constant_value, $._case_clause_guard_unit],
[$.integer],
[$.echo],
], ],
rules: { rules: {
/* General rules */ /* General rules */
source_file: ($) => source_file: ($) =>
repeat(choice($.target_group, $._statement, $._expression_seq)), repeat(choice($.target_group, $._module_statement, $._statement_seq)),
_statement: ($) => _module_statement: ($) =>
choice( choice(
$.import, $.import,
$.constant, $.constant,
@ -39,7 +36,8 @@ module.exports = grammar({
$.external_function, $.external_function,
$.function, $.function,
$.type_definition, $.type_definition,
$.type_alias $.type_alias,
$.attribute
), ),
/* Comments */ /* Comments */
@ -47,18 +45,43 @@ module.exports = grammar({
statement_comment: ($) => token(seq("///", /.*/)), statement_comment: ($) => token(seq("///", /.*/)),
comment: ($) => token(seq("//", /.*/)), comment: ($) => token(seq("//", /.*/)),
/* Target groups */ /* Target groups
* DEPRECATED: This syntax was replaced with attributes in v0.30.
*/
target_group: ($) => target_group: ($) =>
seq("if", field("target", $.target), "{", repeat($._statement), "}"), seq(
"if",
field("target", $.target),
"{",
repeat($._module_statement),
"}"
),
target: ($) => choice("erlang", "javascript"), target: ($) => choice("erlang", "javascript"),
/* Attributes */
attribute: ($) =>
seq(
"@",
field("name", $.identifier),
optional(field("arguments", alias($._attribute_arguments, $.arguments)))
),
_attribute_arguments: ($) =>
seq("(", series_of($.attribute_value, ","), ")"),
attribute_value: ($) =>
choice(
$._constant_value,
seq(field("label", $.label), ":", field("value", $._constant_value))
),
/* Import statements */ /* Import statements */
import: ($) => import: ($) =>
seq( seq(
"import", "import",
field("module", $.module), field("module", $.module),
optional(seq(".", field("imports", $.unqualified_imports))), optional(seq(".", field("imports", $.unqualified_imports))),
optional(seq("as", field("alias", $.identifier))) optional(seq("as", field("alias", choice($.identifier, $.discard))))
), ),
module: ($) => seq($._name, repeat(seq("/", $._name))), module: ($) => seq($._name, repeat(seq("/", $._name))),
unqualified_imports: ($) => unqualified_imports: ($) =>
@ -70,8 +93,13 @@ module.exports = grammar({
optional(seq("as", field("alias", $.identifier))) optional(seq("as", field("alias", $.identifier)))
), ),
seq( seq(
"type",
field("name", $.type_identifier), field("name", $.type_identifier),
optional(seq("as", field("alias", $.type_identifier))) optional(seq("as", field("alias", $.type_identifier)))
),
seq(
field("name", $.constructor_name),
optional(seq("as", field("alias", $.constructor_name)))
) )
), ),
@ -93,7 +121,9 @@ module.exports = grammar({
alias($.constant_tuple, $.tuple), alias($.constant_tuple, $.tuple),
alias($.constant_list, $.list), alias($.constant_list, $.list),
alias($._constant_bit_string, $.bit_string), alias($._constant_bit_string, $.bit_string),
alias($.constant_record, $.record) alias($.constant_record, $.record),
$.identifier,
alias($.constant_field_access, $.field_access)
), ),
constant_tuple: ($) => constant_tuple: ($) =>
seq("#", "(", optional(series_of($._constant_value, ",")), ")"), seq("#", "(", optional(series_of($._constant_value, ",")), ")"),
@ -114,10 +144,17 @@ module.exports = grammar({
")" ")"
), ),
constant_record_argument: ($) => constant_record_argument: ($) =>
seq( choice(
optional(seq(field("label", $.label), ":")), seq(
field("value", $._constant_value) optional(seq(field("label", $.label), ":")),
field("value", $._constant_value)
),
seq(field("label", $.label), ":")
), ),
// This rule exists to parse remote function references which are generally
// indistinguishable from field accesses and so share an AST node.
constant_field_access: ($) =>
seq(field("record", $.identifier), ".", field("field", $.label)),
/* Special constant types */ /* Special constant types */
// Versions of $._type, $._type_annotation, etc, that have constraints // Versions of $._type, $._type_annotation, etc, that have constraints
@ -126,11 +163,29 @@ module.exports = grammar({
choice( choice(
$.type_hole, $.type_hole,
alias($.constant_tuple_type, $.tuple_type), alias($.constant_tuple_type, $.tuple_type),
alias($.constant_function_type, $.function_type),
alias($.constant_type, $.type) alias($.constant_type, $.type)
), ),
_constant_type_annotation: ($) => seq(":", field("type", $._constant_type)), _constant_type_annotation: ($) => seq(":", field("type", $._constant_type)),
constant_tuple_type: ($) => constant_tuple_type: ($) =>
seq("#", "(", optional(series_of($._constant_type, ",")), ")"), seq("#", "(", optional(series_of($._constant_type, ",")), ")"),
constant_function_type: ($) =>
seq(
"fn",
optional(
field(
"parameter_types",
alias(
$.constant_function_parameter_types,
$.function_parameter_types
)
)
),
"->",
field("return_type", $._constant_type)
),
constant_function_parameter_types: ($) =>
seq("(", optional(series_of($._constant_type, ",")), ")"),
constant_type: ($) => constant_type: ($) =>
seq( seq(
field("name", choice($.type_identifier, $.remote_type_identifier)), field("name", choice($.type_identifier, $.remote_type_identifier)),
@ -149,7 +204,15 @@ module.exports = grammar({
constant_type_argument: ($) => $._constant_type, constant_type_argument: ($) => $._constant_type,
external_type: ($) => external_type: ($) =>
seq(optional($.visibility_modifier), "external", "type", $.type_name), prec.right(
seq(
optional($.visibility_modifier),
// DEPRECATED: the external token was removed in v0.30.
optional("external"),
"type",
$.type_name
)
),
/* External function */ /* External function */
external_function: ($) => external_function: ($) =>
@ -189,15 +252,15 @@ module.exports = grammar({
/* Functions */ /* Functions */
function: ($) => function: ($) =>
seq( prec.right(
optional($.visibility_modifier), seq(
"fn", optional($.visibility_modifier),
field("name", $.identifier), "fn",
field("parameters", $.function_parameters), field("name", $.identifier),
optional(seq("->", field("return_type", $._type))), field("parameters", $.function_parameters),
"{", optional(seq("->", field("return_type", $._type))),
field("body", alias($._expression_seq, $.function_body)), optional(field("body", $.block))
"}" )
), ),
function_parameters: ($) => function_parameters: ($) =>
seq("(", optional(series_of($.function_parameter, ",")), ")"), seq("(", optional(series_of($.function_parameter, ",")), ")"),
@ -217,36 +280,9 @@ module.exports = grammar({
_labeled_name_param: ($) => _labeled_name_param: ($) =>
seq(field("label", $.label), field("name", $.identifier)), seq(field("label", $.label), field("name", $.identifier)),
_name_param: ($) => field("name", $.identifier), _name_param: ($) => field("name", $.identifier),
// This method diverges from the parser's `parse_expression_seq` somewhat. _statement_seq: ($) => repeat1($._statement),
// The parser considers all expressions after a `try` to be part of its AST _statement: ($) =>
// node, namely the "then" section. Gleam code like this: choice($._expression, $.let, $.let_assert, $.use, $.assert),
//
// try int_a = parse(a)
// try int_b = parse(b)
// Ok(int_a + int_b)
//
// is parsed as:
//
// (try
// pattern: (pattern)
// value: (call (identifier))
// then: (try
// pattern: (pattern)
// value: (call (identifier))
// then: (record (...))))
//
// This makes sense for the parser, but (IMO) would be more confusing for
// users and tooling which don't think about `try`s as having a "then". Thus,
// `try`s are essentially treated the same as any other expression.
_expression_seq: ($) => repeat1(choice($._expression, $.try)),
try: ($) =>
seq(
"try",
field("pattern", $._pattern),
optional($._type_annotation),
"=",
field("value", $._expression)
),
_expression: ($) => choice($._expression_unit, $.binary_expression), _expression: ($) => choice($._expression_unit, $.binary_expression),
binary_expression: ($) => binary_expression: ($) =>
choice( choice(
@ -262,7 +298,13 @@ module.exports = grammar({
binaryExpr(prec.left, 4, ">=", $._expression), binaryExpr(prec.left, 4, ">=", $._expression),
binaryExpr(prec.left, 4, ">.", $._expression), binaryExpr(prec.left, 4, ">.", $._expression),
binaryExpr(prec.left, 4, ">=.", $._expression), binaryExpr(prec.left, 4, ">=.", $._expression),
binaryExpr(prec.left, 5, "|>", $._expression), binaryExpr(
prec.left,
5,
"|>",
$._expression,
choice($.pipeline_echo, $._expression)
),
binaryExpr(prec.left, 6, "+", $._expression), binaryExpr(prec.left, 6, "+", $._expression),
binaryExpr(prec.left, 6, "+.", $._expression), binaryExpr(prec.left, 6, "+.", $._expression),
binaryExpr(prec.left, 6, "-", $._expression), binaryExpr(prec.left, 6, "-", $._expression),
@ -271,7 +313,8 @@ module.exports = grammar({
binaryExpr(prec.left, 7, "*.", $._expression), binaryExpr(prec.left, 7, "*.", $._expression),
binaryExpr(prec.left, 7, "/", $._expression), binaryExpr(prec.left, 7, "/", $._expression),
binaryExpr(prec.left, 7, "/.", $._expression), binaryExpr(prec.left, 7, "/.", $._expression),
binaryExpr(prec.left, 7, "%", $._expression) binaryExpr(prec.left, 7, "%", $._expression),
binaryExpr(prec.left, 7, "<>", $._expression)
), ),
// The way that this function is written in the Gleam parser is essentially // The way that this function is written in the Gleam parser is essentially
// incompatible with tree-sitter. It first parses some base expression, // incompatible with tree-sitter. It first parses some base expression,
@ -291,15 +334,16 @@ module.exports = grammar({
$.record, $.record,
$.identifier, $.identifier,
$.todo, $.todo,
$.panic,
$.tuple, $.tuple,
$.echo,
$.list, $.list,
alias($._expression_bit_string, $.bit_string), alias($._expression_bit_string, $.bit_string),
$.anonymous_function, $.anonymous_function,
$.expression_group, $.block,
$.case, $.case,
$.let, $.boolean_negation,
$.assert, $.integer_negation,
$.negation,
$.record_update, $.record_update,
$.tuple_access, $.tuple_access,
$.field_access, $.field_access,
@ -311,7 +355,32 @@ module.exports = grammar({
optional(field("arguments", $.arguments)) optional(field("arguments", $.arguments))
), ),
todo: ($) => todo: ($) =>
seq("todo", optional(seq("(", field("message", $.string), ")"))), prec.left(
seq(
"todo",
optional(
choice(
// DEPRECATED: The 'as' syntax was introduced in v0.30.
seq("(", field("message", $.string), ")"),
seq("as", field("message", $._expression))
)
)
)
),
panic: ($) =>
prec.left(
seq(
"panic",
optional(
choice(
seq("(", field("message", $.string), ")"),
seq("as", field("message", $._expression))
)
)
)
),
pipeline_echo: (_$) => prec.left("echo"),
echo: ($) => seq("echo", $._expression),
tuple: ($) => seq("#", "(", optional(series_of($._expression, ",")), ")"), tuple: ($) => seq("#", "(", optional(series_of($._expression, ",")), ")"),
list: ($) => list: ($) =>
seq( seq(
@ -335,9 +404,7 @@ module.exports = grammar({
alias($.anonymous_function_parameters, $.function_parameters) alias($.anonymous_function_parameters, $.function_parameters)
), ),
optional(seq("->", field("return_type", $._type))), optional(seq("->", field("return_type", $._type))),
"{", field("body", $.block)
field("body", alias($._expression_seq, $.function_body)),
"}"
), ),
anonymous_function_parameters: ($) => anonymous_function_parameters: ($) =>
seq( seq(
@ -355,13 +422,13 @@ module.exports = grammar({
choice($._discard_param, $._name_param), choice($._discard_param, $._name_param),
optional($._type_annotation) optional($._type_annotation)
), ),
expression_group: ($) => seq("{", $._expression_seq, "}"), block: ($) => seq("{", optional($._statement_seq), "}"),
case: ($) => case: ($) =>
seq( seq(
"case", "case",
field("subjects", $.case_subjects), field("subjects", $.case_subjects),
"{", "{",
field("clauses", $.case_clauses), optional(field("clauses", $.case_clauses)),
"}" "}"
), ),
case_subjects: ($) => seq(series_of($._expression, ",")), case_subjects: ($) => seq(series_of($._expression, ",")),
@ -385,7 +452,8 @@ module.exports = grammar({
_case_clause_guard_expression: ($) => _case_clause_guard_expression: ($) =>
choice( choice(
$._case_clause_guard_unit, $._case_clause_guard_unit,
alias($._case_clause_guard_binary_expression, $.binary_expression) alias($._case_clause_guard_binary_expression, $.binary_expression),
$.boolean_negation
), ),
_case_clause_guard_binary_expression: ($) => _case_clause_guard_binary_expression: ($) =>
choice( choice(
@ -400,7 +468,16 @@ module.exports = grammar({
binaryExpr(prec.left, 4, ">", $._case_clause_guard_expression), binaryExpr(prec.left, 4, ">", $._case_clause_guard_expression),
binaryExpr(prec.left, 4, ">=", $._case_clause_guard_expression), binaryExpr(prec.left, 4, ">=", $._case_clause_guard_expression),
binaryExpr(prec.left, 4, ">.", $._case_clause_guard_expression), binaryExpr(prec.left, 4, ">.", $._case_clause_guard_expression),
binaryExpr(prec.left, 4, ">=.", $._case_clause_guard_expression) binaryExpr(prec.left, 4, ">=.", $._case_clause_guard_expression),
binaryExpr(prec.left, 5, "+", $._case_clause_guard_expression),
binaryExpr(prec.left, 5, "+.", $._case_clause_guard_expression),
binaryExpr(prec.left, 5, "-", $._case_clause_guard_expression),
binaryExpr(prec.left, 5, "-.", $._case_clause_guard_expression),
binaryExpr(prec.left, 6, "*", $._case_clause_guard_expression),
binaryExpr(prec.left, 6, "*.", $._case_clause_guard_expression),
binaryExpr(prec.left, 6, "/", $._case_clause_guard_expression),
binaryExpr(prec.left, 6, "/.", $._case_clause_guard_expression),
binaryExpr(prec.left, 6, "%", $._case_clause_guard_expression)
), ),
_case_clause_guard_unit: ($) => _case_clause_guard_unit: ($) =>
choice( choice(
@ -411,9 +488,31 @@ module.exports = grammar({
), ),
_case_clause_tuple_access: ($) => _case_clause_tuple_access: ($) =>
seq(field("tuple", $.identifier), ".", field("index", $.integer)), seq(field("tuple", $.identifier), ".", field("index", $.integer)),
let_assert: ($) =>
seq(
"let",
"assert",
$._assignment,
optional(seq("as", field("message", $._expression)))
),
assert: ($) =>
seq(
"assert",
field("value", $._expression),
optional(seq("as", field("message", $._expression)))
),
let: ($) => seq("let", $._assignment), let: ($) => seq("let", $._assignment),
assert: ($) => seq("assert", $._assignment), use: ($) =>
negation: ($) => seq("!", $._expression_unit), seq(
"use",
optional(field("assignments", $.use_assignments)),
"<-",
field("value", $._expression)
),
use_assignments: ($) => series_of($.use_assignment, ","),
use_assignment: ($) => seq($._pattern, optional($._type_annotation)),
boolean_negation: ($) => seq("!", $._expression_unit),
integer_negation: ($) => seq("-", $._expression_unit),
_assignment: ($) => _assignment: ($) =>
seq( seq(
field("pattern", $._pattern), field("pattern", $._pattern),
@ -436,7 +535,10 @@ module.exports = grammar({
), ),
record_update_arguments: ($) => series_of($.record_update_argument, ","), record_update_arguments: ($) => series_of($.record_update_argument, ","),
record_update_argument: ($) => record_update_argument: ($) =>
seq(field("label", $.label), ":", field("value", $._expression)), choice(
seq(field("label", $.label), ":", field("value", $._expression)),
seq(field("label", $.label), ":")
),
// As with other AST nodes in this section, `_maybe_record_expression`, // As with other AST nodes in this section, `_maybe_record_expression`,
// `_maybe_tuple_expression`, and `_maybe_function_expresssion` have no // `_maybe_tuple_expression`, and `_maybe_function_expresssion` have no
// corollaries in the Gleam parser. These anonymous AST node denote any // corollaries in the Gleam parser. These anonymous AST node denote any
@ -450,7 +552,7 @@ module.exports = grammar({
$.identifier, $.identifier,
$.function_call, $.function_call,
$.tuple, $.tuple,
$.expression_group, $.block,
$.case, $.case,
$.field_access, $.field_access,
$.tuple_access $.tuple_access
@ -468,7 +570,7 @@ module.exports = grammar({
$.record, $.record,
$.identifier, $.identifier,
$.function_call, $.function_call,
$.expression_group, $.block,
$.case, $.case,
$.record_update, $.record_update,
$.field_access, $.field_access,
@ -494,7 +596,7 @@ module.exports = grammar({
choice( choice(
$.identifier, $.identifier,
$.anonymous_function, $.anonymous_function,
$.expression_group, $.block,
$.case, $.case,
$.tuple_access, $.tuple_access,
$.field_access, $.field_access,
@ -504,9 +606,12 @@ module.exports = grammar({
// record arguments, hence the ambiguous name. // record arguments, hence the ambiguous name.
arguments: ($) => seq("(", optional(series_of($.argument, ",")), ")"), arguments: ($) => seq("(", optional(series_of($.argument, ",")), ")"),
argument: ($) => argument: ($) =>
seq( choice(
optional(seq(field("label", $.label), ":")), seq(
field("value", choice($.hole, $._expression)) optional(seq(field("label", $.label), ":")),
field("value", choice($.hole, $._expression))
),
seq(field("label", $.label), ":")
), ),
hole: ($) => $._discard_name, hole: ($) => $._discard_name,
function_call: ($) => function_call: ($) =>
@ -514,19 +619,27 @@ module.exports = grammar({
field("function", $._maybe_function_expression), field("function", $._maybe_function_expression),
field("arguments", $.arguments) field("arguments", $.arguments)
), ),
_pattern_expression: ($) =>
choice(
$.identifier,
$.discard,
$.record_pattern,
$.string,
$.integer,
$.float,
$.tuple_pattern,
alias($._pattern_bit_string, $.bit_string_pattern),
$.list_pattern,
alias($._pattern_binary_expression, $.binary_expression)
),
_pattern_binary_expression: ($) =>
choice(
binaryExpr(prec.left, 1, "<>", $._pattern_expression),
binaryExpr(prec.left, 1, "as", $.string, $.identifier)
),
_pattern: ($) => _pattern: ($) =>
seq( seq(
choice( $._pattern_expression,
$.identifier,
$.discard,
$.record_pattern,
$.string,
$.integer,
$.float,
$.tuple_pattern,
alias($._pattern_bit_string, $.bit_string_pattern),
$.list_pattern
),
optional(field("assign", seq("as", $.identifier))) optional(field("assign", seq("as", $.identifier)))
), ),
record_pattern: ($) => record_pattern: ($) =>
@ -542,9 +655,12 @@ module.exports = grammar({
")" ")"
), ),
record_pattern_argument: ($) => record_pattern_argument: ($) =>
seq( choice(
optional(seq(field("label", $.label), ":")), seq(
field("pattern", $._pattern) optional(seq(field("label", $.label), ":")),
field("pattern", $._pattern)
),
seq(field("label", $.label), ":")
), ),
pattern_spread: ($) => seq("..", optional(",")), pattern_spread: ($) => seq("..", optional(",")),
tuple_pattern: ($) => tuple_pattern: ($) =>
@ -586,6 +702,7 @@ module.exports = grammar({
data_constructors: ($) => repeat1($.data_constructor), data_constructors: ($) => repeat1($.data_constructor),
data_constructor: ($) => data_constructor: ($) =>
seq( seq(
optional($.attribute),
field("name", $.constructor_name), field("name", $.constructor_name),
optional(field("arguments", $.data_constructor_arguments)) optional(field("arguments", $.data_constructor_arguments))
), ),
@ -612,8 +729,12 @@ module.exports = grammar({
repeat(choice($.escape_sequence, $.quoted_content)), repeat(choice($.escape_sequence, $.quoted_content)),
token.immediate('"') token.immediate('"')
), ),
escape_sequence: ($) => token.immediate(/\\[efnrt\"\\]/), escape_sequence: ($) =>
float: ($) => /-?[0-9_]+\.[0-9_]*/, choice(
token.immediate(/\\[efnrt\"\\]/),
token.immediate(/\\u\{[0-9a-fA-F]{1,6}\}/)
),
float: ($) => /-?[0-9_]+\.[0-9_]*(e-?[0-9_]+)?/,
integer: ($) => integer: ($) =>
seq(optional("-"), choice($._hex, $._decimal, $._octal, $._binary)), seq(optional("-"), choice($._hex, $._decimal, $._octal, $._binary)),
_hex: ($) => /0[xX][0-9a-fA-F_]+/, _hex: ($) => /0[xX][0-9a-fA-F_]+/,
@ -769,9 +890,13 @@ function series_of(rule, separator) {
// A binary expression with a left-hand side, infix operator, and then right-hand-side // A binary expression with a left-hand side, infix operator, and then right-hand-side
// https://github.com/elixir-lang/tree-sitter-elixir/blob/de20391afe5cb03ef1e8a8e43167e7b58cc52869/grammar.js#L850-L859 // https://github.com/elixir-lang/tree-sitter-elixir/blob/de20391afe5cb03ef1e8a8e43167e7b58cc52869/grammar.js#L850-L859
function binaryExpr(assoc, precedence, operator, expr) { function binaryExpr(assoc, precedence, operator, left, right = null) {
return assoc( return assoc(
precedence, precedence,
seq(field("left", expr), field("operator", operator), field("right", expr)) seq(
field("left", left),
field("operator", operator),
field("right", right || left)
)
); );
} }

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "tree-sitter-gleam", "name": "tree-sitter-gleam",
"version": "0.22.1", "version": "1.0.0",
"description": "A tree-sitter grammar for the Gleam programming language", "description": "A tree-sitter grammar for the Gleam programming language",
"main": "bindings/node", "main": "bindings/node",
"scripts": { "scripts": {
@ -27,11 +27,12 @@
}, },
"homepage": "https://github.com/J3RN/tree-sitter-gleam#readme", "homepage": "https://github.com/J3RN/tree-sitter-gleam#readme",
"dependencies": { "dependencies": {
"nan": "^2.15.0" "nan": "^2.18.0"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^2.5.1", "prettier": "^2.5.1",
"tree-sitter-cli": "^0.20.6" "tree-sitter-cli": "^0.20.6",
"node-gyp": "^10.0.1"
}, },
"tree-sitter": [ "tree-sitter": [
{ {

@ -21,6 +21,8 @@
; Functions ; Functions
(unqualified_import (identifier) @function) (unqualified_import (identifier) @function)
(unqualified_import "type" (type_identifier) @type)
(unqualified_import (type_identifier) @constructor)
(function (function
name: (identifier) @function) name: (identifier) @function)
(external_function (external_function
@ -43,6 +45,13 @@
(tuple_access (tuple_access
index: (integer) @property) index: (integer) @property)
; Attributes
(attribute
"@" @attribute
name: (identifier) @attribute)
(attribute_value (identifier) @constant)
; Type names ; Type names
(remote_type_identifier) @type (remote_type_identifier) @type
(type_identifier) @type (type_identifier) @type
@ -52,19 +61,24 @@
; Literals ; Literals
(string) @string (string) @string
((escape_sequence) @warning
; Deprecated in v0.33.0-rc2:
(#eq? @warning "\\e"))
(escape_sequence) @string.escape
(bit_string_segment_option) @function.builtin (bit_string_segment_option) @function.builtin
(integer) @number (integer) @number
(float) @number (float) @number
; Reserved identifiers
; TODO: when tree-sitter supports `#any-of?` in the Rust bindings,
; refactor this to use `#any-of?` rather than `#match?`
((identifier) @error
(#match? @error "^(auto|delegate|derive|else|implement|macro|test)$"))
; Variables ; Variables
(identifier) @variable (identifier) @variable
(discard) @comment.unused (discard) @comment.unused
; Operators
(binary_expression
operator: _ @operator)
"!" @operator
; Keywords ; Keywords
[ [
(visibility_modifier) ; "pub" (visibility_modifier) ; "pub"
@ -73,16 +87,25 @@
"assert" "assert"
"case" "case"
"const" "const"
"echo"
; DEPRECATED: 'external' was removed in v0.30.
"external" "external"
"fn" "fn"
"if" "if"
"import" "import"
"let" "let"
"panic"
"todo" "todo"
"try"
"type" "type"
"use"
] @keyword ] @keyword
; Operators
(binary_expression
operator: _ @operator)
(boolean_negation "!" @operator)
(integer_negation "-" @operator)
; Punctuation ; Punctuation
[ [
"(" "("
@ -104,4 +127,5 @@
"->" "->"
".." ".."
"-" "-"
"<-"
] @punctuation.delimiter ] @punctuation.delimiter

@ -1,4 +1,6 @@
; Scopes ; Scopes
(block) @local.scope
(function) @local.scope (function) @local.scope
(case_clause) @local.scope (case_clause) @local.scope

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,101 @@
================================================================================
Target attribute
================================================================================
@target(erlang)
pub fn main() { todo }
--------------------------------------------------------------------------------
(source_file
(attribute
name: (identifier)
arguments: (arguments
(attribute_value
(identifier))))
(function
(visibility_modifier)
name: (identifier)
parameters: (function_parameters)
body: (block
(todo))))
================================================================================
Attribute with multiple values
================================================================================
@deprecated(since: "1.2.0", replacement: wobble)
pub fn wibble() { todo }
--------------------------------------------------------------------------------
(source_file
(attribute
name: (identifier)
arguments: (arguments
(attribute_value
label: (label)
value: (string
(quoted_content)))
(attribute_value
label: (label)
value: (identifier))))
(function
(visibility_modifier)
name: (identifier)
parameters: (function_parameters)
body: (block
(todo))))
================================================================================
Attribute without arguments
================================================================================
@internal
pub fn wibble() { todo }
--------------------------------------------------------------------------------
(source_file
(attribute
name: (identifier))
(function
(visibility_modifier)
name: (identifier)
parameters: (function_parameters)
body: (block
(todo))))
================================================================================
Individually deprecated constructors
================================================================================
pub type SomeType {
NotDeprecated
@deprecated("Please use the NotDeprecated variant")
Deprecated(reason: String)
}
--------------------------------------------------------------------------------
(source_file
(type_definition
(visibility_modifier)
(type_name
name: (type_identifier))
(data_constructors
(data_constructor
name: (constructor_name))
(data_constructor
(attribute
name: (identifier)
arguments: (arguments
(attribute_value
(string
(quoted_content)))))
name: (constructor_name)
arguments: (data_constructor_arguments
(data_constructor_argument
label: (label)
value: (type
name: (type_identifier))))))))

@ -0,0 +1,255 @@
================================================================================
Case examples
================================================================================
case value {
"A" -> True
_ -> False
}
case value {}
--------------------------------------------------------------------------------
(source_file
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(string
(quoted_content))))
(record
(constructor_name)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(discard)))
(record
(constructor_name)))))
(case
(case_subjects
(identifier))))
================================================================================
Case examples
================================================================================
// From https://gleam.run/news/v0.31-keeping-dependencies-explicit/#quality-of-life-improvements
pub fn listed(names: List(String), person: Person) -> String {
case names {
[name, ..names] if name == person.name -> True
[_, ..names] -> listed(names, person)
[] -> False
}
}
--------------------------------------------------------------------------------
(source_file
(comment)
(function
(visibility_modifier)
(identifier)
(function_parameters
(function_parameter
(identifier)
(type
(type_identifier)
(type_arguments
(type_argument
(type
(type_identifier))))))
(function_parameter
(identifier)
(type
(type_identifier))))
(type
(type_identifier))
(block
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(list_pattern
(identifier)
(list_pattern_tail
(identifier)))))
(case_clause_guard
(binary_expression
(identifier)
(field_access
(identifier)
(label))))
(record
(constructor_name)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(list_pattern
(discard)
(list_pattern_tail
(identifier)))))
(function_call
(identifier)
(arguments
(argument
(identifier))
(argument
(identifier)))))
(case_clause
(case_clause_patterns
(case_clause_pattern
(list_pattern)))
(record
(constructor_name))))))))
================================================================================
Pattern matching binaries with 'as'
================================================================================
// From https://gleam.run/news/v0.31-keeping-dependencies-explicit/#quality-of-life-improvements
case tag {
"category " as key <> value
| "region " as key <> value
| "priority " as key <> value -> {
let key = string.trim(key)
Ok(Tag(key, value))
}
_ -> Error(Nil)
}
--------------------------------------------------------------------------------
(source_file
(comment)
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(binary_expression
(binary_expression
(string
(quoted_content))
(identifier))
(identifier)))
(case_clause_pattern
(binary_expression
(binary_expression
(string
(quoted_content))
(identifier))
(identifier)))
(case_clause_pattern
(binary_expression
(binary_expression
(string
(quoted_content))
(identifier))
(identifier))))
(block
(let
(identifier)
(function_call
(field_access
(identifier)
(label))
(arguments
(argument
(identifier)))))
(record
(constructor_name)
(arguments
(argument
(record
(constructor_name)
(arguments
(argument
(identifier))
(argument
(identifier)))))))))
(case_clause
(case_clause_patterns
(case_clause_pattern
(discard)))
(record
(constructor_name)
(arguments
(argument
(record
(constructor_name)))))))))
================================================================================
Case with boolean negation in a guard
================================================================================
case var {
1 if !other_var -> True
_ -> False
}
--------------------------------------------------------------------------------
(source_file
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(integer)))
(case_clause_guard
(boolean_negation
(identifier)))
(record
(constructor_name)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(discard)))
(record
(constructor_name))))))
================================================================================
Case with int remainder in guard
================================================================================
case var {
_ if 11 % 2 == 0 -> True
_ -> False
}
--------------------------------------------------------------------------------
(source_file
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(discard)))
(case_clause_guard
(binary_expression
(binary_expression
(integer)
(integer))
(integer)))
(record
(constructor_name)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(discard)))
(record
(constructor_name))))))

@ -26,6 +26,9 @@ const a = Cat("Ginny", 1950)
const a = Person(name: "Billy", age: 52) const a = Person(name: "Billy", age: 52)
const a = uri.Uri(host: "github.com") const a = uri.Uri(host: "github.com")
const a: option.Option(String) = option.Some("Hello, World!") const a: option.Option(String) = option.Some("Hello, World!")
const var_alias = b
const int_identity_alias: fn(Int) -> Int = int_identity
const fun_tuple: #(fn(Float) -> String, fn(Int) -> String) = #(float.to_string, int.to_string)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -206,7 +209,41 @@ const a: option.Option(String) = option.Some("Hello, World!")
arguments: (arguments arguments: (arguments
(argument (argument
value: (string value: (string
(quoted_content))))))) (quoted_content))))))
(constant
name: (identifier)
value: (identifier))
(constant
name: (identifier)
type: (function_type
parameter_types: (function_parameter_types
(type
name: (type_identifier)))
return_type: (type
name: (type_identifier)))
value: (identifier))
(constant
name: (identifier)
type: (tuple_type
(function_type
parameter_types: (function_parameter_types
(type
name: (type_identifier)))
return_type: (type
name: (type_identifier)))
(function_type
parameter_types: (function_parameter_types
(type
name: (type_identifier)))
return_type: (type
name: (type_identifier))))
value: (tuple
(field_access
record: (identifier)
field: (label))
(field_access
record: (identifier)
field: (label)))))
================================================================================ ================================================================================
Public constants Public constants
@ -386,3 +423,43 @@ pub const a = uri.Uri(host: "github.com")
label: (label) label: (label)
value: (string value: (string
(quoted_content))))))) (quoted_content)))))))
================================================================================
Scientific notation
================================================================================
const a = 0.0e0
const a = 1.0e123_456
const a = -100.001e-1_230
--------------------------------------------------------------------------------
(source_file
(constant
(identifier)
(float))
(constant
(identifier)
(float))
(constant
(identifier)
(float)))
================================================================================
Constant with shorthand labels
================================================================================
const b = Wibble(arg:, arg:)
--------------------------------------------------------------------------------
(source_file
(constant
(identifier)
(record
(constructor_name)
(arguments
(argument
(label))
(argument
(label))))))

@ -247,3 +247,26 @@ pub opaque type Animal(name, cuteness) {
label: (label) label: (label)
value: (type value: (type
name: (type_identifier)))))))) name: (type_identifier))))))))
================================================================================
Record update with shorthand labels
================================================================================
Wibble(..wibble, arg:, arg:, arg: todo as "no shorthand")
--------------------------------------------------------------------------------
(source_file
(record_update
(constructor_name)
(identifier)
(record_update_arguments
(record_update_argument
(label))
(record_update_argument
(label))
(record_update_argument
(label)
(todo
(string
(quoted_content)))))))

@ -16,7 +16,7 @@ pub fn main() {
(visibility_modifier) (visibility_modifier)
(identifier) (identifier)
(function_parameters) (function_parameters)
(function_body (block
(case (case
(case_subjects (case_subjects
(identifier)) (identifier))
@ -48,3 +48,31 @@ pub fn main() {
(argument (argument
(string (string
(quoted_content))))))))))) (quoted_content)))))))))))
================================================================================
Pattern with label shorthand
================================================================================
pub fn main() {
let Wibble(arg1:, arg2:) = todo as "a"
}
--------------------------------------------------------------------------------
(source_file
(function
(visibility_modifier)
(identifier)
(function_parameters)
(block
(let
(record_pattern
(constructor_name)
(record_pattern_arguments
(record_pattern_argument
(label))
(record_pattern_argument
(label))))
(todo
(string
(quoted_content)))))))

@ -0,0 +1,112 @@
================================================================================
Echo with expression
================================================================================
pub fn main() {
echo 1
}
--------------------------------------------------------------------------------
(source_file
(function
(visibility_modifier)
(identifier)
(function_parameters)
(block
(echo
(integer)))))
================================================================================
Echo in pipeline
================================================================================
pub fn main() {
[]
|> echo
|> panic
}
--------------------------------------------------------------------------------
(source_file
(function
(visibility_modifier)
(identifier)
(function_parameters)
(block
(binary_expression
(binary_expression
(list)
(pipeline_echo))
(panic)))))
================================================================================
Echo last in pipeline
================================================================================
pub fn main() {
[]
|> echo
1
}
--------------------------------------------------------------------------------
(source_file
(function
(visibility_modifier)
(identifier)
(function_parameters)
(block
(binary_expression
(list)
(pipeline_echo))
(integer))))
================================================================================
Echo precedence with pipes
================================================================================
pub fn main() {
echo 1 |> 2
3
}
--------------------------------------------------------------------------------
(source_file
(function
(visibility_modifier)
(identifier)
(function_parameters)
(block
(echo
(binary_expression
(integer)
(integer)))
(integer))))
================================================================================
Echo precedence with binop
================================================================================
pub fn main() {
echo 1 + 2
3
}
--------------------------------------------------------------------------------
(source_file
(function
(visibility_modifier)
(identifier)
(function_parameters)
(block
(echo
(binary_expression
(integer)
(integer)))
(integer))))

@ -22,7 +22,7 @@ Bit-string expression
(bit_string_segment_option))))) (bit_string_segment_option)))))
================================================================================ ================================================================================
Negation Boolean Negation
================================================================================ ================================================================================
!False !False
@ -31,12 +31,149 @@ True && !False
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
(source_file (source_file
(negation (boolean_negation
(record (record
(constructor_name))) (constructor_name)))
(binary_expression (binary_expression
(record (record
(constructor_name)) (constructor_name))
(negation (boolean_negation
(record (record
(constructor_name))))) (constructor_name)))))
================================================================================
Integer Negation
================================================================================
{-x}
{-{5*30}}
{-my_fun()}
--------------------------------------------------------------------------------
(source_file
(block
(integer_negation
(identifier)))
(block
(integer_negation
(block
(binary_expression
(integer)
(integer)))))
(block
(integer_negation
(function_call
(identifier)
(arguments)))))
================================================================================
Concatenation
================================================================================
let concat = "a" <> "b"
case "12345" {
"0" <> rest -> rest
"12" <> "34" <> "5" -> "match"
_ -> ""
}
--------------------------------------------------------------------------------
(source_file
(let
(identifier)
(binary_expression
(string
(quoted_content))
(string
(quoted_content))))
(case
(case_subjects
(string
(quoted_content)))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(binary_expression
(string
(quoted_content))
(identifier))))
(identifier))
(case_clause
(case_clause_patterns
(case_clause_pattern
(binary_expression
(binary_expression
(string
(quoted_content))
(string
(quoted_content)))
(string
(quoted_content)))))
(string
(quoted_content)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(discard)))
(string)))))
================================================================================
Todo and panic 'as' with string expressions
================================================================================
todo as { "Hello, " <> "world!" }
panic as { "Hello, " <> "world!" }
--------------------------------------------------------------------------------
(source_file
(todo
(block
(binary_expression
(string
(quoted_content))
(string
(quoted_content)))))
(panic
(block
(binary_expression
(string
(quoted_content))
(string
(quoted_content))))))
================================================================================
Todo and panic in function application style
================================================================================
todo("don't panic")
panic("aaaah!")
--------------------------------------------------------------------------------
(source_file
(todo
(string
(quoted_content)))
(panic
(string
(quoted_content))))
================================================================================
Nested field access
================================================================================
config.connection.host
--------------------------------------------------------------------------------
(source_file
(field_access
(field_access
(identifier)
(label))
(label)))

@ -136,3 +136,40 @@ pub external fn a() -> #(List(Int), fn(Int) -> String) = "x" "y"
(quoted_content)) (quoted_content))
(string (string
(quoted_content))))) (quoted_content)))))
================================================================================
External function with attribute syntax
================================================================================
@external(erlang, "erlang", "integer_to_list")
fn integer_to_list(int int: Int, base base: Int) -> String
---
(source_file
(attribute
(identifier)
(arguments
(attribute_value
(identifier))
(attribute_value
(string
(quoted_content)))
(attribute_value
(string
(quoted_content)))))
(function
(identifier)
(function_parameters
(function_parameter
(label)
(identifier)
(type
(type_identifier)))
(function_parameter
(label)
(identifier)
(type
(type_identifier))))
(type
(type_identifier))))

@ -2,9 +2,9 @@
External types External types
================================================================================ ================================================================================
external type IODevice type IODevice
external type IODevice() type IODevice()
external type Map(key, value) type Map(key, value)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -27,9 +27,9 @@ external type Map(key, value)
Public external types Public external types
================================================================================ ================================================================================
pub external type IODevice pub type IODevice
pub external type IODevice() pub type IODevice()
pub external type Map(key, value) pub type Map(key, value)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

@ -35,7 +35,7 @@ fn replace(
name: (type_identifier)))) name: (type_identifier))))
return_type: (type return_type: (type
name: (type_identifier)) name: (type_identifier))
body: (function_body body: (block
(binary_expression (binary_expression
left: (identifier) left: (identifier)
right: (identifier)))) right: (identifier))))
@ -52,7 +52,7 @@ fn replace(
name: (identifier) name: (identifier)
type: (type_var))) type: (type_var)))
return_type: (type_var) return_type: (type_var)
body: (function_body body: (block
(function_call (function_call
function: (identifier) function: (identifier)
arguments: (arguments arguments: (arguments
@ -67,7 +67,7 @@ fn replace(
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(identifier))) (identifier)))
(function (function
name: (identifier) name: (identifier)
@ -87,7 +87,7 @@ fn replace(
name: (identifier) name: (identifier)
type: (type type: (type
name: (type_identifier)))) name: (type_identifier))))
body: (function_body body: (block
(function_call (function_call
function: (field_access function: (field_access
record: (identifier) record: (identifier)
@ -141,7 +141,7 @@ pub fn replace(
name: (type_identifier)))) name: (type_identifier))))
return_type: (type return_type: (type
name: (type_identifier)) name: (type_identifier))
body: (function_body body: (block
(binary_expression (binary_expression
left: (identifier) left: (identifier)
right: (identifier)))) right: (identifier))))
@ -159,7 +159,7 @@ pub fn replace(
name: (identifier) name: (identifier)
type: (type_var))) type: (type_var)))
return_type: (type_var) return_type: (type_var)
body: (function_body body: (block
(function_call (function_call
function: (identifier) function: (identifier)
arguments: (arguments arguments: (arguments
@ -175,7 +175,7 @@ pub fn replace(
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(identifier))) (identifier)))
(function (function
(visibility_modifier) (visibility_modifier)
@ -196,7 +196,7 @@ pub fn replace(
name: (identifier) name: (identifier)
type: (type type: (type
name: (type_identifier)))) name: (type_identifier))))
body: (function_body body: (block
(function_call (function_call
function: (field_access function: (field_access
record: (identifier) record: (identifier)
@ -216,6 +216,7 @@ pub fn replace(
Basic functions Basic functions
================================================================================ ================================================================================
fn unfinished() {}
fn str() { fn str() {
"Hello, World!" "Hello, World!"
} }
@ -238,7 +239,11 @@ fn funcall() {
myfun() myfun()
} }
fn unfinished() { fn unfinished() {
todo("Finish me!") todo as "Finish me!"
}
fn do_panic() {
panic
panic as "aaaah!"
} }
fn tuple(x) { fn tuple(x) {
#(x, 1) #(x, 1)
@ -252,7 +257,7 @@ fn bit_string() {
fn return_fun(x) { fn return_fun(x) {
fn(y: Int) { x + y } fn(y: Int) { x + y }
} }
fn expression_group() { fn block() {
{ {
1 + 1 1 + 1
"Hello, World!" "Hello, World!"
@ -264,6 +269,17 @@ fn foob(x, y) {
_else -> False _else -> False
} }
} }
fn assert_assignment() {
let assert Ok(a) = Ok(1)
let assert x = {
1 + 1
"Hello, World!"
}
let assert y = x
let assert #(x, _) = #(1, 2)
let assert Ok(_) = Ok(10) as "This never fails"
let assert 10 = 11 as { "message1" <> "message2" }
}
fn assignment() { fn assignment() {
let x = { let x = {
1 + 1 1 + 1
@ -272,13 +288,15 @@ fn assignment() {
let y = x let y = x
let #(x, _) = #(1, 2) let #(x, _) = #(1, 2)
} }
fn assertations() { fn assertions() {
assert x = { assert True && False
1 + 1 assert add(1, 2) == 3
"Hello, World!" assert !some_value
} assert {
assert y = x 10
assert #(x, _) = #(1, 2) 11
} != 10
assert result.is_ok(Ok(10))
} }
fn update(x) { fn update(x) {
Cat(..x, name: "Nubi", cuteness: 1000 + 1001) Cat(..x, name: "Nubi", cuteness: 1000 + 1001)
@ -297,23 +315,27 @@ fn field_access(x) {
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block))
(function
name: (identifier)
parameters: (function_parameters)
body: (block
(string (string
(quoted_content)))) (quoted_content))))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(integer))) (integer)))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(float))) (float)))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(record (record
name: (constructor_name) name: (constructor_name)
arguments: (arguments arguments: (arguments
@ -324,7 +346,7 @@ fn field_access(x) {
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(record (record
name: (remote_constructor_name name: (remote_constructor_name
module: (identifier) module: (identifier)
@ -339,28 +361,36 @@ fn field_access(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(identifier))) (identifier)))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(function_call (function_call
function: (identifier) function: (identifier)
arguments: (arguments)))) arguments: (arguments))))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(todo (todo
message: (string message: (string
(quoted_content))))) (quoted_content)))))
(function
name: (identifier)
parameters: (function_parameters)
body: (block
(panic)
(panic
message: (string
(quoted_content)))))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(tuple (tuple
(identifier) (identifier)
(integer)))) (integer))))
@ -369,7 +399,7 @@ fn field_access(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(list (list
(integer) (integer)
(integer) (integer)
@ -377,7 +407,7 @@ fn field_access(x) {
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(bit_string (bit_string
(bit_string_segment (bit_string_segment
value: (integer) value: (integer)
@ -396,22 +426,22 @@ fn field_access(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(anonymous_function (anonymous_function
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier) name: (identifier)
type: (type type: (type
name: (type_identifier)))) name: (type_identifier))))
body: (function_body body: (block
(binary_expression (binary_expression
left: (identifier) left: (identifier)
right: (identifier)))))) right: (identifier))))))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(expression_group (block
(binary_expression (binary_expression
left: (integer) left: (integer)
right: (integer)) right: (integer))
@ -424,7 +454,7 @@ fn field_access(x) {
name: (identifier)) name: (identifier))
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(case (case
subjects: (case_subjects subjects: (case_subjects
(identifier) (identifier)
@ -449,53 +479,127 @@ fn field_access(x) {
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(let (let_assert
pattern: (record_pattern
name: (constructor_name)
arguments: (record_pattern_arguments
(record_pattern_argument
pattern: (identifier))))
value: (record
name: (constructor_name)
arguments: (arguments
(argument
value: (integer)))))
(let_assert
pattern: (identifier) pattern: (identifier)
value: (expression_group value: (block
(binary_expression (binary_expression
left: (integer) left: (integer)
right: (integer)) right: (integer))
(string (string
(quoted_content)))) (quoted_content))))
(let (let_assert
pattern: (identifier) pattern: (identifier)
value: (identifier)) value: (identifier))
(let (let_assert
pattern: (tuple_pattern pattern: (tuple_pattern
(identifier) (identifier)
(discard)) (discard))
value: (tuple value: (tuple
(integer) (integer)
(integer))))) (integer)))
(let_assert
pattern: (record_pattern
name: (constructor_name)
arguments: (record_pattern_arguments
(record_pattern_argument
pattern: (discard))))
value: (record
name: (constructor_name)
arguments: (arguments
(argument
value: (integer))))
message: (string
(quoted_content)))
(let_assert
pattern: (integer)
value: (integer)
message: (block
(binary_expression
left: (string
(quoted_content))
right: (string
(quoted_content)))))))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters) parameters: (function_parameters)
body: (function_body body: (block
(assert (let
pattern: (identifier) pattern: (identifier)
value: (expression_group value: (block
(binary_expression (binary_expression
left: (integer) left: (integer)
right: (integer)) right: (integer))
(string (string
(quoted_content)))) (quoted_content))))
(assert (let
pattern: (identifier) pattern: (identifier)
value: (identifier)) value: (identifier))
(assert (let
pattern: (tuple_pattern pattern: (tuple_pattern
(identifier) (identifier)
(discard)) (discard))
value: (tuple value: (tuple
(integer) (integer)
(integer))))) (integer)))))
(function
name: (identifier)
parameters: (function_parameters)
body: (block
(assert
value: (binary_expression
left: (record
name: (constructor_name))
right: (record
name: (constructor_name))))
(assert
value: (binary_expression
left: (function_call
function: (identifier)
arguments: (arguments
(argument
value: (integer))
(argument
value: (integer))))
right: (integer)))
(assert
value: (boolean_negation
(identifier)))
(assert
value: (binary_expression
left: (block
(integer)
(integer))
right: (integer)))
(assert
value: (function_call
function: (field_access
record: (identifier)
field: (label))
arguments: (arguments
(argument
value: (record
name: (constructor_name)
arguments: (arguments
(argument
value: (integer))))))))))
(function (function
name: (identifier) name: (identifier)
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(record_update (record_update
constructor: (constructor_name) constructor: (constructor_name)
spread: (identifier) spread: (identifier)
@ -529,7 +633,7 @@ fn field_access(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(tuple_access (tuple_access
tuple: (identifier) tuple: (identifier)
index: (integer)))) index: (integer))))
@ -538,7 +642,7 @@ fn field_access(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(field_access (field_access
record: (identifier) record: (identifier)
field: (label))))) field: (label)))))
@ -582,7 +686,7 @@ fn trial(x, y, z) {
name: (identifier)) name: (identifier))
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(case (case
subjects: (case_subjects subjects: (case_subjects
(identifier)) (identifier))
@ -697,84 +801,34 @@ fn trial(x, y, z) {
name: (constructor_name)))))))) name: (constructor_name))))))))
================================================================================ ================================================================================
Try patterns Let expressions
================================================================================ ================================================================================
fn try_try_again(x, y) -> Int { let foo: fn(Int) -> Int = fn(x) { x }
try int_x = todo let fun_ref = float.to_string
try _who_cares = todo
try file.IODevice() = todo
try Node = todo
try "hello" = todo
try 1 = todo
try 12.34 = todo
try #(a, b) = todo
try <<a:utf8, b:size(8)>> = todo
try [a, b] = todo
}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
(source_file (source_file
(function (let
name: (identifier) pattern: (identifier)
parameters: (function_parameters type: (function_type
(function_parameter parameter_types: (function_parameter_types
name: (identifier)) (type
(function_parameter name: (type_identifier)))
name: (identifier))) return_type: (type
return_type: (type name: (type_identifier)))
name: (type_identifier)) value: (anonymous_function
body: (function_body parameters: (function_parameters
(try (function_parameter
pattern: (identifier) name: (identifier)))
value: (todo)) body: (block
(try (identifier))))
pattern: (discard) (let
value: (todo)) pattern: (identifier)
(try value: (field_access
pattern: (record_pattern record: (identifier)
name: (remote_constructor_name field: (label))))
module: (identifier)
name: (constructor_name))
arguments: (record_pattern_arguments))
value: (todo))
(try
pattern: (record_pattern
name: (constructor_name))
value: (todo))
(try
pattern: (string
(quoted_content))
value: (todo))
(try
pattern: (integer)
value: (todo))
(try
pattern: (float)
value: (todo))
(try
pattern: (tuple_pattern
(identifier)
(identifier))
value: (todo))
(try
pattern: (bit_string_pattern
(bit_string_segment
value: (identifier)
options: (bit_string_segment_options
(bit_string_segment_option)))
(bit_string_segment
value: (identifier)
options: (bit_string_segment_options
(bit_string_segment_option
(integer)))))
value: (todo))
(try
pattern: (list_pattern
(identifier)
(identifier))
value: (todo)))))
================================================================================ ================================================================================
Complex binary expressions Complex binary expressions
@ -794,7 +848,7 @@ fn complicated(x, y) {
name: (identifier)) name: (identifier))
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(binary_expression (binary_expression
left: (binary_expression left: (binary_expression
left: (binary_expression left: (binary_expression
@ -838,7 +892,7 @@ fn complex_data_fun(x, y) {
name: (identifier)) name: (identifier))
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(field_access (field_access
record: (tuple_access record: (tuple_access
tuple: (field_access tuple: (field_access
@ -906,7 +960,7 @@ fn weird(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(function_call (function_call
function: (identifier) function: (identifier)
arguments: (arguments arguments: (arguments
@ -919,7 +973,7 @@ fn weird(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(binary_expression (binary_expression
left: (identifier) left: (identifier)
right: (integer)))) right: (integer))))
@ -927,14 +981,14 @@ fn weird(x) {
(argument (argument
value: (integer)))) value: (integer))))
(function_call (function_call
function: (expression_group function: (block
(let (let
pattern: (identifier) pattern: (identifier)
value: (anonymous_function value: (anonymous_function
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(binary_expression (binary_expression
left: (identifier) left: (identifier)
right: (integer)))))) right: (integer))))))
@ -954,7 +1008,7 @@ fn weird(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(binary_expression (binary_expression
left: (identifier) left: (identifier)
right: (integer))))))) right: (integer)))))))
@ -990,7 +1044,7 @@ fn ignores(foo, _bar) {
name: (identifier)) name: (identifier))
(function_parameter (function_parameter
name: (discard))) name: (discard)))
body: (function_body body: (block
(let (let
pattern: (discard) pattern: (discard)
value: (integer)) value: (integer))
@ -1021,7 +1075,7 @@ fn lists(x) {
parameters: (function_parameters parameters: (function_parameters
(function_parameter (function_parameter
name: (identifier))) name: (identifier)))
body: (function_body body: (block
(list) (list)
(list (list
(integer)) (integer))
@ -1048,5 +1102,22 @@ io.println("// hello world!\n")
arguments: (arguments arguments: (arguments
(argument (argument
value: (string value: (string
(quoted_content) (quoted_content)
(escape_sequence)))))) (escape_sequence))))))
================================================================================
Call with label shorthand
================================================================================
wibble(arg1:, arg2:)
--------------------------------------------------------------------------------
(source_file
(function_call
(identifier)
(arguments
(argument
(label))
(argument
(label)))))

@ -0,0 +1,73 @@
================================================================================
Guard Expressions
================================================================================
case value {
n if n + 1 > 10 -> True
n if n / 2 < 5 -> False
}
case value {
n if n -. 1.0 <. 10.0 -> True
n if n *. 2 >. 5 -> False
}
--------------------------------------------------------------------------------
(source_file
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(identifier)))
(case_clause_guard
(binary_expression
(binary_expression
(identifier)
(integer))
(integer)))
(record
(constructor_name)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(identifier)))
(case_clause_guard
(binary_expression
(binary_expression
(identifier)
(integer))
(integer)))
(record
(constructor_name)))))
(case
(case_subjects
(identifier))
(case_clauses
(case_clause
(case_clause_patterns
(case_clause_pattern
(identifier)))
(case_clause_guard
(binary_expression
(binary_expression
(identifier)
(float))
(float)))
(record
(constructor_name)))
(case_clause
(case_clause_patterns
(case_clause_pattern
(identifier)))
(case_clause_guard
(binary_expression
(binary_expression
(identifier)
(integer))
(integer)))
(record
(constructor_name))))))

@ -21,6 +21,7 @@ import a.{b}
import a/b.{c, d} import a/b.{c, d}
import a/b.{c as d, e} import a/b.{c as d, e}
import a/b.{c, D as E} import a/b.{c, D as E}
import a/b.{A as B, type C as D}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -50,6 +51,15 @@ import a/b.{c, D as E}
imports: (unqualified_imports imports: (unqualified_imports
(unqualified_import (unqualified_import
name: (identifier)) name: (identifier))
(unqualified_import
name: (constructor_name)
alias: (constructor_name))))
(import
module: (module)
imports: (unqualified_imports
(unqualified_import
name: (constructor_name)
alias: (constructor_name))
(unqualified_import (unqualified_import
name: (type_identifier) name: (type_identifier)
alias: (type_identifier))))) alias: (type_identifier)))))
@ -61,6 +71,7 @@ Aliased imports
import a/b.{c as d} as e import a/b.{c as d} as e
import animal/cat as kitty import animal/cat as kitty
import animal.{Cat as Kitty} as a import animal.{Cat as Kitty} as a
import animal.{type Cat as Kitty} as a
import animal.{} import animal.{}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -76,6 +87,13 @@ import animal.{}
(import (import
module: (module) module: (module)
alias: (identifier)) alias: (identifier))
(import
module: (module)
imports: (unqualified_imports
(unqualified_import
name: (constructor_name)
alias: (constructor_name)))
alias: (identifier))
(import (import
module: (module) module: (module)
imports: (unqualified_imports imports: (unqualified_imports
@ -86,3 +104,41 @@ import animal.{}
(import (import
module: (module) module: (module)
imports: (unqualified_imports))) imports: (unqualified_imports)))
================================================================================
Type imports
================================================================================
import a/b.{type C}
import animal.{type Cat as Kitty}
--------------------------------------------------------------------------------
(source_file
(import
module: (module)
imports: (unqualified_imports
(unqualified_import
name: (type_identifier))))
(import
module: (module)
imports: (unqualified_imports
(unqualified_import
name: (type_identifier)
alias: (type_identifier)))))
================================================================================
Discard module imports
================================================================================
import wibble.{wobble} as _
--------------------------------------------------------------------------------
(source_file
(import
(module)
(unqualified_imports
(unqualified_import
(identifier)))
(discard)))

@ -0,0 +1,59 @@
================================================================================
Use
================================================================================
use <- f()
use a <- f()
use a, b, c, d, e <- f()
use #(a, b) <- blah
use x : OfType <- f()
--------------------------------------------------------------------------------
(source_file
(use
(function_call
(identifier)
(arguments)))
(use
(use_assignments
(use_assignment
(identifier)))
(function_call
(identifier)
(arguments)))
(use
(use_assignments
(use_assignment
(identifier))
(use_assignment
(identifier))
(use_assignment
(identifier))
(use_assignment
(identifier))
(use_assignment
(identifier)))
(function_call
(identifier)
(arguments)))
(use
(use_assignments
(use_assignment
(tuple_pattern
(identifier)
(identifier))))
(identifier))
(use
(use_assignments
(use_assignment
(identifier)
(type
(type_identifier))))
(function_call
(identifier)
(arguments))))

@ -0,0 +1,38 @@
================================================================================
Escape sequences
================================================================================
"\t\t\r\nHello, CRLF!"
"¯\\_(ツ)_/¯"
"\"\""
"Hello, \e\f"
// 🏴‍☠️ is 🏴 and ☠️ joined with a zero-width joiner (U+200D)
"🏴‍☠️ == \u{1F3F4}\u{200D}\u{2620}\u{FE0F}"
--------------------------------------------------------------------------------
(source_file
(string
(escape_sequence)
(escape_sequence)
(escape_sequence)
(escape_sequence)
(quoted_content))
(string
(quoted_content)
(escape_sequence)
(quoted_content))
(string
(escape_sequence)
(escape_sequence))
(string
(quoted_content)
(escape_sequence)
(escape_sequence))
(comment)
(string
(quoted_content)
(escape_sequence)
(escape_sequence)
(escape_sequence)
(escape_sequence)))

@ -48,7 +48,7 @@ if javascript {
name: (type_identifier)))) name: (type_identifier))))
return_type: (type return_type: (type
name: (type_identifier)) name: (type_identifier))
body: (function_body body: (block
(let (let
pattern: (identifier) pattern: (identifier)
value: (function_call value: (function_call
@ -161,7 +161,7 @@ pub fn negate(bool: Bool) -> Bool {
module: (module) module: (module)
imports: (unqualified_imports imports: (unqualified_imports
(unqualified_import (unqualified_import
name: (type_identifier)))) name: (constructor_name))))
(statement_comment) (statement_comment)
(statement_comment) (statement_comment)
(statement_comment) (statement_comment)
@ -184,7 +184,7 @@ pub fn negate(bool: Bool) -> Bool {
name: (type_identifier)))) name: (type_identifier))))
return_type: (type return_type: (type
name: (type_identifier)) name: (type_identifier))
body: (function_body body: (block
(case (case
subjects: (case_subjects subjects: (case_subjects
(identifier)) (identifier))
@ -234,7 +234,7 @@ fn foo(a,) {
(module) (module)
(unqualified_imports (unqualified_imports
(unqualified_import (unqualified_import
(type_identifier)))) (constructor_name))))
(constant (constant
(identifier) (identifier)
(tuple_type (tuple_type
@ -290,7 +290,7 @@ fn foo(a,) {
(function_parameters (function_parameters
(function_parameter (function_parameter
(identifier))) (identifier)))
(function_body (block
(function_call (function_call
(identifier) (identifier)
(arguments (arguments

@ -0,0 +1,11 @@
pub fn case_with_remainder() {
case todo {
_ if 1 % 2 == 0 -> todo
// ^ number
// ^ operator
// ^ number
// ^ operator
// ^ number
_ -> todo
}
}

@ -0,0 +1,10 @@
const f = 100.001e523
// ^ number
// ^ number
// ^ number
const s = "Hello, \e\t\n"
// ^ warning
// ^ warning
// ^ string.escape
// ^ string.escape

@ -2,8 +2,8 @@ fn case_case(x: Option(String)) {
// ^ variable.parameter // ^ variable.parameter
// ^ type // ^ type
case #(x, x) { case #(x, x) {
// ^ variable.parameter // ^ variable.parameter
// ^ variable.parameter // ^ variable.parameter
#(None, None) -> None #(None, None) -> None
// ^ constructor // ^ constructor
// ^ constructor // ^ constructor
@ -22,3 +22,11 @@ fn case_case(x: Option(String)) {
z.foo() z.foo()
// <- module // <- module
} }
fn shorthand_label_pattern_arg() {
case todo {
Wibble(arg1:, arg2:) -> todo
// ^ property
// ^ property
}
}

@ -0,0 +1,4 @@
pub fn main() {
echo 2
// ^ keyword
}

@ -0,0 +1,16 @@
case "12345" {
"123" <> rest -> rest
// <- string
// ^ operator
// ^ variable
_ -> ""
// ^ string
}
-x
// ^ operator
// ^ variable
panic as "aaah!"
// <- keyword
// ^ keyword
// ^ string

@ -108,3 +108,22 @@ fn comment_string_test() {
// ^ function // ^ function
// ^ string // ^ string
} }
fn let_assert_test() {
let assert #(x, _) = #(1, 2)
// <- keyword
// ^ keyword
}
fn assert_test() {
assert x == add(1, 4)
// <- keyword
// ^ function
}
fn punned_call_arg_test() {
wibble(arg:, arg2:)
// ^ function
// ^ property
// ^ property
}

@ -6,6 +6,16 @@ import animal/cat as kitty
// ^ module // ^ module
// ^ module // ^ module
import animal/cat.{Cat, type Cat}
// ^ constructor
// ^ keyword
// ^ type
import wibble.{wobble} as _
// ^ module
// ^ function
// ^ comment.unused
pub fn main() { pub fn main() {
io.println("hello world") io.println("hello world")
// <- module // <- module
@ -48,3 +58,27 @@ fn make_cat() -> kitty.Cat {
// ^ module // ^ module
// ^ constructor // ^ constructor
} }
@target(erlang)
// <- attribute
// ^ attribute
// ^ constant
pub external fn display() -> Bool = "erlang" "display"
@target(erlang)
@external(erlang, "wobble", "main")
// <- attribute
// ^ attribute
// ^ constant
// ^ string
// string
pub fn main() -> Int
@deprecated(since: "1.2.0", replacement: wobble)
// <- attribute
// ^ attribute
// ^ property
// ^ string
// ^ property
// ^constant
pub fn wibble() { todo }

@ -12,3 +12,17 @@ pub fn new(name: String) {
// ^ property // ^ property
// ^ variable.parameter // ^ variable.parameter
} }
pub fn access() {
let config = Config()
config.connection.host
// ^ variable
// ^ property
// ^ property
}
pub fn record_update_shorthand_label() {
User(..user, name:)
// ^ constructor
// ^ property
}

@ -0,0 +1,14 @@
auto
// <- error
delegate
// <- error
derive
// <- error
else
// <- error
implement
// <- error
macro
// <- error
test
// <- error

@ -1,8 +1,8 @@
import gleam/option.{Option, Some, None} import gleam/option.{type Option, Some, None}
// ^ reference.module // ^ reference.module
// ^ reference.type // ^ reference.type
// ^ reference.type // ^ reference.constructor
// ^ reference.type // ^ reference.constructor
import gleam/bit_builder import gleam/bit_builder
// ^ reference.module // ^ reference.module