├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tool-versions ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── binding.gyp ├── bindings ├── node │ ├── binding.cc │ └── index.js └── rust │ ├── build.rs │ └── lib.rs ├── grammar.js ├── out ├── .gitkeep ├── corpus │ └── .gitkeep └── fuzz-results │ └── .gitkeep ├── package-lock.json ├── package.json ├── playground ├── index.html ├── playground.js ├── tree-sitter.js └── tree-sitter.wasm ├── queries └── highlights.scm ├── references.md ├── scripts ├── build-fuzzer ├── generate-unicode-range └── run-fuzzer ├── src ├── scanner.cc └── unicode.h └── test ├── corpus ├── anonymous_function.txt ├── binary.txt ├── block.txt ├── call.txt ├── case.txt ├── comment.txt ├── cond.txt ├── data_types.txt ├── error.txt ├── expression.txt ├── function.txt ├── if.txt ├── line_break.txt ├── map.txt ├── module.txt ├── operator.txt ├── sigil.txt ├── spec.txt ├── struct.txt ├── try.txt └── unicode.txt ├── fuzz ├── README.md ├── fuzzer.cc └── gen-dict.py └── highlight ├── nested.ex └── sandbox.ex /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | paths-ignore: 7 | - '**.md' 8 | 9 | jobs: 10 | test: 11 | name: Run tests 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | - name: Setup Node 17 | uses: actions/setup-node@v2-beta 18 | with: 19 | node-version: "12" 20 | - name: Node versions 21 | run: | 22 | node --version 23 | npm --version 24 | - name: Install 25 | run: npm install || true 26 | - name: Test 27 | run: make generate test 28 | - name: Integration 29 | run: make integration 30 | publish: 31 | name: Publish playground 32 | runs-on: ubuntu-latest 33 | needs: test 34 | if: github.ref == 'refs/heads/master' 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v2.3.1 38 | - name: Install emscripten 39 | uses: mymindstorm/setup-emsdk@v7 40 | with: 41 | version: 2.0.17 42 | - name: Install 43 | run: npm install || true 44 | - name: Build 45 | run: make generate build-wasm 46 | - name: Deploy 47 | uses: JamesIves/github-pages-deploy-action@4.1.0 48 | with: 49 | BRANCH: gh-pages 50 | FOLDER: playground 51 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tree-sitter-elixir.wasm 3 | log.html 4 | test.ex 5 | src/GPATH 6 | src/GRTAGS 7 | src/GTAGS 8 | src/* 9 | !src/scanner.cc 10 | !src/unicode.h 11 | scripts/target 12 | build 13 | out 14 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | elixir 1.12.0-rc.1-otp-23 2 | erlang 23.1 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-elixir" 3 | description = "elixir grammar for the tree-sitter parsing library" 4 | version = "0.0.1" 5 | keywords = ["incremental", "parsing", "elixir"] 6 | categories = ["parsing", "text-editors"] 7 | repository = "https://github.com/tree-sitter/tree-sitter-javascript" 8 | edition = "2018" 9 | license = "MIT" 10 | 11 | build = "bindings/rust/build.rs" 12 | include = [ 13 | "bindings/rust/*", 14 | "grammar.js", 15 | "queries/*", 16 | "src/*", 17 | ] 18 | 19 | [lib] 20 | path = "bindings/rust/lib.rs" 21 | 22 | [dependencies] 23 | tree-sitter = "0.17" 24 | 25 | [build-dependencies] 26 | cc = "1.0" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Anantha Kumaran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | generate: 4 | ./node_modules/.bin/tree-sitter generate 5 | 6 | build-wasm: 7 | ./node_modules/.bin/tree-sitter build-wasm 8 | cp tree-sitter-elixir.wasm playground/tree-sitter-parser.wasm 9 | 10 | ui: 11 | ./node_modules/.bin/tree-sitter build-wasm 12 | ./node_modules/tree-sitter-cli/tree-sitter web-ui -q 13 | 14 | integration: 15 | -[ ! -d "../elixir" ] && cd .. && git clone https://github.com/elixir-lang/elixir.git 16 | ./node_modules/.bin/tree-sitter parse '../elixir/**/*.ex*' --quiet --stat 17 | 18 | parse: 19 | ./node_modules/.bin/tree-sitter parse -x 'test.ex' 20 | 21 | debug-graph: 22 | ./node_modules/.bin/tree-sitter parse 'test.ex' --debug-graph 23 | 24 | debug: 25 | ./node_modules/.bin/tree-sitter parse 'test.ex' --debug 26 | 27 | test: 28 | ./node_modules/.bin/tree-sitter test 29 | 30 | install: generate test 31 | 32 | update-corpus: 33 | ./node_modules/.bin/tree-sitter test -u 34 | 35 | generate-header: 36 | mkdir -p scripts/target 37 | cd scripts && ./generate-unicode-range > ../src/unicode.h 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is no longer maintained. Please checkout the official 2 | elixir [grammar](https://github.com/elixir-lang/tree-sitter-elixir) for treesitter. 3 | 4 | ## Status 5 | 6 | The grammar is still in progress, as of now, it can parse all the 7 | files in the elixir source repo. On a largish repo (around 3000 source 8 | files), it can parse 100% of the files. I haven't verified if it builds 9 | ast node with correct structure (precedence and associativity). 10 | 11 | ## Install 12 | 13 | ```bash 14 | git clone git@github.com:ananthakumaran/tree-sitter-elixir.git 15 | npm install # ignore node-gyp error, we will fix it in the next step 16 | make install # this should generate dynamic libraries under ~/.tree-sitter/bin/{elixir.so, elixir.so.dSYM} 17 | ``` 18 | 19 | ## Emacs 20 | 21 | Emacs users can refer 22 | [init-tree-sitter.el](https://gist.github.com/ananthakumaran/ed91ef5a7bbf679cdf13e8a65ea54abe) 23 | for a sample config. 24 | 25 | ## Vim 26 | 27 | [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) supports [elixir](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages) 28 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_elixir_binding", 5 | "include_dirs": [ 6 | " 3 | #include "nan.h" 4 | 5 | using namespace v8; 6 | 7 | extern "C" TSLanguage * tree_sitter_elixir(); 8 | 9 | namespace { 10 | 11 | NAN_METHOD(New) {} 12 | 13 | void Init(Local exports, Local module) { 14 | Local tpl = Nan::New(New); 15 | tpl->SetClassName(Nan::New("Language").ToLocalChecked()); 16 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 17 | 18 | Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); 19 | Local instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked(); 20 | Nan::SetInternalFieldPointer(instance, 0, tree_sitter_elixir()); 21 | 22 | Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("elixir").ToLocalChecked()); 23 | Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance); 24 | } 25 | 26 | NODE_MODULE(tree_sitter_elixir_binding, Init) 27 | 28 | } // namespace 29 | -------------------------------------------------------------------------------- /bindings/node/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | module.exports = require("../../build/Release/tree_sitter_elixir_binding"); 3 | } catch (error1) { 4 | if (error1.code !== 'MODULE_NOT_FOUND') { 5 | throw error1; 6 | } 7 | try { 8 | module.exports = require("../../build/Debug/tree_sitter_elixir_binding"); 9 | } catch (error2) { 10 | if (error2.code !== 'MODULE_NOT_FOUND') { 11 | throw error2; 12 | } 13 | throw error1 14 | } 15 | } 16 | 17 | try { 18 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 19 | } catch (_) {} 20 | -------------------------------------------------------------------------------- /bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | 4 | let mut c_config = cc::Build::new(); 5 | c_config.include(&src_dir); 6 | c_config 7 | .flag_if_supported("-Wno-unused-parameter") 8 | .flag_if_supported("-Wno-unused-but-set-variable") 9 | .flag_if_supported("-Wno-trigraphs"); 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | 13 | // If your language uses an external scanner written in C, 14 | // then include this block of code: 15 | 16 | /* 17 | let scanner_path = src_dir.join("scanner.c"); 18 | c_config.file(&scanner_path); 19 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 20 | */ 21 | 22 | c_config.compile("parser"); 23 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 24 | 25 | // If your language uses an external scanner written in C++, 26 | // then include this block of code: 27 | 28 | /* 29 | let mut cpp_config = cc::Build::new(); 30 | cpp_config.cpp(true); 31 | cpp_config.include(&src_dir); 32 | cpp_config 33 | .flag_if_supported("-Wno-unused-parameter") 34 | .flag_if_supported("-Wno-unused-but-set-variable"); 35 | let scanner_path = src_dir.join("scanner.cc"); 36 | cpp_config.file(&scanner_path); 37 | cpp_config.compile("scanner"); 38 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides elixir language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [language][language func] function to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = ""; 8 | //! let mut parser = tree_sitter::Parser::new(); 9 | //! parser.set_language(tree_sitter_elixir::language()).expect("Error loading elixir grammar"); 10 | //! let tree = parser.parse(code, None).unwrap(); 11 | //! ``` 12 | //! 13 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 14 | //! [language func]: fn.language.html 15 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 16 | //! [tree-sitter]: https://tree-sitter.github.io/ 17 | 18 | use tree_sitter::Language; 19 | 20 | extern "C" { 21 | fn tree_sitter_elixir() -> Language; 22 | } 23 | 24 | /// Get the tree-sitter [Language][] for this grammar. 25 | /// 26 | /// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 27 | pub fn language() -> Language { 28 | unsafe { tree_sitter_elixir() } 29 | } 30 | 31 | /// The content of the [`node-types.json`][] file for this grammar. 32 | /// 33 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 34 | pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); 35 | 36 | // Uncomment these to include any queries that this grammar contains 37 | 38 | // pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); 39 | // pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); 40 | // pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm"); 41 | // pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm"); 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | #[test] 46 | fn test_can_load_grammar() { 47 | let mut parser = tree_sitter::Parser::new(); 48 | parser 49 | .set_language(super::language()) 50 | .expect("Error loading elixir language"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | /* global optional, seq, choice, prec, field, repeat, grammar, alias, token */ 2 | 3 | function sep(rule, separator) { 4 | return optional(sep1(rule, separator)); 5 | } 6 | 7 | function sep1(rule, separator) { 8 | return seq(rule, repeat(seq(separator, rule))); 9 | } 10 | 11 | function commaSep1($, rule) { 12 | return sep1(rule, prec.left(20, seq(",", optional($._terminator)))); 13 | } 14 | 15 | function commaSep($, rule) { 16 | return optional(commaSep1($, rule)); 17 | } 18 | 19 | function atleastOnce(rule) { 20 | return seq(rule, repeat(rule)); 21 | } 22 | 23 | function binaryOp($, assoc, precedence, operator, right) { 24 | return assoc( 25 | precedence, 26 | seq( 27 | field("left", $._expression), 28 | field("operator", operator), 29 | optional($._terminator), 30 | field("right", right || $._expression) 31 | ) 32 | ); 33 | } 34 | 35 | function aliases(rules, symbol) { 36 | return rules.map(rule => alias(rule, symbol)); 37 | } 38 | 39 | function unaryOp($, assoc, precedence, operator) { 40 | return assoc( 41 | precedence, 42 | seq(field("operator", operator), optional($._line_break), $._expression) 43 | ); 44 | } 45 | 46 | function blockExpression($, name) { 47 | return prec.right( 48 | seq( 49 | name, 50 | optional($._terminator), 51 | choice( 52 | sep($.stab_expression, $._terminator), 53 | sep($._expression, $._terminator) 54 | ), 55 | optional($._terminator) 56 | ) 57 | ); 58 | } 59 | 60 | const OPERATORS = [ 61 | "@", 62 | ".", 63 | "+", 64 | "-", 65 | "!", 66 | "^", 67 | "~~~", 68 | "*", 69 | "/", 70 | "+", 71 | "-", 72 | "++", 73 | "--", 74 | "..", 75 | "<>", 76 | "+++", 77 | "---", 78 | "^^^", 79 | "|>", 80 | "<<<", 81 | ">>>", 82 | "<<~", 83 | "~>>", 84 | "<~", 85 | "~>", 86 | "<~>", 87 | "<|>", 88 | "<", 89 | ">", 90 | "<=", 91 | ">=", 92 | "==", 93 | "!=", 94 | "=~", 95 | "===", 96 | "!==", 97 | "&&", 98 | "&&&", 99 | "||", 100 | "|||", 101 | "=", 102 | "&", 103 | "=>", 104 | "|", 105 | "::", 106 | "<-", 107 | "\\\\", 108 | "..//" 109 | ]; 110 | 111 | const PREC = { 112 | COMMENT: -2, 113 | CALL: -1, 114 | DOT_CALL: 310, 115 | ACCESS_CALL: 8 116 | }; 117 | 118 | module.exports = grammar({ 119 | name: "elixir", 120 | 121 | externals: $ => [ 122 | $._line_break, 123 | $._non_breaking_line, 124 | $.heredoc_start, 125 | $.heredoc_content, 126 | $.heredoc_end, 127 | $.sigil_start, 128 | $.sigil_content, 129 | $.sigil_end, 130 | $.string_start, 131 | $.string_content, 132 | $.string_end, 133 | $.identifier, 134 | $.unused_identifier, 135 | $.special_identifier, 136 | $.keyword_literal, 137 | $.atom_literal, 138 | $.atom_start, 139 | $.atom_content, 140 | $.atom_end, 141 | $.true, 142 | $.false, 143 | $.nil, 144 | $._when, 145 | $._and, 146 | $._or, 147 | $._not, 148 | $._in, 149 | $._not_in, 150 | $._fn, 151 | $._do, 152 | $._end, 153 | $._catch, 154 | $._rescue, 155 | $._after, 156 | $._else 157 | ], 158 | 159 | extras: $ => [$.comment, /[\t \f]/, $._non_breaking_line, $._escaped_newline], 160 | 161 | conflicts: $ => [ 162 | [$.call], 163 | [$._bare_arguments], 164 | [$._clause_body], 165 | [$.keyword_list], 166 | [$.block, $._bare_arguments], 167 | [$.block, $.paren_expr, $._bare_arguments], 168 | [$.block, $.stab_expression] 169 | ], 170 | 171 | inline: $ => [$._identifier], 172 | 173 | word: $ => $.identifier, 174 | 175 | rules: { 176 | program: $ => 177 | seq( 178 | optional($._terminator), 179 | sep($._expression, $._terminator), 180 | optional($._terminator) 181 | ), 182 | 183 | _expression: $ => 184 | choice( 185 | $.binary_op, 186 | $.unary_op, 187 | alias($._capture_op, $.unary_op), 188 | $.block, 189 | alias($.paren_call, $.call), 190 | $.call, 191 | $.dot_call, 192 | $.access_call, 193 | $.anonymous_function, 194 | $.sigil, 195 | $.heredoc, 196 | $.integer, 197 | $.float, 198 | $.module, 199 | $.atom, 200 | $.list, 201 | $.binary, 202 | $.map, 203 | $.struct, 204 | $.string, 205 | $.tuple, 206 | $._literal, 207 | $.char, 208 | $._identifier 209 | ), 210 | 211 | _identifier: $ => 212 | choice($.identifier, $.unused_identifier, $.special_identifier), 213 | 214 | block: $ => 215 | seq( 216 | "(", 217 | optional($._terminator), 218 | sep(choice($.stab_expression, $._expression), $._terminator), 219 | optional($._terminator), 220 | ")" 221 | ), 222 | 223 | paren_expr: $ => 224 | seq( 225 | "(", 226 | optional($._terminator), 227 | $._expression, 228 | optional($._terminator), 229 | ")" 230 | ), 231 | 232 | paren_call: $ => 233 | seq( 234 | field("function", alias($._identifier, $.function_identifier)), 235 | $.arguments 236 | ), 237 | 238 | call: $ => 239 | prec( 240 | PREC.CALL, 241 | choice( 242 | seq( 243 | field( 244 | "function", 245 | choice( 246 | alias($._identifier, $.function_identifier), 247 | $.dot_call, 248 | alias($.paren_call, $.call) 249 | ) 250 | ), 251 | choice($._bare_arguments, $.arguments), 252 | optional(choice(seq($._terminator, $.do_block), $.do_block)) 253 | ), 254 | seq( 255 | field( 256 | "function", 257 | choice( 258 | alias($._identifier, $.function_identifier), 259 | $.dot_call, 260 | alias($.paren_call, $.call) 261 | ) 262 | ), 263 | $.do_block 264 | ) 265 | ) 266 | ), 267 | 268 | binary_op: $ => 269 | choice( 270 | binaryOp($, prec.left, 40, choice("\\\\", "<-")), 271 | binaryOp( 272 | $, 273 | prec.right, 274 | 50, 275 | alias($._when, "when"), 276 | choice($._expression, $.keyword_list) 277 | ), 278 | binaryOp($, prec.right, 60, "::"), 279 | binaryOp($, prec.right, 70, "|", choice($._expression, $.keyword_list)), 280 | binaryOp($, prec.right, 80, "=>"), 281 | binaryOp($, prec.right, 100, "="), 282 | binaryOp($, prec.left, 120, choice("||", "|||", alias($._or, "or"))), 283 | binaryOp($, prec.left, 130, choice("&&", "&&&", alias($._and, "and"))), 284 | binaryOp($, prec.left, 140, choice("==", "!=", "=~", "===", "!==")), 285 | binaryOp($, prec.left, 150, choice("<", ">", "<=", ">=")), 286 | binaryOp( 287 | $, 288 | prec.left, 289 | 160, 290 | choice("|>", "<<<", ">>>", "<<~", "~>>", "<~", "~>", "<~>", "<|>") 291 | ), 292 | binaryOp( 293 | $, 294 | prec.left, 295 | 170, 296 | choice(alias($._in, "in"), alias($._not_in, "not in")) 297 | ), 298 | binaryOp($, prec.left, 180, choice("^^^")), 299 | binaryOp($, prec.right, 200, choice("++", "--", "<>", "+++", "---")), 300 | binaryOp($, prec.right, 190, choice("//")), 301 | binaryOp($, prec.right, 200, ".."), 302 | binaryOp($, prec.left, 210, choice("+", "-")), 303 | binaryOp($, prec.left, 220, choice("*", "/")), 304 | $._op_capture 305 | ), 306 | 307 | unary_op: $ => 308 | choice( 309 | unaryOp($, prec, 90, "&"), 310 | unaryOp( 311 | $, 312 | prec, 313 | 300, 314 | choice("+", "-", "!", "^", "~~~", alias($._not, "not")) 315 | ), 316 | unaryOp($, prec, 320, "@") 317 | ), 318 | 319 | _op_capture: $ => 320 | prec.left( 321 | 220, 322 | seq( 323 | choice( 324 | alias($._and, "and"), 325 | alias($._or, "or"), 326 | alias($._not, "not"), 327 | alias($._when, "when"), 328 | alias($._in, "in"), 329 | ...OPERATORS 330 | ), 331 | optional($._terminator), 332 | field("operator", "/"), 333 | optional($._terminator), 334 | $.integer 335 | ) 336 | ), 337 | 338 | _capture_op: $ => 339 | prec( 340 | 320, 341 | seq(field("operator", "&"), optional($._terminator), $.integer) 342 | ), 343 | 344 | _dot_call_function_args: $ => 345 | choice( 346 | prec.right( 347 | seq( 348 | field( 349 | "function", 350 | choice( 351 | ...aliases( 352 | [$._and, $._or, $._not, $._when, $._in, ...OPERATORS], 353 | $.function_identifier 354 | ) 355 | ) 356 | ), 357 | optional($.arguments) 358 | ) 359 | ), 360 | prec.right(seq(field("function", $.string), optional($.arguments))), 361 | prec.right( 362 | seq( 363 | field( 364 | "function", 365 | choice( 366 | ...aliases( 367 | [ 368 | $._identifier, 369 | $.true, 370 | $.false, 371 | $.nil, 372 | $._when, 373 | $._and, 374 | $._or, 375 | $._not, 376 | $._in, 377 | $._fn, 378 | $._do, 379 | $._end, 380 | $._catch, 381 | $._rescue, 382 | $._after, 383 | $._else 384 | ], 385 | $.function_identifier 386 | ) 387 | ) 388 | ), 389 | optional($.arguments) 390 | ) 391 | ), 392 | $.module, 393 | $.arguments, 394 | $.tuple 395 | ), 396 | 397 | _simple_dot_call: $ => 398 | prec.left( 399 | PREC.DOT_CALL, 400 | seq( 401 | field( 402 | "remote", 403 | choice( 404 | $.module, 405 | $._identifier, 406 | $.atom, 407 | alias($._simple_dot_call, $.dot_call), 408 | alias($.paren_call, $.call), 409 | alias($._capture_op, $.unary_op), 410 | $.integer, 411 | $.char 412 | ) 413 | ), 414 | ".", 415 | optional($._line_break), 416 | $._dot_call_function_args 417 | ) 418 | ), 419 | 420 | _complex_dot_call: $ => 421 | prec.left( 422 | PREC.DOT_CALL, 423 | seq( 424 | field( 425 | "remote", 426 | choice( 427 | $.dot_call, 428 | $.access_call, 429 | $.paren_expr, 430 | $.map, 431 | $.struct, 432 | $.anonymous_function 433 | ) 434 | ), 435 | ".", 436 | optional($._line_break), 437 | $._dot_call_function_args 438 | ) 439 | ), 440 | 441 | dot_call: $ => choice($._simple_dot_call, $._complex_dot_call), 442 | 443 | access_call: $ => 444 | prec.left( 445 | PREC.ACCESS_CALL, 446 | seq($._expression, token.immediate("["), $._expression, "]") 447 | ), 448 | 449 | after_block: $ => blockExpression($, alias($._after, "after")), 450 | rescue_block: $ => blockExpression($, alias($._rescue, "rescue")), 451 | catch_block: $ => blockExpression($, alias($._catch, "catch")), 452 | else_block: $ => blockExpression($, alias($._else, "else")), 453 | 454 | do_block: $ => 455 | prec.left( 456 | 5, 457 | seq( 458 | blockExpression($, alias($._do, "do")), 459 | repeat( 460 | choice($.after_block, $.rescue_block, $.catch_block, $.else_block) 461 | ), 462 | optional($._terminator), 463 | alias($._end, "end") 464 | ) 465 | ), 466 | 467 | anonymous_function: $ => 468 | seq( 469 | alias($._fn, "fn"), 470 | optional($._terminator), 471 | sep1($.stab_expression, $._terminator), 472 | optional($._terminator), 473 | alias($._end, "end") 474 | ), 475 | 476 | arguments: $ => 477 | seq( 478 | token.immediate("("), 479 | optional($._terminator), 480 | choice( 481 | seq($.keyword_list, optional(",")), 482 | seq(commaSep($, $._expression)), 483 | seq( 484 | commaSep1($, $._expression), 485 | seq(",", optional($._terminator), $.keyword_list, optional(",")) 486 | ) 487 | ), 488 | optional($._terminator), 489 | ")" 490 | ), 491 | 492 | bare_arguments: $ => $._bare_arguments, 493 | 494 | _bare_arguments: $ => 495 | choice( 496 | seq( 497 | commaSep1($, $._expression), 498 | optional(seq(",", optional($._terminator), $.keyword_list)) 499 | ), 500 | $.keyword_list 501 | ), 502 | 503 | map: $ => 504 | seq( 505 | "%{", 506 | optional($._terminator), 507 | optional($._bare_arguments), 508 | optional(","), 509 | optional($._terminator), 510 | "}" 511 | ), 512 | 513 | struct: $ => 514 | seq( 515 | "%", 516 | choice( 517 | $.module, 518 | $._identifier, 519 | $.atom, 520 | alias($._simple_dot_call, $.dot_call), 521 | alias($.paren_call, $.call), 522 | seq("^", $._identifier) 523 | ), 524 | "{", 525 | optional($._terminator), 526 | optional($._bare_arguments), 527 | optional(","), 528 | optional($._terminator), 529 | "}" 530 | ), 531 | 532 | list: $ => 533 | seq( 534 | "[", 535 | optional($._terminator), 536 | optional($._bare_arguments), 537 | optional(","), 538 | optional($._terminator), 539 | "]" 540 | ), 541 | 542 | binary: $ => 543 | seq( 544 | "<<", 545 | optional($._terminator), 546 | optional($._bare_arguments), 547 | optional(","), 548 | optional($._terminator), 549 | ">>" 550 | ), 551 | 552 | keyword_list: $ => 553 | commaSep1($, seq($.keyword, optional($._terminator), $._expression)), 554 | 555 | tuple: $ => 556 | seq( 557 | "{", 558 | optional($._terminator), 559 | optional($._bare_arguments), 560 | optional(","), 561 | optional($._terminator), 562 | "}" 563 | ), 564 | 565 | stab_expression: $ => 566 | seq( 567 | optional( 568 | field( 569 | "left", 570 | seq( 571 | choice( 572 | seq( 573 | "(", 574 | optional($._terminator), 575 | optional($.bare_arguments), 576 | optional($._terminator), 577 | ")" 578 | ), 579 | $.bare_arguments 580 | ) 581 | ) 582 | ) 583 | ), 584 | "->", 585 | field("right", seq(optional($._terminator), $._clause_body)) 586 | ), 587 | 588 | _clause_body: $ => 589 | seq($._expression, optional(seq($._terminator, $._clause_body))), 590 | 591 | heredoc: $ => 592 | seq( 593 | $.heredoc_start, 594 | repeat(choice($.heredoc_content, $.escape_sequence, $.interpolation)), 595 | $.heredoc_end 596 | ), 597 | 598 | sigil: $ => 599 | seq( 600 | $.sigil_start, 601 | repeat(choice($.sigil_content, $.escape_sequence, $.interpolation)), 602 | $.sigil_end 603 | ), 604 | 605 | string: $ => 606 | seq( 607 | $.string_start, 608 | repeat(choice($.string_content, $.escape_sequence, $.interpolation)), 609 | $.string_end 610 | ), 611 | 612 | atom: $ => 613 | choice( 614 | $.atom_literal, 615 | seq( 616 | $.atom_start, 617 | repeat(choice($.atom_content, $.escape_sequence, $.interpolation)), 618 | $.atom_end 619 | ) 620 | ), 621 | 622 | keyword: $ => 623 | choice( 624 | $.keyword_literal, 625 | seq(alias($.string, $.keyword_string), token.immediate(":"), /[\s]+/) 626 | ), 627 | 628 | interpolation: $ => 629 | seq( 630 | "#{", 631 | optional($._terminator), 632 | sep($._expression, $._terminator), 633 | optional($._terminator), 634 | "}" 635 | ), 636 | 637 | // https://hexdocs.pm/elixir/master/String.html#module-escape-characters 638 | escape_sequence: $ => 639 | token( 640 | seq( 641 | "\\", 642 | choice( 643 | /[^ux]/, // single character 644 | /x[0-9a-fA-F]{1,2}/, // hex code 645 | /x{[0-9a-fA-F]+}/, // hex code 646 | /u[0-9a-fA-F]{4}/, // single unicode 647 | /u{[0-9a-fA-F ]+}/ // multiple unicode 648 | ) 649 | ) 650 | ), 651 | 652 | integer: $ => 653 | /0[bB][01](_?[01])*|0[oO]?[0-7](_?[0-7])*|(0[dD])?\d(_?\d)*|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*/, 654 | float: $ => /\d(_?\d)*(\.\d)?(_?\d)*([eE][\+-]?\d(_?\d)*)?/, 655 | module: $ => /[A-Z][_a-zA-Z0-9]*(\.[A-Z][_a-zA-Z0-9]*)*/, 656 | comment: $ => token(prec(PREC.COMMENT, seq("#", /.*/))), 657 | _terminator: $ => prec.right(atleastOnce(choice($._line_break, ";"))), 658 | _literal: $ => choice($.true, $.false, $.nil), 659 | char: $ => /\?(.|\\.)/, 660 | _escaped_newline: $ => /\\\n/ 661 | } 662 | }); 663 | -------------------------------------------------------------------------------- /out/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthakumaran/tree-sitter-elixir/a5a3b0cc82a44933f921565f64df49a4366cfddb/out/.gitkeep -------------------------------------------------------------------------------- /out/corpus/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthakumaran/tree-sitter-elixir/a5a3b0cc82a44933f921565f64df49a4366cfddb/out/corpus/.gitkeep -------------------------------------------------------------------------------- /out/fuzz-results/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthakumaran/tree-sitter-elixir/a5a3b0cc82a44933f921565f64df49a4366cfddb/out/fuzz-results/.gitkeep -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-elixir", 3 | "version": "0.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "0.1.0", 9 | "license": "MIT", 10 | "dependencies": { 11 | "nan": "^2.14.2" 12 | }, 13 | "devDependencies": { 14 | "tree-sitter-cli": "^0.19.4" 15 | } 16 | }, 17 | "node_modules/nan": { 18 | "version": "2.14.2", 19 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", 20 | "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" 21 | }, 22 | "node_modules/tree-sitter-cli": { 23 | "version": "0.19.4", 24 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.4.tgz", 25 | "integrity": "sha512-p2kxjuoQeauXBu5eE+j7c5BMCRXmc17EiAswnnWn3ieUlHXBkA0Z7vRnaCSElDR34MhZnSgqgzuuzQk0cDqCjw==", 26 | "dev": true, 27 | "hasInstallScript": true, 28 | "bin": { 29 | "tree-sitter": "cli.js" 30 | } 31 | } 32 | }, 33 | "dependencies": { 34 | "nan": { 35 | "version": "2.14.2", 36 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", 37 | "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" 38 | }, 39 | "tree-sitter-cli": { 40 | "version": "0.19.4", 41 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.4.tgz", 42 | "integrity": "sha512-p2kxjuoQeauXBu5eE+j7c5BMCRXmc17EiAswnnWn3ieUlHXBkA0Z7vRnaCSElDR34MhZnSgqgzuuzQk0cDqCjw==", 43 | "dev": true 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-elixir", 3 | "version": "0.1.0", 4 | "description": "tree sitter grammar for elixir", 5 | "main": "bindings/node", 6 | "scripts": { 7 | "test": "tree-sitter test" 8 | }, 9 | "author": "Anantha Kumaran ", 10 | "license": "MIT", 11 | "dependencies": { 12 | "nan": "^2.14.2" 13 | }, 14 | "devDependencies": { 15 | "tree-sitter-cli": "^0.19.4" 16 | }, 17 | "tree-sitter": [ 18 | { 19 | "scope": "source.elixir", 20 | "file-types": [ 21 | "ex", 22 | "exs" 23 | ] 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tree-sitter elixir 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 172 | 173 | -------------------------------------------------------------------------------- /playground/playground.js: -------------------------------------------------------------------------------- 1 | let tree; 2 | 3 | (async () => { 4 | const CAPTURE_REGEX = /@\s*([\w\._-]+)/g; 5 | const COLORS_BY_INDEX = [ 6 | 'blue', 7 | 'chocolate', 8 | 'darkblue', 9 | 'darkcyan', 10 | 'darkgreen', 11 | 'darkred', 12 | 'darkslategray', 13 | 'dimgray', 14 | 'green', 15 | 'indigo', 16 | 'navy', 17 | 'red', 18 | 'sienna', 19 | ]; 20 | 21 | const scriptURL = document.currentScript.getAttribute('src'); 22 | 23 | const codeInput = document.getElementById('code-input'); 24 | const languageSelect = document.getElementById('language-select'); 25 | const loggingCheckbox = document.getElementById('logging-checkbox'); 26 | const outputContainer = document.getElementById('output-container'); 27 | const outputContainerScroll = document.getElementById('output-container-scroll'); 28 | const playgroundContainer = document.getElementById('playground-container'); 29 | const queryCheckbox = document.getElementById('query-checkbox'); 30 | const queryContainer = document.getElementById('query-container'); 31 | const queryInput = document.getElementById('query-input'); 32 | const updateTimeSpan = document.getElementById('update-time'); 33 | const languagesByName = {}; 34 | 35 | loadState(); 36 | 37 | await TreeSitter.init(); 38 | 39 | const parser = new TreeSitter(); 40 | const codeEditor = CodeMirror.fromTextArea(codeInput, { 41 | lineNumbers: true, 42 | showCursorWhenSelecting: true 43 | }); 44 | 45 | const queryEditor = CodeMirror.fromTextArea(queryInput, { 46 | lineNumbers: true, 47 | showCursorWhenSelecting: true 48 | }); 49 | 50 | const cluster = new Clusterize({ 51 | rows: [], 52 | noDataText: null, 53 | contentElem: outputContainer, 54 | scrollElem: outputContainerScroll 55 | }); 56 | const renderTreeOnCodeChange = debounce(renderTree, 50); 57 | const saveStateOnChange = debounce(saveState, 2000); 58 | const runTreeQueryOnChange = debounce(runTreeQuery, 50); 59 | 60 | let languageName = languageSelect.value; 61 | let treeRows = null; 62 | let treeRowHighlightedIndex = -1; 63 | let parseCount = 0; 64 | let isRendering = 0; 65 | let query; 66 | 67 | codeEditor.on('changes', handleCodeChange); 68 | codeEditor.on('viewportChange', runTreeQueryOnChange); 69 | codeEditor.on('cursorActivity', debounce(handleCursorMovement, 150)); 70 | queryEditor.on('changes', debounce(handleQueryChange, 150)); 71 | 72 | loggingCheckbox.addEventListener('change', handleLoggingChange); 73 | queryCheckbox.addEventListener('change', handleQueryEnableChange); 74 | languageSelect.addEventListener('change', handleLanguageChange); 75 | outputContainer.addEventListener('click', handleTreeClick); 76 | 77 | handleQueryEnableChange(); 78 | await handleLanguageChange() 79 | 80 | playgroundContainer.style.visibility = 'visible'; 81 | 82 | async function handleLanguageChange() { 83 | const newLanguageName = languageSelect.value; 84 | if (!languagesByName[newLanguageName]) { 85 | const url = `${LANGUAGE_BASE_URL}/tree-sitter-${newLanguageName}.wasm` 86 | languageSelect.disabled = true; 87 | try { 88 | languagesByName[newLanguageName] = await TreeSitter.Language.load(url); 89 | } catch (e) { 90 | console.error(e); 91 | languageSelect.value = languageName; 92 | return 93 | } finally { 94 | languageSelect.disabled = false; 95 | } 96 | } 97 | 98 | tree = null; 99 | languageName = newLanguageName; 100 | parser.setLanguage(languagesByName[newLanguageName]); 101 | handleCodeChange(); 102 | handleQueryChange(); 103 | } 104 | 105 | async function handleCodeChange(editor, changes) { 106 | const newText = codeEditor.getValue() + '\n'; 107 | const edits = tree && changes && changes.map(treeEditForEditorChange); 108 | 109 | const start = performance.now(); 110 | if (edits) { 111 | for (const edit of edits) { 112 | tree.edit(edit); 113 | } 114 | } 115 | const newTree = parser.parse(newText, tree); 116 | const duration = (performance.now() - start).toFixed(1); 117 | 118 | updateTimeSpan.innerText = `${duration} ms`; 119 | if (tree) tree.delete(); 120 | tree = newTree; 121 | parseCount++; 122 | renderTreeOnCodeChange(); 123 | runTreeQueryOnChange(); 124 | saveStateOnChange(); 125 | } 126 | 127 | async function renderTree() { 128 | isRendering++; 129 | const cursor = tree.walk(); 130 | 131 | let currentRenderCount = parseCount; 132 | let row = ''; 133 | let rows = []; 134 | let finishedRow = false; 135 | let visitedChildren = false; 136 | let indentLevel = 0; 137 | 138 | for (let i = 0;; i++) { 139 | if (i > 0 && i % 10000 === 0) { 140 | await new Promise(r => setTimeout(r, 0)); 141 | if (parseCount !== currentRenderCount) { 142 | cursor.delete(); 143 | isRendering--; 144 | return; 145 | } 146 | } 147 | 148 | let displayName; 149 | if (cursor.nodeIsMissing) { 150 | displayName = `MISSING ${cursor.nodeType}` 151 | } else if (cursor.nodeIsNamed) { 152 | displayName = cursor.nodeType; 153 | } 154 | 155 | if (visitedChildren) { 156 | if (displayName) { 157 | finishedRow = true; 158 | } 159 | 160 | if (cursor.gotoNextSibling()) { 161 | visitedChildren = false; 162 | } else if (cursor.gotoParent()) { 163 | visitedChildren = true; 164 | indentLevel--; 165 | } else { 166 | break; 167 | } 168 | } else { 169 | if (displayName) { 170 | if (finishedRow) { 171 | row += ''; 172 | rows.push(row); 173 | finishedRow = false; 174 | } 175 | const start = cursor.startPosition; 176 | const end = cursor.endPosition; 177 | const id = cursor.nodeId; 178 | let fieldName = cursor.currentFieldName(); 179 | if (fieldName) { 180 | fieldName += ': '; 181 | } else { 182 | fieldName = ''; 183 | } 184 | row = `
${' '.repeat(indentLevel)}${fieldName}${displayName} [${start.row}, ${start.column}] - [${end.row}, ${end.column}])`; 185 | finishedRow = true; 186 | } 187 | 188 | if (cursor.gotoFirstChild()) { 189 | visitedChildren = false; 190 | indentLevel++; 191 | } else { 192 | visitedChildren = true; 193 | } 194 | } 195 | } 196 | if (finishedRow) { 197 | row += '
'; 198 | rows.push(row); 199 | } 200 | 201 | cursor.delete(); 202 | cluster.update(rows); 203 | treeRows = rows; 204 | isRendering--; 205 | handleCursorMovement(); 206 | } 207 | 208 | function runTreeQuery(_, startRow, endRow) { 209 | if (endRow == null) { 210 | const viewport = codeEditor.getViewport(); 211 | startRow = viewport.from; 212 | endRow = viewport.to; 213 | } 214 | 215 | codeEditor.operation(() => { 216 | const marks = codeEditor.getAllMarks(); 217 | marks.forEach(m => m.clear()); 218 | 219 | if (tree && query) { 220 | const captures = query.captures( 221 | tree.rootNode, 222 | {row: startRow, column: 0}, 223 | {row: endRow, column: 0}, 224 | ); 225 | let lastNodeId; 226 | for (const {name, node} of captures) { 227 | if (node.id === lastNodeId) continue; 228 | lastNodeId = node.id; 229 | const {startPosition, endPosition} = node; 230 | codeEditor.markText( 231 | {line: startPosition.row, ch: startPosition.column}, 232 | {line: endPosition.row, ch: endPosition.column}, 233 | { 234 | inclusiveLeft: true, 235 | inclusiveRight: true, 236 | css: `color: ${colorForCaptureName(name)}` 237 | } 238 | ); 239 | } 240 | } 241 | }); 242 | } 243 | 244 | function handleQueryChange() { 245 | if (query) { 246 | query.delete(); 247 | query.deleted = true; 248 | query = null; 249 | } 250 | 251 | queryEditor.operation(() => { 252 | queryEditor.getAllMarks().forEach(m => m.clear()); 253 | if (!queryCheckbox.checked) return; 254 | 255 | const queryText = queryEditor.getValue(); 256 | 257 | try { 258 | query = parser.getLanguage().query(queryText); 259 | let match; 260 | 261 | let row = 0; 262 | queryEditor.eachLine((line) => { 263 | while (match = CAPTURE_REGEX.exec(line.text)) { 264 | queryEditor.markText( 265 | {line: row, ch: match.index}, 266 | {line: row, ch: match.index + match[0].length}, 267 | { 268 | inclusiveLeft: true, 269 | inclusiveRight: true, 270 | css: `color: ${colorForCaptureName(match[1])}` 271 | } 272 | ); 273 | } 274 | row++; 275 | }); 276 | } catch (error) { 277 | const startPosition = queryEditor.posFromIndex(error.index); 278 | const endPosition = { 279 | line: startPosition.line, 280 | ch: startPosition.ch + (error.length || Infinity) 281 | }; 282 | 283 | if (error.index === queryText.length) { 284 | if (startPosition.ch > 0) { 285 | startPosition.ch--; 286 | } else if (startPosition.row > 0) { 287 | startPosition.row--; 288 | startPosition.column = Infinity; 289 | } 290 | } 291 | 292 | queryEditor.markText( 293 | startPosition, 294 | endPosition, 295 | { 296 | className: 'query-error', 297 | inclusiveLeft: true, 298 | inclusiveRight: true, 299 | attributes: {title: error.message} 300 | } 301 | ); 302 | } 303 | }); 304 | 305 | runTreeQuery(); 306 | saveQueryState(); 307 | } 308 | 309 | function handleCursorMovement() { 310 | if (isRendering) return; 311 | 312 | const selection = codeEditor.getDoc().listSelections()[0]; 313 | let start = {row: selection.anchor.line, column: selection.anchor.ch}; 314 | let end = {row: selection.head.line, column: selection.head.ch}; 315 | if ( 316 | start.row > end.row || 317 | ( 318 | start.row === end.row && 319 | start.column > end.column 320 | ) 321 | ) { 322 | let swap = end; 323 | end = start; 324 | start = swap; 325 | } 326 | const node = tree.rootNode.namedDescendantForPosition(start, end); 327 | if (treeRows) { 328 | if (treeRowHighlightedIndex !== -1) { 329 | const row = treeRows[treeRowHighlightedIndex]; 330 | if (row) treeRows[treeRowHighlightedIndex] = row.replace('highlighted', 'plain'); 331 | } 332 | treeRowHighlightedIndex = treeRows.findIndex(row => row.includes(`data-id=${node.id}`)); 333 | if (treeRowHighlightedIndex !== -1) { 334 | const row = treeRows[treeRowHighlightedIndex]; 335 | if (row) treeRows[treeRowHighlightedIndex] = row.replace('plain', 'highlighted'); 336 | } 337 | cluster.update(treeRows); 338 | const lineHeight = cluster.options.item_height; 339 | const scrollTop = outputContainerScroll.scrollTop; 340 | const containerHeight = outputContainerScroll.clientHeight; 341 | const offset = treeRowHighlightedIndex * lineHeight; 342 | if (scrollTop > offset - 20) { 343 | $(outputContainerScroll).animate({scrollTop: offset - 20}, 150); 344 | } else if (scrollTop < offset + lineHeight + 40 - containerHeight) { 345 | $(outputContainerScroll).animate({scrollTop: offset - containerHeight + 40}, 150); 346 | } 347 | } 348 | } 349 | 350 | function handleTreeClick(event) { 351 | if (event.target.tagName === 'A') { 352 | event.preventDefault(); 353 | const [startRow, startColumn, endRow, endColumn] = event 354 | .target 355 | .dataset 356 | .range 357 | .split(',') 358 | .map(n => parseInt(n)); 359 | codeEditor.focus(); 360 | codeEditor.setSelection( 361 | {line: startRow, ch: startColumn}, 362 | {line: endRow, ch: endColumn} 363 | ); 364 | } 365 | } 366 | 367 | function handleLoggingChange() { 368 | if (loggingCheckbox.checked) { 369 | parser.setLogger((message, lexing) => { 370 | if (lexing) { 371 | console.log(" ", message) 372 | } else { 373 | console.log(message) 374 | } 375 | }); 376 | } else { 377 | parser.setLogger(null); 378 | } 379 | } 380 | 381 | function handleQueryEnableChange() { 382 | if (queryCheckbox.checked) { 383 | queryContainer.style.visibility = ''; 384 | queryContainer.style.position = ''; 385 | } else { 386 | queryContainer.style.visibility = 'hidden'; 387 | queryContainer.style.position = 'absolute'; 388 | } 389 | handleQueryChange(); 390 | } 391 | 392 | function treeEditForEditorChange(change) { 393 | const oldLineCount = change.removed.length; 394 | const newLineCount = change.text.length; 395 | const lastLineLength = change.text[newLineCount - 1].length; 396 | 397 | const startPosition = {row: change.from.line, column: change.from.ch}; 398 | const oldEndPosition = {row: change.to.line, column: change.to.ch}; 399 | const newEndPosition = { 400 | row: startPosition.row + newLineCount - 1, 401 | column: newLineCount === 1 402 | ? startPosition.column + lastLineLength 403 | : lastLineLength 404 | }; 405 | 406 | const startIndex = codeEditor.indexFromPos(change.from); 407 | let newEndIndex = startIndex + newLineCount - 1; 408 | let oldEndIndex = startIndex + oldLineCount - 1; 409 | for (let i = 0; i < newLineCount; i++) newEndIndex += change.text[i].length; 410 | for (let i = 0; i < oldLineCount; i++) oldEndIndex += change.removed[i].length; 411 | 412 | return { 413 | startIndex, oldEndIndex, newEndIndex, 414 | startPosition, oldEndPosition, newEndPosition 415 | }; 416 | } 417 | 418 | function colorForCaptureName(capture) { 419 | const id = query.captureNames.indexOf(capture); 420 | return COLORS_BY_INDEX[id % COLORS_BY_INDEX.length]; 421 | } 422 | 423 | function loadState() { 424 | const language = localStorage.getItem("language"); 425 | const sourceCode = localStorage.getItem("sourceCode"); 426 | const query = localStorage.getItem("query"); 427 | const queryEnabled = localStorage.getItem("queryEnabled"); 428 | if (language != null && sourceCode != null && query != null) { 429 | queryInput.value = query; 430 | codeInput.value = sourceCode; 431 | languageSelect.value = language; 432 | queryCheckbox.checked = (queryEnabled === 'true'); 433 | } 434 | } 435 | 436 | function saveState() { 437 | localStorage.setItem("language", languageSelect.value); 438 | localStorage.setItem("sourceCode", codeEditor.getValue()); 439 | saveQueryState(); 440 | } 441 | 442 | function saveQueryState() { 443 | localStorage.setItem("queryEnabled", queryCheckbox.checked); 444 | localStorage.setItem("query", queryEditor.getValue()); 445 | } 446 | 447 | function debounce(func, wait, immediate) { 448 | var timeout; 449 | return function() { 450 | var context = this, args = arguments; 451 | var later = function() { 452 | timeout = null; 453 | if (!immediate) func.apply(context, args); 454 | }; 455 | var callNow = immediate && !timeout; 456 | clearTimeout(timeout); 457 | timeout = setTimeout(later, wait); 458 | if (callNow) func.apply(context, args); 459 | }; 460 | } 461 | })(); 462 | -------------------------------------------------------------------------------- /playground/tree-sitter.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthakumaran/tree-sitter-elixir/a5a3b0cc82a44933f921565f64df49a4366cfddb/playground/tree-sitter.wasm -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ["when" "and" "or" "not in" "not" "in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword 2 | 3 | [(true) (false) (nil)] @constant.builtin 4 | 5 | (keyword 6 | [(keyword_literal) 7 | ":"] @tag) 8 | 9 | (keyword 10 | (keyword_string 11 | [(string_start) 12 | (string_content) 13 | (string_end)] @tag)) 14 | 15 | [(atom_literal) 16 | (atom_start) 17 | (atom_content) 18 | (atom_end)] @tag 19 | 20 | [(comment) 21 | (unused_identifier)] @comment 22 | 23 | (escape_sequence) @escape 24 | 25 | (call function: (function_identifier) @keyword 26 | (#match? @keyword "^(defmodule|defexception|defp|def|with|case|cond|raise|import|require|use|defmacrop|defmacro|defguardp|defguard|defdelegate|defstruct|alias|defimpl|defprotocol|defoverridable|receive|if|for|try|throw|unless|reraise|super|quote|unquote|unquote_splicing)$")) 27 | 28 | (call function: (function_identifier) @keyword 29 | [(call 30 | function: (function_identifier) @function 31 | (arguments 32 | [(identifier) @variable.parameter 33 | (_ (identifier) @variable.parameter) 34 | (_ (_ (identifier) @variable.parameter)) 35 | (_ (_ (_ (identifier) @variable.parameter))) 36 | (_ (_ (_ (_ (identifier) @variable.parameter)))) 37 | (_ (_ (_ (_ (_ (identifier) @variable.parameter)))))])) 38 | (binary_op 39 | left: 40 | (call 41 | function: (function_identifier) @function 42 | (arguments 43 | [(identifier) @variable.parameter 44 | (_ (identifier) @variable.parameter) 45 | (_ (_ (identifier) @variable.parameter)) 46 | (_ (_ (_ (identifier) @variable.parameter))) 47 | (_ (_ (_ (_ (identifier) @variable.parameter)))) 48 | (_ (_ (_ (_ (_ (identifier) @variable.parameter)))))])) 49 | operator: "when") 50 | (binary_op 51 | left: (identifier) @variable.parameter 52 | operator: _ @function 53 | right: (identifier) @variable.parameter)] 54 | (#match? @keyword "^(defp|def|defmacrop|defmacro|defguardp|defguard|defdelegate)$")) 55 | 56 | (call (function_identifier) @keyword 57 | [(call 58 | function: (function_identifier) @function) 59 | (identifier) @function 60 | (binary_op 61 | left: 62 | [(call 63 | function: (function_identifier) @function) 64 | (identifier) @function] 65 | operator: "when")] 66 | (#match? @keyword "^(defp|def|defmacrop|defmacro|defguardp|defguard|defdelegate)$")) 67 | 68 | (anonymous_function 69 | (stab_expression 70 | left: (bare_arguments 71 | [(identifier) @variable.parameter 72 | (_ (identifier) @variable.parameter) 73 | (_ (_ (identifier) @variable.parameter)) 74 | (_ (_ (_ (identifier) @variable.parameter))) 75 | (_ (_ (_ (_ (identifier) @variable.parameter)))) 76 | (_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))) 77 | 78 | (unary_op 79 | operator: "@" 80 | (call (function_identifier) @attribute 81 | (heredoc 82 | [(heredoc_start) 83 | (heredoc_content) 84 | (heredoc_end)] @doc)) 85 | (#match? @attribute "^(doc|moduledoc)$")) 86 | 87 | (module) @type 88 | 89 | (unary_op 90 | operator: "@" @attribute 91 | [(call 92 | function: (function_identifier) @attribute) 93 | (identifier) @attribute]) 94 | 95 | (unary_op 96 | operator: _ @operator) 97 | 98 | (binary_op 99 | operator: _ @operator) 100 | 101 | (heredoc 102 | [(heredoc_start) 103 | (heredoc_content) 104 | (heredoc_end)] @string) 105 | 106 | (string 107 | [(string_start) 108 | (string_content) 109 | (string_end)] @string) 110 | 111 | (sigil_start) @string.special 112 | (sigil_content) @string 113 | (sigil_end) @string.special 114 | 115 | (interpolation 116 | "#{" @punctuation.special 117 | "}" @punctuation.special) 118 | 119 | [ 120 | "," 121 | "->" 122 | "." 123 | ] @punctuation.delimiter 124 | 125 | [ 126 | "(" 127 | ")" 128 | "[" 129 | "]" 130 | "{" 131 | "}" 132 | "<<" 133 | ">>" 134 | ] @punctuation.bracket 135 | 136 | (special_identifier) @function.special 137 | 138 | (ERROR) @warning 139 | -------------------------------------------------------------------------------- /references.md: -------------------------------------------------------------------------------- 1 | * https://tree-sitter.github.io/tree-sitter/creating-parsers 2 | * http://github.com/elixir-lang/elixir/blob/master/lib/elixir/src/elixir_parser.yrl 3 | * https://erlang.org/doc/man/yecc.html 4 | * https://github.com/elixir-makeup/makeup_elixir 5 | -------------------------------------------------------------------------------- /scripts/build-fuzzer: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [[ "$(uname -s)" != Linux ]]; then 5 | echo "Fuzzing is only supported on Linux" 6 | exit 1 7 | fi 8 | 9 | if [[ -z "$LIB_FUZZER_PATH" ]]; then 10 | echo "LIB_FUZZER_PATH not set" 11 | exit 1 12 | fi 13 | 14 | export CPLUS_INCLUDE_PATH="${CPLUS_INCLUDE_PATH:+${CPLUS_INCLUDE_PATH}:}/usr/include/c++/10:/usr/include/x86_64-linux-gnu/c++/10/" 15 | 16 | CC=${CC:-clang} 17 | CXX=${CXX:-clang++} 18 | LINK=${LINK:-clang++} 19 | 20 | default_fuzz_flags="-fsanitize=fuzzer,address,undefined" 21 | 22 | CFLAGS=${CFLAGS:-"$default_fuzz_flags"} 23 | CXXFLAGS=${CXXFLAGS:-"$default_fuzz_flags"} 24 | 25 | export CFLAGS 26 | 27 | echo "Building fuzzer..." 28 | lang_dir="." 29 | 30 | # The following assumes each language is implemented as src/parser.c plus an 31 | # optional scanner in src/scanner.{c,cc} 32 | objects=() 33 | 34 | lang_scanner="src/scanner" 35 | $CXX $CXXFLAGS -g -O1 "-I${lang_dir}/src" -c "${lang_scanner}.cc" -o "${lang_scanner}.o" 36 | objects+=("${lang_scanner}.o") 37 | 38 | 39 | # Compiling with -O0 speeds up the build dramatically 40 | $CC $CFLAGS -g -O0 "-I${lang_dir}/src" "${lang_dir}/src/parser.c" -c -o "${lang_dir}/src/parser.o" 41 | objects+=("${lang_dir}/src/parser.o") 42 | 43 | cp "queries/highlights.scm" "out/elixir.scm" 44 | 45 | $CXX $CXXFLAGS -std=c++11 -I ../tree-sitter/lib/include -D TS_LANG="tree_sitter_elixir" -D TS_LANG_QUERY_FILENAME="\"${ts_lang_query_filename}\"" \ 46 | "test/fuzz/fuzzer.cc" "${objects[@]}" \ 47 | ../tree-sitter/libtree-sitter.a "$LIB_FUZZER_PATH" \ 48 | -o "out/elixir_fuzzer" 49 | 50 | python test/fuzz/gen-dict.py "${lang_dir}/src/grammar.json" > "out/elixir.dict" 51 | -------------------------------------------------------------------------------- /scripts/generate-unicode-range: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // This script generates a JSON file that is used by the CLI to handle unicode property escapes. 4 | 5 | const CATEGORY_OUTPUT_PATH = 'unicode-categories.json' 6 | const PROPERTY_OUTPUT_PATH = 'unicode-properties.json' 7 | 8 | const CATEGORY_URL = 'https://unicode.org/Public/13.0.0/ucd/UnicodeData.txt' 9 | const PROPERTY_URL = 'https://unicode.org/Public/13.0.0/ucd/PropList.txt' 10 | const DERIVED_PROPERTY_URL = 'https://unicode.org/Public/13.0.0/ucd/DerivedCoreProperties.txt' 11 | 12 | const fs = require('fs'); 13 | const path = require('path'); 14 | const {spawnSync} = require('child_process'); 15 | 16 | // Download the unicode data files, caching them inside the 'target' directory. 17 | const categoryData = cachedDownload(CATEGORY_URL); 18 | const propertyData = cachedDownload(PROPERTY_URL); 19 | const derivedPopertyData = cachedDownload(DERIVED_PROPERTY_URL); 20 | function cachedDownload(url) { 21 | let downloadPath = path.join('.', 'target', path.basename(url)) 22 | if (fs.existsSync(downloadPath)) { 23 | return fs.readFileSync(downloadPath, 'utf8'); 24 | } else { 25 | const data = spawnSync('curl', [url], {encoding: 'utf8'}).stdout; 26 | fs.writeFileSync(downloadPath, data, 'utf8'); 27 | return data; 28 | } 29 | } 30 | 31 | const categories = {}; 32 | const properties = {}; 33 | let data, row, lineStart, lineEnd; 34 | 35 | // Parse the properties 36 | data = propertyData + derivedPopertyData; 37 | row = 0; 38 | lineStart = 0; 39 | lineEnd = -1; 40 | const CODE_POINT = /[0-9A-Fa-f]/ 41 | while (lineStart < data.length) { 42 | row++; 43 | lineStart = lineEnd + 1; 44 | lineEnd = data.indexOf('\n', lineStart); 45 | if (lineEnd === -1) break; 46 | 47 | // Skip over blank and comment lines 48 | if (!CODE_POINT.test(data[lineStart])) continue; 49 | 50 | // Parse the first two semicolon fields: 51 | // * code point or code point range 52 | // * property 53 | const codePointEnd = data.indexOf(';', lineStart); 54 | const propertyStart = codePointEnd + 1; 55 | const propertyEnd = data.indexOf('#', propertyStart); 56 | 57 | if ( 58 | codePointEnd === -1 || 59 | propertyEnd === -1 60 | ) { 61 | throw new Error(`Unexpected format on line ${row}`); 62 | } 63 | 64 | // Process ranges (separated by '..) 65 | const codePoints = data.slice(lineStart, codePointEnd).trim() 66 | .split('..') 67 | .map(p => parseInt(p, 16)); 68 | if (codePoints.length === 1) { 69 | codePoints.push(codePoints[0]); 70 | } 71 | 72 | const property = data.slice(propertyStart, propertyEnd).trim(); 73 | 74 | // console.log(codePoints, property); 75 | 76 | 77 | for (let c = codePoints[0]; c <= codePoints[1]; c++) { 78 | if (!properties[property]) { 79 | properties[property] = []; 80 | } 81 | properties[property].push(c); 82 | } 83 | } 84 | 85 | // Parse the categories. 86 | // Each line represents a code point. 87 | data = categoryData; 88 | row = 0; 89 | lineStart = 0; 90 | lineEnd = -1; 91 | while (lineStart < data.length) { 92 | row++; 93 | lineStart = lineEnd + 1; 94 | lineEnd = data.indexOf('\n', lineStart); 95 | if (lineEnd === -1) break; 96 | 97 | // Parse the first three semicolon-separated fields: 98 | // * code point (hexadecimal) 99 | // * name 100 | // * category 101 | const codePointEnd = data.indexOf(';', lineStart); 102 | const nameStart = codePointEnd + 1; 103 | const nameEnd = data.indexOf(';', nameStart); 104 | const categoryStart = nameEnd + 1; 105 | const categoryEnd = data.indexOf(';', categoryStart) 106 | if ( 107 | nameStart === 0 || 108 | categoryStart == 0 || 109 | categoryEnd === 0 110 | ) { 111 | throw new Error(`Unexpected format on line ${row}`); 112 | } 113 | 114 | const codePoint = parseInt(data.slice(lineStart, codePointEnd), 16); 115 | const name = data.slice(nameStart, nameEnd); 116 | const category = data.slice(categoryStart, categoryEnd); 117 | 118 | // console.log(codePoint, category, name); 119 | 120 | // Group the code points by their category. 121 | if (!categories[category]) { 122 | categories[category] = []; 123 | } 124 | categories[category].push(codePoint); 125 | } 126 | 127 | function deriveRanges(array) { 128 | array.sort((a, b) => a - b) 129 | const ranges = [] 130 | let start = null, last = null; 131 | for (let i = 0; i < array.length; i++) { 132 | const current = array[i]; 133 | if (start === null && last === null) { 134 | start = current; 135 | last = current 136 | } else if (current !== last + 1) { 137 | ranges.push([start, last]); 138 | start = current; 139 | last = current; 140 | } else { 141 | last++; 142 | } 143 | } 144 | 145 | if (start !== null && last !== null) { 146 | ranges.push([start, last]) 147 | } 148 | return ranges 149 | } 150 | 151 | function print(property) { 152 | properties[property].forEach((x) => console.log(String.fromCharCode(x))); 153 | } 154 | 155 | function remove(a, b) { 156 | return a.filter((x) => b.indexOf(x) < 0); 157 | } 158 | 159 | function generateFunction(name, list) { 160 | const ranges = deriveRanges(list) 161 | const fnBody = ranges.map((range) => "(c >= " + range[0].toString() + " && c <= " + range[1].toString() + ')') 162 | .join(' || ') 163 | return "bool is_unicode_" + name.toLowerCase() + "(int32_t c) {\n" + ' return ' + fnBody + ";\n}\n"; 164 | } 165 | 166 | console.log(generateFunction('ID_Start', properties['ID_Start'])) 167 | console.log(generateFunction('ID_Continue', properties['ID_Continue'])) 168 | console.log(generateFunction('ID_Start_Without_Lu_Lt', remove(remove(properties['ID_Start'], categories['Lu']), categories['Lt']))) 169 | 170 | -------------------------------------------------------------------------------- /scripts/run-fuzzer: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | root=$(dirname "$0")/.. 6 | export ASAN_OPTIONS="quarantine_size_mb=10:detect_leaks=1:symbolize=1" 7 | export UBSAN="print_stacktrace=1:halt_on_error=1:symbolize=1" 8 | 9 | declare -A mode_config=( ["halt"]="-timeout=1 -rss_limit_mb=256" ["recover"]="-timeout=10 -rss_limit_mb=256" ) 10 | 11 | run_fuzzer() { 12 | if [ "$#" -lt 2 ]; then 13 | echo "usage: $0 " 14 | exit 1 15 | fi 16 | 17 | mode="$1" 18 | shift 19 | # Treat remainder of arguments as libFuzzer arguments 20 | 21 | # Fuzzing logs and testcases are always written to `pwd`, so `cd` there first 22 | results="${root}/out/fuzz-results/elixir_${mode}" 23 | mkdir -p "${results}" 24 | cd "${results}" 25 | 26 | # Create a corpus directory, so new discoveries are stored on disk. These will 27 | # then be loaded on subsequent fuzzing runs 28 | mkdir -p corpus 29 | 30 | "../../elixir_fuzzer" "-dict=../../elixir.dict" "-artifact_prefix=elixir_${mode}_" -max_len=2048 ${mode_config[$mode]} "./corpus" "$@" 31 | } 32 | 33 | reproduce() { 34 | if [ "$#" -lt 3 ]; then 35 | echo "usage: $0 (halt|recover) " 36 | exit 1 37 | fi 38 | 39 | mode="$1" 40 | shift 41 | testcase="$1" 42 | shift 43 | # Treat remainder of arguments as libFuzzer arguments 44 | 45 | "${root}/out/elixir_fuzzer_${mode}" ${mode_config[$mode]} -runs=1 "${testcase}" "$@" 46 | } 47 | 48 | script=$(basename "$0") 49 | if [ "$script" == "run-fuzzer" ]; then 50 | run_fuzzer "$@" 51 | elif [ "$script" == "reproduce" ]; then 52 | reproduce "$@" 53 | fi 54 | -------------------------------------------------------------------------------- /test/corpus/anonymous_function.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | parenthesized no arg 3 | ================================================================================ 4 | 5 | fn() -> a + b end 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (anonymous_function 11 | (stab_expression 12 | (bare_arguments 13 | (block)) 14 | (binary_op 15 | (identifier) 16 | (identifier))))) 17 | 18 | ================================================================================ 19 | parenthesized one arg 20 | ================================================================================ 21 | 22 | fn(a) -> a + b end 23 | 24 | -------------------------------------------------------------------------------- 25 | 26 | (program 27 | (anonymous_function 28 | (stab_expression 29 | (bare_arguments 30 | (block 31 | (identifier))) 32 | (binary_op 33 | (identifier) 34 | (identifier))))) 35 | 36 | ================================================================================ 37 | parenthesized multi args 38 | ================================================================================ 39 | 40 | fn(a, b) -> a + b end 41 | 42 | -------------------------------------------------------------------------------- 43 | 44 | (program 45 | (anonymous_function 46 | (stab_expression 47 | (bare_arguments 48 | (identifier) 49 | (identifier)) 50 | (binary_op 51 | (identifier) 52 | (identifier))))) 53 | 54 | ================================================================================ 55 | parenthesized multi args, multi line body 56 | ================================================================================ 57 | 58 | fn(a, b) -> 59 | a + b 60 | 5 61 | end 62 | 63 | -------------------------------------------------------------------------------- 64 | 65 | (program 66 | (anonymous_function 67 | (stab_expression 68 | (bare_arguments 69 | (identifier) 70 | (identifier)) 71 | (binary_op 72 | (identifier) 73 | (identifier)) 74 | (integer)))) 75 | 76 | ================================================================================ 77 | non parenthesized no arg 78 | ================================================================================ 79 | 80 | fn -> a + b end 81 | 82 | -------------------------------------------------------------------------------- 83 | 84 | (program 85 | (anonymous_function 86 | (stab_expression 87 | (binary_op 88 | (identifier) 89 | (identifier))))) 90 | 91 | ================================================================================ 92 | non parenthesized one arg 93 | ================================================================================ 94 | 95 | fn a -> a + b end 96 | 97 | -------------------------------------------------------------------------------- 98 | 99 | (program 100 | (anonymous_function 101 | (stab_expression 102 | (bare_arguments 103 | (identifier)) 104 | (binary_op 105 | (identifier) 106 | (identifier))))) 107 | 108 | ================================================================================ 109 | non parenthesized multi args 110 | ================================================================================ 111 | 112 | fn a, b -> a + b end 113 | 114 | -------------------------------------------------------------------------------- 115 | 116 | (program 117 | (anonymous_function 118 | (stab_expression 119 | (bare_arguments 120 | (identifier) 121 | (identifier)) 122 | (binary_op 123 | (identifier) 124 | (identifier))))) 125 | 126 | ================================================================================ 127 | non parenthesized multi args, multi line body 128 | ================================================================================ 129 | 130 | fn a, b -> 131 | a + b 132 | 5 133 | end 134 | 135 | -------------------------------------------------------------------------------- 136 | 137 | (program 138 | (anonymous_function 139 | (stab_expression 140 | (bare_arguments 141 | (identifier) 142 | (identifier)) 143 | (binary_op 144 | (identifier) 145 | (identifier)) 146 | (integer)))) 147 | 148 | ================================================================================ 149 | non parenthesized body with dot call 150 | ================================================================================ 151 | 152 | File.cd!(dir, fn -> 153 | Mix.Task.run("deps.loadpaths") 154 | end) 155 | 156 | -------------------------------------------------------------------------------- 157 | 158 | (program 159 | (dot_call 160 | (module) 161 | (function_identifier) 162 | (arguments 163 | (identifier) 164 | (anonymous_function 165 | (stab_expression 166 | (dot_call 167 | (module) 168 | (function_identifier) 169 | (arguments 170 | (string 171 | (string_start) 172 | (string_content) 173 | (string_end))))))))) 174 | 175 | ================================================================================ 176 | no arg with guard 177 | ================================================================================ 178 | 179 | fun = fn () when node() == :nonode@nohost -> true end 180 | 181 | -------------------------------------------------------------------------------- 182 | 183 | (program 184 | (binary_op 185 | (identifier) 186 | (anonymous_function 187 | (stab_expression 188 | (bare_arguments 189 | (binary_op 190 | (block) 191 | (binary_op 192 | (call 193 | (function_identifier) 194 | (arguments)) 195 | (atom 196 | (atom_literal))))) 197 | (true))))) 198 | -------------------------------------------------------------------------------- /test/corpus/binary.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | binary expression 3 | ================================================================================ 4 | 5 | <<"Elixir.Mix.Tasks.", rest::binary-size(part), rest!::binary-size(part), rest?::binary-size(part), ".beam">> 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (binary 11 | (string 12 | (string_start) 13 | (string_content) 14 | (string_end)) 15 | (binary_op 16 | (identifier) 17 | (binary_op 18 | (identifier) 19 | (call 20 | (function_identifier) 21 | (arguments 22 | (identifier))))) 23 | (binary_op 24 | (identifier) 25 | (binary_op 26 | (identifier) 27 | (call 28 | (function_identifier) 29 | (arguments 30 | (identifier))))) 31 | (binary_op 32 | (identifier) 33 | (binary_op 34 | (identifier) 35 | (call 36 | (function_identifier) 37 | (arguments 38 | (identifier))))) 39 | (string 40 | (string_start) 41 | (string_content) 42 | (string_end)))) 43 | 44 | ================================================================================ 45 | simple 46 | ================================================================================ 47 | 48 | <<"ABC">> 49 | 50 | -------------------------------------------------------------------------------- 51 | 52 | (program 53 | (binary 54 | (string 55 | (string_start) 56 | (string_content) 57 | (string_end)))) 58 | 59 | ================================================================================ 60 | empty 61 | ================================================================================ 62 | 63 | <<>> 64 | 65 | -------------------------------------------------------------------------------- 66 | 67 | (program 68 | (binary)) 69 | -------------------------------------------------------------------------------- /test/corpus/block.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | def 3 | ================================================================================ 4 | 5 | @spec env() :: atom() 6 | def env do 7 | # env is not available on bootstrapping, so set a :dev default 8 | Mix.State.get(:env, :dev) 9 | end 10 | 11 | 12 | -------------------------------------------------------------------------------- 13 | 14 | (program 15 | (unary_op 16 | (call 17 | (function_identifier) 18 | (binary_op 19 | (call 20 | (function_identifier) 21 | (arguments)) 22 | (call 23 | (function_identifier) 24 | (arguments))))) 25 | (call 26 | (function_identifier) 27 | (identifier) 28 | (do_block 29 | (comment) 30 | (dot_call 31 | (module) 32 | (function_identifier) 33 | (arguments 34 | (atom 35 | (atom_literal)) 36 | (atom 37 | (atom_literal))))))) 38 | 39 | ================================================================================ 40 | def with guard 41 | ================================================================================ 42 | 43 | def trim_trailing(string) when is_binary(string) do 44 | trim_trailing(string, byte_size(string)) 45 | end 46 | 47 | -------------------------------------------------------------------------------- 48 | 49 | (program 50 | (call 51 | (function_identifier) 52 | (binary_op 53 | (call 54 | (function_identifier) 55 | (arguments 56 | (identifier))) 57 | (call 58 | (function_identifier) 59 | (arguments 60 | (identifier)))) 61 | (do_block 62 | (call 63 | (function_identifier) 64 | (arguments 65 | (identifier) 66 | (call 67 | (function_identifier) 68 | (arguments 69 | (identifier)))))))) 70 | 71 | ================================================================================ 72 | def with multiline guard 73 | ================================================================================ 74 | 75 | defp ascii_printable_guarded?([char | rest], counter) 76 | # 7..13 is the range '\a\b\t\n\v\f\r'. 32..126 are ASCII printables. 77 | when is_integer(char) and 78 | ((char >= 7 and char <= 13) or char == ?\e or (char >= 32 and char <= 126)) do 79 | ascii_printable_guarded?(rest, decrement(counter)) 80 | end 81 | 82 | -------------------------------------------------------------------------------- 83 | 84 | (program 85 | (call 86 | (function_identifier) 87 | (binary_op 88 | (call 89 | (function_identifier) 90 | (arguments 91 | (list 92 | (binary_op 93 | (identifier) 94 | (identifier))) 95 | (identifier))) 96 | (comment) 97 | (binary_op 98 | (call 99 | (function_identifier) 100 | (arguments 101 | (identifier))) 102 | (block 103 | (binary_op 104 | (binary_op 105 | (block 106 | (binary_op 107 | (binary_op 108 | (identifier) 109 | (integer)) 110 | (binary_op 111 | (identifier) 112 | (integer)))) 113 | (binary_op 114 | (identifier) 115 | (char))) 116 | (block 117 | (binary_op 118 | (binary_op 119 | (identifier) 120 | (integer)) 121 | (binary_op 122 | (identifier) 123 | (integer)))))))) 124 | (do_block 125 | (call 126 | (function_identifier) 127 | (arguments 128 | (identifier) 129 | (call 130 | (function_identifier) 131 | (arguments 132 | (identifier)))))))) 133 | -------------------------------------------------------------------------------- /test/corpus/call.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | module parens call 3 | ================================================================================ 4 | 5 | A.B.c("a") 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (dot_call 11 | (module) 12 | (function_identifier) 13 | (arguments 14 | (string 15 | (string_start) 16 | (string_content) 17 | (string_end))))) 18 | 19 | ================================================================================ 20 | bare keyword list 21 | ================================================================================ 22 | 23 | Module.func!( 24 | args, 25 | aliases: [r: :require, p: :parallel, e: :eval, c: :config], 26 | strict: [ 27 | parallel: :boolean, 28 | require: :keep 29 | ] 30 | ) 31 | 32 | -------------------------------------------------------------------------------- 33 | 34 | (program 35 | (dot_call 36 | (module) 37 | (function_identifier) 38 | (arguments 39 | (identifier) 40 | (keyword_list 41 | (keyword 42 | (keyword_literal)) 43 | (list 44 | (keyword_list 45 | (keyword 46 | (keyword_literal)) 47 | (atom 48 | (atom_literal)) 49 | (keyword 50 | (keyword_literal)) 51 | (atom 52 | (atom_literal)) 53 | (keyword 54 | (keyword_literal)) 55 | (atom 56 | (atom_literal)) 57 | (keyword 58 | (keyword_literal)) 59 | (atom 60 | (atom_literal)))) 61 | (keyword 62 | (keyword_literal)) 63 | (list 64 | (keyword_list 65 | (keyword 66 | (keyword_literal)) 67 | (atom 68 | (atom_literal)) 69 | (keyword 70 | (keyword_literal)) 71 | (atom 72 | (atom_literal)))))))) 73 | 74 | ================================================================================ 75 | local var call 76 | ================================================================================ 77 | 78 | a.()() 79 | 80 | -------------------------------------------------------------------------------- 81 | 82 | (program 83 | (call 84 | (dot_call 85 | (identifier) 86 | (arguments)) 87 | (arguments))) 88 | 89 | ================================================================================ 90 | operator call 91 | ================================================================================ 92 | 93 | Kernel.+(1, 1) 94 | 95 | -------------------------------------------------------------------------------- 96 | 97 | (program 98 | (dot_call 99 | (module) 100 | (function_identifier) 101 | (arguments 102 | (integer) 103 | (integer)))) 104 | 105 | ================================================================================ 106 | reserved word call 107 | ================================================================================ 108 | 109 | a.and 110 | 111 | -------------------------------------------------------------------------------- 112 | 113 | (program 114 | (dot_call 115 | (identifier) 116 | (function_identifier))) 117 | 118 | ================================================================================ 119 | access call followed by dot call 120 | ================================================================================ 121 | 122 | cover[:tool].start(compile_path, cover) 123 | 124 | -------------------------------------------------------------------------------- 125 | 126 | (program 127 | (dot_call 128 | (access_call 129 | (identifier) 130 | (atom 131 | (atom_literal))) 132 | (function_identifier) 133 | (arguments 134 | (identifier) 135 | (identifier)))) 136 | 137 | ================================================================================ 138 | qualified call followed by dot call 139 | ================================================================================ 140 | 141 | quote do 142 | unquote(__MODULE__).profile( 143 | fn -> 144 | unquote(Code.string_to_quoted!(code_string)) 145 | end, 146 | unquote(Macro.escape(Enum.map(opts, &parse_opt/1))) 147 | ) 148 | end 149 | 150 | -------------------------------------------------------------------------------- 151 | 152 | (program 153 | (call 154 | (function_identifier) 155 | (do_block 156 | (dot_call 157 | (call 158 | (function_identifier) 159 | (arguments 160 | (special_identifier))) 161 | (function_identifier) 162 | (arguments 163 | (anonymous_function 164 | (stab_expression 165 | (call 166 | (function_identifier) 167 | (arguments 168 | (dot_call 169 | (module) 170 | (function_identifier) 171 | (arguments 172 | (identifier))))))) 173 | (call 174 | (function_identifier) 175 | (arguments 176 | (dot_call 177 | (module) 178 | (function_identifier) 179 | (arguments 180 | (dot_call 181 | (module) 182 | (function_identifier) 183 | (arguments 184 | (identifier) 185 | (unary_op 186 | (binary_op 187 | (identifier) 188 | (integer)))))))))))))) 189 | 190 | ================================================================================ 191 | keyword args 192 | ================================================================================ 193 | 194 | if true do 195 | raise Error, a: 1, b: 2, c: 3 196 | end 197 | 198 | -------------------------------------------------------------------------------- 199 | 200 | (program 201 | (call 202 | (function_identifier) 203 | (true) 204 | (do_block 205 | (call 206 | (function_identifier) 207 | (module) 208 | (keyword_list 209 | (keyword 210 | (keyword_literal)) 211 | (integer) 212 | (keyword 213 | (keyword_literal)) 214 | (integer) 215 | (keyword 216 | (keyword_literal)) 217 | (integer)))))) 218 | 219 | ================================================================================ 220 | captured arg dot call 221 | ================================================================================ 222 | 223 | [1] 224 | |> Enum.map(& &1.app) 225 | 226 | -------------------------------------------------------------------------------- 227 | 228 | (program 229 | (binary_op 230 | (list 231 | (integer)) 232 | (dot_call 233 | (module) 234 | (function_identifier) 235 | (arguments 236 | (unary_op 237 | (dot_call 238 | (unary_op 239 | (integer)) 240 | (function_identifier))))))) 241 | 242 | ================================================================================ 243 | captured arg qualified dot call 244 | ================================================================================ 245 | 246 | Enum.reduce(funcs, result, & &1.(&2)) 247 | 248 | -------------------------------------------------------------------------------- 249 | 250 | (program 251 | (dot_call 252 | (module) 253 | (function_identifier) 254 | (arguments 255 | (identifier) 256 | (identifier) 257 | (unary_op 258 | (dot_call 259 | (unary_op 260 | (integer)) 261 | (arguments 262 | (unary_op 263 | (integer)))))))) 264 | 265 | ================================================================================ 266 | alias 267 | ================================================================================ 268 | 269 | alias ExUnit.{FailuresManifest, Test} 270 | 271 | -------------------------------------------------------------------------------- 272 | 273 | (program 274 | (call 275 | (function_identifier) 276 | (dot_call 277 | (module) 278 | (tuple 279 | (module) 280 | (module))))) 281 | 282 | ================================================================================ 283 | import 284 | ================================================================================ 285 | 286 | import Kernel, except: [@: 1, defmodule: 2, def: 1, def: 2, defp: 2, defmacro: 1, defmacro: 2, defmacrop: 2] 287 | 288 | -------------------------------------------------------------------------------- 289 | 290 | (program 291 | (call 292 | (function_identifier) 293 | (module) 294 | (keyword_list 295 | (keyword 296 | (keyword_literal)) 297 | (list 298 | (keyword_list 299 | (keyword 300 | (keyword_literal)) 301 | (integer) 302 | (keyword 303 | (keyword_literal)) 304 | (integer) 305 | (keyword 306 | (keyword_literal)) 307 | (integer) 308 | (keyword 309 | (keyword_literal)) 310 | (integer) 311 | (keyword 312 | (keyword_literal)) 313 | (integer) 314 | (keyword 315 | (keyword_literal)) 316 | (integer) 317 | (keyword 318 | (keyword_literal)) 319 | (integer) 320 | (keyword 321 | (keyword_literal)) 322 | (integer)))))) 323 | 324 | ================================================================================ 325 | capture mfa 326 | ================================================================================ 327 | 328 | &Kernel.is_atom/1 329 | 330 | -------------------------------------------------------------------------------- 331 | 332 | (program 333 | (unary_op 334 | (binary_op 335 | (dot_call 336 | (module) 337 | (function_identifier)) 338 | (integer)))) 339 | 340 | ================================================================================ 341 | capture local 342 | ================================================================================ 343 | 344 | &local_function/1 345 | 346 | -------------------------------------------------------------------------------- 347 | 348 | (program 349 | (unary_op 350 | (binary_op 351 | (identifier) 352 | (integer)))) 353 | 354 | ================================================================================ 355 | anonymous capture 356 | ================================================================================ 357 | 358 | double = &(&1 * 2) 359 | 360 | -------------------------------------------------------------------------------- 361 | 362 | (program 363 | (binary_op 364 | (identifier) 365 | (unary_op 366 | (block 367 | (binary_op 368 | (unary_op 369 | (integer)) 370 | (integer)))))) 371 | 372 | ================================================================================ 373 | operator capture 374 | ================================================================================ 375 | 376 | &>=/2 377 | 378 | -------------------------------------------------------------------------------- 379 | 380 | (program 381 | (unary_op 382 | (binary_op 383 | (integer)))) 384 | 385 | ================================================================================ 386 | string dot call 387 | ================================================================================ 388 | 389 | :erlang."=<"(left, right) 390 | 391 | -------------------------------------------------------------------------------- 392 | 393 | (program 394 | (dot_call 395 | (atom 396 | (atom_literal)) 397 | (string 398 | (string_start) 399 | (string_content) 400 | (string_end)) 401 | (arguments 402 | (identifier) 403 | (identifier)))) 404 | 405 | ================================================================================ 406 | macro call 407 | ================================================================================ 408 | 409 | h(== / 2) 410 | 411 | -------------------------------------------------------------------------------- 412 | 413 | (program 414 | (call 415 | (function_identifier) 416 | (arguments 417 | (binary_op 418 | (integer))))) 419 | 420 | ================================================================================ 421 | pin on % 422 | ================================================================================ 423 | 424 | assert_diff( 425 | %^type{age: ^age, name: "john"} = %User{name: "john", age: 33}, 426 | [], 427 | pins 428 | ) 429 | 430 | -------------------------------------------------------------------------------- 431 | 432 | (program 433 | (call 434 | (function_identifier) 435 | (arguments 436 | (binary_op 437 | (struct 438 | (identifier) 439 | (keyword_list 440 | (keyword 441 | (keyword_literal)) 442 | (unary_op 443 | (identifier)) 444 | (keyword 445 | (keyword_literal)) 446 | (string 447 | (string_start) 448 | (string_content) 449 | (string_end)))) 450 | (struct 451 | (module) 452 | (keyword_list 453 | (keyword 454 | (keyword_literal)) 455 | (string 456 | (string_start) 457 | (string_content) 458 | (string_end)) 459 | (keyword 460 | (keyword_literal)) 461 | (integer)))) 462 | (list) 463 | (identifier)))) 464 | 465 | ================================================================================ 466 | range call 467 | ================================================================================ 468 | 469 | assert (1..3).last == 3 470 | 471 | -------------------------------------------------------------------------------- 472 | 473 | (program 474 | (call 475 | (function_identifier) 476 | (binary_op 477 | (dot_call 478 | (paren_expr 479 | (binary_op 480 | (integer) 481 | (integer))) 482 | (function_identifier)) 483 | (integer)))) 484 | 485 | ================================================================================ 486 | atom on % 487 | ================================================================================ 488 | 489 | %:"Elixir.Module.Types.PatternTest.Struct"{} 490 | 491 | -------------------------------------------------------------------------------- 492 | 493 | (program 494 | (struct 495 | (atom 496 | (atom_start) 497 | (atom_content) 498 | (atom_end)))) 499 | 500 | ================================================================================ 501 | map call 502 | ================================================================================ 503 | 504 | %{foo: :bar}.foo 505 | 506 | -------------------------------------------------------------------------------- 507 | 508 | (program 509 | (dot_call 510 | (map 511 | (keyword_list 512 | (keyword 513 | (keyword_literal)) 514 | (atom 515 | (atom_literal)))) 516 | (function_identifier))) 517 | 518 | ================================================================================ 519 | quoted struct 520 | ================================================================================ 521 | 522 | quote do 523 | %unquote(User){foo: 1} 524 | end 525 | 526 | -------------------------------------------------------------------------------- 527 | 528 | (program 529 | (call 530 | (function_identifier) 531 | (do_block 532 | (struct 533 | (call 534 | (function_identifier) 535 | (arguments 536 | (module))) 537 | (keyword_list 538 | (keyword 539 | (keyword_literal)) 540 | (integer)))))) 541 | 542 | ================================================================================ 543 | chained string qualified call 544 | ================================================================================ 545 | 546 | Macro.to_string(quote(do: Foo."bar baz"().""())) == ~s/Foo."bar baz"().""()/ 547 | 548 | -------------------------------------------------------------------------------- 549 | 550 | (program 551 | (binary_op 552 | (dot_call 553 | (module) 554 | (function_identifier) 555 | (arguments 556 | (call 557 | (function_identifier) 558 | (arguments 559 | (keyword_list 560 | (keyword 561 | (keyword_literal)) 562 | (dot_call 563 | (dot_call 564 | (module) 565 | (string 566 | (string_start) 567 | (string_content) 568 | (string_end)) 569 | (arguments)) 570 | (string 571 | (string_start) 572 | (string_end)) 573 | (arguments))))))) 574 | (sigil 575 | (sigil_start) 576 | (sigil_content) 577 | (sigil_end)))) 578 | 579 | ================================================================================ 580 | integer call 581 | ================================================================================ 582 | 583 | 1.(1, 2, 3) 584 | 585 | -------------------------------------------------------------------------------- 586 | 587 | (program 588 | (dot_call 589 | (integer) 590 | (arguments 591 | (integer) 592 | (integer) 593 | (integer)))) 594 | 595 | ================================================================================ 596 | string dot call without args 597 | ================================================================================ 598 | 599 | inspect(&__MODULE__."weirdly named/fun-"/0) 600 | 601 | -------------------------------------------------------------------------------- 602 | 603 | (program 604 | (call 605 | (function_identifier) 606 | (arguments 607 | (unary_op 608 | (binary_op 609 | (dot_call 610 | (special_identifier) 611 | (string 612 | (string_start) 613 | (string_content) 614 | (string_end))) 615 | (integer)))))) 616 | 617 | ================================================================================ 618 | capture operator 619 | ================================================================================ 620 | 621 | is_function(&and/2) 622 | 623 | -------------------------------------------------------------------------------- 624 | 625 | (program 626 | (call 627 | (function_identifier) 628 | (arguments 629 | (unary_op 630 | (binary_op 631 | (integer)))))) 632 | 633 | ================================================================================ 634 | escaped newline 635 | ================================================================================ 636 | 637 | a \ 638 | b 639 | 640 | -------------------------------------------------------------------------------- 641 | 642 | (program 643 | (call 644 | (function_identifier) 645 | (identifier))) 646 | 647 | ================================================================================ 648 | anonymous function call 649 | ================================================================================ 650 | 651 | Enum.join(" ") 652 | |> fn(command) -> "MIX_ENV=test #{command}" end.() 653 | 654 | -------------------------------------------------------------------------------- 655 | 656 | (program 657 | (binary_op 658 | (dot_call 659 | (module) 660 | (function_identifier) 661 | (arguments 662 | (string 663 | (string_start) 664 | (string_content) 665 | (string_end)))) 666 | (dot_call 667 | (anonymous_function 668 | (stab_expression 669 | (bare_arguments 670 | (block 671 | (identifier))) 672 | (string 673 | (string_start) 674 | (string_content) 675 | (interpolation 676 | (identifier)) 677 | (string_end)))) 678 | (arguments)))) 679 | 680 | ================================================================================ 681 | captured operator call 682 | ================================================================================ 683 | 684 | [&Kernel.<=/2, &Kernel.>=/2, &Kernel./2] 685 | 686 | -------------------------------------------------------------------------------- 687 | 688 | (program 689 | (list 690 | (unary_op 691 | (binary_op 692 | (dot_call 693 | (module) 694 | (function_identifier)) 695 | (integer))) 696 | (unary_op 697 | (binary_op 698 | (dot_call 699 | (module) 700 | (function_identifier)) 701 | (integer))) 702 | (unary_op 703 | (binary_op 704 | (dot_call 705 | (module) 706 | (function_identifier)) 707 | (integer))) 708 | (unary_op 709 | (binary_op 710 | (dot_call 711 | (module) 712 | (function_identifier)) 713 | (integer))))) 714 | 715 | ================================================================================ 716 | struct call 717 | ================================================================================ 718 | 719 | %Inspect.Opts{}.width 720 | 721 | -------------------------------------------------------------------------------- 722 | 723 | (program 724 | (dot_call 725 | (struct 726 | (module)) 727 | (function_identifier))) 728 | 729 | ================================================================================ 730 | bare keyword hanging comma 731 | ================================================================================ 732 | 733 | call(a: a, b: b, 734 | ) 735 | 736 | -------------------------------------------------------------------------------- 737 | 738 | (program 739 | (call 740 | (function_identifier) 741 | (arguments 742 | (keyword_list 743 | (keyword 744 | (keyword_literal)) 745 | (identifier) 746 | (keyword 747 | (keyword_literal)) 748 | (identifier))))) 749 | 750 | ================================================================================ 751 | newline before dot 752 | ================================================================================ 753 | 754 | IO 755 | .inspect("hello") 756 | 757 | -------------------------------------------------------------------------------- 758 | 759 | (program 760 | (dot_call 761 | (module) 762 | (function_identifier) 763 | (arguments 764 | (string 765 | (string_start) 766 | (string_content) 767 | (string_end))))) 768 | 769 | ================================================================================ 770 | newline after dot 771 | ================================================================================ 772 | 773 | IO. 774 | inspect("hello") 775 | 776 | -------------------------------------------------------------------------------- 777 | 778 | (program 779 | (dot_call 780 | (module) 781 | (function_identifier) 782 | (arguments 783 | (string 784 | (string_start) 785 | (string_content) 786 | (string_end))))) 787 | 788 | ================================================================================ 789 | character dot call 790 | ================================================================================ 791 | 792 | ?0.Bar.baz([1, 2, 3]) 793 | 794 | -------------------------------------------------------------------------------- 795 | 796 | (program 797 | (dot_call 798 | (dot_call 799 | (char) 800 | (module)) 801 | (function_identifier) 802 | (arguments 803 | (list 804 | (integer) 805 | (integer) 806 | (integer))))) 807 | -------------------------------------------------------------------------------- /test/corpus/case.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | case multi clause 3 | ================================================================================ 4 | 5 | case s do 6 | 1 -> "a" 7 | 2 -> "b" 8 | end 9 | 10 | -------------------------------------------------------------------------------- 11 | 12 | (program 13 | (call 14 | (function_identifier) 15 | (identifier) 16 | (do_block 17 | (stab_expression 18 | (bare_arguments 19 | (integer)) 20 | (string 21 | (string_start) 22 | (string_content) 23 | (string_end))) 24 | (stab_expression 25 | (bare_arguments 26 | (integer)) 27 | (string 28 | (string_start) 29 | (string_content) 30 | (string_end)))))) 31 | 32 | ================================================================================ 33 | case single clause 34 | ================================================================================ 35 | 36 | case s do 37 | {a} -> "b" 38 | end 39 | 40 | -------------------------------------------------------------------------------- 41 | 42 | (program 43 | (call 44 | (function_identifier) 45 | (identifier) 46 | (do_block 47 | (stab_expression 48 | (bare_arguments 49 | (tuple 50 | (identifier))) 51 | (string 52 | (string_start) 53 | (string_content) 54 | (string_end)))))) 55 | 56 | ================================================================================ 57 | case multi clause multi line body 58 | ================================================================================ 59 | 60 | case s do 61 | 1 -> "a" 62 | 2 -> 63 | a.call() 64 | 5 65 | end 66 | 67 | -------------------------------------------------------------------------------- 68 | 69 | (program 70 | (call 71 | (function_identifier) 72 | (identifier) 73 | (do_block 74 | (stab_expression 75 | (bare_arguments 76 | (integer)) 77 | (string 78 | (string_start) 79 | (string_content) 80 | (string_end))) 81 | (stab_expression 82 | (bare_arguments 83 | (integer)) 84 | (dot_call 85 | (identifier) 86 | (function_identifier) 87 | (arguments)) 88 | (integer))))) 89 | 90 | ================================================================================ 91 | case with guards 92 | ================================================================================ 93 | 94 | case 1 do 95 | x when hd(x) -> "Won't match" 96 | x -> "Got " <> y 97 | end 98 | 99 | -------------------------------------------------------------------------------- 100 | 101 | (program 102 | (call 103 | (function_identifier) 104 | (integer) 105 | (do_block 106 | (stab_expression 107 | (bare_arguments 108 | (binary_op 109 | (identifier) 110 | (call 111 | (function_identifier) 112 | (arguments 113 | (identifier))))) 114 | (string 115 | (string_start) 116 | (string_content) 117 | (string_end))) 118 | (stab_expression 119 | (bare_arguments 120 | (identifier)) 121 | (binary_op 122 | (string 123 | (string_start) 124 | (string_content) 125 | (string_end)) 126 | (identifier)))))) 127 | 128 | ================================================================================ 129 | case line break 130 | ================================================================================ 131 | 132 | case args do 133 | ["callers", callee] -> 134 | callers(callee, opts) 135 | 136 | ["graph"] -> 137 | graph(opts) 138 | end 139 | 140 | -------------------------------------------------------------------------------- 141 | 142 | (program 143 | (call 144 | (function_identifier) 145 | (identifier) 146 | (do_block 147 | (stab_expression 148 | (bare_arguments 149 | (list 150 | (string 151 | (string_start) 152 | (string_content) 153 | (string_end)) 154 | (identifier))) 155 | (call 156 | (function_identifier) 157 | (arguments 158 | (identifier) 159 | (identifier)))) 160 | (stab_expression 161 | (bare_arguments 162 | (list 163 | (string 164 | (string_start) 165 | (string_content) 166 | (string_end)))) 167 | (call 168 | (function_identifier) 169 | (arguments 170 | (identifier))))))) 171 | 172 | ================================================================================ 173 | case binary line break 174 | ================================================================================ 175 | 176 | case x do 177 | <<"sigil_", name>> when name >= ?A and name <= ?Z -> 178 | a 179 | <<"sigil_", name>> when name >= ?a and name <= ?z -> 180 | b 181 | end 182 | 183 | -------------------------------------------------------------------------------- 184 | 185 | (program 186 | (call 187 | (function_identifier) 188 | (identifier) 189 | (do_block 190 | (stab_expression 191 | (bare_arguments 192 | (binary_op 193 | (binary 194 | (string 195 | (string_start) 196 | (string_content) 197 | (string_end)) 198 | (identifier)) 199 | (binary_op 200 | (binary_op 201 | (identifier) 202 | (char)) 203 | (binary_op 204 | (identifier) 205 | (char))))) 206 | (identifier)) 207 | (stab_expression 208 | (bare_arguments 209 | (binary_op 210 | (binary 211 | (string 212 | (string_start) 213 | (string_content) 214 | (string_end)) 215 | (identifier)) 216 | (binary_op 217 | (binary_op 218 | (identifier) 219 | (char)) 220 | (binary_op 221 | (identifier) 222 | (char))))) 223 | (identifier))))) 224 | 225 | ================================================================================ 226 | case with unary ops 227 | ================================================================================ 228 | 229 | case order do 230 | 1 -> and_op([%{field => %{"$gt" => value}} | acc]) 231 | -1 -> and_op([%{field => %{"$lt" => value}} | acc]) 232 | end 233 | 234 | -------------------------------------------------------------------------------- 235 | 236 | (program 237 | (call 238 | (function_identifier) 239 | (identifier) 240 | (do_block 241 | (stab_expression 242 | (bare_arguments 243 | (integer)) 244 | (call 245 | (function_identifier) 246 | (arguments 247 | (list 248 | (binary_op 249 | (map 250 | (binary_op 251 | (identifier) 252 | (map 253 | (binary_op 254 | (string 255 | (string_start) 256 | (string_content) 257 | (string_end)) 258 | (identifier))))) 259 | (identifier)))))) 260 | (stab_expression 261 | (bare_arguments 262 | (unary_op 263 | (integer))) 264 | (call 265 | (function_identifier) 266 | (arguments 267 | (list 268 | (binary_op 269 | (map 270 | (binary_op 271 | (identifier) 272 | (map 273 | (binary_op 274 | (string 275 | (string_start) 276 | (string_content) 277 | (string_end)) 278 | (identifier))))) 279 | (identifier))))))))) 280 | 281 | ================================================================================ 282 | case multiline clause 283 | ================================================================================ 284 | 285 | case 1 do 286 | 1 -> 287 | :ok = :ok 288 | foo 289 | 290 | [] -> 291 | 2 292 | end 293 | 294 | -------------------------------------------------------------------------------- 295 | 296 | (program 297 | (call 298 | (function_identifier) 299 | (integer) 300 | (do_block 301 | (stab_expression 302 | (bare_arguments 303 | (integer)) 304 | (binary_op 305 | (atom 306 | (atom_literal)) 307 | (atom 308 | (atom_literal))) 309 | (identifier)) 310 | (stab_expression 311 | (bare_arguments 312 | (list)) 313 | (integer))))) 314 | -------------------------------------------------------------------------------- /test/corpus/comment.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | hanging comment 3 | ================================================================================ 4 | 5 | %{a: 1, 6 | b: 2, # fix it 7 | c: 3} 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | (program 12 | (map 13 | (keyword_list 14 | (keyword 15 | (keyword_literal)) 16 | (integer) 17 | (keyword 18 | (keyword_literal)) 19 | (integer) 20 | (comment) 21 | (keyword 22 | (keyword_literal)) 23 | (integer)))) 24 | -------------------------------------------------------------------------------- /test/corpus/cond.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | cond multi clause 3 | ================================================================================ 4 | 5 | cond do 6 | 1 -> "a" 7 | 2 -> "b" 8 | end 9 | 10 | -------------------------------------------------------------------------------- 11 | 12 | (program 13 | (call 14 | (function_identifier) 15 | (do_block 16 | (stab_expression 17 | (bare_arguments 18 | (integer)) 19 | (string 20 | (string_start) 21 | (string_content) 22 | (string_end))) 23 | (stab_expression 24 | (bare_arguments 25 | (integer)) 26 | (string 27 | (string_start) 28 | (string_content) 29 | (string_end)))))) 30 | 31 | ================================================================================ 32 | cond single clause 33 | ================================================================================ 34 | 35 | cond do 36 | {a} -> "b" 37 | end 38 | 39 | -------------------------------------------------------------------------------- 40 | 41 | (program 42 | (call 43 | (function_identifier) 44 | (do_block 45 | (stab_expression 46 | (bare_arguments 47 | (tuple 48 | (identifier))) 49 | (string 50 | (string_start) 51 | (string_content) 52 | (string_end)))))) 53 | 54 | ================================================================================ 55 | cond multi clause multi line body 56 | ================================================================================ 57 | 58 | cond do 59 | 1 -> "a" 60 | 2 -> 61 | a.call() 62 | 5 63 | end 64 | 65 | -------------------------------------------------------------------------------- 66 | 67 | (program 68 | (call 69 | (function_identifier) 70 | (do_block 71 | (stab_expression 72 | (bare_arguments 73 | (integer)) 74 | (string 75 | (string_start) 76 | (string_content) 77 | (string_end))) 78 | (stab_expression 79 | (bare_arguments 80 | (integer)) 81 | (dot_call 82 | (identifier) 83 | (function_identifier) 84 | (arguments)) 85 | (integer))))) 86 | 87 | ================================================================================ 88 | cond with parens 89 | ================================================================================ 90 | 91 | cond do 92 | (negated = false) && :boolean in List.wrap(b) -> 93 | 1 94 | true -> 95 | :unknown 96 | end 97 | 98 | -------------------------------------------------------------------------------- 99 | 100 | (program 101 | (call 102 | (function_identifier) 103 | (do_block 104 | (stab_expression 105 | (bare_arguments 106 | (binary_op 107 | (block 108 | (binary_op 109 | (identifier) 110 | (false))) 111 | (binary_op 112 | (atom 113 | (atom_literal)) 114 | (dot_call 115 | (module) 116 | (function_identifier) 117 | (arguments 118 | (identifier)))))) 119 | (integer)) 120 | (stab_expression 121 | (bare_arguments 122 | (true)) 123 | (atom 124 | (atom_literal)))))) 125 | -------------------------------------------------------------------------------- /test/corpus/data_types.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | keyword list 3 | ================================================================================ 4 | 5 | [ 6 | version: "0.1.0", 7 | build_per_environment: true, 8 | build_path: "_build", 9 | lockfile: "mix.lock", 10 | deps_path: "deps", 11 | deps: deps, 12 | app: :mix_install, 13 | erlc_paths: ["src"], 14 | elixirc_paths: ["lib"], 15 | consolidate_protocols: false 16 | ] 17 | 18 | -------------------------------------------------------------------------------- 19 | 20 | (program 21 | (list 22 | (keyword_list 23 | (keyword 24 | (keyword_literal)) 25 | (string 26 | (string_start) 27 | (string_content) 28 | (string_end)) 29 | (keyword 30 | (keyword_literal)) 31 | (true) 32 | (keyword 33 | (keyword_literal)) 34 | (string 35 | (string_start) 36 | (string_content) 37 | (string_end)) 38 | (keyword 39 | (keyword_literal)) 40 | (string 41 | (string_start) 42 | (string_content) 43 | (string_end)) 44 | (keyword 45 | (keyword_literal)) 46 | (string 47 | (string_start) 48 | (string_content) 49 | (string_end)) 50 | (keyword 51 | (keyword_literal)) 52 | (identifier) 53 | (keyword 54 | (keyword_literal)) 55 | (atom 56 | (atom_literal)) 57 | (keyword 58 | (keyword_literal)) 59 | (list 60 | (string 61 | (string_start) 62 | (string_content) 63 | (string_end))) 64 | (keyword 65 | (keyword_literal)) 66 | (list 67 | (string 68 | (string_start) 69 | (string_content) 70 | (string_end))) 71 | (keyword 72 | (keyword_literal)) 73 | (false)))) 74 | 75 | ================================================================================ 76 | heredoc 77 | ================================================================================ 78 | 79 | """ 80 | a 81 | """ 82 | 83 | -------------------------------------------------------------------------------- 84 | 85 | (program 86 | (heredoc 87 | (heredoc_start) 88 | (heredoc_content) 89 | (heredoc_end))) 90 | 91 | ================================================================================ 92 | string 93 | ================================================================================ 94 | 95 | "abc" 96 | 97 | -------------------------------------------------------------------------------- 98 | 99 | (program 100 | (string 101 | (string_start) 102 | (string_content) 103 | (string_end))) 104 | 105 | ================================================================================ 106 | interpolated string 107 | ================================================================================ 108 | 109 | "abc#{x+2}xyz" 110 | 111 | -------------------------------------------------------------------------------- 112 | 113 | (program 114 | (string 115 | (string_start) 116 | (string_content) 117 | (interpolation 118 | (binary_op 119 | (identifier) 120 | (integer))) 121 | (string_content) 122 | (string_end))) 123 | 124 | ================================================================================ 125 | string escape sequence 126 | ================================================================================ 127 | 128 | "a\"quote\"b" 129 | 130 | -------------------------------------------------------------------------------- 131 | 132 | (program 133 | (string 134 | (string_start) 135 | (string_content) 136 | (escape_sequence) 137 | (string_content) 138 | (escape_sequence) 139 | (string_content) 140 | (string_end))) 141 | 142 | ================================================================================ 143 | heredoc escape sequence 144 | ================================================================================ 145 | 146 | """ 147 | a\"quote\"b 148 | """ 149 | 150 | -------------------------------------------------------------------------------- 151 | 152 | (program 153 | (heredoc 154 | (heredoc_start) 155 | (heredoc_content) 156 | (escape_sequence) 157 | (heredoc_content) 158 | (escape_sequence) 159 | (heredoc_content) 160 | (heredoc_end))) 161 | 162 | ================================================================================ 163 | atoms 164 | ================================================================================ 165 | 166 | :atom 167 | 168 | -------------------------------------------------------------------------------- 169 | 170 | (program 171 | (atom 172 | (atom_literal))) 173 | 174 | ================================================================================ 175 | operator atom 176 | ================================================================================ 177 | 178 | [:@, :., :+, :-, :!, :^, :not, :~~~, :*, :/, :+, :-, :++, :--, :.., :..., :<>, :+++, :---, :^^^, :in, :not, :|>, :<<<, :>>>, :<<~, :~>>, :<~, :~>, :<~>, :<|>, :<, :>, :<=, :>=, :==, :!=, :=~, :===, :!==, :&&, :&&&, :and, :||, :|||, :=, :&, :=>, :|, :::, :when, :<-, :\\, :%, :%{}, :{}, :->, :<<>>, :..//] 179 | 180 | -------------------------------------------------------------------------------- 181 | 182 | (program 183 | (list 184 | (atom 185 | (atom_literal)) 186 | (atom 187 | (atom_literal)) 188 | (atom 189 | (atom_literal)) 190 | (atom 191 | (atom_literal)) 192 | (atom 193 | (atom_literal)) 194 | (atom 195 | (atom_literal)) 196 | (atom 197 | (atom_literal)) 198 | (atom 199 | (atom_literal)) 200 | (atom 201 | (atom_literal)) 202 | (atom 203 | (atom_literal)) 204 | (atom 205 | (atom_literal)) 206 | (atom 207 | (atom_literal)) 208 | (atom 209 | (atom_literal)) 210 | (atom 211 | (atom_literal)) 212 | (atom 213 | (atom_literal)) 214 | (atom 215 | (atom_literal)) 216 | (atom 217 | (atom_literal)) 218 | (atom 219 | (atom_literal)) 220 | (atom 221 | (atom_literal)) 222 | (atom 223 | (atom_literal)) 224 | (atom 225 | (atom_literal)) 226 | (atom 227 | (atom_literal)) 228 | (atom 229 | (atom_literal)) 230 | (atom 231 | (atom_literal)) 232 | (atom 233 | (atom_literal)) 234 | (atom 235 | (atom_literal)) 236 | (atom 237 | (atom_literal)) 238 | (atom 239 | (atom_literal)) 240 | (atom 241 | (atom_literal)) 242 | (atom 243 | (atom_literal)) 244 | (atom 245 | (atom_literal)) 246 | (atom 247 | (atom_literal)) 248 | (atom 249 | (atom_literal)) 250 | (atom 251 | (atom_literal)) 252 | (atom 253 | (atom_literal)) 254 | (atom 255 | (atom_literal)) 256 | (atom 257 | (atom_literal)) 258 | (atom 259 | (atom_literal)) 260 | (atom 261 | (atom_literal)) 262 | (atom 263 | (atom_literal)) 264 | (atom 265 | (atom_literal)) 266 | (atom 267 | (atom_literal)) 268 | (atom 269 | (atom_literal)) 270 | (atom 271 | (atom_literal)) 272 | (atom 273 | (atom_literal)) 274 | (atom 275 | (atom_literal)) 276 | (atom 277 | (atom_literal)) 278 | (atom 279 | (atom_literal)) 280 | (atom 281 | (atom_literal)) 282 | (atom 283 | (atom_literal)) 284 | (atom 285 | (atom_literal)) 286 | (atom 287 | (atom_literal)) 288 | (atom 289 | (atom_literal)) 290 | (atom 291 | (atom_literal)) 292 | (atom 293 | (atom_literal)) 294 | (atom 295 | (atom_literal)) 296 | (atom 297 | (atom_literal)) 298 | (atom 299 | (atom_literal)) 300 | (atom 301 | (atom_literal)))) 302 | 303 | ================================================================================ 304 | single quote heredoc 305 | ================================================================================ 306 | 307 | ''' 308 | a" 309 | ''' 310 | 311 | -------------------------------------------------------------------------------- 312 | 313 | (program 314 | (heredoc 315 | (heredoc_start) 316 | (heredoc_content) 317 | (heredoc_end))) 318 | 319 | ================================================================================ 320 | single quote string 321 | ================================================================================ 322 | 323 | 'abc' 324 | 325 | -------------------------------------------------------------------------------- 326 | 327 | (program 328 | (string 329 | (string_start) 330 | (string_content) 331 | (string_end))) 332 | 333 | ================================================================================ 334 | single quote interpolated string 335 | ================================================================================ 336 | 337 | 'abc#{x+2}xyz' 338 | 339 | -------------------------------------------------------------------------------- 340 | 341 | (program 342 | (string 343 | (string_start) 344 | (string_content) 345 | (interpolation 346 | (binary_op 347 | (identifier) 348 | (integer))) 349 | (string_content) 350 | (string_end))) 351 | 352 | ================================================================================ 353 | single quote string escape sequence 354 | ================================================================================ 355 | 356 | 'a\'quote\'b' 357 | 358 | -------------------------------------------------------------------------------- 359 | 360 | (program 361 | (string 362 | (string_start) 363 | (string_content) 364 | (escape_sequence) 365 | (string_content) 366 | (escape_sequence) 367 | (string_content) 368 | (string_end))) 369 | 370 | ================================================================================ 371 | char 372 | ================================================================================ 373 | 374 | ?a 375 | 376 | -------------------------------------------------------------------------------- 377 | 378 | (program 379 | (char)) 380 | 381 | ================================================================================ 382 | char escaped 383 | ================================================================================ 384 | 385 | ?\n 386 | 387 | -------------------------------------------------------------------------------- 388 | 389 | (program 390 | (char)) 391 | 392 | ================================================================================ 393 | interpolated atom 394 | ================================================================================ 395 | 396 | :"a#{i}b" 397 | 398 | -------------------------------------------------------------------------------- 399 | 400 | (program 401 | (atom 402 | (atom_start) 403 | (atom_content) 404 | (interpolation 405 | (identifier)) 406 | (atom_content) 407 | (atom_end))) 408 | 409 | ================================================================================ 410 | single quote interpolated atom 411 | ================================================================================ 412 | 413 | :'"a#{i}b' 414 | 415 | -------------------------------------------------------------------------------- 416 | 417 | (program 418 | (atom 419 | (atom_start) 420 | (atom_content) 421 | (interpolation 422 | (identifier)) 423 | (atom_content) 424 | (atom_end))) 425 | 426 | ================================================================================ 427 | keyword list with caps start 428 | ================================================================================ 429 | 430 | eval_binds(CONFIG: config, SCRIPT: script) 431 | 432 | -------------------------------------------------------------------------------- 433 | 434 | (program 435 | (call 436 | (function_identifier) 437 | (arguments 438 | (keyword_list 439 | (keyword 440 | (keyword_literal)) 441 | (identifier) 442 | (keyword 443 | (keyword_literal)) 444 | (identifier))))) 445 | 446 | ================================================================================ 447 | operator keyword 448 | ================================================================================ 449 | 450 | [~~~: 1, &&&: 2, |||: 2, ^^^: 2, <<<: 2, >>>: 2] 451 | 452 | -------------------------------------------------------------------------------- 453 | 454 | (program 455 | (list 456 | (keyword_list 457 | (keyword 458 | (keyword_literal)) 459 | (integer) 460 | (keyword 461 | (keyword_literal)) 462 | (integer) 463 | (keyword 464 | (keyword_literal)) 465 | (integer) 466 | (keyword 467 | (keyword_literal)) 468 | (integer) 469 | (keyword 470 | (keyword_literal)) 471 | (integer) 472 | (keyword 473 | (keyword_literal)) 474 | (integer)))) 475 | 476 | ================================================================================ 477 | string keyword 478 | ================================================================================ 479 | 480 | ["a": 1, "b#{2}3": 2] 481 | 482 | -------------------------------------------------------------------------------- 483 | 484 | (program 485 | (list 486 | (keyword_list 487 | (keyword 488 | (keyword_string 489 | (string_start) 490 | (string_content) 491 | (string_end))) 492 | (integer) 493 | (keyword 494 | (keyword_string 495 | (string_start) 496 | (string_content) 497 | (interpolation 498 | (integer)) 499 | (string_content) 500 | (string_end))) 501 | (integer)))) 502 | 503 | ================================================================================ 504 | single quote string keyword 505 | ================================================================================ 506 | 507 | ['a': 1, 'b#{1}': 2] 508 | 509 | -------------------------------------------------------------------------------- 510 | 511 | (program 512 | (list 513 | (keyword_list 514 | (keyword 515 | (keyword_string 516 | (string_start) 517 | (string_content) 518 | (string_end))) 519 | (integer) 520 | (keyword 521 | (keyword_string 522 | (string_start) 523 | (string_content) 524 | (interpolation 525 | (integer)) 526 | (string_end))) 527 | (integer)))) 528 | 529 | ================================================================================ 530 | hanging comma 531 | ================================================================================ 532 | 533 | [ 534 | %{a: b,}, 535 | %Struct{a: b,}, 536 | {a, b,}, 537 | [a: b,], 538 | <>, 539 | ] 540 | 541 | -------------------------------------------------------------------------------- 542 | 543 | (program 544 | (list 545 | (map 546 | (keyword_list 547 | (keyword 548 | (keyword_literal)) 549 | (identifier))) 550 | (struct 551 | (module) 552 | (keyword_list 553 | (keyword 554 | (keyword_literal)) 555 | (identifier))) 556 | (tuple 557 | (identifier) 558 | (identifier)) 559 | (list 560 | (keyword_list 561 | (keyword 562 | (keyword_literal)) 563 | (identifier))) 564 | (binary 565 | (identifier) 566 | (identifier)))) 567 | 568 | ================================================================================ 569 | atom escape 570 | ================================================================================ 571 | :"a\'##{}" 572 | 573 | -------------------------------------------------------------------------------- 574 | 575 | (program 576 | (atom 577 | (atom_start) 578 | (atom_content) 579 | (escape_sequence) 580 | (atom_content) 581 | (interpolation) 582 | (atom_end))) 583 | 584 | ================================================================================ 585 | heredoc escape 586 | ================================================================================ 587 | 588 | r""" 589 | \["\$callers" 590 | \$"#{} 591 | \$"# 592 | """ 593 | 594 | -------------------------------------------------------------------------------- 595 | 596 | (program 597 | (call 598 | (function_identifier) 599 | (heredoc 600 | (heredoc_start) 601 | (heredoc_content) 602 | (escape_sequence) 603 | (heredoc_content) 604 | (escape_sequence) 605 | (heredoc_content) 606 | (escape_sequence) 607 | (heredoc_content) 608 | (interpolation) 609 | (heredoc_content) 610 | (escape_sequence) 611 | (heredoc_content) 612 | (heredoc_end)))) 613 | 614 | ================================================================================ 615 | multiline interpolation 616 | ================================================================================ 617 | 618 | raise ArgumentError, 619 | message: 620 | "the option #{option_name} need arguments. Be sure to pass arguments to option_#{prefix}#{ 621 | option_name 622 | }(arg)" 623 | 624 | -------------------------------------------------------------------------------- 625 | 626 | (program 627 | (call 628 | (function_identifier) 629 | (module) 630 | (keyword_list 631 | (keyword 632 | (keyword_literal)) 633 | (string 634 | (string_start) 635 | (string_content) 636 | (interpolation 637 | (identifier)) 638 | (string_content) 639 | (interpolation 640 | (identifier)) 641 | (interpolation 642 | (identifier)) 643 | (string_content) 644 | (string_end))))) 645 | 646 | ================================================================================ 647 | keyword with special chars 648 | ================================================================================ 649 | [ 650 | [_12: 0], 651 | [ola: 0], 652 | [ólá: 0], 653 | [ólá?: 0], 654 | [ólá!: 0], 655 | [ól@: 0], 656 | [ól@!: 0], 657 | [ó@@!: 0], 658 | [Ola: 0], 659 | [Ólá: 0], 660 | [ÓLÁ: 0], 661 | [ÓLÁ?: 0], 662 | [ÓLÁ!: 0], 663 | [ÓL@!: 0], 664 | [Ó@@!: 0], 665 | [こんにちは世界: 0], 666 | ] 667 | 668 | -------------------------------------------------------------------------------- 669 | 670 | (program 671 | (list 672 | (list 673 | (keyword_list 674 | (keyword 675 | (keyword_literal)) 676 | (integer))) 677 | (list 678 | (keyword_list 679 | (keyword 680 | (keyword_literal)) 681 | (integer))) 682 | (list 683 | (keyword_list 684 | (keyword 685 | (keyword_literal)) 686 | (integer))) 687 | (list 688 | (keyword_list 689 | (keyword 690 | (keyword_literal)) 691 | (integer))) 692 | (list 693 | (keyword_list 694 | (keyword 695 | (keyword_literal)) 696 | (integer))) 697 | (list 698 | (keyword_list 699 | (keyword 700 | (keyword_literal)) 701 | (integer))) 702 | (list 703 | (keyword_list 704 | (keyword 705 | (keyword_literal)) 706 | (integer))) 707 | (list 708 | (keyword_list 709 | (keyword 710 | (keyword_literal)) 711 | (integer))) 712 | (list 713 | (keyword_list 714 | (keyword 715 | (keyword_literal)) 716 | (integer))) 717 | (list 718 | (keyword_list 719 | (keyword 720 | (keyword_literal)) 721 | (integer))) 722 | (list 723 | (keyword_list 724 | (keyword 725 | (keyword_literal)) 726 | (integer))) 727 | (list 728 | (keyword_list 729 | (keyword 730 | (keyword_literal)) 731 | (integer))) 732 | (list 733 | (keyword_list 734 | (keyword 735 | (keyword_literal)) 736 | (integer))) 737 | (list 738 | (keyword_list 739 | (keyword 740 | (keyword_literal)) 741 | (integer))) 742 | (list 743 | (keyword_list 744 | (keyword 745 | (keyword_literal)) 746 | (integer))) 747 | (list 748 | (keyword_list 749 | (keyword 750 | (keyword_literal)) 751 | (integer))))) 752 | 753 | ================================================================================ 754 | operator keyword 755 | ================================================================================ 756 | 757 | [ 758 | when: "when", 759 | in: "in", 760 | not: "not", 761 | and: "and", 762 | or: "or", 763 | ] 764 | 765 | -------------------------------------------------------------------------------- 766 | 767 | (program 768 | (list 769 | (keyword_list 770 | (keyword 771 | (keyword_literal)) 772 | (string 773 | (string_start) 774 | (string_content) 775 | (string_end)) 776 | (keyword 777 | (keyword_literal)) 778 | (string 779 | (string_start) 780 | (string_content) 781 | (string_end)) 782 | (keyword 783 | (keyword_literal)) 784 | (string 785 | (string_start) 786 | (string_content) 787 | (string_end)) 788 | (keyword 789 | (keyword_literal)) 790 | (string 791 | (string_start) 792 | (string_content) 793 | (string_end)) 794 | (keyword 795 | (keyword_literal)) 796 | (string 797 | (string_start) 798 | (string_content) 799 | (string_end))))) 800 | 801 | ================================================================================ 802 | string with # 803 | ================================================================================ 804 | 805 | "##{1}" 806 | 807 | -------------------------------------------------------------------------------- 808 | 809 | (program 810 | (string 811 | (string_start) 812 | (string_content) 813 | (interpolation 814 | (integer)) 815 | (string_end))) 816 | 817 | ================================================================================ 818 | linebreak followed by operator keyword 819 | ================================================================================ 820 | 821 | [ 822 | @: 1] 823 | 824 | [ 825 | .: 1] 826 | 827 | [ 828 | +: 1] 829 | 830 | [ 831 | -: 1] 832 | 833 | [ 834 | !: 1] 835 | 836 | [ 837 | ^: 1] 838 | 839 | [ 840 | ~~~: 1] 841 | 842 | [ 843 | *: 1] 844 | 845 | [ 846 | /: 1] 847 | 848 | [ 849 | +: 1] 850 | 851 | [ 852 | -: 1] 853 | 854 | [ 855 | ++: 1] 856 | 857 | [ 858 | --: 1] 859 | 860 | [ 861 | ..: 1] 862 | 863 | [ 864 | ...: 1] 865 | 866 | [ 867 | ..//: 1] 868 | 869 | [ 870 | <>: 1] 871 | 872 | [ 873 | +++: 1] 874 | 875 | [ 876 | ---: 1] 877 | 878 | [ 879 | ^^^: 1] 880 | 881 | [ 882 | |>: 1] 883 | 884 | [ 885 | <<<: 1] 886 | 887 | [ 888 | >>>: 1] 889 | 890 | [ 891 | <<~: 1] 892 | 893 | [ 894 | ~>>: 1] 895 | 896 | [ 897 | <~: 1] 898 | 899 | [ 900 | ~>: 1] 901 | 902 | [ 903 | <~>: 1] 904 | 905 | [ 906 | <|>: 1] 907 | 908 | [ 909 | <: 1] 910 | 911 | [ 912 | >: 1] 913 | 914 | [ 915 | <=: 1] 916 | 917 | [ 918 | >=: 1] 919 | 920 | [ 921 | ==: 1] 922 | 923 | [ 924 | !=: 1] 925 | 926 | [ 927 | =~: 1] 928 | 929 | [ 930 | ===: 1] 931 | 932 | [ 933 | !==: 1] 934 | 935 | [ 936 | &&: 1] 937 | 938 | [ 939 | &&&: 1] 940 | 941 | [ 942 | ||: 1] 943 | 944 | [ 945 | |||: 1] 946 | 947 | [ 948 | =: 1] 949 | 950 | [ 951 | &: 1] 952 | 953 | [ 954 | |: 1] 955 | 956 | [ 957 | <-: 1] 958 | 959 | [ 960 | when: 1] 961 | 962 | [ 963 | or: 1] 964 | 965 | [ 966 | and: 1] 967 | 968 | [ 969 | not: 1] 970 | 971 | [ 972 | in: 1] 973 | 974 | -------------------------------------------------------------------------------- 975 | 976 | (program 977 | (list 978 | (keyword_list 979 | (keyword 980 | (keyword_literal)) 981 | (integer))) 982 | (list 983 | (keyword_list 984 | (keyword 985 | (keyword_literal)) 986 | (integer))) 987 | (list 988 | (keyword_list 989 | (keyword 990 | (keyword_literal)) 991 | (integer))) 992 | (list 993 | (keyword_list 994 | (keyword 995 | (keyword_literal)) 996 | (integer))) 997 | (list 998 | (keyword_list 999 | (keyword 1000 | (keyword_literal)) 1001 | (integer))) 1002 | (list 1003 | (keyword_list 1004 | (keyword 1005 | (keyword_literal)) 1006 | (integer))) 1007 | (list 1008 | (keyword_list 1009 | (keyword 1010 | (keyword_literal)) 1011 | (integer))) 1012 | (list 1013 | (keyword_list 1014 | (keyword 1015 | (keyword_literal)) 1016 | (integer))) 1017 | (list 1018 | (keyword_list 1019 | (keyword 1020 | (keyword_literal)) 1021 | (integer))) 1022 | (list 1023 | (keyword_list 1024 | (keyword 1025 | (keyword_literal)) 1026 | (integer))) 1027 | (list 1028 | (keyword_list 1029 | (keyword 1030 | (keyword_literal)) 1031 | (integer))) 1032 | (list 1033 | (keyword_list 1034 | (keyword 1035 | (keyword_literal)) 1036 | (integer))) 1037 | (list 1038 | (keyword_list 1039 | (keyword 1040 | (keyword_literal)) 1041 | (integer))) 1042 | (list 1043 | (keyword_list 1044 | (keyword 1045 | (keyword_literal)) 1046 | (integer))) 1047 | (list 1048 | (keyword_list 1049 | (keyword 1050 | (keyword_literal)) 1051 | (integer))) 1052 | (list 1053 | (keyword_list 1054 | (keyword 1055 | (keyword_literal)) 1056 | (integer))) 1057 | (list 1058 | (keyword_list 1059 | (keyword 1060 | (keyword_literal)) 1061 | (integer))) 1062 | (list 1063 | (keyword_list 1064 | (keyword 1065 | (keyword_literal)) 1066 | (integer))) 1067 | (list 1068 | (keyword_list 1069 | (keyword 1070 | (keyword_literal)) 1071 | (integer))) 1072 | (list 1073 | (keyword_list 1074 | (keyword 1075 | (keyword_literal)) 1076 | (integer))) 1077 | (list 1078 | (keyword_list 1079 | (keyword 1080 | (keyword_literal)) 1081 | (integer))) 1082 | (list 1083 | (keyword_list 1084 | (keyword 1085 | (keyword_literal)) 1086 | (integer))) 1087 | (list 1088 | (keyword_list 1089 | (keyword 1090 | (keyword_literal)) 1091 | (integer))) 1092 | (list 1093 | (keyword_list 1094 | (keyword 1095 | (keyword_literal)) 1096 | (integer))) 1097 | (list 1098 | (keyword_list 1099 | (keyword 1100 | (keyword_literal)) 1101 | (integer))) 1102 | (list 1103 | (keyword_list 1104 | (keyword 1105 | (keyword_literal)) 1106 | (integer))) 1107 | (list 1108 | (keyword_list 1109 | (keyword 1110 | (keyword_literal)) 1111 | (integer))) 1112 | (list 1113 | (keyword_list 1114 | (keyword 1115 | (keyword_literal)) 1116 | (integer))) 1117 | (list 1118 | (keyword_list 1119 | (keyword 1120 | (keyword_literal)) 1121 | (integer))) 1122 | (list 1123 | (keyword_list 1124 | (keyword 1125 | (keyword_literal)) 1126 | (integer))) 1127 | (list 1128 | (keyword_list 1129 | (keyword 1130 | (keyword_literal)) 1131 | (integer))) 1132 | (list 1133 | (keyword_list 1134 | (keyword 1135 | (keyword_literal)) 1136 | (integer))) 1137 | (list 1138 | (keyword_list 1139 | (keyword 1140 | (keyword_literal)) 1141 | (integer))) 1142 | (list 1143 | (keyword_list 1144 | (keyword 1145 | (keyword_literal)) 1146 | (integer))) 1147 | (list 1148 | (keyword_list 1149 | (keyword 1150 | (keyword_literal)) 1151 | (integer))) 1152 | (list 1153 | (keyword_list 1154 | (keyword 1155 | (keyword_literal)) 1156 | (integer))) 1157 | (list 1158 | (keyword_list 1159 | (keyword 1160 | (keyword_literal)) 1161 | (integer))) 1162 | (list 1163 | (keyword_list 1164 | (keyword 1165 | (keyword_literal)) 1166 | (integer))) 1167 | (list 1168 | (keyword_list 1169 | (keyword 1170 | (keyword_literal)) 1171 | (integer))) 1172 | (list 1173 | (keyword_list 1174 | (keyword 1175 | (keyword_literal)) 1176 | (integer))) 1177 | (list 1178 | (keyword_list 1179 | (keyword 1180 | (keyword_literal)) 1181 | (integer))) 1182 | (list 1183 | (keyword_list 1184 | (keyword 1185 | (keyword_literal)) 1186 | (integer))) 1187 | (list 1188 | (keyword_list 1189 | (keyword 1190 | (keyword_literal)) 1191 | (integer))) 1192 | (list 1193 | (keyword_list 1194 | (keyword 1195 | (keyword_literal)) 1196 | (integer))) 1197 | (list 1198 | (keyword_list 1199 | (keyword 1200 | (keyword_literal)) 1201 | (integer))) 1202 | (list 1203 | (keyword_list 1204 | (keyword 1205 | (keyword_literal)) 1206 | (integer))) 1207 | (list 1208 | (keyword_list 1209 | (keyword 1210 | (keyword_literal)) 1211 | (integer))) 1212 | (list 1213 | (keyword_list 1214 | (keyword 1215 | (keyword_literal)) 1216 | (integer))) 1217 | (list 1218 | (keyword_list 1219 | (keyword 1220 | (keyword_literal)) 1221 | (integer))) 1222 | (list 1223 | (keyword_list 1224 | (keyword 1225 | (keyword_literal)) 1226 | (integer))) 1227 | (list 1228 | (keyword_list 1229 | (keyword 1230 | (keyword_literal)) 1231 | (integer)))) 1232 | 1233 | ================================================================================ 1234 | escaped binary 1235 | ================================================================================ 1236 | 1237 | "Interspersed \x{ff} codes \7 \8 \65 \016 and \t\s\\s\z\+ \\ escapes" 1238 | 1239 | -------------------------------------------------------------------------------- 1240 | 1241 | (program 1242 | (string 1243 | (string_start) 1244 | (string_content) 1245 | (escape_sequence) 1246 | (string_content) 1247 | (escape_sequence) 1248 | (string_content) 1249 | (escape_sequence) 1250 | (string_content) 1251 | (escape_sequence) 1252 | (string_content) 1253 | (escape_sequence) 1254 | (string_content) 1255 | (escape_sequence) 1256 | (escape_sequence) 1257 | (escape_sequence) 1258 | (string_content) 1259 | (escape_sequence) 1260 | (escape_sequence) 1261 | (string_content) 1262 | (escape_sequence) 1263 | (string_content) 1264 | (string_end))) 1265 | -------------------------------------------------------------------------------- /test/corpus/error.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | call can't start with , 3 | ================================================================================ 4 | 5 | call(, k: t) 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (call 11 | (function_identifier) 12 | (arguments 13 | (ERROR) 14 | (keyword_list 15 | (keyword 16 | (keyword_literal)) 17 | (identifier))))) 18 | 19 | ================================================================================ 20 | when can't end with , 21 | ================================================================================ 22 | 23 | a when k: t, 24 | 25 | 26 | -------------------------------------------------------------------------------- 27 | 28 | (program 29 | (binary_op 30 | (identifier) 31 | (keyword_list 32 | (keyword 33 | (keyword_literal)) 34 | (identifier))) 35 | (ERROR)) 36 | -------------------------------------------------------------------------------- /test/corpus/expression.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | unary 3 | ================================================================================ 4 | 5 | y(+1) 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (call 11 | (function_identifier) 12 | (arguments 13 | (unary_op 14 | (integer))))) 15 | 16 | ================================================================================ 17 | binary 18 | ================================================================================ 19 | 20 | y+1 21 | 22 | -------------------------------------------------------------------------------- 23 | 24 | (program 25 | (binary_op 26 | (identifier) 27 | (integer))) 28 | 29 | ================================================================================ 30 | and expression 31 | ================================================================================ 32 | 33 | y < 1 and x > 1 34 | 35 | -------------------------------------------------------------------------------- 36 | 37 | (program 38 | (binary_op 39 | (binary_op 40 | (identifier) 41 | (integer)) 42 | (binary_op 43 | (identifier) 44 | (integer)))) 45 | 46 | ================================================================================ 47 | bitwise shift 48 | ================================================================================ 49 | 50 | -1 <<< -2 51 | 52 | -------------------------------------------------------------------------------- 53 | 54 | (program 55 | (binary_op 56 | (unary_op 57 | (integer)) 58 | (unary_op 59 | (integer)))) 60 | 61 | ================================================================================ 62 | bitwise or 63 | ================================================================================ 64 | 65 | a ||| b 66 | 67 | -------------------------------------------------------------------------------- 68 | 69 | (program 70 | (binary_op 71 | (identifier) 72 | (identifier))) 73 | 74 | ================================================================================ 75 | bitwise expression 76 | ================================================================================ 77 | 78 | ~~~2 &&& 3 79 | 80 | -------------------------------------------------------------------------------- 81 | 82 | (program 83 | (binary_op 84 | (unary_op 85 | (integer)) 86 | (integer))) 87 | 88 | ================================================================================ 89 | and on next line 90 | ================================================================================ 91 | a 92 | && b 93 | 94 | -------------------------------------------------------------------------------- 95 | 96 | (program 97 | (binary_op 98 | (identifier) 99 | (identifier))) 100 | 101 | ================================================================================ 102 | triple dot 103 | ================================================================================ 104 | 105 | ... = 144 106 | ... == !x && y || z 107 | 108 | -------------------------------------------------------------------------------- 109 | 110 | (program 111 | (binary_op 112 | (identifier) 113 | (integer)) 114 | (binary_op 115 | (binary_op 116 | (binary_op 117 | (identifier) 118 | (unary_op 119 | (identifier))) 120 | (identifier)) 121 | (identifier))) 122 | 123 | ================================================================================ 124 | Unary operator with newline 125 | ================================================================================ 126 | - 127 | 10 128 | 129 | -------------------------------------------------------------------------------- 130 | 131 | (program 132 | (unary_op 133 | (integer))) 134 | 135 | ================================================================================ 136 | stepped range 137 | ================================================================================ 138 | 139 | 1..2 140 | // 4 141 | 142 | -------------------------------------------------------------------------------- 143 | 144 | (program 145 | (binary_op 146 | (binary_op 147 | (integer) 148 | (integer)) 149 | (integer))) 150 | -------------------------------------------------------------------------------- /test/corpus/function.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | defmacro 3 | ================================================================================ 4 | 5 | defmacro unquote(level)(message_or_fun, metadata \\ []) do 6 | maybe_log(unquote(level), message_or_fun, metadata, __CALLER__) 7 | end 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | (program 12 | (call 13 | (function_identifier) 14 | (call 15 | (call 16 | (function_identifier) 17 | (arguments 18 | (identifier))) 19 | (arguments 20 | (identifier) 21 | (binary_op 22 | (identifier) 23 | (list))) 24 | (do_block 25 | (call 26 | (function_identifier) 27 | (arguments 28 | (call 29 | (function_identifier) 30 | (arguments 31 | (identifier))) 32 | (identifier) 33 | (identifier) 34 | (special_identifier))))))) 35 | 36 | ================================================================================ 37 | multine def 38 | ================================================================================ 39 | 40 | def a 41 | do 42 | 1 43 | end 44 | 45 | -------------------------------------------------------------------------------- 46 | 47 | (program 48 | (call 49 | (function_identifier) 50 | (identifier) 51 | (do_block 52 | (integer)))) 53 | -------------------------------------------------------------------------------- /test/corpus/if.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | if not 3 | ================================================================================ 4 | 5 | if not a do 6 | a 7 | else 8 | x 9 | end 10 | 11 | -------------------------------------------------------------------------------- 12 | 13 | (program 14 | (call 15 | (function_identifier) 16 | (unary_op 17 | (identifier)) 18 | (do_block 19 | (identifier) 20 | (else_block 21 | (identifier))))) 22 | -------------------------------------------------------------------------------- /test/corpus/line_break.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | not in 3 | ================================================================================ 4 | 5 | x 6 | not in ["a"] 7 | 8 | -------------------------------------------------------------------------------- 9 | 10 | (program 11 | (binary_op 12 | (identifier) 13 | (list 14 | (string 15 | (string_start) 16 | (string_content) 17 | (string_end))))) 18 | 19 | ================================================================================ 20 | in 21 | ================================================================================ 22 | 23 | x 24 | in ["a"] 25 | 26 | -------------------------------------------------------------------------------- 27 | 28 | (program 29 | (binary_op 30 | (identifier) 31 | (list 32 | (string 33 | (string_start) 34 | (string_content) 35 | (string_end))))) 36 | 37 | ================================================================================ 38 | not 39 | ================================================================================ 40 | 41 | x 42 | not y 43 | 44 | -------------------------------------------------------------------------------- 45 | 46 | (program 47 | (identifier) 48 | (unary_op 49 | (identifier))) 50 | -------------------------------------------------------------------------------- /test/corpus/map.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | empty 3 | ================================================================================ 4 | 5 | %{} 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (map)) 11 | 12 | ================================================================================ 13 | keyword 14 | ================================================================================ 15 | 16 | %{a: 1, b: 2} 17 | 18 | -------------------------------------------------------------------------------- 19 | 20 | (program 21 | (map 22 | (keyword_list 23 | (keyword 24 | (keyword_literal)) 25 | (integer) 26 | (keyword 27 | (keyword_literal)) 28 | (integer)))) 29 | 30 | ================================================================================ 31 | non atom key 32 | ================================================================================ 33 | 34 | %{"s" => 1, b => 1} 35 | 36 | -------------------------------------------------------------------------------- 37 | 38 | (program 39 | (map 40 | (binary_op 41 | (string 42 | (string_start) 43 | (string_content) 44 | (string_end)) 45 | (integer)) 46 | (binary_op 47 | (identifier) 48 | (integer)))) 49 | 50 | ================================================================================ 51 | update syntax 52 | ================================================================================ 53 | 54 | %{john | name: "Jane"} 55 | 56 | -------------------------------------------------------------------------------- 57 | 58 | (program 59 | (map 60 | (binary_op 61 | (identifier) 62 | (keyword_list 63 | (keyword 64 | (keyword_literal)) 65 | (string 66 | (string_start) 67 | (string_content) 68 | (string_end)))))) 69 | 70 | ================================================================================ 71 | mixed 72 | ================================================================================ 73 | 74 | %{"hello" => "world", a: 1, b: 2} 75 | 76 | -------------------------------------------------------------------------------- 77 | 78 | (program 79 | (map 80 | (binary_op 81 | (string 82 | (string_start) 83 | (string_content) 84 | (string_end)) 85 | (string 86 | (string_start) 87 | (string_content) 88 | (string_end))) 89 | (keyword_list 90 | (keyword 91 | (keyword_literal)) 92 | (integer) 93 | (keyword 94 | (keyword_literal)) 95 | (integer)))) 96 | 97 | ================================================================================ 98 | update syntax with hanging comma 99 | ================================================================================ 100 | 101 | %{john | name: "Jane",} 102 | 103 | -------------------------------------------------------------------------------- 104 | 105 | (program 106 | (map 107 | (binary_op 108 | (identifier) 109 | (keyword_list 110 | (keyword 111 | (keyword_literal)) 112 | (string 113 | (string_start) 114 | (string_content) 115 | (string_end)))))) 116 | -------------------------------------------------------------------------------- /test/corpus/module.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | empty module 3 | ================================================================================ 4 | 5 | defmodule B do 6 | end 7 | 8 | -------------------------------------------------------------------------------- 9 | 10 | (program 11 | (call 12 | (function_identifier) 13 | (module) 14 | (do_block))) 15 | -------------------------------------------------------------------------------- /test/corpus/operator.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | not 3 | ================================================================================ 4 | 5 | not Map.has_key?(set, tuple) 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (unary_op 11 | (dot_call 12 | (module) 13 | (function_identifier) 14 | (arguments 15 | (identifier) 16 | (identifier))))) 17 | 18 | ================================================================================ 19 | not in 20 | ================================================================================ 21 | 22 | x not in [y, z] 23 | 24 | -------------------------------------------------------------------------------- 25 | 26 | (program 27 | (binary_op 28 | (identifier) 29 | (list 30 | (identifier) 31 | (identifier)))) 32 | 33 | ================================================================================ 34 | ^^^ 35 | ================================================================================ 36 | 37 | abcd 38 | ^^^ xyz 39 | 40 | -------------------------------------------------------------------------------- 41 | 42 | (program 43 | (binary_op 44 | (identifier) 45 | (identifier))) 46 | 47 | ================================================================================ 48 | ~> 49 | ================================================================================ 50 | a 51 | ~> b 52 | -------------------------------------------------------------------------------- 53 | 54 | (program 55 | (binary_op 56 | (identifier) 57 | (identifier))) 58 | 59 | ================================================================================ 60 | ~>> 61 | ================================================================================ 62 | a 63 | ~>> b 64 | -------------------------------------------------------------------------------- 65 | 66 | (program 67 | (binary_op 68 | (identifier) 69 | (identifier))) 70 | 71 | ================================================================================ 72 | stepped ranges 73 | ================================================================================ 74 | 75 | 1 .. 2 // 3 76 | (1..2//3).step 77 | 1..2//3 78 | 0..1//-1 79 | -------------------------------------------------------------------------------- 80 | 81 | (program 82 | (binary_op 83 | (binary_op 84 | (integer) 85 | (integer)) 86 | (integer)) 87 | (dot_call 88 | (paren_expr 89 | (binary_op 90 | (binary_op 91 | (integer) 92 | (integer)) 93 | (integer))) 94 | (function_identifier)) 95 | (binary_op 96 | (binary_op 97 | (integer) 98 | (integer)) 99 | (integer)) 100 | (binary_op 101 | (binary_op 102 | (integer) 103 | (integer)) 104 | (unary_op 105 | (integer)))) 106 | 107 | ================================================================================ 108 | stepped blocks 109 | ================================================================================ 110 | 111 | foo do end..bar do end//baz do end 112 | 1..(2//3) 113 | (1..2)//3 114 | (1..2)//(3) 115 | 116 | -------------------------------------------------------------------------------- 117 | 118 | (program 119 | (binary_op 120 | (binary_op 121 | (call 122 | (function_identifier) 123 | (do_block)) 124 | (call 125 | (function_identifier) 126 | (do_block))) 127 | (call 128 | (function_identifier) 129 | (do_block))) 130 | (binary_op 131 | (integer) 132 | (block 133 | (binary_op 134 | (integer) 135 | (integer)))) 136 | (binary_op 137 | (block 138 | (binary_op 139 | (integer) 140 | (integer))) 141 | (integer)) 142 | (binary_op 143 | (block 144 | (binary_op 145 | (integer) 146 | (integer))) 147 | (block 148 | (integer)))) 149 | 150 | ================================================================================ 151 | multiline stepped blocks 152 | ================================================================================ 153 | 154 | 1..2 155 | // 4 156 | 157 | -------------------------------------------------------------------------------- 158 | 159 | (program 160 | (binary_op 161 | (binary_op 162 | (integer) 163 | (integer)) 164 | (integer))) 165 | -------------------------------------------------------------------------------- /test/corpus/sigil.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | interpolation 3 | ================================================================================ 4 | 5 | ~s/f#{"o"}o/ 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (sigil 11 | (sigil_start) 12 | (sigil_content) 13 | (interpolation 14 | (string 15 | (string_start) 16 | (string_content) 17 | (string_end))) 18 | (sigil_content) 19 | (sigil_end))) 20 | 21 | ================================================================================ 22 | regex 23 | ================================================================================ 24 | 25 | ~r/foo|bar/ 26 | 27 | -------------------------------------------------------------------------------- 28 | 29 | (program 30 | (sigil 31 | (sigil_start) 32 | (sigil_content) 33 | (sigil_end))) 34 | 35 | ================================================================================ 36 | regex with modifier 37 | ================================================================================ 38 | 39 | ~r/hello/i 40 | 41 | -------------------------------------------------------------------------------- 42 | 43 | (program 44 | (sigil 45 | (sigil_start) 46 | (sigil_content) 47 | (sigil_end))) 48 | 49 | ================================================================================ 50 | quotes 51 | ================================================================================ 52 | 53 | ~s(this is a string with "double" quotes, not 'single' ones) 54 | 55 | -------------------------------------------------------------------------------- 56 | 57 | (program 58 | (sigil 59 | (sigil_start) 60 | (sigil_content) 61 | (sigil_end))) 62 | 63 | ================================================================================ 64 | string with interpolation 65 | ================================================================================ 66 | 67 | ~s(String with escape codes \x26 #{"inter" <> "polation"}) 68 | 69 | -------------------------------------------------------------------------------- 70 | 71 | (program 72 | (sigil 73 | (sigil_start) 74 | (sigil_content) 75 | (escape_sequence) 76 | (sigil_content) 77 | (interpolation 78 | (binary_op 79 | (string 80 | (string_start) 81 | (string_content) 82 | (string_end)) 83 | (string 84 | (string_start) 85 | (string_content) 86 | (string_end)))) 87 | (sigil_end))) 88 | 89 | ================================================================================ 90 | string without interpolation 91 | ================================================================================ 92 | 93 | ~S(String without escape codes \x26 without #{interpolation}) 94 | 95 | -------------------------------------------------------------------------------- 96 | 97 | (program 98 | (sigil 99 | (sigil_start) 100 | (sigil_content) 101 | (sigil_end))) 102 | 103 | ================================================================================ 104 | triple quotes 105 | ================================================================================ 106 | 107 | ~S""" 108 | Converts double-quotes to single-quotes. 109 | 110 | ## Examples 111 | 112 | iex> convert("\"foo\"") 113 | "'foo'" 114 | 115 | """ 116 | 117 | -------------------------------------------------------------------------------- 118 | 119 | (program 120 | (sigil 121 | (sigil_start) 122 | (sigil_content) 123 | (sigil_end))) 124 | 125 | ================================================================================ 126 | date time 127 | ================================================================================ 128 | 129 | ~U[2019-10-31 19:59:03Z] 130 | 131 | -------------------------------------------------------------------------------- 132 | 133 | (program 134 | (sigil 135 | (sigil_start) 136 | (sigil_content) 137 | (sigil_end))) 138 | 139 | ================================================================================ 140 | single quote triple quotes 141 | ================================================================================ 142 | 143 | ~S''' 144 | Converts double-quotes to single-quotes. 145 | 146 | ## Examples 147 | 148 | iex> convert("\"foo\"") 149 | "'foo'" 150 | 151 | ''' 152 | 153 | -------------------------------------------------------------------------------- 154 | 155 | (program 156 | (sigil 157 | (sigil_start) 158 | (sigil_content) 159 | (sigil_end))) 160 | 161 | ================================================================================ 162 | regex with triple quotes 163 | ================================================================================ 164 | 165 | ~r""" 166 | \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating 167 | \*\* \(RuntimeError\) oops 168 | .* 169 | Last message \(from #PID<\d+\.\d+\.\d+>\): :error 170 | State: :ok 171 | Client #PID<\d+\.\d+\.\d+> is alive 172 | .* 173 | """s 174 | 175 | -------------------------------------------------------------------------------- 176 | 177 | (program 178 | (sigil 179 | (sigil_start) 180 | (sigil_content) 181 | (escape_sequence) 182 | (sigil_content) 183 | (escape_sequence) 184 | (sigil_content) 185 | (escape_sequence) 186 | (sigil_content) 187 | (escape_sequence) 188 | (escape_sequence) 189 | (sigil_content) 190 | (escape_sequence) 191 | (escape_sequence) 192 | (sigil_content) 193 | (escape_sequence) 194 | (escape_sequence) 195 | (sigil_content) 196 | (escape_sequence) 197 | (sigil_content) 198 | (escape_sequence) 199 | (sigil_content) 200 | (escape_sequence) 201 | (sigil_content) 202 | (escape_sequence) 203 | (sigil_content) 204 | (escape_sequence) 205 | (escape_sequence) 206 | (sigil_content) 207 | (escape_sequence) 208 | (escape_sequence) 209 | (sigil_content) 210 | (escape_sequence) 211 | (sigil_content) 212 | (escape_sequence) 213 | (sigil_content) 214 | (escape_sequence) 215 | (escape_sequence) 216 | (sigil_content) 217 | (escape_sequence) 218 | (escape_sequence) 219 | (sigil_content) 220 | (sigil_end))) 221 | 222 | ================================================================================ 223 | sigil escape 224 | ================================================================================ 225 | 226 | ~r""" 227 | \["\$callers" 228 | \$"#{} 229 | \$"# 230 | """ 231 | 232 | -------------------------------------------------------------------------------- 233 | 234 | (program 235 | (sigil 236 | (sigil_start) 237 | (sigil_content) 238 | (escape_sequence) 239 | (sigil_content) 240 | (escape_sequence) 241 | (sigil_content) 242 | (escape_sequence) 243 | (sigil_content) 244 | (interpolation) 245 | (sigil_content) 246 | (escape_sequence) 247 | (sigil_content) 248 | (sigil_end))) 249 | 250 | ================================================================================ 251 | sigil always escape terminator 252 | ================================================================================ 253 | 254 | [~S(foo\)), ~S[foo\]], ~S[foo"\]]] 255 | 256 | -------------------------------------------------------------------------------- 257 | 258 | (program 259 | (list 260 | (sigil 261 | (sigil_start) 262 | (sigil_content) 263 | (escape_sequence) 264 | (sigil_end)) 265 | (sigil 266 | (sigil_start) 267 | (sigil_content) 268 | (escape_sequence) 269 | (sigil_end)) 270 | (sigil 271 | (sigil_start) 272 | (sigil_content) 273 | (escape_sequence) 274 | (sigil_end)))) 275 | -------------------------------------------------------------------------------- /test/corpus/spec.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | spec with splat 3 | ================================================================================ 4 | 5 | @spec write_dot_graph!( 6 | Path.t(), 7 | String.t(), 8 | [node], 9 | (node -> {formatted_node, [node]}), 10 | keyword 11 | ) :: :ok 12 | when node: term() 13 | 14 | -------------------------------------------------------------------------------- 15 | 16 | (program 17 | (unary_op 18 | (call 19 | (function_identifier) 20 | (binary_op 21 | (binary_op 22 | (call 23 | (function_identifier) 24 | (arguments 25 | (dot_call 26 | (module) 27 | (function_identifier) 28 | (arguments)) 29 | (dot_call 30 | (module) 31 | (function_identifier) 32 | (arguments)) 33 | (list 34 | (identifier)) 35 | (block 36 | (stab_expression 37 | (bare_arguments 38 | (identifier)) 39 | (tuple 40 | (identifier) 41 | (list 42 | (identifier))))) 43 | (identifier))) 44 | (atom 45 | (atom_literal))) 46 | (keyword_list 47 | (keyword 48 | (keyword_literal)) 49 | (call 50 | (function_identifier) 51 | (arguments))))))) 52 | 53 | ================================================================================ 54 | non empty list 55 | ================================================================================ 56 | 57 | @spec [type, ...] 58 | 59 | -------------------------------------------------------------------------------- 60 | 61 | (program 62 | (unary_op 63 | (call 64 | (function_identifier) 65 | (list 66 | (identifier) 67 | (identifier))))) 68 | -------------------------------------------------------------------------------- /test/corpus/struct.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | empty 3 | ================================================================================ 4 | 5 | %User{} 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (struct 11 | (module))) 12 | 13 | ================================================================================ 14 | identifier 15 | ================================================================================ 16 | 17 | %_{} 18 | 19 | -------------------------------------------------------------------------------- 20 | 21 | (program 22 | (struct 23 | (unused_identifier))) 24 | 25 | ================================================================================ 26 | with fields 27 | ================================================================================ 28 | 29 | %User{age: 27, name: "John"} 30 | 31 | -------------------------------------------------------------------------------- 32 | 33 | (program 34 | (struct 35 | (module) 36 | (keyword_list 37 | (keyword 38 | (keyword_literal)) 39 | (integer) 40 | (keyword 41 | (keyword_literal)) 42 | (string 43 | (string_start) 44 | (string_content) 45 | (string_end))))) 46 | 47 | ================================================================================ 48 | with dot call 49 | ================================================================================ 50 | 51 | %__MODULE__.ABC{} 52 | 53 | -------------------------------------------------------------------------------- 54 | 55 | (program 56 | (struct 57 | (dot_call 58 | (special_identifier) 59 | (module)))) 60 | -------------------------------------------------------------------------------- /test/corpus/try.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | try 3 | ================================================================================ 4 | 5 | try do 6 | do_something_that_may_fail(some_arg) 7 | rescue 8 | ArgumentError -> 9 | IO.puts("Invalid argument given") 10 | catch 11 | value -> 12 | IO.puts("Caught #{inspect(value)}") 13 | else 14 | value -> 15 | IO.puts("Success! The result was #{inspect(value)}") 16 | after 17 | IO.puts("This is printed regardless if it failed or succeeded") 18 | end 19 | 20 | -------------------------------------------------------------------------------- 21 | 22 | (program 23 | (call 24 | (function_identifier) 25 | (do_block 26 | (call 27 | (function_identifier) 28 | (arguments 29 | (identifier))) 30 | (rescue_block 31 | (stab_expression 32 | (bare_arguments 33 | (module)) 34 | (dot_call 35 | (module) 36 | (function_identifier) 37 | (arguments 38 | (string 39 | (string_start) 40 | (string_content) 41 | (string_end)))))) 42 | (catch_block 43 | (stab_expression 44 | (bare_arguments 45 | (identifier)) 46 | (dot_call 47 | (module) 48 | (function_identifier) 49 | (arguments 50 | (string 51 | (string_start) 52 | (string_content) 53 | (interpolation 54 | (call 55 | (function_identifier) 56 | (arguments 57 | (identifier)))) 58 | (string_end)))))) 59 | (else_block 60 | (stab_expression 61 | (bare_arguments 62 | (identifier)) 63 | (dot_call 64 | (module) 65 | (function_identifier) 66 | (arguments 67 | (string 68 | (string_start) 69 | (string_content) 70 | (interpolation 71 | (call 72 | (function_identifier) 73 | (arguments 74 | (identifier)))) 75 | (string_end)))))) 76 | (after_block 77 | (dot_call 78 | (module) 79 | (function_identifier) 80 | (arguments 81 | (string 82 | (string_start) 83 | (string_content) 84 | (string_end)))))))) 85 | 86 | ================================================================================ 87 | try with rescue only 88 | ================================================================================ 89 | 90 | try do 91 | UndefinedModule.undefined_function 92 | rescue 93 | x in [UndefinedFunctionError] -> nil 94 | end 95 | 96 | -------------------------------------------------------------------------------- 97 | 98 | (program 99 | (call 100 | (function_identifier) 101 | (do_block 102 | (dot_call 103 | (module) 104 | (function_identifier)) 105 | (rescue_block 106 | (stab_expression 107 | (bare_arguments 108 | (binary_op 109 | (identifier) 110 | (list 111 | (module)))) 112 | (nil)))))) 113 | 114 | ================================================================================ 115 | try catch 116 | ================================================================================ 117 | 118 | try do 119 | exit(:shutdown) 120 | catch 121 | :exit, value -> 122 | IO.puts("Exited with value #{inspect(value)}") 123 | end 124 | 125 | -------------------------------------------------------------------------------- 126 | 127 | (program 128 | (call 129 | (function_identifier) 130 | (do_block 131 | (call 132 | (function_identifier) 133 | (arguments 134 | (atom 135 | (atom_literal)))) 136 | (catch_block 137 | (stab_expression 138 | (bare_arguments 139 | (atom 140 | (atom_literal)) 141 | (identifier)) 142 | (dot_call 143 | (module) 144 | (function_identifier) 145 | (arguments 146 | (string 147 | (string_start) 148 | (string_content) 149 | (interpolation 150 | (call 151 | (function_identifier) 152 | (arguments 153 | (identifier)))) 154 | (string_end))))))))) 155 | 156 | ================================================================================ 157 | try rescue else 158 | ================================================================================ 159 | 160 | try do 161 | 1 / x 162 | rescue 163 | ArithmeticError -> 164 | :infinity 165 | else 166 | y when y < 1 and y > 5 -> 167 | :small 168 | _ -> 169 | :large 170 | end 171 | 172 | -------------------------------------------------------------------------------- 173 | 174 | (program 175 | (call 176 | (function_identifier) 177 | (do_block 178 | (binary_op 179 | (integer) 180 | (identifier)) 181 | (rescue_block 182 | (stab_expression 183 | (bare_arguments 184 | (module)) 185 | (atom 186 | (atom_literal)))) 187 | (else_block 188 | (stab_expression 189 | (bare_arguments 190 | (binary_op 191 | (identifier) 192 | (binary_op 193 | (binary_op 194 | (identifier) 195 | (integer)) 196 | (binary_op 197 | (identifier) 198 | (integer))))) 199 | (atom 200 | (atom_literal))) 201 | (stab_expression 202 | (bare_arguments 203 | (unused_identifier)) 204 | (atom 205 | (atom_literal))))))) 206 | 207 | ================================================================================ 208 | try after 209 | ================================================================================ 210 | 211 | try do 212 | do_something_with("tmp/story.txt") 213 | after 214 | File.rm("tmp/story.txt") 215 | end 216 | 217 | -------------------------------------------------------------------------------- 218 | 219 | (program 220 | (call 221 | (function_identifier) 222 | (do_block 223 | (call 224 | (function_identifier) 225 | (arguments 226 | (string 227 | (string_start) 228 | (string_content) 229 | (string_end)))) 230 | (after_block 231 | (dot_call 232 | (module) 233 | (function_identifier) 234 | (arguments 235 | (string 236 | (string_start) 237 | (string_content) 238 | (string_end)))))))) 239 | -------------------------------------------------------------------------------- /test/corpus/unicode.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | atom 3 | ================================================================================ 4 | 5 | {:runtime, :time_μs, :"£", "£", '£', :こんにちは世界, :Ólá, :olá, :Olá} 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (program 10 | (tuple 11 | (atom 12 | (atom_literal)) 13 | (atom 14 | (atom_literal)) 15 | (atom 16 | (atom_start) 17 | (atom_content) 18 | (atom_end)) 19 | (string 20 | (string_start) 21 | (string_content) 22 | (string_end)) 23 | (string 24 | (string_start) 25 | (string_content) 26 | (string_end)) 27 | (atom 28 | (atom_literal)) 29 | (atom 30 | (atom_literal)) 31 | (atom 32 | (atom_literal)) 33 | (atom 34 | (atom_literal)))) 35 | 36 | ================================================================================ 37 | char 38 | ================================================================================ 39 | 40 | [?ł, ?μ, ?£] 41 | 42 | -------------------------------------------------------------------------------- 43 | 44 | (program 45 | (list 46 | (char) 47 | (char) 48 | (char))) 49 | 50 | ================================================================================ 51 | string 52 | ================================================================================ 53 | 54 | ["über", 'hełło', 'olá', "olá"] 55 | 56 | -------------------------------------------------------------------------------- 57 | 58 | (program 59 | (list 60 | (string 61 | (string_start) 62 | (string_content) 63 | (string_end)) 64 | (string 65 | (string_start) 66 | (string_content) 67 | (string_end)) 68 | (string 69 | (string_start) 70 | (string_content) 71 | (string_end)) 72 | (string 73 | (string_start) 74 | (string_content) 75 | (string_end)))) 76 | -------------------------------------------------------------------------------- /test/fuzz/README.md: -------------------------------------------------------------------------------- 1 | # Fuzzing tree-sitter 2 | 3 | The tree-sitter fuzzing support requires 1) the `libFuzzer` runtime library and 2) a recent version of clang 4 | 5 | ``` 6 | sudo apt-get install clang-12 libfuzzer-12-dev 7 | ``` 8 | 9 | The fuzzers can then be built with: 10 | 11 | ``` 12 | LIB_FUZZER_PATH=/usr/lib/llvm-12/lib/libFuzzer.a \ 13 | ./scripts/build-fuzzer 14 | ``` 15 | 16 | This will generate a separate fuzzer for each grammar defined in `test/fixtures/grammars` and will be instrumented with [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html). Individual fuzzers can be built with, for example, `./script/build-fuzzers python ruby`. 17 | 18 | The `run-fuzzer` script handles running an individual fuzzer with a sensible default set of arguments: 19 | ``` 20 | ./scripts/run-fuzzer (halt|recover) 21 | ``` 22 | 23 | which will log information to stdout. Failing testcases and a fuzz corpus will be saved to `fuzz-results/`. The most important extra `libFuzzer` options are `-jobs` and `-workers` which allow parallel fuzzing. This is can done with, e.g.: 24 | ``` 25 | ./scripts/run-fuzzer halt -jobs=8 -workers=8 26 | ``` 27 | 28 | The testcase can be used to reproduce the crash by running: 29 | ``` 30 | ./scripts/reproduce (halt|recover) 31 | ``` 32 | -------------------------------------------------------------------------------- /test/fuzz/fuzzer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "tree_sitter/api.h" 4 | 5 | extern "C" const TSLanguage *TS_LANG(); 6 | 7 | static TSQuery *lang_query; 8 | 9 | extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { 10 | if(TS_LANG_QUERY_FILENAME[0]) { 11 | // The query filename is relative to the fuzzing binary. Convert it 12 | // to an absolute path first 13 | auto binary_filename = std::string((*argv)[0]); 14 | auto binary_directory = binary_filename.substr(0, binary_filename.find_last_of("\\/")); 15 | auto lang_query_filename = binary_directory + "/" + TS_LANG_QUERY_FILENAME; 16 | 17 | auto f = std::ifstream(lang_query_filename); 18 | assert(f.good()); 19 | std::string lang_query_source((std::istreambuf_iterator(f)), std::istreambuf_iterator()); 20 | 21 | uint32_t error_offset = 0; 22 | TSQueryError error_type = TSQueryErrorNone; 23 | 24 | lang_query = ts_query_new( 25 | TS_LANG(), 26 | lang_query_source.c_str(), 27 | lang_query_source.size(), 28 | &error_offset, 29 | &error_type 30 | ); 31 | 32 | assert(lang_query); 33 | } 34 | 35 | return 0; 36 | } 37 | 38 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 39 | const char *str = reinterpret_cast(data); 40 | 41 | TSParser *parser = ts_parser_new(); 42 | 43 | // This can fail if the language version doesn't match the runtime version 44 | bool language_ok = ts_parser_set_language(parser, TS_LANG()); 45 | assert(language_ok); 46 | 47 | TSTree *tree = ts_parser_parse_string(parser, NULL, str, size); 48 | TSNode root_node = ts_tree_root_node(tree); 49 | 50 | if (lang_query) { 51 | { 52 | TSQueryCursor *cursor = ts_query_cursor_new(); 53 | 54 | ts_query_cursor_exec(cursor, lang_query, root_node); 55 | TSQueryMatch match; 56 | while (ts_query_cursor_next_match(cursor, &match)) { 57 | } 58 | 59 | ts_query_cursor_delete(cursor); 60 | } 61 | 62 | { 63 | TSQueryCursor *cursor = ts_query_cursor_new(); 64 | 65 | ts_query_cursor_exec(cursor, lang_query, root_node); 66 | TSQueryMatch match; 67 | uint32_t capture_index; 68 | while (ts_query_cursor_next_capture(cursor, &match, &capture_index)) { 69 | } 70 | 71 | ts_query_cursor_delete(cursor); 72 | } 73 | } 74 | 75 | ts_tree_delete(tree); 76 | ts_parser_delete(parser); 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /test/fuzz/gen-dict.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | 4 | def find_literals(literals, node): 5 | '''Recursively find STRING literals in the grammar definition''' 6 | 7 | if type(node) is dict: 8 | if 'type' in node and node['type'] == 'STRING' and 'value' in node: 9 | literals.add(node['value']) 10 | 11 | for key, value in node.iteritems(): 12 | find_literals(literals, value) 13 | 14 | elif type(node) is list: 15 | for item in node: 16 | find_literals(literals, item) 17 | 18 | def main(): 19 | '''Generate a libFuzzer / AFL dictionary from a tree-sitter grammar.json''' 20 | with open(sys.argv[1]) as f: 21 | grammar = json.load(f) 22 | 23 | literals = set() 24 | find_literals(literals, grammar) 25 | 26 | for lit in sorted(literals): 27 | if lit: 28 | print '"%s"' % ''.join(['\\x%02x' % ord(b) for b in lit.encode('utf-8')]) 29 | 30 | if __name__ == '__main__': 31 | main() 32 | -------------------------------------------------------------------------------- /test/highlight/nested.ex: -------------------------------------------------------------------------------- 1 | defmodule MyModule do 2 | @moduledoc """ 3 | This is documentation for the module 4 | """ 5 | 6 | @doc """ 7 | This is documentation for the function 8 | """ 9 | def f(x, y, z) do 10 | # Place the cursor over the delimiters (...), [...], etc. 11 | list = [x, y, z] 12 | tuple = {x, y, z} 13 | map = %{"x" => x, "y" => y, "z" => z} 14 | struct = %Long.Module.Name{x: x, y: y, z: y} 15 | << a::size(x), b::size(y), x::size(z) >> = "abcde" 16 | # Nested delimiters work too: 17 | nested = %{ 18 | a: [x, y, z], 19 | b: {x, {x, y}, {x, {x, y}, {x, y, z, [x, y, z]}}}, 20 | c: %Long.Module.Name{ 21 | x: x, 22 | y: y, 23 | z: << a::size(x), b::size(y) >> 24 | } 25 | } 26 | end 27 | def g() do 28 | IO.inspect do 29 | "a" 30 | else 31 | IO.inspect do 32 | fn x -> x + 2 end # yes, it works on fn ... end too! 33 | else 34 | "b" 35 | catch 36 | if c do 37 | x 38 | else 39 | case y do 40 | 1 -> if a do 41 | b 42 | else 43 | c 44 | end 45 | _ -> "Wow, we're deep now!" 46 | end 47 | end 48 | rescue 49 | "d" 50 | after 51 | "e" 52 | end 53 | "b" 54 | after 55 | "e" 56 | end 57 | end 58 | end 59 | --------------------------------------------------------------------------------