├── bindings ├── python │ ├── tree_sitter_javascript │ │ ├── py.typed │ │ ├── __init__.pyi │ │ ├── binding.c │ │ └── __init__.py │ └── tests │ │ └── test_binding.py ├── node │ ├── binding_test.js │ ├── index.js │ ├── index.d.ts │ └── binding.cc ├── c │ ├── tree_sitter │ │ └── tree-sitter-javascript.h │ └── tree-sitter-javascript.pc.in ├── swift │ ├── TreeSitterJavaScript │ │ └── javascript.h │ └── TreeSitterJavaScriptTests │ │ └── TreeSitterJavaScriptTests.swift ├── go │ ├── binding.go │ └── binding_test.go └── rust │ ├── build.rs │ └── lib.rs ├── eslint.config.mjs ├── test ├── highlight │ ├── injection.js │ ├── imports.js │ ├── keywords.js │ ├── functions.js │ └── variables.js ├── tags │ ├── classes.js │ └── functions.js └── corpus │ ├── injectables.txt │ ├── literals.txt │ ├── destructuring.txt │ ├── semicolon_insertion.txt │ └── statements.txt ├── go.mod ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ ├── fuzz.yml │ ├── lint.yml │ ├── publish.yml │ └── ci.yml └── FUNDING.yml ├── queries ├── highlights-params.scm ├── locals.scm ├── highlights-jsx.scm ├── injections.scm ├── tags.scm └── highlights.scm ├── Package.resolved ├── .gitignore ├── .editorconfig ├── binding.gyp ├── Cargo.toml ├── pyproject.toml ├── LICENSE ├── .gitattributes ├── src ├── tree_sitter │ ├── alloc.h │ ├── parser.h │ └── array.h └── scanner.c ├── tree-sitter.json ├── Package.swift ├── package.json ├── README.md ├── setup.py ├── CMakeLists.txt ├── Makefile ├── go.sum ├── Cargo.lock └── grammar.js /bindings/python/tree_sitter_javascript/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import treesitter from 'eslint-config-treesitter'; 2 | 3 | export default [ 4 | ...treesitter, 5 | ]; 6 | -------------------------------------------------------------------------------- /test/highlight/injection.js: -------------------------------------------------------------------------------- 1 | eval(js `var foo`) 2 | // <- function 3 | // ^ function 4 | // ^ keyword 5 | // ^ variable 6 | -------------------------------------------------------------------------------- /test/highlight/imports.js: -------------------------------------------------------------------------------- 1 | import pkg from "./package.json" with { type: "json" }; 2 | // <- keyword 3 | // ^ string 4 | // ^ keyword 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tree-sitter/tree-sitter-javascript 2 | 3 | go 1.22 4 | 5 | require github.com/tree-sitter/go-tree-sitter v0.24.0 6 | 7 | require github.com/mattn/go-pointer v0.0.1 // indirect 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | commit-message: 8 | prefix: "ci" 9 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_javascript/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Final 2 | 3 | HIGHLIGHTS_QUERY: Final[str] 4 | INJECTIONS_QUERY: Final[str] 5 | LOCALS_QUERY: Final[str] 6 | TAGS_QUERY: Final[str] 7 | 8 | def language() -> object: ... 9 | -------------------------------------------------------------------------------- /test/highlight/keywords.js: -------------------------------------------------------------------------------- 1 | do {} while (a); 2 | // <- keyword 3 | // ^ keyword 4 | 5 | try {} catch (e) {} finally {} 6 | // <- keyword 7 | // ^ keyword 8 | // ^ keyword 9 | 10 | throw e 11 | // <- keyword 12 | // ^ variable 13 | -------------------------------------------------------------------------------- /test/tags/classes.js: -------------------------------------------------------------------------------- 1 | class Person { 2 | // ^ definition.class 3 | static foo = bar; 4 | 5 | getName() { 6 | // ^ definition.method 7 | } 8 | } 9 | 10 | var person = new Person(); 11 | // ^ reference.class 12 | 13 | person.getName() 14 | // ^ reference.call 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | -------------------------------------------------------------------------------- /bindings/node/binding_test.js: -------------------------------------------------------------------------------- 1 | const assert = require("node:assert"); 2 | const { test } = require("node:test"); 3 | 4 | const Parser = require("tree-sitter"); 5 | 6 | test("can load grammar", () => { 7 | const parser = new Parser(); 8 | assert.doesNotThrow(() => parser.setLanguage(require("."))); 9 | }); 10 | -------------------------------------------------------------------------------- /queries/highlights-params.scm: -------------------------------------------------------------------------------- 1 | (formal_parameters 2 | [ 3 | (identifier) @variable.parameter 4 | (array_pattern 5 | (identifier) @variable.parameter) 6 | (object_pattern 7 | [ 8 | (pair_pattern value: (identifier) @variable.parameter) 9 | (shorthand_property_identifier_pattern) @variable.parameter 10 | ]) 11 | ] 12 | ) 13 | -------------------------------------------------------------------------------- /bindings/c/tree_sitter/tree-sitter-javascript.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_JAVASCRIPT_H_ 2 | #define TREE_SITTER_JAVASCRIPT_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_javascript(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_JAVASCRIPT_H_ 17 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterJavaScript/javascript.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_JAVASCRIPT_H_ 2 | #define TREE_SITTER_JAVASCRIPT_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_javascript(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_JAVASCRIPT_H_ 17 | -------------------------------------------------------------------------------- /bindings/c/tree-sitter-javascript.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ 3 | includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ 4 | 5 | Name: tree-sitter-javascript 6 | Description: @PROJECT_DESCRIPTION@ 7 | URL: @PROJECT_HOMEPAGE_URL@ 8 | Version: @PROJECT_VERSION@ 9 | Libs: -L${libdir} -ltree-sitter-javascript 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /bindings/python/tests/test_binding.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import tree_sitter, tree_sitter_javascript 4 | 5 | 6 | class TestLanguage(TestCase): 7 | def test_can_load_grammar(self): 8 | try: 9 | tree_sitter.Language(tree_sitter_javascript.language()) 10 | except Exception: 11 | self.fail("Error loading JavaScript grammar") 12 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "SwiftTreeSitter", 6 | "repositoryURL": "https://github.com/ChimeHQ/SwiftTreeSitter", 7 | "state": { 8 | "branch": null, 9 | "revision": "2599e95310b3159641469d8a21baf2d3d200e61f", 10 | "version": "0.8.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Fuzz Parser 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - src/scanner.c 8 | pull_request: 9 | paths: 10 | - src/scanner.c 11 | 12 | jobs: 13 | fuzz: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v5 18 | - name: Run fuzzer 19 | uses: tree-sitter/fuzz-action@v4 20 | -------------------------------------------------------------------------------- /bindings/go/binding.go: -------------------------------------------------------------------------------- 1 | package tree_sitter_javascript 2 | 3 | // #cgo CFLAGS: -std=c11 -fPIC 4 | // #include "../../src/parser.c" 5 | // #if __has_include("../../src/scanner.c") 6 | // #include "../../src/scanner.c" 7 | // #endif 8 | import "C" 9 | 10 | import "unsafe" 11 | 12 | // Get the tree-sitter Language for this grammar. 13 | func Language() unsafe.Pointer { 14 | return unsafe.Pointer(C.tree_sitter_javascript()) 15 | } 16 | -------------------------------------------------------------------------------- /queries/locals.scm: -------------------------------------------------------------------------------- 1 | ; Scopes 2 | ;------- 3 | 4 | [ 5 | (statement_block) 6 | (function_expression) 7 | (arrow_function) 8 | (function_declaration) 9 | (method_definition) 10 | ] @local.scope 11 | 12 | ; Definitions 13 | ;------------ 14 | 15 | (pattern/identifier) @local.definition 16 | 17 | (variable_declarator 18 | name: (identifier) @local.definition) 19 | 20 | ; References 21 | ;------------ 22 | 23 | (identifier) @local.reference 24 | -------------------------------------------------------------------------------- /bindings/go/binding_test.go: -------------------------------------------------------------------------------- 1 | package tree_sitter_javascript_test 2 | 3 | import ( 4 | "testing" 5 | 6 | tree_sitter "github.com/tree-sitter/go-tree-sitter" 7 | tree_sitter_javascript "github.com/tree-sitter/tree-sitter-javascript/bindings/go" 8 | ) 9 | 10 | func TestCanLoadGrammar(t *testing.T) { 11 | language := tree_sitter.NewLanguage(tree_sitter_javascript.Language()) 12 | if language == nil { 13 | t.Errorf("Error loading JavaScript grammar") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /queries/highlights-jsx.scm: -------------------------------------------------------------------------------- 1 | (jsx_opening_element (identifier) @tag (#match? @tag "^[a-z][^.]*$")) 2 | (jsx_closing_element (identifier) @tag (#match? @tag "^[a-z][^.]*$")) 3 | (jsx_self_closing_element (identifier) @tag (#match? @tag "^[a-z][^.]*$")) 4 | 5 | (jsx_attribute (property_identifier) @attribute) 6 | (jsx_opening_element (["<" ">"]) @punctuation.bracket) 7 | (jsx_closing_element ([""]) @punctuation.bracket) 8 | (jsx_self_closing_element (["<" "/>"]) @punctuation.bracket) 9 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterJavaScriptTests/TreeSitterJavaScriptTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftTreeSitter 3 | import TreeSitterJavaScript 4 | 5 | final class TreeSitterJavaScriptTests: XCTestCase { 6 | func testCanLoadGrammar() throws { 7 | let parser = Parser() 8 | let language = Language(language: tree_sitter_javascript()) 9 | XCTAssertNoThrow(try parser.setLanguage(language), 10 | "Error loading JavaScript grammar") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bindings/node/index.js: -------------------------------------------------------------------------------- 1 | const root = require("path").join(__dirname, "..", ".."); 2 | 3 | module.exports = 4 | typeof process.versions.bun === "string" 5 | // Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time 6 | ? require(`../../prebuilds/${process.platform}-${process.arch}/tree-sitter-javascript.node`) 7 | : require("node-gyp-build")(root); 8 | 9 | try { 10 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 11 | } catch (_) {} 12 | -------------------------------------------------------------------------------- /test/tags/functions.js: -------------------------------------------------------------------------------- 1 | function foo() { 2 | // ^ definition.function 3 | } 4 | 5 | foo() 6 | // <- reference.call 7 | 8 | { source: $ => repeat($._expression) } 9 | // ^ definition.function 10 | // ^ reference.call 11 | 12 | let plus1 = x => x + 1 13 | // ^ definition.function 14 | 15 | let plus2 = function(x) { return x + 2 } 16 | // ^ definition.function 17 | 18 | function *gen() { } 19 | // ^ definition.function 20 | 21 | async function* foo() { yield 1; } 22 | // ^ definition.function 23 | -------------------------------------------------------------------------------- /bindings/node/index.d.ts: -------------------------------------------------------------------------------- 1 | type BaseNode = { 2 | type: string; 3 | named: boolean; 4 | }; 5 | 6 | type ChildNode = { 7 | multiple: boolean; 8 | required: boolean; 9 | types: BaseNode[]; 10 | }; 11 | 12 | type NodeInfo = 13 | | (BaseNode & { 14 | subtypes: BaseNode[]; 15 | }) 16 | | (BaseNode & { 17 | fields: { [name: string]: ChildNode }; 18 | children: ChildNode[]; 19 | }); 20 | 21 | type Language = { 22 | language: unknown; 23 | nodeTypeInfo: NodeInfo[]; 24 | }; 25 | 26 | declare const language: Language; 27 | export = language; 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust artifacts 2 | target/ 3 | 4 | # Node artifacts 5 | build/ 6 | prebuilds/ 7 | node_modules/ 8 | 9 | # Swift artifacts 10 | .build/ 11 | 12 | # Go artifacts 13 | _obj/ 14 | 15 | # Python artifacts 16 | .venv/ 17 | dist/ 18 | *.egg-info 19 | *.whl 20 | 21 | # C artifacts 22 | *.a 23 | *.so 24 | *.so.* 25 | *.dylib 26 | *.dll 27 | *.pc 28 | *.exp 29 | *.lib 30 | 31 | # Zig artifacts 32 | .zig-cache/ 33 | zig-cache/ 34 | zig-out/ 35 | 36 | # Example dirs 37 | /examples/*/ 38 | 39 | # Grammar volatiles 40 | *.wasm 41 | *.obj 42 | *.o 43 | 44 | # Archives 45 | *.tar.gz 46 | *.tgz 47 | *.zip 48 | -------------------------------------------------------------------------------- /bindings/node/binding.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | extern "C" TSLanguage *tree_sitter_javascript(); 6 | 7 | // "tree-sitter", "language" hashed with BLAKE2 8 | const napi_type_tag LANGUAGE_TYPE_TAG = { 9 | 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16 10 | }; 11 | 12 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 13 | auto language = Napi::External::New(env, tree_sitter_javascript()); 14 | language.TypeTag(&LANGUAGE_TYPE_TAG); 15 | exports["language"] = language; 16 | return exports; 17 | } 18 | 19 | NODE_API_MODULE(tree_sitter_javascript_binding, Init) 20 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - grammar.js 8 | pull_request: 9 | paths: 10 | - grammar.js 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v5 18 | - name: Set up Node.js 19 | uses: actions/setup-node@v5 20 | with: 21 | cache: npm 22 | node-version: ${{vars.NODE_VERSION}} 23 | - name: Install modules 24 | run: npm ci --legacy-peer-deps 25 | - name: Run ESLint 26 | run: npm run lint 27 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.{json,toml,yml,gyp}] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [*.js] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.scm] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [*.{c,cc,h}] 19 | indent_style = space 20 | indent_size = 4 21 | 22 | [*.rs] 23 | indent_style = space 24 | indent_size = 4 25 | 26 | [*.{py,pyi}] 27 | indent_style = space 28 | indent_size = 4 29 | 30 | [*.swift] 31 | indent_style = space 32 | indent_size = 4 33 | 34 | [*.go] 35 | indent_style = tab 36 | indent_size = 8 37 | 38 | [Makefile] 39 | indent_style = tab 40 | indent_size = 8 41 | 42 | [parser.c] 43 | indent_size = 2 44 | 45 | [{alloc,array,parser}.h] 46 | indent_size = 2 47 | -------------------------------------------------------------------------------- /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 6 | .std("c11") 7 | .include(src_dir) 8 | .flag_if_supported("-Wno-unused-parameter"); 9 | 10 | #[cfg(target_env = "msvc")] 11 | c_config.flag("-utf-8"); 12 | 13 | let parser_path = src_dir.join("parser.c"); 14 | c_config.file(&parser_path); 15 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 16 | 17 | let scanner_path = src_dir.join("scanner.c"); 18 | if scanner_path.exists() { 19 | c_config.file(&scanner_path); 20 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 21 | } 22 | 23 | c_config.compile("tree-sitter-javascript"); 24 | } 25 | -------------------------------------------------------------------------------- /test/highlight/functions.js: -------------------------------------------------------------------------------- 1 | var a = 'a'; 2 | // ^ variable 3 | 4 | var b = function() {}; 5 | // ^ function 6 | 7 | var c = () => {}; 8 | // ^ function 9 | 10 | var d = async () => {}; 11 | // ^ function 12 | 13 | module.e = 'e'; 14 | // ^ property 15 | 16 | module.f = function() {}; 17 | // ^ function.method 18 | 19 | module.g = async function() {}; 20 | // ^ function.method 21 | 22 | module.h = () => {}; 23 | // ^ function.method 24 | 25 | function i() { 26 | // ^ function 27 | } 28 | 29 | class Person { 30 | static foo = bar; 31 | // ^ property 32 | 33 | getName() { 34 | // ^ function.method 35 | } 36 | } 37 | 38 | foo(function callback() { 39 | // ^ keyword 40 | // ^ function 41 | }) 42 | 43 | 44 | c(); 45 | // <- function 46 | 47 | module.e(); 48 | // ^ function.method 49 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: tree-sitter 4 | patreon: # Replace with a single Patreon username 5 | open_collective: tree-sitter # Replace with a single Open Collective username 6 | ko_fi: amaanq 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_javascript_binding", 5 | "dependencies": [ 6 | "{{foo}} 26 | 27 | ---- 28 | 29 | (program 30 | (lexical_declaration 31 | (variable_declarator 32 | (identifier) 33 | (number))) 34 | (expression_statement 35 | (jsx_element 36 | (jsx_opening_element 37 | (identifier)) 38 | (jsx_expression 39 | (object 40 | (shorthand_property_identifier))) 41 | (jsx_closing_element 42 | (identifier))))) 43 | 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report unexpected parsing results 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | The following piece of code is valid but it is parsed incorrectly: 11 | 12 | ```javascript 13 | 14 | ``` 15 | 16 | Here's a link to the TypeScript Playground showing that the snippet above is valid JavaScript or TypeScript: 17 | 19 | 20 | 21 | The output of `tree-sitter parse` is the following: 22 | 23 | ``` 24 | 25 | ``` 26 | 27 | 29 | 30 | 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-javascript" 3 | description = "JavaScript grammar for tree-sitter" 4 | version = "0.25.0" 5 | authors = [ 6 | "Max Brunsfeld ", 7 | "Amaan Qureshi ", 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | keywords = ["incremental", "parsing", "tree-sitter", "javascript"] 12 | categories = ["parser-implementations", "parsing", "text-editors"] 13 | repository = "https://github.com/tree-sitter/tree-sitter-javascript" 14 | edition = "2021" 15 | autoexamples = false 16 | 17 | build = "bindings/rust/build.rs" 18 | include = [ 19 | "bindings/rust/*", 20 | "grammar.js", 21 | "queries/*", 22 | "src/*", 23 | "tree-sitter.json", 24 | "LICENSE", 25 | ] 26 | 27 | [lib] 28 | path = "bindings/rust/lib.rs" 29 | 30 | [dependencies] 31 | tree-sitter-language = "0.1" 32 | 33 | [build-dependencies] 34 | cc = "1.2" 35 | 36 | [dev-dependencies] 37 | tree-sitter = "0.25.8" 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish packages 2 | 3 | on: 4 | push: 5 | tags: ["*"] 6 | 7 | permissions: 8 | contents: write 9 | id-token: write 10 | attestations: write 11 | 12 | jobs: 13 | github: 14 | uses: tree-sitter/workflows/.github/workflows/release.yml@main 15 | with: 16 | generate: true 17 | attestations: true 18 | npm: 19 | uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main 20 | secrets: 21 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 22 | with: 23 | generate: true 24 | crates: 25 | uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main 26 | secrets: 27 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_REGISTRY_TOKEN}} 28 | with: 29 | generate: true 30 | pypi: 31 | uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main 32 | secrets: 33 | PYPI_API_TOKEN: ${{secrets.PYPI_API_TOKEN}} 34 | with: 35 | generate: true 36 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=62.4.0", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "tree-sitter-javascript" 7 | description = "JavaScript grammar for tree-sitter" 8 | version = "0.25.0" 9 | keywords = ["incremental", "parsing", "tree-sitter", "javascript"] 10 | classifiers = [ 11 | "Intended Audience :: Developers", 12 | "Topic :: Software Development :: Compilers", 13 | "Topic :: Text Processing :: Linguistic", 14 | "Typing :: Typed", 15 | ] 16 | authors = [ 17 | { name = "Max Brunsfeld", email = "maxbrunsfeld@gmail.com" }, 18 | { name = "Amaan Qureshi", email = "amaanq12@gmail.com" }, 19 | ] 20 | requires-python = ">=3.10" 21 | license.text = "MIT" 22 | readme = "README.md" 23 | 24 | [project.urls] 25 | Homepage = "https://github.com/tree-sitter/tree-sitter-javascript" 26 | 27 | [project.optional-dependencies] 28 | core = ["tree-sitter~=0.24"] 29 | 30 | [tool.cibuildwheel] 31 | build = "cp310-*" 32 | build-frontend = "build" 33 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_javascript/binding.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | TSLanguage *tree_sitter_javascript(void); 6 | 7 | static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 8 | return PyCapsule_New(tree_sitter_javascript(), "tree_sitter.Language", NULL); 9 | } 10 | 11 | static struct PyModuleDef_Slot slots[] = { 12 | #ifdef Py_GIL_DISABLED 13 | {Py_mod_gil, Py_MOD_GIL_NOT_USED}, 14 | #endif 15 | {0, NULL} 16 | }; 17 | 18 | static PyMethodDef methods[] = { 19 | {"language", _binding_language, METH_NOARGS, 20 | "Get the tree-sitter language for this grammar."}, 21 | {NULL, NULL, 0, NULL} 22 | }; 23 | 24 | static struct PyModuleDef module = { 25 | .m_base = PyModuleDef_HEAD_INIT, 26 | .m_name = "_binding", 27 | .m_doc = NULL, 28 | .m_size = 0, 29 | .m_methods = methods, 30 | .m_slots = slots, 31 | }; 32 | 33 | PyMODINIT_FUNC PyInit__binding(void) { 34 | return PyModuleDef_Init(&module); 35 | } 36 | -------------------------------------------------------------------------------- /queries/injections.scm: -------------------------------------------------------------------------------- 1 | ; Parse the contents of tagged template literals using 2 | ; a language inferred from the tag. 3 | 4 | (call_expression 5 | function: [ 6 | (identifier) @injection.language 7 | (member_expression 8 | property: (property_identifier) @injection.language) 9 | ] 10 | arguments: (template_string (string_fragment) @injection.content) 11 | (#set! injection.combined) 12 | (#set! injection.include-children)) 13 | 14 | 15 | ; Parse regex syntax within regex literals 16 | 17 | ((regex_pattern) @injection.content 18 | (#set! injection.language "regex")) 19 | 20 | ; Parse JSDoc annotations in comments 21 | 22 | ((comment) @injection.content 23 | (#set! injection.language "jsdoc")) 24 | 25 | ; Parse Ember/Glimmer/Handlebars/HTMLBars/etc. template literals 26 | ; e.g.: await render(hbs``) 27 | (call_expression 28 | function: ((identifier) @_name 29 | (#eq? @_name "hbs")) 30 | arguments: ((template_string) @glimmer 31 | (#offset! @glimmer 0 1 0 -1))) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Max Brunsfeld 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 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | # Generated source files 4 | src/*.json linguist-generated 5 | src/parser.c linguist-generated 6 | src/tree_sitter/* linguist-generated 7 | 8 | # C bindings 9 | bindings/c/** linguist-generated 10 | CMakeLists.txt linguist-generated 11 | Makefile linguist-generated 12 | 13 | # Rust bindings 14 | bindings/rust/* linguist-generated 15 | Cargo.toml linguist-generated 16 | Cargo.lock linguist-generated 17 | 18 | # Node.js bindings 19 | bindings/node/* linguist-generated 20 | binding.gyp linguist-generated 21 | package.json linguist-generated 22 | package-lock.json linguist-generated 23 | 24 | # Python bindings 25 | bindings/python/** linguist-generated 26 | setup.py linguist-generated 27 | pyproject.toml linguist-generated 28 | 29 | # Go bindings 30 | bindings/go/* linguist-generated 31 | go.mod linguist-generated 32 | go.sum linguist-generated 33 | 34 | # Swift bindings 35 | bindings/swift/** linguist-generated 36 | Package.swift linguist-generated 37 | Package.resolved linguist-generated 38 | 39 | # Zig bindings 40 | bindings/zig/* linguist-generated 41 | build.zig linguist-generated 42 | build.zig.zon linguist-generated 43 | -------------------------------------------------------------------------------- /src/tree_sitter/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ALLOC_H_ 2 | #define TREE_SITTER_ALLOC_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // Allow clients to override allocation functions 13 | #ifdef TREE_SITTER_REUSE_ALLOCATOR 14 | 15 | extern void *(*ts_current_malloc)(size_t size); 16 | extern void *(*ts_current_calloc)(size_t count, size_t size); 17 | extern void *(*ts_current_realloc)(void *ptr, size_t size); 18 | extern void (*ts_current_free)(void *ptr); 19 | 20 | #ifndef ts_malloc 21 | #define ts_malloc ts_current_malloc 22 | #endif 23 | #ifndef ts_calloc 24 | #define ts_calloc ts_current_calloc 25 | #endif 26 | #ifndef ts_realloc 27 | #define ts_realloc ts_current_realloc 28 | #endif 29 | #ifndef ts_free 30 | #define ts_free ts_current_free 31 | #endif 32 | 33 | #else 34 | 35 | #ifndef ts_malloc 36 | #define ts_malloc malloc 37 | #endif 38 | #ifndef ts_calloc 39 | #define ts_calloc calloc 40 | #endif 41 | #ifndef ts_realloc 42 | #define ts_realloc realloc 43 | #endif 44 | #ifndef ts_free 45 | #define ts_free free 46 | #endif 47 | 48 | #endif 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // TREE_SITTER_ALLOC_H_ 55 | -------------------------------------------------------------------------------- /tree-sitter.json: -------------------------------------------------------------------------------- 1 | { 2 | "grammars": [ 3 | { 4 | "name": "javascript", 5 | "camelcase": "JavaScript", 6 | "scope": "source.js", 7 | "path": ".", 8 | "file-types": [ 9 | "js", 10 | "mjs", 11 | "cjs", 12 | "jsx" 13 | ], 14 | "highlights": [ 15 | "queries/highlights.scm", 16 | "queries/highlights-jsx.scm", 17 | "queries/highlights-params.scm" 18 | ], 19 | "tags": [ 20 | "queries/tags.scm" 21 | ], 22 | "injection-regex": "^(js|javascript)$" 23 | } 24 | ], 25 | "metadata": { 26 | "version": "0.25.0", 27 | "license": "MIT", 28 | "description": "JavaScript grammar for tree-sitter", 29 | "authors": [ 30 | { 31 | "name": "Max Brunsfeld", 32 | "email": "maxbrunsfeld@gmail.com" 33 | }, 34 | { 35 | "name": "Amaan Qureshi", 36 | "email": "amaanq12@gmail.com" 37 | } 38 | ], 39 | "links": { 40 | "repository": "https://github.com/tree-sitter/tree-sitter-javascript" 41 | } 42 | }, 43 | "bindings": { 44 | "c": true, 45 | "go": true, 46 | "node": true, 47 | "python": true, 48 | "rust": true, 49 | "swift": true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_javascript/__init__.py: -------------------------------------------------------------------------------- 1 | """JavaScript grammar for tree-sitter""" 2 | 3 | from importlib.resources import files as _files 4 | 5 | from ._binding import language 6 | 7 | 8 | def _get_query(name, file): 9 | query = _files(f"{__package__}.queries") / file 10 | globals()[name] = query.read_text() 11 | return globals()[name] 12 | 13 | 14 | def __getattr__(name): 15 | if name == "HIGHLIGHTS_QUERY": 16 | return _get_query("HIGHLIGHTS_QUERY", "highlights.scm") 17 | if name == "INJECTIONS_QUERY": 18 | return _get_query("INJECTIONS_QUERY", "injections.scm") 19 | if name == "LOCALS_QUERY": 20 | return _get_query("LOCALS_QUERY", "locals.scm") 21 | if name == "TAGS_QUERY": 22 | return _get_query("TAGS_QUERY", "tags.scm") 23 | 24 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 25 | 26 | 27 | __all__ = [ 28 | "language", 29 | "HIGHLIGHTS_QUERY", 30 | "INJECTIONS_QUERY", 31 | "LOCALS_QUERY", 32 | "TAGS_QUERY", 33 | ] 34 | 35 | 36 | def __dir__(): 37 | return sorted(__all__ + [ 38 | "__all__", "__builtins__", "__cached__", "__doc__", "__file__", 39 | "__loader__", "__name__", "__package__", "__path__", "__spec__", 40 | ]) 41 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import Foundation 4 | import PackageDescription 5 | 6 | var sources = ["src/parser.c"] 7 | if FileManager.default.fileExists(atPath: "src/scanner.c") { 8 | sources.append("src/scanner.c") 9 | } 10 | 11 | let package = Package( 12 | name: "TreeSitterJavaScript", 13 | products: [ 14 | .library(name: "TreeSitterJavaScript", targets: ["TreeSitterJavaScript"]), 15 | ], 16 | dependencies: [ 17 | .package(name: "SwiftTreeSitter", url: "https://github.com/tree-sitter/swift-tree-sitter", from: "0.9.0"), 18 | ], 19 | targets: [ 20 | .target( 21 | name: "TreeSitterJavaScript", 22 | dependencies: [], 23 | path: ".", 24 | sources: sources, 25 | resources: [ 26 | .copy("queries") 27 | ], 28 | publicHeadersPath: "bindings/swift", 29 | cSettings: [.headerSearchPath("src")] 30 | ), 31 | .testTarget( 32 | name: "TreeSitterJavaScriptTests", 33 | dependencies: [ 34 | "SwiftTreeSitter", 35 | "TreeSitterJavaScript", 36 | ], 37 | path: "bindings/swift/TreeSitterJavaScriptTests" 38 | ) 39 | ], 40 | cLanguageStandard: .c11 41 | ) 42 | -------------------------------------------------------------------------------- /test/highlight/variables.js: -------------------------------------------------------------------------------- 1 | class A {} 2 | // ^ constructor 3 | const ABC = 1 4 | // ^ constant 5 | const AB_C1 = 2 6 | // ^ constant 7 | const {AB_C2_D3} = x 8 | // ^ constant 9 | 10 | module.exports = function(one, two) { 11 | // <- variable.builtin 12 | // ^ variable.parameter 13 | 14 | if (something()) { 15 | let module = null, one = 1; 16 | // ^ variable 17 | // ^ variable 18 | 19 | console.log(module, one, two); 20 | // ^ variable.builtin 21 | // ^ variable 22 | // ^ variable 23 | // ^ variable.parameter 24 | } 25 | 26 | console.log(module, one, two); 27 | // ^ variable.builtin 28 | // ^ variable.builtin 29 | // ^ variable.parameter 30 | // ^ variable.parameter 31 | }; 32 | 33 | console.log(module, one, two); 34 | // ^ variable.builtin 35 | // ^ variable.builtin 36 | // ^ variable 37 | // ^ variable 38 | 39 | function one({two: three}, [four]) { 40 | // ^ property 41 | // ^ variable.parameter 42 | // ^ variable.parameter 43 | 44 | console.log(two, three, four) 45 | // ^ variable 46 | // ^ variable.parameter 47 | // ^ variable.parameter 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - grammar.js 8 | - src/** 9 | - test/** 10 | - bindings/** 11 | - binding.gyp 12 | pull_request: 13 | paths: 14 | - grammar.js 15 | - src/** 16 | - test/** 17 | - bindings/** 18 | - binding.gyp 19 | 20 | concurrency: 21 | group: ${{github.workflow}}-${{github.ref}} 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | test: 26 | name: Test parser 27 | runs-on: ${{matrix.os}} 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | os: [ubuntu-latest, windows-latest, macos-latest] 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v5 35 | - name: Set up tree-sitter 36 | uses: tree-sitter/setup-action/cli@v2 37 | - name: Set up examples 38 | run: |- 39 | git config --global core.longpaths true 40 | git clone https://github.com/npm/cli examples/cli --single-branch --depth=1 --filter=blob:none 41 | - name: Run tests 42 | uses: tree-sitter/parser-test-action@v3 43 | with: 44 | test-rust: true 45 | test-node: true 46 | test-python: true 47 | test-go: true 48 | test-swift: true 49 | - name: Parse examples 50 | uses: tree-sitter/parse-action@v4 51 | with: 52 | files: | 53 | examples/*.js 54 | examples/*.jsx 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-javascript", 3 | "version": "0.25.0", 4 | "description": "JavaScript grammar for tree-sitter", 5 | "repository": "https://github.com/tree-sitter/tree-sitter-javascript", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Max Brunsfeld", 9 | "email": "maxbrunsfeld@gmail.com" 10 | }, 11 | "maintainers": [ 12 | { 13 | "name": "Amaan Qureshi", 14 | "email": "amaanq12@gmail.com" 15 | } 16 | ], 17 | "main": "bindings/node", 18 | "types": "bindings/node", 19 | "keywords": [ 20 | "incremental", 21 | "parsing", 22 | "tree-sitter", 23 | "javascript" 24 | ], 25 | "files": [ 26 | "grammar.js", 27 | "tree-sitter.json", 28 | "binding.gyp", 29 | "prebuilds/**", 30 | "bindings/node/*", 31 | "queries/*", 32 | "src/**", 33 | "*.wasm" 34 | ], 35 | "dependencies": { 36 | "node-addon-api": "^8.3.1", 37 | "node-gyp-build": "^4.8.4" 38 | }, 39 | "devDependencies": { 40 | "eslint": "^9.14.0", 41 | "eslint-config-treesitter": "^1.0.2", 42 | "prebuildify": "^6.0.1", 43 | "tree-sitter-cli": "^0.25.8" 44 | }, 45 | "peerDependencies": { 46 | "tree-sitter": "^0.25.0" 47 | }, 48 | "peerDependenciesMeta": { 49 | "tree-sitter": { 50 | "optional": true 51 | } 52 | }, 53 | "scripts": { 54 | "install": "node-gyp-build", 55 | "lint": "eslint grammar.js", 56 | "prestart": "tree-sitter build --wasm", 57 | "start": "tree-sitter playground", 58 | "test": "node --test bindings/node/*_test.js" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-javascript 2 | 3 | [![CI][ci]](https://github.com/tree-sitter/tree-sitter-javascript/actions/workflows/ci.yml) 4 | [![discord][discord]](https://discord.gg/w7nTvsVJhm) 5 | [![matrix][matrix]](https://matrix.to/#/#tree-sitter-chat:matrix.org) 6 | [![crates][crates]](https://crates.io/crates/tree-sitter-javascript) 7 | [![npm][npm]](https://www.npmjs.com/package/tree-sitter-javascript) 8 | [![pypi][pypi]](https://pypi.org/project/tree-sitter-javascript) 9 | 10 | JavaScript and JSX grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter). 11 | 12 | This grammar intends to be a close approximation of the [ECMAScript](https://ecma-international.org/publications-and-standards/standards/ecma-262/) 13 | specification, with some extensions to support JSX syntax. We try to support the 14 | latest version of the spec, though it is possible that some very new features may 15 | not be supported yet. 16 | 17 | References 18 | 19 | - [The ESTree Spec](https://github.com/estree/estree) 20 | - [The ECMAScript 2025 Spec](https://tc39.es/ecma262/2025/) 21 | 22 | [ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter/tree-sitter-javascript/ci.yml?logo=github&label=CI 23 | [discord]: https://img.shields.io/discord/1063097320771698699?logo=discord&label=discord 24 | [matrix]: https://img.shields.io/matrix/tree-sitter-chat%3Amatrix.org?logo=matrix&label=matrix 25 | [npm]: https://img.shields.io/npm/v/tree-sitter-javascript?logo=npm 26 | [crates]: https://img.shields.io/crates/v/tree-sitter-javascript?logo=rust 27 | [pypi]: https://img.shields.io/pypi/v/tree-sitter-javascript?logo=pypi&logoColor=ffd242 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from platform import system 3 | from sysconfig import get_config_var 4 | 5 | from setuptools import Extension, find_packages, setup 6 | from setuptools.command.build import build 7 | from setuptools.command.egg_info import egg_info 8 | from wheel.bdist_wheel import bdist_wheel 9 | 10 | sources = [ 11 | "bindings/python/tree_sitter_javascript/binding.c", 12 | "src/parser.c", 13 | ] 14 | if path.exists("src/scanner.c"): 15 | sources.append("src/scanner.c") 16 | 17 | macros: list[tuple[str, str | None]] = [ 18 | ("PY_SSIZE_T_CLEAN", None), 19 | ("TREE_SITTER_HIDE_SYMBOLS", None), 20 | ] 21 | if limited_api := not get_config_var("Py_GIL_DISABLED"): 22 | macros.append(("Py_LIMITED_API", "0x030A0000")) 23 | 24 | if system() != "Windows": 25 | cflags = ["-std=c11", "-fvisibility=hidden"] 26 | else: 27 | cflags = ["/std:c11", "/utf-8"] 28 | 29 | 30 | class Build(build): 31 | def run(self): 32 | if path.isdir("queries"): 33 | dest = path.join(self.build_lib, "tree_sitter_javascript", "queries") 34 | self.copy_tree("queries", dest) 35 | super().run() 36 | 37 | 38 | class BdistWheel(bdist_wheel): 39 | def get_tag(self): 40 | python, abi, platform = super().get_tag() 41 | if python.startswith("cp"): 42 | python, abi = "cp310", "abi3" 43 | return python, abi, platform 44 | 45 | 46 | class EggInfo(egg_info): 47 | def find_sources(self): 48 | super().find_sources() 49 | self.filelist.recursive_include("queries", "*.scm") 50 | self.filelist.include("src/tree_sitter/*.h") 51 | 52 | 53 | setup( 54 | packages=find_packages("bindings/python"), 55 | package_dir={"": "bindings/python"}, 56 | package_data={ 57 | "tree_sitter_javascript": ["*.pyi", "py.typed"], 58 | "tree_sitter_javascript.queries": ["*.scm"], 59 | }, 60 | ext_package="tree_sitter_javascript", 61 | ext_modules=[ 62 | Extension( 63 | name="_binding", 64 | sources=sources, 65 | extra_compile_args=cflags, 66 | define_macros=macros, 67 | include_dirs=["src"], 68 | py_limited_api=limited_api, 69 | ) 70 | ], 71 | cmdclass={ 72 | "build": Build, 73 | "bdist_wheel": BdistWheel, 74 | "egg_info": EggInfo, 75 | }, 76 | zip_safe=False 77 | ) 78 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides JavaScript language support for the [tree-sitter] parsing library. 2 | //! 3 | //! Typically, you will use the [`LANGUAGE`] constant to add this language to a 4 | //! tree-sitter [`Parser`], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = r#" 8 | //! function double(x) { 9 | //! return x * 2; 10 | //! } 11 | //! "#; 12 | //! let mut parser = tree_sitter::Parser::new(); 13 | //! let language = tree_sitter_javascript::LANGUAGE; 14 | //! parser 15 | //! .set_language(&language.into()) 16 | //! .expect("Error loading JavaScript parser"); 17 | //! let tree = parser.parse(code, None).unwrap(); 18 | //! assert!(!tree.root_node().has_error()); 19 | //! ``` 20 | //! 21 | //! [`Parser`]: https://docs.rs/tree-sitter/0.25.8/tree_sitter/struct.Parser.html 22 | //! [tree-sitter]: https://tree-sitter.github.io/ 23 | 24 | use tree_sitter_language::LanguageFn; 25 | 26 | extern "C" { 27 | fn tree_sitter_javascript() -> *const (); 28 | } 29 | 30 | /// The tree-sitter [`LanguageFn`] for this grammar. 31 | pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_javascript) }; 32 | 33 | /// The content of the [`node-types.json`] file for this grammar. 34 | /// 35 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types 36 | pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 37 | 38 | /// The syntax highlighting query for this language. 39 | pub const HIGHLIGHT_QUERY: &str = include_str!("../../queries/highlights.scm"); 40 | 41 | /// The syntax highlighting query for languages injected into this one. 42 | pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm"); 43 | 44 | /// The syntax highlighting query for JSX. 45 | pub const JSX_HIGHLIGHT_QUERY: &str = include_str!("../../queries/highlights-jsx.scm"); 46 | 47 | /// The local-variable syntax highlighting query for this language. 48 | pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm"); 49 | 50 | /// The symbol tagging query for this language. 51 | pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm"); 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | #[test] 56 | fn test_can_load_grammar() { 57 | let mut parser = tree_sitter::Parser::new(); 58 | parser 59 | .set_language(&super::LANGUAGE.into()) 60 | .expect("Error loading JavaScript parser"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /queries/tags.scm: -------------------------------------------------------------------------------- 1 | ( 2 | (comment)* @doc 3 | . 4 | (method_definition 5 | name: (property_identifier) @name) @definition.method 6 | (#not-eq? @name "constructor") 7 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 8 | (#select-adjacent! @doc @definition.method) 9 | ) 10 | 11 | ( 12 | (comment)* @doc 13 | . 14 | [ 15 | (class 16 | name: (_) @name) 17 | (class_declaration 18 | name: (_) @name) 19 | ] @definition.class 20 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 21 | (#select-adjacent! @doc @definition.class) 22 | ) 23 | 24 | ( 25 | (comment)* @doc 26 | . 27 | [ 28 | (function_expression 29 | name: (identifier) @name) 30 | (function_declaration 31 | name: (identifier) @name) 32 | (generator_function 33 | name: (identifier) @name) 34 | (generator_function_declaration 35 | name: (identifier) @name) 36 | ] @definition.function 37 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 38 | (#select-adjacent! @doc @definition.function) 39 | ) 40 | 41 | ( 42 | (comment)* @doc 43 | . 44 | (lexical_declaration 45 | (variable_declarator 46 | name: (identifier) @name 47 | value: [(arrow_function) (function_expression)]) @definition.function) 48 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 49 | (#select-adjacent! @doc @definition.function) 50 | ) 51 | 52 | ( 53 | (comment)* @doc 54 | . 55 | (variable_declaration 56 | (variable_declarator 57 | name: (identifier) @name 58 | value: [(arrow_function) (function_expression)]) @definition.function) 59 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 60 | (#select-adjacent! @doc @definition.function) 61 | ) 62 | 63 | (assignment_expression 64 | left: [ 65 | (identifier) @name 66 | (member_expression 67 | property: (property_identifier) @name) 68 | ] 69 | right: [(arrow_function) (function_expression)] 70 | ) @definition.function 71 | 72 | (pair 73 | key: (property_identifier) @name 74 | value: [(arrow_function) (function_expression)]) @definition.function 75 | 76 | ( 77 | (call_expression 78 | function: (identifier) @name) @reference.call 79 | (#not-match? @name "^(require)$") 80 | ) 81 | 82 | (call_expression 83 | function: (member_expression 84 | property: (property_identifier) @name) 85 | arguments: (_) @reference.call) 86 | 87 | (new_expression 88 | constructor: (_) @name) @reference.class 89 | 90 | (export_statement value: (assignment_expression left: (identifier) @name right: ([ 91 | (number) 92 | (string) 93 | (identifier) 94 | (undefined) 95 | (null) 96 | (new_expression) 97 | (binary_expression) 98 | (call_expression) 99 | ]))) @definition.constant 100 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(tree-sitter-javascript 4 | VERSION "0.25.0" 5 | DESCRIPTION "JavaScript grammar for tree-sitter" 6 | HOMEPAGE_URL "https://github.com/tree-sitter/tree-sitter-javascript" 7 | LANGUAGES C) 8 | 9 | option(BUILD_SHARED_LIBS "Build using shared libraries" ON) 10 | option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF) 11 | 12 | set(TREE_SITTER_ABI_VERSION 15 CACHE STRING "Tree-sitter ABI version") 13 | if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$") 14 | unset(TREE_SITTER_ABI_VERSION CACHE) 15 | message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer") 16 | endif() 17 | 18 | include(GNUInstallDirs) 19 | 20 | find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") 21 | 22 | add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 23 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" 24 | COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json 25 | --abi=${TREE_SITTER_ABI_VERSION} 26 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 27 | COMMENT "Generating parser.c") 28 | 29 | add_library(tree-sitter-javascript src/parser.c) 30 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) 31 | target_sources(tree-sitter-javascript PRIVATE src/scanner.c) 32 | endif() 33 | target_include_directories(tree-sitter-javascript 34 | PRIVATE src 35 | INTERFACE $ 36 | $) 37 | 38 | target_compile_definitions(tree-sitter-javascript PRIVATE 39 | $<$:TREE_SITTER_REUSE_ALLOCATOR> 40 | $<$:TREE_SITTER_DEBUG>) 41 | 42 | set_target_properties(tree-sitter-javascript 43 | PROPERTIES 44 | C_STANDARD 11 45 | POSITION_INDEPENDENT_CODE ON 46 | SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" 47 | DEFINE_SYMBOL "") 48 | 49 | configure_file(bindings/c/tree-sitter-javascript.pc.in 50 | "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-javascript.pc" @ONLY) 51 | 52 | install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bindings/c/tree_sitter" 53 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 54 | FILES_MATCHING PATTERN "*.h") 55 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-javascript.pc" 56 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 57 | install(TARGETS tree-sitter-javascript 58 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") 59 | 60 | file(GLOB QUERIES queries/*.scm) 61 | install(FILES ${QUERIES} 62 | DESTINATION "${CMAKE_INSTALL_DATADIR}/tree-sitter/queries/javascript") 63 | 64 | add_custom_target(ts-test "${TREE_SITTER_CLI}" test 65 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 66 | COMMENT "tree-sitter test") 67 | -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ; Variables 2 | ;---------- 3 | 4 | (identifier) @variable 5 | 6 | ; Properties 7 | ;----------- 8 | 9 | (property_identifier) @property 10 | 11 | ; Function and method definitions 12 | ;-------------------------------- 13 | 14 | (function_expression 15 | name: (identifier) @function) 16 | (function_declaration 17 | name: (identifier) @function) 18 | (method_definition 19 | name: (property_identifier) @function.method) 20 | 21 | (pair 22 | key: (property_identifier) @function.method 23 | value: [(function_expression) (arrow_function)]) 24 | 25 | (assignment_expression 26 | left: (member_expression 27 | property: (property_identifier) @function.method) 28 | right: [(function_expression) (arrow_function)]) 29 | 30 | (variable_declarator 31 | name: (identifier) @function 32 | value: [(function_expression) (arrow_function)]) 33 | 34 | (assignment_expression 35 | left: (identifier) @function 36 | right: [(function_expression) (arrow_function)]) 37 | 38 | ; Function and method calls 39 | ;-------------------------- 40 | 41 | (call_expression 42 | function: (identifier) @function) 43 | 44 | (call_expression 45 | function: (member_expression 46 | property: (property_identifier) @function.method)) 47 | 48 | ; Special identifiers 49 | ;-------------------- 50 | 51 | ((identifier) @constructor 52 | (#match? @constructor "^[A-Z]")) 53 | 54 | ([ 55 | (identifier) 56 | (shorthand_property_identifier) 57 | (shorthand_property_identifier_pattern) 58 | ] @constant 59 | (#match? @constant "^[A-Z_][A-Z\\d_]+$")) 60 | 61 | ((identifier) @variable.builtin 62 | (#match? @variable.builtin "^(arguments|module|console|window|document)$") 63 | (#is-not? local)) 64 | 65 | ((identifier) @function.builtin 66 | (#eq? @function.builtin "require") 67 | (#is-not? local)) 68 | 69 | ; Literals 70 | ;--------- 71 | 72 | (this) @variable.builtin 73 | (super) @variable.builtin 74 | 75 | [ 76 | (true) 77 | (false) 78 | (null) 79 | (undefined) 80 | ] @constant.builtin 81 | 82 | (comment) @comment 83 | 84 | [ 85 | (string) 86 | (template_string) 87 | ] @string 88 | 89 | (regex) @string.special 90 | (number) @number 91 | 92 | ; Tokens 93 | ;------- 94 | 95 | [ 96 | ";" 97 | (optional_chain) 98 | "." 99 | "," 100 | ] @punctuation.delimiter 101 | 102 | [ 103 | "-" 104 | "--" 105 | "-=" 106 | "+" 107 | "++" 108 | "+=" 109 | "*" 110 | "*=" 111 | "**" 112 | "**=" 113 | "/" 114 | "/=" 115 | "%" 116 | "%=" 117 | "<" 118 | "<=" 119 | "<<" 120 | "<<=" 121 | "=" 122 | "==" 123 | "===" 124 | "!" 125 | "!=" 126 | "!==" 127 | "=>" 128 | ">" 129 | ">=" 130 | ">>" 131 | ">>=" 132 | ">>>" 133 | ">>>=" 134 | "~" 135 | "^" 136 | "&" 137 | "|" 138 | "^=" 139 | "&=" 140 | "|=" 141 | "&&" 142 | "||" 143 | "??" 144 | "&&=" 145 | "||=" 146 | "??=" 147 | ] @operator 148 | 149 | [ 150 | "(" 151 | ")" 152 | "[" 153 | "]" 154 | "{" 155 | "}" 156 | ] @punctuation.bracket 157 | 158 | (template_substitution 159 | "${" @punctuation.special 160 | "}" @punctuation.special) @embedded 161 | 162 | [ 163 | "as" 164 | "async" 165 | "await" 166 | "break" 167 | "case" 168 | "catch" 169 | "class" 170 | "const" 171 | "continue" 172 | "debugger" 173 | "default" 174 | "delete" 175 | "do" 176 | "else" 177 | "export" 178 | "extends" 179 | "finally" 180 | "for" 181 | "from" 182 | "function" 183 | "get" 184 | "if" 185 | "import" 186 | "in" 187 | "instanceof" 188 | "let" 189 | "new" 190 | "of" 191 | "return" 192 | "set" 193 | "static" 194 | "switch" 195 | "target" 196 | "throw" 197 | "try" 198 | "typeof" 199 | "var" 200 | "void" 201 | "while" 202 | "with" 203 | "yield" 204 | ] @keyword 205 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | $(error Windows is not supported) 3 | endif 4 | 5 | LANGUAGE_NAME := tree-sitter-javascript 6 | HOMEPAGE_URL := https://github.com/tree-sitter/tree-sitter-javascript 7 | VERSION := 0.25.0 8 | 9 | # repository 10 | SRC_DIR := src 11 | 12 | TS ?= tree-sitter 13 | 14 | # install directory layout 15 | PREFIX ?= /usr/local 16 | DATADIR ?= $(PREFIX)/share 17 | INCLUDEDIR ?= $(PREFIX)/include 18 | LIBDIR ?= $(PREFIX)/lib 19 | PCLIBDIR ?= $(LIBDIR)/pkgconfig 20 | 21 | # source/object files 22 | PARSER := $(SRC_DIR)/parser.c 23 | EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c)) 24 | OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS)) 25 | 26 | # flags 27 | ARFLAGS ?= rcs 28 | override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC 29 | 30 | # ABI versioning 31 | SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER)) 32 | SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION))) 33 | 34 | # OS-specific bits 35 | ifeq ($(shell uname),Darwin) 36 | SOEXT = dylib 37 | SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT) 38 | SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT) 39 | LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks 40 | else 41 | SOEXT = so 42 | SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) 43 | SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) 44 | LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) 45 | endif 46 | ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) 47 | PCLIBDIR := $(PREFIX)/libdata/pkgconfig 48 | endif 49 | 50 | all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc 51 | 52 | lib$(LANGUAGE_NAME).a: $(OBJS) 53 | $(AR) $(ARFLAGS) $@ $^ 54 | 55 | lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS) 56 | $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@ 57 | ifneq ($(STRIP),) 58 | $(STRIP) $@ 59 | endif 60 | 61 | $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in 62 | sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \ 63 | -e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \ 64 | -e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \ 65 | -e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \ 66 | -e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \ 67 | -e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@ 68 | 69 | $(PARSER): $(SRC_DIR)/grammar.json 70 | $(TS) generate $^ 71 | 72 | install: all 73 | install -d '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/javascript '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)' 74 | install -m644 bindings/c/tree_sitter/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h 75 | install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 76 | install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a 77 | install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) 78 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) 79 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) 80 | ifneq ($(wildcard queries/*.scm),) 81 | install -m644 queries/*.scm '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/javascript 82 | endif 83 | 84 | uninstall: 85 | $(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \ 86 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \ 87 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \ 88 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \ 89 | '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \ 90 | '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 91 | $(RM) -r '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/javascript 92 | 93 | clean: 94 | $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) 95 | 96 | test: 97 | $(TS) test 98 | 99 | .PHONY: all install uninstall clean test 100 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= 4 | github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 8 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 9 | github.com/tree-sitter/go-tree-sitter v0.24.0 h1:kRZb6aBNfcI/u0Qh8XEt3zjNVnmxTisDBN+kXK0xRYQ= 10 | github.com/tree-sitter/go-tree-sitter v0.24.0/go.mod h1:x681iFVoLMEwOSIHA1chaLkXlroXEN7WY+VHGFaoDbk= 11 | github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb h1:A8425heRM8mylnv4H58FPUiH+aYivyitre0PzxrfmWs= 12 | github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb/go.mod h1:dOF6gtQiF9UwNh995T5OphYmtIypkjsp3ap7r9AN/iA= 13 | github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148 h1:AfFPZwtwGN01BW1jDdqBVqscTwetvMpydqYZz57RSlc= 14 | github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148/go.mod h1:Bh6U3viD57rFXRYIQ+kmiYtr+1Bx0AceypDLJJSyi9s= 15 | github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33 h1:TwqSV3qLp3tKSqirGLRHnjFk9Tc2oy57LIl+FQ4GjI4= 16 | github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33/go.mod h1:CvCKCt3v04Ufos1zZnNCelBDeCGRpPucaN8QczoUsN4= 17 | github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012 h1:Xvxck3tE5FW7F7bTS97iNM2ADMyCMJztVqn5HYKdJGo= 18 | github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012/go.mod h1:T40D0O1cPvUU/+AmiXVXy1cncYQT6wem4Z0g4SfAYvY= 19 | github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0 h1:c46K6uh5Dz00zJeU9BfjXdb8I+E4RkUdfnWJpQADXFo= 20 | github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0/go.mod h1:hcNt/kOJHcIcuMvouE7LJcYdeFUFbVpBJ6d4wmOA+tU= 21 | github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495 h1:jrt4qbJVEFs4H93/ITxygHc6u0TGqAkkate7TQ4wFSA= 22 | github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495/go.mod h1:oyaR7fLnRV0hT9z6qwE9GkaeTom/hTDwK3H2idcOJFc= 23 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5 h1:pfV3G3k7NCKqKk8THBmyuh2zA33lgYHS3GVrzRR8ry4= 24 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5/go.mod h1:GbMKRjLfk0H+PI7nLi1Sx5lHf5wCpLz9al8tQYSxpEk= 25 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1 h1:ZXZMDwE+IhUtGug4Brv6NjJWUU3rfkZBKpemf6RY8/g= 26 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1/go.mod h1:UKCLuYnJ312Mei+3cyTmGOHzn0YAnaPRECgJmHtzrqs= 27 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb h1:EXEM82lFM7JjJb6qiKZXkpIDaCcbV2obNn82ghwj9lw= 28 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb/go.mod h1:lXCF1nGG5Dr4J3BTS0ObN4xJCCICiSu/b+Xe/VqMV7g= 29 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d h1:fcYCvoXdcP1uRQYXqJHRy6Hec+uKScQdKVtMwK9JeCI= 30 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d/go.mod h1:T1nShQ4v5AJtozZ8YyAS4uzUtDAJj/iv4YfwXSbUHzg= 31 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447 h1:o9alBu1J/WjrcTKEthYtXmdkDc5OVXD+PqlvnEZ0Lzc= 32 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447/go.mod h1:1Oh95COkkTn6Ezp0vcMbvfhRP5gLeqqljR0BYnBzWvc= 33 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 34 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 35 | -------------------------------------------------------------------------------- /test/corpus/literals.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Numbers 3 | ============================================ 4 | 5 | 04000 6 | 400 7 | 100n 8 | 0xffffffffn 9 | 0b00111n 10 | 0o1234n 11 | 0xa_b_c 12 | 0o1_1 13 | 0b1_000_000 14 | 1_2_3 15 | 12_3.4_5e6_7 16 | .4_5e6_7 17 | 0b1_000_000n 18 | 01 19 | 00000123 20 | 21 | --- 22 | 23 | (program 24 | (expression_statement (number)) 25 | (expression_statement (number)) 26 | (expression_statement (number)) 27 | (expression_statement (number)) 28 | (expression_statement (number)) 29 | (expression_statement (number)) 30 | (expression_statement (number)) 31 | (expression_statement (number)) 32 | (expression_statement (number)) 33 | (expression_statement (number)) 34 | (expression_statement (number)) 35 | (expression_statement (number)) 36 | (expression_statement (number)) 37 | (expression_statement (number)) 38 | (expression_statement (number))) 39 | 40 | ============================================ 41 | Unicode identifiers 42 | ============================================ 43 | 44 | const últimaVez = 1 45 | { 県: '大阪府', '': '' } 46 | 47 | --- 48 | 49 | (program 50 | (lexical_declaration (variable_declarator (identifier) (number))) 51 | (expression_statement 52 | (object 53 | (pair (property_identifier) (string (string_fragment))) 54 | (pair (string) (string))))) 55 | 56 | ========================================== 57 | Strings containing comment-like content 58 | ========================================== 59 | 60 | "//ok\n//what" 61 | 62 | --- 63 | 64 | (program 65 | (expression_statement 66 | (string (string_fragment) (escape_sequence) (string_fragment)))) 67 | 68 | ========================================== 69 | Quote escaping 70 | ========================================== 71 | 72 | ""; 73 | ''; 74 | "\""; 75 | "a\"b"; 76 | '\''; 77 | 'a\'b'; 78 | "it's a tiny tiny world"; 79 | '"hello"'; 80 | 81 | --- 82 | 83 | (program 84 | (expression_statement (string)) 85 | (expression_statement (string)) 86 | (expression_statement (string (escape_sequence))) 87 | (expression_statement 88 | (string (string_fragment) (escape_sequence) (string_fragment))) 89 | (expression_statement (string (escape_sequence))) 90 | (expression_statement 91 | (string (string_fragment) (escape_sequence) (string_fragment))) 92 | (expression_statement (string (string_fragment))) 93 | (expression_statement (string (string_fragment)))) 94 | 95 | ========================================== 96 | Line continuations 97 | ========================================== 98 | 99 | "hello\ 100 | world"; 101 | 102 | 'hello\ 103 | world'; 104 | 105 | --- 106 | 107 | (program 108 | (expression_statement 109 | (string (string_fragment) (escape_sequence) (string_fragment))) 110 | (expression_statement 111 | (string (string_fragment) (escape_sequence) (string_fragment)))) 112 | 113 | ========================================================= 114 | JSX strings with unescaped newlines for TSX attributes 115 | ========================================================= 116 | 117 | ; 119 | 120 | ; 122 | 123 | --- 124 | 125 | (program 126 | (expression_statement 127 | (jsx_element 128 | (jsx_opening_element 129 | (identifier) 130 | (jsx_attribute (property_identifier) (string (string_fragment)))) 131 | (jsx_closing_element 132 | (identifier)))) 133 | (expression_statement 134 | (jsx_element 135 | (jsx_opening_element 136 | (identifier) 137 | (jsx_attribute (property_identifier) (string (string_fragment)))) 138 | (jsx_closing_element 139 | (identifier))))) 140 | 141 | =============================================== 142 | JSX with HTML character references (entities) 143 | =============================================== 144 | 145 | foo   bar; 146 | 147 | foo; 148 | 149 | ---- 150 | 151 | (program 152 | (expression_statement 153 | (jsx_element 154 | (jsx_opening_element 155 | (identifier)) 156 | (jsx_text) 157 | (html_character_reference) 158 | (jsx_text) 159 | (jsx_closing_element 160 | (identifier)))) 161 | (expression_statement 162 | (jsx_element 163 | (jsx_opening_element 164 | (identifier) 165 | (jsx_attribute 166 | (property_identifier) 167 | (string 168 | (string_fragment) 169 | (html_character_reference) 170 | (string_fragment)))) 171 | (jsx_text) 172 | (jsx_closing_element 173 | (identifier))))) 174 | -------------------------------------------------------------------------------- /test/corpus/destructuring.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Object destructuring assignments 3 | ============================================ 4 | 5 | ({a, b: c.d, ...e[f]} = object); 6 | let {a, b, ...c} = object 7 | const {a, b: {c, d}} = object 8 | 9 | --- 10 | 11 | (program 12 | (expression_statement (parenthesized_expression (assignment_expression 13 | (object_pattern 14 | (shorthand_property_identifier_pattern) 15 | (pair_pattern 16 | (property_identifier) 17 | (member_expression (identifier) (property_identifier))) 18 | (rest_pattern (subscript_expression (identifier) (identifier)))) 19 | (identifier)))) 20 | (lexical_declaration (variable_declarator 21 | (object_pattern 22 | (shorthand_property_identifier_pattern) 23 | (shorthand_property_identifier_pattern) 24 | (rest_pattern (identifier))) 25 | (identifier))) 26 | (lexical_declaration (variable_declarator 27 | (object_pattern 28 | (shorthand_property_identifier_pattern) 29 | (pair_pattern 30 | (property_identifier) 31 | (object_pattern 32 | (shorthand_property_identifier_pattern) 33 | (shorthand_property_identifier_pattern)))) 34 | (identifier)))) 35 | 36 | ============================================ 37 | Object destructuring parameters 38 | ============================================ 39 | 40 | function a ({b, c}, {d}) {} 41 | 42 | --- 43 | 44 | (program 45 | (function_declaration (identifier) 46 | (formal_parameters 47 | (object_pattern (shorthand_property_identifier_pattern) (shorthand_property_identifier_pattern)) 48 | (object_pattern (shorthand_property_identifier_pattern))) 49 | (statement_block))) 50 | 51 | ============================================ 52 | Array destructuring assignments 53 | ============================================ 54 | 55 | [a, b.c, ...c[d]] = array; 56 | [a, b, ...c] = array; 57 | [,, c,, d,] = array; 58 | 59 | --- 60 | 61 | (program 62 | (expression_statement (assignment_expression 63 | (array_pattern 64 | (identifier) 65 | (member_expression (identifier) (property_identifier)) 66 | (rest_pattern (subscript_expression (identifier) (identifier)))) 67 | (identifier))) 68 | (expression_statement (assignment_expression 69 | (array_pattern 70 | (identifier) 71 | (identifier) 72 | (rest_pattern (identifier))) 73 | (identifier))) 74 | (expression_statement (assignment_expression 75 | (array_pattern 76 | (identifier) 77 | (identifier)) 78 | (identifier)))) 79 | 80 | ================================================ 81 | Object destructuring patterns w/ default values 82 | ================================================ 83 | 84 | let {a: b = c} = object; 85 | for await (var {a: {b} = object} of asyncIter) {} 86 | function a({b = true}, [c, d = false]) {} 87 | function b({c} = {}) {} 88 | 89 | --- 90 | 91 | (program 92 | (lexical_declaration (variable_declarator 93 | (object_pattern (pair_pattern 94 | (property_identifier) 95 | (assignment_pattern (identifier) (identifier)))) 96 | (identifier))) 97 | (for_in_statement 98 | (object_pattern (pair_pattern 99 | (property_identifier) 100 | (assignment_pattern (object_pattern (shorthand_property_identifier_pattern)) (identifier)))) 101 | (identifier) 102 | (statement_block)) 103 | (function_declaration (identifier) 104 | (formal_parameters 105 | (object_pattern (object_assignment_pattern (shorthand_property_identifier_pattern) (true))) 106 | (array_pattern (identifier) (assignment_pattern (identifier) (false)))) 107 | (statement_block)) 108 | (function_declaration (identifier) 109 | (formal_parameters 110 | (assignment_pattern (object_pattern (shorthand_property_identifier_pattern)) (object))) 111 | (statement_block))) 112 | 113 | =================================================== 114 | Extra complex literals in expressions 115 | =================================================== 116 | 117 | if ({a: 'b'} {c: 'd'}) { 118 | x = function(a) { b; } function(c) { d; } 119 | } 120 | 121 | --- 122 | 123 | (program 124 | (if_statement 125 | (parenthesized_expression 126 | (ERROR (object (pair (property_identifier) (string (string_fragment))))) 127 | (object (pair (property_identifier) (string (string_fragment))))) 128 | (statement_block 129 | (expression_statement 130 | (assignment_expression 131 | (identifier) 132 | (function_expression (formal_parameters (identifier)) (statement_block (expression_statement (identifier))))) 133 | (MISSING ";")) 134 | (expression_statement 135 | (function_expression (formal_parameters (identifier)) (statement_block (expression_statement (identifier)))))))) 136 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "cc" 16 | version = "1.2.34" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" 19 | dependencies = [ 20 | "shlex", 21 | ] 22 | 23 | [[package]] 24 | name = "equivalent" 25 | version = "1.0.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 28 | 29 | [[package]] 30 | name = "hashbrown" 31 | version = "0.15.5" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 34 | 35 | [[package]] 36 | name = "indexmap" 37 | version = "2.11.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" 40 | dependencies = [ 41 | "equivalent", 42 | "hashbrown", 43 | ] 44 | 45 | [[package]] 46 | name = "itoa" 47 | version = "1.0.15" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 50 | 51 | [[package]] 52 | name = "memchr" 53 | version = "2.7.5" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 56 | 57 | [[package]] 58 | name = "proc-macro2" 59 | version = "1.0.101" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 62 | dependencies = [ 63 | "unicode-ident", 64 | ] 65 | 66 | [[package]] 67 | name = "quote" 68 | version = "1.0.40" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 71 | dependencies = [ 72 | "proc-macro2", 73 | ] 74 | 75 | [[package]] 76 | name = "regex" 77 | version = "1.11.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" 80 | dependencies = [ 81 | "aho-corasick", 82 | "memchr", 83 | "regex-automata", 84 | "regex-syntax", 85 | ] 86 | 87 | [[package]] 88 | name = "regex-automata" 89 | version = "0.4.10" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" 92 | dependencies = [ 93 | "aho-corasick", 94 | "memchr", 95 | "regex-syntax", 96 | ] 97 | 98 | [[package]] 99 | name = "regex-syntax" 100 | version = "0.8.6" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" 103 | 104 | [[package]] 105 | name = "ryu" 106 | version = "1.0.20" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 109 | 110 | [[package]] 111 | name = "serde" 112 | version = "1.0.219" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 115 | dependencies = [ 116 | "serde_derive", 117 | ] 118 | 119 | [[package]] 120 | name = "serde_derive" 121 | version = "1.0.219" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 124 | dependencies = [ 125 | "proc-macro2", 126 | "quote", 127 | "syn", 128 | ] 129 | 130 | [[package]] 131 | name = "serde_json" 132 | version = "1.0.143" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" 135 | dependencies = [ 136 | "indexmap", 137 | "itoa", 138 | "memchr", 139 | "ryu", 140 | "serde", 141 | ] 142 | 143 | [[package]] 144 | name = "shlex" 145 | version = "1.3.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 148 | 149 | [[package]] 150 | name = "streaming-iterator" 151 | version = "0.1.9" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" 154 | 155 | [[package]] 156 | name = "syn" 157 | version = "2.0.106" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 160 | dependencies = [ 161 | "proc-macro2", 162 | "quote", 163 | "unicode-ident", 164 | ] 165 | 166 | [[package]] 167 | name = "tree-sitter" 168 | version = "0.25.8" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "6d7b8994f367f16e6fa14b5aebbcb350de5d7cbea82dc5b00ae997dd71680dd2" 171 | dependencies = [ 172 | "cc", 173 | "regex", 174 | "regex-syntax", 175 | "serde_json", 176 | "streaming-iterator", 177 | "tree-sitter-language", 178 | ] 179 | 180 | [[package]] 181 | name = "tree-sitter-javascript" 182 | version = "0.25.0" 183 | dependencies = [ 184 | "cc", 185 | "tree-sitter", 186 | "tree-sitter-language", 187 | ] 188 | 189 | [[package]] 190 | name = "tree-sitter-language" 191 | version = "0.1.5" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" 194 | 195 | [[package]] 196 | name = "unicode-ident" 197 | version = "1.0.18" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 200 | -------------------------------------------------------------------------------- /src/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | #ifndef TREE_SITTER_API_H_ 17 | typedef uint16_t TSStateId; 18 | typedef uint16_t TSSymbol; 19 | typedef uint16_t TSFieldId; 20 | typedef struct TSLanguage TSLanguage; 21 | typedef struct TSLanguageMetadata { 22 | uint8_t major_version; 23 | uint8_t minor_version; 24 | uint8_t patch_version; 25 | } TSLanguageMetadata; 26 | #endif 27 | 28 | typedef struct { 29 | TSFieldId field_id; 30 | uint8_t child_index; 31 | bool inherited; 32 | } TSFieldMapEntry; 33 | 34 | // Used to index the field and supertype maps. 35 | typedef struct { 36 | uint16_t index; 37 | uint16_t length; 38 | } TSMapSlice; 39 | 40 | typedef struct { 41 | bool visible; 42 | bool named; 43 | bool supertype; 44 | } TSSymbolMetadata; 45 | 46 | typedef struct TSLexer TSLexer; 47 | 48 | struct TSLexer { 49 | int32_t lookahead; 50 | TSSymbol result_symbol; 51 | void (*advance)(TSLexer *, bool); 52 | void (*mark_end)(TSLexer *); 53 | uint32_t (*get_column)(TSLexer *); 54 | bool (*is_at_included_range_start)(const TSLexer *); 55 | bool (*eof)(const TSLexer *); 56 | void (*log)(const TSLexer *, const char *, ...); 57 | }; 58 | 59 | typedef enum { 60 | TSParseActionTypeShift, 61 | TSParseActionTypeReduce, 62 | TSParseActionTypeAccept, 63 | TSParseActionTypeRecover, 64 | } TSParseActionType; 65 | 66 | typedef union { 67 | struct { 68 | uint8_t type; 69 | TSStateId state; 70 | bool extra; 71 | bool repetition; 72 | } shift; 73 | struct { 74 | uint8_t type; 75 | uint8_t child_count; 76 | TSSymbol symbol; 77 | int16_t dynamic_precedence; 78 | uint16_t production_id; 79 | } reduce; 80 | uint8_t type; 81 | } TSParseAction; 82 | 83 | typedef struct { 84 | uint16_t lex_state; 85 | uint16_t external_lex_state; 86 | } TSLexMode; 87 | 88 | typedef struct { 89 | uint16_t lex_state; 90 | uint16_t external_lex_state; 91 | uint16_t reserved_word_set_id; 92 | } TSLexerMode; 93 | 94 | typedef union { 95 | TSParseAction action; 96 | struct { 97 | uint8_t count; 98 | bool reusable; 99 | } entry; 100 | } TSParseActionEntry; 101 | 102 | typedef struct { 103 | int32_t start; 104 | int32_t end; 105 | } TSCharacterRange; 106 | 107 | struct TSLanguage { 108 | uint32_t abi_version; 109 | uint32_t symbol_count; 110 | uint32_t alias_count; 111 | uint32_t token_count; 112 | uint32_t external_token_count; 113 | uint32_t state_count; 114 | uint32_t large_state_count; 115 | uint32_t production_id_count; 116 | uint32_t field_count; 117 | uint16_t max_alias_sequence_length; 118 | const uint16_t *parse_table; 119 | const uint16_t *small_parse_table; 120 | const uint32_t *small_parse_table_map; 121 | const TSParseActionEntry *parse_actions; 122 | const char * const *symbol_names; 123 | const char * const *field_names; 124 | const TSMapSlice *field_map_slices; 125 | const TSFieldMapEntry *field_map_entries; 126 | const TSSymbolMetadata *symbol_metadata; 127 | const TSSymbol *public_symbol_map; 128 | const uint16_t *alias_map; 129 | const TSSymbol *alias_sequences; 130 | const TSLexerMode *lex_modes; 131 | bool (*lex_fn)(TSLexer *, TSStateId); 132 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 133 | TSSymbol keyword_capture_token; 134 | struct { 135 | const bool *states; 136 | const TSSymbol *symbol_map; 137 | void *(*create)(void); 138 | void (*destroy)(void *); 139 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 140 | unsigned (*serialize)(void *, char *); 141 | void (*deserialize)(void *, const char *, unsigned); 142 | } external_scanner; 143 | const TSStateId *primary_state_ids; 144 | const char *name; 145 | const TSSymbol *reserved_words; 146 | uint16_t max_reserved_word_set_size; 147 | uint32_t supertype_count; 148 | const TSSymbol *supertype_symbols; 149 | const TSMapSlice *supertype_map_slices; 150 | const TSSymbol *supertype_map_entries; 151 | TSLanguageMetadata metadata; 152 | }; 153 | 154 | static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { 155 | uint32_t index = 0; 156 | uint32_t size = len - index; 157 | while (size > 1) { 158 | uint32_t half_size = size / 2; 159 | uint32_t mid_index = index + half_size; 160 | const TSCharacterRange *range = &ranges[mid_index]; 161 | if (lookahead >= range->start && lookahead <= range->end) { 162 | return true; 163 | } else if (lookahead > range->end) { 164 | index = mid_index; 165 | } 166 | size -= half_size; 167 | } 168 | const TSCharacterRange *range = &ranges[index]; 169 | return (lookahead >= range->start && lookahead <= range->end); 170 | } 171 | 172 | /* 173 | * Lexer Macros 174 | */ 175 | 176 | #ifdef _MSC_VER 177 | #define UNUSED __pragma(warning(suppress : 4101)) 178 | #else 179 | #define UNUSED __attribute__((unused)) 180 | #endif 181 | 182 | #define START_LEXER() \ 183 | bool result = false; \ 184 | bool skip = false; \ 185 | UNUSED \ 186 | bool eof = false; \ 187 | int32_t lookahead; \ 188 | goto start; \ 189 | next_state: \ 190 | lexer->advance(lexer, skip); \ 191 | start: \ 192 | skip = false; \ 193 | lookahead = lexer->lookahead; 194 | 195 | #define ADVANCE(state_value) \ 196 | { \ 197 | state = state_value; \ 198 | goto next_state; \ 199 | } 200 | 201 | #define ADVANCE_MAP(...) \ 202 | { \ 203 | static const uint16_t map[] = { __VA_ARGS__ }; \ 204 | for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \ 205 | if (map[i] == lookahead) { \ 206 | state = map[i + 1]; \ 207 | goto next_state; \ 208 | } \ 209 | } \ 210 | } 211 | 212 | #define SKIP(state_value) \ 213 | { \ 214 | skip = true; \ 215 | state = state_value; \ 216 | goto next_state; \ 217 | } 218 | 219 | #define ACCEPT_TOKEN(symbol_value) \ 220 | result = true; \ 221 | lexer->result_symbol = symbol_value; \ 222 | lexer->mark_end(lexer); 223 | 224 | #define END_STATE() return result; 225 | 226 | /* 227 | * Parse Table Macros 228 | */ 229 | 230 | #define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT) 231 | 232 | #define STATE(id) id 233 | 234 | #define ACTIONS(id) id 235 | 236 | #define SHIFT(state_value) \ 237 | {{ \ 238 | .shift = { \ 239 | .type = TSParseActionTypeShift, \ 240 | .state = (state_value) \ 241 | } \ 242 | }} 243 | 244 | #define SHIFT_REPEAT(state_value) \ 245 | {{ \ 246 | .shift = { \ 247 | .type = TSParseActionTypeShift, \ 248 | .state = (state_value), \ 249 | .repetition = true \ 250 | } \ 251 | }} 252 | 253 | #define SHIFT_EXTRA() \ 254 | {{ \ 255 | .shift = { \ 256 | .type = TSParseActionTypeShift, \ 257 | .extra = true \ 258 | } \ 259 | }} 260 | 261 | #define REDUCE(symbol_name, children, precedence, prod_id) \ 262 | {{ \ 263 | .reduce = { \ 264 | .type = TSParseActionTypeReduce, \ 265 | .symbol = symbol_name, \ 266 | .child_count = children, \ 267 | .dynamic_precedence = precedence, \ 268 | .production_id = prod_id \ 269 | }, \ 270 | }} 271 | 272 | #define RECOVER() \ 273 | {{ \ 274 | .type = TSParseActionTypeRecover \ 275 | }} 276 | 277 | #define ACCEPT_INPUT() \ 278 | {{ \ 279 | .type = TSParseActionTypeAccept \ 280 | }} 281 | 282 | #ifdef __cplusplus 283 | } 284 | #endif 285 | 286 | #endif // TREE_SITTER_PARSER_H_ 287 | -------------------------------------------------------------------------------- /test/corpus/semicolon_insertion.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Automatic semicolon insertion 3 | ============================================ 4 | 5 | if (a) { 6 | var b = c 7 | d() 8 | e() 9 | return f 10 | } 11 | 12 | --- 13 | 14 | (program 15 | (if_statement 16 | (parenthesized_expression (identifier)) 17 | (statement_block 18 | (variable_declaration (variable_declarator (identifier) (identifier))) 19 | (expression_statement (call_expression (identifier) (arguments))) 20 | (expression_statement (call_expression (identifier) (arguments))) 21 | (return_statement (identifier))))) 22 | 23 | ============================================ 24 | Semicolon insertion before update expressions 25 | ============================================ 26 | 27 | if (a) 28 | d() 29 | ++b 30 | 31 | if (a) 32 | d() 33 | --b 34 | 35 | --- 36 | (program 37 | (if_statement 38 | (parenthesized_expression (identifier)) 39 | (expression_statement 40 | (call_expression (identifier) (arguments)))) 41 | (expression_statement (update_expression (identifier))) 42 | 43 | (if_statement 44 | (parenthesized_expression (identifier)) 45 | (expression_statement 46 | (call_expression (identifier) (arguments)))) 47 | (expression_statement (update_expression (identifier)))) 48 | 49 | ========================================== 50 | property access across lines 51 | ========================================== 52 | 53 | object 54 | .someProperty 55 | .otherProperty 56 | 57 | --- 58 | 59 | (program (expression_statement 60 | (member_expression 61 | (member_expression (identifier) (property_identifier)) 62 | (property_identifier)))) 63 | 64 | =========================================== 65 | indented code after blocks 66 | =========================================== 67 | 68 | function x() {} 69 | return z; 70 | 71 | --- 72 | 73 | (program 74 | (function_declaration 75 | (identifier) 76 | (formal_parameters) 77 | (statement_block)) 78 | (return_statement (identifier))) 79 | 80 | ================================================ 81 | operator expressions split across lines 82 | ================================================ 83 | 84 | a 85 | ? b 86 | : c 87 | 88 | a 89 | || b 90 | 91 | a 92 | ^ b 93 | 94 | a 95 | !== b 96 | 97 | a 98 | !b; // standalone statement 99 | 100 | --- 101 | 102 | (program 103 | (expression_statement (ternary_expression (identifier) (identifier) (identifier))) 104 | (expression_statement (binary_expression (identifier) (identifier))) 105 | (expression_statement (binary_expression (identifier) (identifier))) 106 | (expression_statement (binary_expression (identifier) (identifier))) 107 | (expression_statement (identifier)) 108 | (expression_statement (unary_expression (identifier))) 109 | (comment)) 110 | 111 | ================================================ 112 | Alphabetical infix operators split across lines 113 | ================================================ 114 | 115 | a 116 | i; 117 | 118 | a 119 | in b; 120 | 121 | a 122 | ins; 123 | 124 | a 125 | inst; 126 | 127 | a 128 | instanceof b; 129 | 130 | a 131 | instanceofX; 132 | 133 | --- 134 | 135 | (program 136 | (expression_statement (identifier)) 137 | (expression_statement (identifier)) 138 | 139 | (expression_statement (binary_expression (identifier) (identifier))) 140 | 141 | (expression_statement (identifier)) 142 | (expression_statement (identifier)) 143 | 144 | (expression_statement (identifier)) 145 | (expression_statement (identifier)) 146 | 147 | (expression_statement (binary_expression (identifier) (identifier))) 148 | 149 | (expression_statement (identifier)) 150 | (expression_statement (identifier))) 151 | 152 | =========================================== 153 | Single-line if/else statements 154 | =========================================== 155 | 156 | if (a) {b} else {c} 157 | 158 | --- 159 | 160 | (program 161 | (if_statement (parenthesized_expression (identifier)) 162 | (statement_block (expression_statement (identifier))) 163 | (else_clause 164 | (statement_block (expression_statement (identifier)))))) 165 | 166 | =========================================== 167 | single-line blocks without semicolons 168 | =========================================== 169 | 170 | function a() {b} 171 | function c() {return d} 172 | 173 | --- 174 | 175 | (program 176 | (function_declaration (identifier) (formal_parameters) (statement_block 177 | (expression_statement (identifier)))) 178 | (function_declaration (identifier) (formal_parameters) (statement_block 179 | (return_statement (identifier))))) 180 | 181 | ============================================== 182 | Multi-line chained expressions in var declarations 183 | ============================================== 184 | 185 | var a = new A() 186 | .b({c: 'd'}) 187 | .e() 188 | 189 | --- 190 | 191 | (program 192 | (variable_declaration (variable_declarator 193 | (identifier) 194 | (call_expression 195 | (member_expression 196 | (call_expression 197 | (member_expression 198 | (new_expression (identifier) (arguments)) 199 | (property_identifier)) 200 | (arguments 201 | (object 202 | (pair (property_identifier) (string (string_fragment)))))) 203 | (property_identifier)) 204 | (arguments))))) 205 | 206 | ============================================== 207 | if/for/while/do statements without semicolons 208 | ============================================== 209 | 210 | if (a) { if (b) return c } 211 | if (d) { for (;;) break } 212 | if (e) { for (f in g) break } 213 | if (h) { for (i of j) continue } 214 | if (k) { while (l) break } 215 | if (m) { do { n; } while (o) } 216 | if (p) { var q } 217 | 218 | --- 219 | 220 | (program 221 | (if_statement (parenthesized_expression (identifier)) (statement_block 222 | (if_statement 223 | (parenthesized_expression (identifier)) 224 | (return_statement (identifier))))) 225 | (if_statement (parenthesized_expression (identifier)) (statement_block 226 | (for_statement 227 | (empty_statement) 228 | (empty_statement) 229 | (break_statement)))) 230 | (if_statement (parenthesized_expression (identifier)) (statement_block 231 | (for_in_statement (identifier) (identifier) 232 | (break_statement)))) 233 | (if_statement (parenthesized_expression (identifier)) (statement_block 234 | (for_in_statement (identifier) (identifier) 235 | (continue_statement)))) 236 | (if_statement (parenthesized_expression (identifier)) (statement_block 237 | (while_statement 238 | (parenthesized_expression (identifier)) 239 | (break_statement)))) 240 | (if_statement (parenthesized_expression (identifier)) (statement_block 241 | (do_statement 242 | (statement_block (expression_statement (identifier))) 243 | (parenthesized_expression (identifier))))) 244 | (if_statement (parenthesized_expression (identifier)) (statement_block 245 | (variable_declaration (variable_declarator (identifier)))))) 246 | 247 | ===================================================== 248 | Single-line declarations without semicolons 249 | ===================================================== 250 | 251 | function a () { function b () {} function *c () {} class D {} return } 252 | --- 253 | 254 | (program 255 | (function_declaration (identifier) (formal_parameters) (statement_block 256 | (function_declaration (identifier) (formal_parameters) (statement_block)) 257 | (generator_function_declaration (identifier) (formal_parameters) (statement_block)) 258 | (class_declaration (identifier) (class_body)) 259 | (return_statement)))) 260 | 261 | ===================================================== 262 | Comments after statements without semicolons 263 | ===================================================== 264 | 265 | let a // comment at end of declaration 266 | 267 | // comment outside of declaration 268 | let b /* comment between declarators */, c 269 | 270 | /** comment with *stars* **/ /* comment with /slashes/ */ 271 | /* third comment in a row */ 272 | 273 | let d 274 | 275 | let e 276 | /* back to back *//* comments */ 277 | 278 | class C { 279 | method/*comment*/() {} 280 | } 281 | 282 | b 283 | /* interleaved non-semi-insertion */ 284 | .c 285 | --- 286 | 287 | (program 288 | (lexical_declaration 289 | (variable_declarator (identifier))) 290 | (comment) 291 | (comment) 292 | (lexical_declaration 293 | (variable_declarator (identifier)) 294 | (comment) 295 | (variable_declarator (identifier))) 296 | (comment) 297 | (comment) 298 | (comment) 299 | (lexical_declaration (variable_declarator (identifier))) 300 | (lexical_declaration (variable_declarator (identifier))) 301 | (comment) 302 | (comment) 303 | (class_declaration (identifier) (class_body (method_definition 304 | (property_identifier) 305 | (comment) 306 | (formal_parameters) 307 | (statement_block)))) 308 | (expression_statement 309 | (member_expression (identifier) (comment) (property_identifier)))) 310 | -------------------------------------------------------------------------------- /src/tree_sitter/array.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ARRAY_H_ 2 | #define TREE_SITTER_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./alloc.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef _MSC_VER 17 | #pragma warning(push) 18 | #pragma warning(disable : 4101) 19 | #elif defined(__GNUC__) || defined(__clang__) 20 | #pragma GCC diagnostic push 21 | #pragma GCC diagnostic ignored "-Wunused-variable" 22 | #endif 23 | 24 | #define Array(T) \ 25 | struct { \ 26 | T *contents; \ 27 | uint32_t size; \ 28 | uint32_t capacity; \ 29 | } 30 | 31 | /// Initialize an array. 32 | #define array_init(self) \ 33 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 34 | 35 | /// Create an empty array. 36 | #define array_new() \ 37 | { NULL, 0, 0 } 38 | 39 | /// Get a pointer to the element at a given `index` in the array. 40 | #define array_get(self, _index) \ 41 | (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) 42 | 43 | /// Get a pointer to the first element in the array. 44 | #define array_front(self) array_get(self, 0) 45 | 46 | /// Get a pointer to the last element in the array. 47 | #define array_back(self) array_get(self, (self)->size - 1) 48 | 49 | /// Clear the array, setting its size to zero. Note that this does not free any 50 | /// memory allocated for the array's contents. 51 | #define array_clear(self) ((self)->size = 0) 52 | 53 | /// Reserve `new_capacity` elements of space in the array. If `new_capacity` is 54 | /// less than the array's current capacity, this function has no effect. 55 | #define array_reserve(self, new_capacity) \ 56 | _array__reserve((Array *)(self), array_elem_size(self), new_capacity) 57 | 58 | /// Free any memory allocated for this array. Note that this does not free any 59 | /// memory allocated for the array's contents. 60 | #define array_delete(self) _array__delete((Array *)(self)) 61 | 62 | /// Push a new `element` onto the end of the array. 63 | #define array_push(self, element) \ 64 | (_array__grow((Array *)(self), 1, array_elem_size(self)), \ 65 | (self)->contents[(self)->size++] = (element)) 66 | 67 | /// Increase the array's size by `count` elements. 68 | /// New elements are zero-initialized. 69 | #define array_grow_by(self, count) \ 70 | do { \ 71 | if ((count) == 0) break; \ 72 | _array__grow((Array *)(self), count, array_elem_size(self)); \ 73 | memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ 74 | (self)->size += (count); \ 75 | } while (0) 76 | 77 | /// Append all elements from one array to the end of another. 78 | #define array_push_all(self, other) \ 79 | array_extend((self), (other)->size, (other)->contents) 80 | 81 | /// Append `count` elements to the end of the array, reading their values from the 82 | /// `contents` pointer. 83 | #define array_extend(self, count, contents) \ 84 | _array__splice( \ 85 | (Array *)(self), array_elem_size(self), (self)->size, \ 86 | 0, count, contents \ 87 | ) 88 | 89 | /// Remove `old_count` elements from the array starting at the given `index`. At 90 | /// the same index, insert `new_count` new elements, reading their values from the 91 | /// `new_contents` pointer. 92 | #define array_splice(self, _index, old_count, new_count, new_contents) \ 93 | _array__splice( \ 94 | (Array *)(self), array_elem_size(self), _index, \ 95 | old_count, new_count, new_contents \ 96 | ) 97 | 98 | /// Insert one `element` into the array at the given `index`. 99 | #define array_insert(self, _index, element) \ 100 | _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) 101 | 102 | /// Remove one element from the array at the given `index`. 103 | #define array_erase(self, _index) \ 104 | _array__erase((Array *)(self), array_elem_size(self), _index) 105 | 106 | /// Pop the last element off the array, returning the element by value. 107 | #define array_pop(self) ((self)->contents[--(self)->size]) 108 | 109 | /// Assign the contents of one array to another, reallocating if necessary. 110 | #define array_assign(self, other) \ 111 | _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) 112 | 113 | /// Swap one array with another 114 | #define array_swap(self, other) \ 115 | _array__swap((Array *)(self), (Array *)(other)) 116 | 117 | /// Get the size of the array contents 118 | #define array_elem_size(self) (sizeof *(self)->contents) 119 | 120 | /// Search a sorted array for a given `needle` value, using the given `compare` 121 | /// callback to determine the order. 122 | /// 123 | /// If an existing element is found to be equal to `needle`, then the `index` 124 | /// out-parameter is set to the existing value's index, and the `exists` 125 | /// out-parameter is set to true. Otherwise, `index` is set to an index where 126 | /// `needle` should be inserted in order to preserve the sorting, and `exists` 127 | /// is set to false. 128 | #define array_search_sorted_with(self, compare, needle, _index, _exists) \ 129 | _array__search_sorted(self, 0, compare, , needle, _index, _exists) 130 | 131 | /// Search a sorted array for a given `needle` value, using integer comparisons 132 | /// of a given struct field (specified with a leading dot) to determine the order. 133 | /// 134 | /// See also `array_search_sorted_with`. 135 | #define array_search_sorted_by(self, field, needle, _index, _exists) \ 136 | _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists) 137 | 138 | /// Insert a given `value` into a sorted array, using the given `compare` 139 | /// callback to determine the order. 140 | #define array_insert_sorted_with(self, compare, value) \ 141 | do { \ 142 | unsigned _index, _exists; \ 143 | array_search_sorted_with(self, compare, &(value), &_index, &_exists); \ 144 | if (!_exists) array_insert(self, _index, value); \ 145 | } while (0) 146 | 147 | /// Insert a given `value` into a sorted array, using integer comparisons of 148 | /// a given struct field (specified with a leading dot) to determine the order. 149 | /// 150 | /// See also `array_search_sorted_by`. 151 | #define array_insert_sorted_by(self, field, value) \ 152 | do { \ 153 | unsigned _index, _exists; \ 154 | array_search_sorted_by(self, field, (value) field, &_index, &_exists); \ 155 | if (!_exists) array_insert(self, _index, value); \ 156 | } while (0) 157 | 158 | // Private 159 | 160 | typedef Array(void) Array; 161 | 162 | /// This is not what you're looking for, see `array_delete`. 163 | static inline void _array__delete(Array *self) { 164 | if (self->contents) { 165 | ts_free(self->contents); 166 | self->contents = NULL; 167 | self->size = 0; 168 | self->capacity = 0; 169 | } 170 | } 171 | 172 | /// This is not what you're looking for, see `array_erase`. 173 | static inline void _array__erase(Array *self, size_t element_size, 174 | uint32_t index) { 175 | assert(index < self->size); 176 | char *contents = (char *)self->contents; 177 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 178 | (self->size - index - 1) * element_size); 179 | self->size--; 180 | } 181 | 182 | /// This is not what you're looking for, see `array_reserve`. 183 | static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { 184 | if (new_capacity > self->capacity) { 185 | if (self->contents) { 186 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 187 | } else { 188 | self->contents = ts_malloc(new_capacity * element_size); 189 | } 190 | self->capacity = new_capacity; 191 | } 192 | } 193 | 194 | /// This is not what you're looking for, see `array_assign`. 195 | static inline void _array__assign(Array *self, const Array *other, size_t element_size) { 196 | _array__reserve(self, element_size, other->size); 197 | self->size = other->size; 198 | memcpy(self->contents, other->contents, self->size * element_size); 199 | } 200 | 201 | /// This is not what you're looking for, see `array_swap`. 202 | static inline void _array__swap(Array *self, Array *other) { 203 | Array swap = *other; 204 | *other = *self; 205 | *self = swap; 206 | } 207 | 208 | /// This is not what you're looking for, see `array_push` or `array_grow_by`. 209 | static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { 210 | uint32_t new_size = self->size + count; 211 | if (new_size > self->capacity) { 212 | uint32_t new_capacity = self->capacity * 2; 213 | if (new_capacity < 8) new_capacity = 8; 214 | if (new_capacity < new_size) new_capacity = new_size; 215 | _array__reserve(self, element_size, new_capacity); 216 | } 217 | } 218 | 219 | /// This is not what you're looking for, see `array_splice`. 220 | static inline void _array__splice(Array *self, size_t element_size, 221 | uint32_t index, uint32_t old_count, 222 | uint32_t new_count, const void *elements) { 223 | uint32_t new_size = self->size + new_count - old_count; 224 | uint32_t old_end = index + old_count; 225 | uint32_t new_end = index + new_count; 226 | assert(old_end <= self->size); 227 | 228 | _array__reserve(self, element_size, new_size); 229 | 230 | char *contents = (char *)self->contents; 231 | if (self->size > old_end) { 232 | memmove( 233 | contents + new_end * element_size, 234 | contents + old_end * element_size, 235 | (self->size - old_end) * element_size 236 | ); 237 | } 238 | if (new_count > 0) { 239 | if (elements) { 240 | memcpy( 241 | (contents + index * element_size), 242 | elements, 243 | new_count * element_size 244 | ); 245 | } else { 246 | memset( 247 | (contents + index * element_size), 248 | 0, 249 | new_count * element_size 250 | ); 251 | } 252 | } 253 | self->size += new_count - old_count; 254 | } 255 | 256 | /// A binary search routine, based on Rust's `std::slice::binary_search_by`. 257 | /// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. 258 | #define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ 259 | do { \ 260 | *(_index) = start; \ 261 | *(_exists) = false; \ 262 | uint32_t size = (self)->size - *(_index); \ 263 | if (size == 0) break; \ 264 | int comparison; \ 265 | while (size > 1) { \ 266 | uint32_t half_size = size / 2; \ 267 | uint32_t mid_index = *(_index) + half_size; \ 268 | comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ 269 | if (comparison <= 0) *(_index) = mid_index; \ 270 | size -= half_size; \ 271 | } \ 272 | comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \ 273 | if (comparison == 0) *(_exists) = true; \ 274 | else if (comparison < 0) *(_index) += 1; \ 275 | } while (0) 276 | 277 | /// Helper macro for the `_sorted_by` routines below. This takes the left (existing) 278 | /// parameter by reference in order to work with the generic sorting function above. 279 | #define _compare_int(a, b) ((int)*(a) - (int)(b)) 280 | 281 | #ifdef _MSC_VER 282 | #pragma warning(pop) 283 | #elif defined(__GNUC__) || defined(__clang__) 284 | #pragma GCC diagnostic pop 285 | #endif 286 | 287 | #ifdef __cplusplus 288 | } 289 | #endif 290 | 291 | #endif // TREE_SITTER_ARRAY_H_ 292 | -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/parser.h" 2 | 3 | #include 4 | #include 5 | 6 | enum TokenType { 7 | AUTOMATIC_SEMICOLON, 8 | TEMPLATE_CHARS, 9 | TERNARY_QMARK, 10 | HTML_COMMENT, 11 | LOGICAL_OR, 12 | ESCAPE_SEQUENCE, 13 | REGEX_PATTERN, 14 | JSX_TEXT, 15 | }; 16 | 17 | void *tree_sitter_javascript_external_scanner_create() { return NULL; } 18 | 19 | void tree_sitter_javascript_external_scanner_destroy(void *p) {} 20 | 21 | unsigned tree_sitter_javascript_external_scanner_serialize(void *payload, char *buffer) { return 0; } 22 | 23 | void tree_sitter_javascript_external_scanner_deserialize(void *p, const char *b, unsigned n) {} 24 | 25 | static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); } 26 | 27 | static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); } 28 | 29 | static bool scan_template_chars(TSLexer *lexer) { 30 | lexer->result_symbol = TEMPLATE_CHARS; 31 | for (bool has_content = false;; has_content = true) { 32 | lexer->mark_end(lexer); 33 | switch (lexer->lookahead) { 34 | case '`': 35 | return has_content; 36 | case '\0': 37 | return false; 38 | case '$': 39 | advance(lexer); 40 | if (lexer->lookahead == '{') { 41 | return has_content; 42 | } 43 | break; 44 | case '\\': 45 | return has_content; 46 | default: 47 | advance(lexer); 48 | } 49 | } 50 | } 51 | 52 | typedef enum { 53 | REJECT, // Semicolon is illegal, ie a syntax error occurred 54 | NO_NEWLINE, // Unclear if semicolon will be legal, continue 55 | ACCEPT, // Semicolon is legal, assuming a comment was encountered 56 | } WhitespaceResult; 57 | 58 | /** 59 | * @param consume If false, only consume enough to check if comment indicates semicolon-legality 60 | */ 61 | static WhitespaceResult scan_whitespace_and_comments(TSLexer *lexer, bool *scanned_comment, bool consume) { 62 | bool saw_block_newline = false; 63 | 64 | for (;;) { 65 | while (iswspace(lexer->lookahead)) { 66 | skip(lexer); 67 | } 68 | 69 | if (lexer->lookahead == '/') { 70 | skip(lexer); 71 | 72 | if (lexer->lookahead == '/') { 73 | skip(lexer); 74 | while (lexer->lookahead != 0 && lexer->lookahead != '\n' && lexer->lookahead != 0x2028 && 75 | lexer->lookahead != 0x2029) { 76 | skip(lexer); 77 | } 78 | *scanned_comment = true; 79 | } else if (lexer->lookahead == '*') { 80 | skip(lexer); 81 | while (lexer->lookahead != 0) { 82 | if (lexer->lookahead == '*') { 83 | skip(lexer); 84 | if (lexer->lookahead == '/') { 85 | skip(lexer); 86 | *scanned_comment = true; 87 | 88 | if (lexer->lookahead != '/' && !consume) { 89 | return saw_block_newline ? ACCEPT : NO_NEWLINE; 90 | } 91 | 92 | break; 93 | } 94 | } else if (lexer->lookahead == '\n' || lexer->lookahead == 0x2028 || lexer->lookahead == 0x2029) { 95 | saw_block_newline = true; 96 | skip(lexer); 97 | } else { 98 | skip(lexer); 99 | } 100 | } 101 | } else { 102 | return REJECT; 103 | } 104 | } else { 105 | return ACCEPT; 106 | } 107 | } 108 | } 109 | 110 | static bool scan_automatic_semicolon(TSLexer *lexer, bool comment_condition, bool *scanned_comment) { 111 | lexer->result_symbol = AUTOMATIC_SEMICOLON; 112 | lexer->mark_end(lexer); 113 | 114 | for (;;) { 115 | if (lexer->lookahead == 0) { 116 | return true; 117 | } 118 | 119 | if (lexer->lookahead == '/') { 120 | WhitespaceResult result = scan_whitespace_and_comments(lexer, scanned_comment, false); 121 | if (result == REJECT) { 122 | return false; 123 | } 124 | 125 | if (result == ACCEPT && comment_condition && lexer->lookahead != ',' && lexer->lookahead != '=') { 126 | return true; 127 | } 128 | } 129 | 130 | if (lexer->lookahead == '}') { 131 | return true; 132 | } 133 | 134 | if (lexer->is_at_included_range_start(lexer)) { 135 | return true; 136 | } 137 | 138 | if (lexer->lookahead == '\n' || lexer->lookahead == 0x2028 || lexer->lookahead == 0x2029) { 139 | break; 140 | } 141 | 142 | if (!iswspace(lexer->lookahead)) { 143 | return false; 144 | } 145 | 146 | skip(lexer); 147 | } 148 | 149 | skip(lexer); 150 | 151 | if (scan_whitespace_and_comments(lexer, scanned_comment, true) == REJECT) { 152 | return false; 153 | } 154 | 155 | switch (lexer->lookahead) { 156 | case '`': 157 | case ',': 158 | case ':': 159 | case ';': 160 | case '*': 161 | case '%': 162 | case '>': 163 | case '<': 164 | case '=': 165 | case '[': 166 | case '(': 167 | case '?': 168 | case '^': 169 | case '|': 170 | case '&': 171 | case '/': 172 | return false; 173 | 174 | // Insert a semicolon before decimals literals but not otherwise. 175 | case '.': 176 | skip(lexer); 177 | return iswdigit(lexer->lookahead); 178 | 179 | // Insert a semicolon before `--` and `++`, but not before binary `+` or `-`. 180 | case '+': 181 | skip(lexer); 182 | return lexer->lookahead == '+'; 183 | case '-': 184 | skip(lexer); 185 | return lexer->lookahead == '-'; 186 | 187 | // Don't insert a semicolon before `!=`, but do insert one before a unary `!`. 188 | case '!': 189 | skip(lexer); 190 | return lexer->lookahead != '='; 191 | 192 | // Don't insert a semicolon before `in` or `instanceof`, but do insert one 193 | // before an identifier. 194 | case 'i': 195 | skip(lexer); 196 | 197 | if (lexer->lookahead != 'n') { 198 | return true; 199 | } 200 | skip(lexer); 201 | 202 | if (!iswalpha(lexer->lookahead)) { 203 | return false; 204 | } 205 | 206 | for (unsigned i = 0; i < 8; i++) { 207 | if (lexer->lookahead != "stanceof"[i]) { 208 | return true; 209 | } 210 | skip(lexer); 211 | } 212 | 213 | if (!iswalpha(lexer->lookahead)) { 214 | return false; 215 | } 216 | break; 217 | 218 | default: 219 | break; 220 | } 221 | 222 | return true; 223 | } 224 | 225 | static bool scan_ternary_qmark(TSLexer *lexer) { 226 | for (;;) { 227 | if (!iswspace(lexer->lookahead)) { 228 | break; 229 | } 230 | skip(lexer); 231 | } 232 | 233 | if (lexer->lookahead == '?') { 234 | advance(lexer); 235 | 236 | if (lexer->lookahead == '?') { 237 | return false; 238 | } 239 | 240 | lexer->mark_end(lexer); 241 | lexer->result_symbol = TERNARY_QMARK; 242 | 243 | if (lexer->lookahead == '.') { 244 | advance(lexer); 245 | if (iswdigit(lexer->lookahead)) { 246 | return true; 247 | } 248 | return false; 249 | } 250 | return true; 251 | } 252 | return false; 253 | } 254 | 255 | static bool scan_html_comment(TSLexer *lexer) { 256 | while (iswspace(lexer->lookahead) || lexer->lookahead == 0x2028 || lexer->lookahead == 0x2029) { 257 | skip(lexer); 258 | } 259 | 260 | const char *comment_start = ""; 262 | 263 | if (lexer->lookahead == '<') { 264 | for (unsigned i = 0; i < 4; i++) { 265 | if (lexer->lookahead != comment_start[i]) { 266 | return false; 267 | } 268 | advance(lexer); 269 | } 270 | } else if (lexer->lookahead == '-') { 271 | for (unsigned i = 0; i < 3; i++) { 272 | if (lexer->lookahead != comment_end[i]) { 273 | return false; 274 | } 275 | advance(lexer); 276 | } 277 | } else { 278 | return false; 279 | } 280 | 281 | while (lexer->lookahead != 0 && lexer->lookahead != '\n' && lexer->lookahead != 0x2028 && 282 | lexer->lookahead != 0x2029) { 283 | advance(lexer); 284 | } 285 | 286 | lexer->result_symbol = HTML_COMMENT; 287 | lexer->mark_end(lexer); 288 | 289 | return true; 290 | } 291 | 292 | static bool scan_jsx_text(TSLexer *lexer) { 293 | // saw_text will be true if we see any non-whitespace content, or any whitespace content that is not a newline and 294 | // does not immediately follow a newline. 295 | bool saw_text = false; 296 | // at_newline will be true if we are currently at a newline, or if we are at whitespace that is not a newline but 297 | // immediately follows a newline. 298 | bool at_newline = false; 299 | 300 | while (lexer->lookahead != 0 && lexer->lookahead != '<' && lexer->lookahead != '>' && lexer->lookahead != '{' && 301 | lexer->lookahead != '}' && lexer->lookahead != '&') { 302 | bool is_wspace = iswspace(lexer->lookahead); 303 | if (lexer->lookahead == '\n') { 304 | at_newline = true; 305 | } else { 306 | // If at_newline is already true, and we see some whitespace, then it must stay true. 307 | // Otherwise, it should be false. 308 | // 309 | // See the table below to determine the logic for computing `saw_text`. 310 | // 311 | // |------------------------------------| 312 | // | at_newline | is_wspace | saw_text | 313 | // |------------|-----------|-----------| 314 | // | false (0) | false (0) | true (1) | 315 | // | false (0) | true (1) | true (1) | 316 | // | true (1) | false (0) | true (1) | 317 | // | true (1) | true (1) | false (0) | 318 | // |------------------------------------| 319 | 320 | at_newline &= is_wspace; 321 | if (!at_newline) { 322 | saw_text = true; 323 | } 324 | } 325 | 326 | advance(lexer); 327 | } 328 | 329 | lexer->result_symbol = JSX_TEXT; 330 | return saw_text; 331 | } 332 | 333 | bool tree_sitter_javascript_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) { 334 | if (valid_symbols[TEMPLATE_CHARS]) { 335 | if (valid_symbols[AUTOMATIC_SEMICOLON]) { 336 | return false; 337 | } 338 | return scan_template_chars(lexer); 339 | } 340 | 341 | if (valid_symbols[JSX_TEXT] && scan_jsx_text(lexer)) { 342 | return true; 343 | } 344 | 345 | if (valid_symbols[AUTOMATIC_SEMICOLON]) { 346 | bool scanned_comment = false; 347 | bool ret = scan_automatic_semicolon(lexer, !valid_symbols[LOGICAL_OR], &scanned_comment); 348 | if (!ret && !scanned_comment && valid_symbols[TERNARY_QMARK] && lexer->lookahead == '?') { 349 | return scan_ternary_qmark(lexer); 350 | } 351 | return ret; 352 | } 353 | 354 | if (valid_symbols[TERNARY_QMARK]) { 355 | return scan_ternary_qmark(lexer); 356 | } 357 | 358 | if (valid_symbols[HTML_COMMENT] && !valid_symbols[LOGICAL_OR] && !valid_symbols[ESCAPE_SEQUENCE] && 359 | !valid_symbols[REGEX_PATTERN]) { 360 | return scan_html_comment(lexer); 361 | } 362 | 363 | return false; 364 | } 365 | -------------------------------------------------------------------------------- /test/corpus/statements.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Assignments 3 | ============================================ 4 | 5 | a = 0; 6 | var b = 0; 7 | const c = 0; 8 | let d = 0; 9 | 10 | --- 11 | 12 | (program 13 | (expression_statement 14 | (assignment_expression 15 | (identifier) 16 | (number))) 17 | (variable_declaration 18 | (variable_declarator 19 | (identifier) 20 | (number))) 21 | (lexical_declaration 22 | (variable_declarator 23 | (identifier) 24 | (number))) 25 | (lexical_declaration 26 | (variable_declarator 27 | (identifier) 28 | (number)))) 29 | 30 | ============================================ 31 | 'undefined' is a variable 32 | ============================================ 33 | 34 | undefined = 42; 35 | var undefined = 42; 36 | const undefined = 42; 37 | let undefined = 42; 38 | 39 | --- 40 | 41 | (program 42 | (expression_statement 43 | (assignment_expression 44 | (undefined) 45 | (number))) 46 | (variable_declaration 47 | (variable_declarator 48 | (identifier) 49 | (number))) 50 | (lexical_declaration 51 | (variable_declarator 52 | (identifier) 53 | (number))) 54 | (lexical_declaration 55 | (variable_declarator 56 | (identifier) 57 | (number)))) 58 | 59 | ============================================ 60 | Imports 61 | ============================================ 62 | 63 | import defaultMember from "module-name"; 64 | import * as name from "module-name"; 65 | import { member } from "module-name"; 66 | import { member1 , member2 } from "module-name"; 67 | import { member1 , member2 as alias2 } from "module-name"; 68 | import { "string name" as alias } from "module-name"; 69 | import defaultMember, { member1, member2 as alias2 } from "module-name"; 70 | import defaultMember, * as name from "module-name"; 71 | import "module-name"; 72 | import { member1 , member2 as alias2, } from "module-name"; 73 | import pkg from "./package.json" with { type: "json" }; 74 | import("a"); 75 | import("a").then((m) => {}); 76 | import.meta.url; 77 | import { b } from 78 | 'b'; /* comment */ import 79 | { a } from 'a'; 80 | 81 | ---- 82 | 83 | (program 84 | (import_statement 85 | (import_clause 86 | (identifier)) 87 | (string 88 | (string_fragment))) 89 | (import_statement 90 | (import_clause 91 | (namespace_import 92 | (identifier))) 93 | (string 94 | (string_fragment))) 95 | (import_statement 96 | (import_clause 97 | (named_imports 98 | (import_specifier 99 | (identifier)))) 100 | (string 101 | (string_fragment))) 102 | (import_statement 103 | (import_clause 104 | (named_imports 105 | (import_specifier 106 | (identifier)) 107 | (import_specifier 108 | (identifier)))) 109 | (string 110 | (string_fragment))) 111 | (import_statement 112 | (import_clause 113 | (named_imports 114 | (import_specifier 115 | (identifier)) 116 | (import_specifier 117 | (identifier) 118 | (identifier)))) 119 | (string 120 | (string_fragment))) 121 | (import_statement 122 | (import_clause 123 | (named_imports 124 | (import_specifier 125 | (string 126 | (string_fragment)) 127 | (identifier)))) 128 | (string 129 | (string_fragment))) 130 | (import_statement 131 | (import_clause 132 | (identifier) 133 | (named_imports 134 | (import_specifier 135 | (identifier)) 136 | (import_specifier 137 | (identifier) 138 | (identifier)))) 139 | (string 140 | (string_fragment))) 141 | (import_statement 142 | (import_clause 143 | (identifier) 144 | (namespace_import 145 | (identifier))) 146 | (string 147 | (string_fragment))) 148 | (import_statement 149 | (string 150 | (string_fragment))) 151 | (import_statement 152 | (import_clause 153 | (named_imports 154 | (import_specifier 155 | (identifier)) 156 | (import_specifier 157 | (identifier) 158 | (identifier)))) 159 | (string 160 | (string_fragment))) 161 | (import_statement 162 | (import_clause 163 | (identifier)) 164 | (string 165 | (string_fragment)) 166 | (import_attribute 167 | (object 168 | (pair 169 | (property_identifier) 170 | (string 171 | (string_fragment)))))) 172 | (expression_statement 173 | (call_expression 174 | (import) 175 | (arguments 176 | (string 177 | (string_fragment))))) 178 | (expression_statement 179 | (call_expression 180 | (member_expression 181 | (call_expression 182 | (import) 183 | (arguments 184 | (string 185 | (string_fragment)))) 186 | (property_identifier)) 187 | (arguments 188 | (arrow_function 189 | (formal_parameters 190 | (identifier)) 191 | (statement_block))))) 192 | (expression_statement 193 | (member_expression 194 | (meta_property) 195 | (property_identifier))) 196 | (import_statement 197 | (import_clause 198 | (named_imports 199 | (import_specifier 200 | (identifier)))) 201 | (string 202 | (string_fragment))) 203 | (comment) 204 | (import_statement 205 | (import_clause 206 | (named_imports 207 | (import_specifier 208 | (identifier)))) 209 | (string 210 | (string_fragment)))) 211 | 212 | ============================================ 213 | Exports 214 | ============================================ 215 | 216 | export { name1, name2, name3, nameN }; 217 | export { variable1 as name1, variable2 as name2, nameN }; 218 | export { variable1 as "string name" }; 219 | export let name1, name2, nameN; 220 | export let name1 = value1, name2 = value2, name3, nameN; 221 | 222 | export default expression; 223 | export default { field1: 42, field2: [] } 224 | export default function () { } 225 | export default function name1() { } 226 | export { name1 as default }; 227 | 228 | export * from 'foo'; 229 | export * as someIdentifier from "someModule"; 230 | export * as "string name" from "someModule"; 231 | export { name1, name2, nameN } from 'foo'; 232 | export { import1 as name1, import2 as name2, nameN } from 'foo'; 233 | export { import1 as "string name" } from 'foo'; 234 | export { "string import" as "string export" } from 'foo'; 235 | 236 | ---- 237 | 238 | (program 239 | (export_statement 240 | (export_clause 241 | (export_specifier 242 | name: (identifier)) 243 | (export_specifier 244 | name: (identifier)) 245 | (export_specifier 246 | name: (identifier)) 247 | (export_specifier 248 | name: (identifier)))) 249 | (export_statement 250 | (export_clause 251 | (export_specifier 252 | name: (identifier) 253 | alias: (identifier)) 254 | (export_specifier 255 | name: (identifier) 256 | alias: (identifier)) 257 | (export_specifier 258 | name: (identifier)))) 259 | (export_statement 260 | (export_clause 261 | (export_specifier 262 | name: (identifier) 263 | alias: (string 264 | (string_fragment))))) 265 | (export_statement 266 | declaration: (lexical_declaration 267 | (variable_declarator 268 | name: (identifier)) 269 | (variable_declarator 270 | name: (identifier)) 271 | (variable_declarator 272 | name: (identifier)))) 273 | (export_statement 274 | declaration: (lexical_declaration 275 | (variable_declarator 276 | name: (identifier) 277 | value: (identifier)) 278 | (variable_declarator 279 | name: (identifier) 280 | value: (identifier)) 281 | (variable_declarator 282 | name: (identifier)) 283 | (variable_declarator 284 | name: (identifier)))) 285 | (export_statement 286 | value: (identifier)) 287 | (export_statement 288 | value: (object 289 | (pair 290 | key: (property_identifier) 291 | value: (number)) 292 | (pair 293 | key: (property_identifier) 294 | value: (array)))) 295 | (export_statement 296 | value: (function_expression 297 | parameters: (formal_parameters) 298 | body: (statement_block))) 299 | (export_statement 300 | declaration: (function_declaration 301 | name: (identifier) 302 | parameters: (formal_parameters) 303 | body: (statement_block))) 304 | (export_statement 305 | (export_clause 306 | (export_specifier 307 | name: (identifier)))) 308 | (export_statement 309 | source: (string 310 | (string_fragment))) 311 | (export_statement 312 | (namespace_export 313 | (identifier)) 314 | source: (string 315 | (string_fragment))) 316 | (export_statement 317 | (namespace_export 318 | (string 319 | (string_fragment))) 320 | source: (string 321 | (string_fragment))) 322 | (export_statement 323 | (export_clause 324 | (export_specifier 325 | name: (identifier)) 326 | (export_specifier 327 | name: (identifier)) 328 | (export_specifier 329 | name: (identifier))) 330 | source: (string 331 | (string_fragment))) 332 | (export_statement 333 | (export_clause 334 | (export_specifier 335 | name: (identifier) 336 | alias: (identifier)) 337 | (export_specifier 338 | name: (identifier) 339 | alias: (identifier)) 340 | (export_specifier 341 | name: (identifier))) 342 | source: (string 343 | (string_fragment))) 344 | (export_statement 345 | (export_clause 346 | (export_specifier 347 | name: (identifier) 348 | alias: (string 349 | (string_fragment)))) 350 | source: (string 351 | (string_fragment))) 352 | (export_statement 353 | (export_clause 354 | (export_specifier 355 | name: (string 356 | (string_fragment)) 357 | alias: (string 358 | (string_fragment)))) 359 | source: (string 360 | (string_fragment)))) 361 | 362 | ============================================ 363 | Decorators before exports 364 | ============================================ 365 | 366 | @injectable() 367 | export class Foo { 368 | } 369 | 370 | --- 371 | 372 | (program 373 | (export_statement 374 | decorator: (decorator 375 | (call_expression 376 | function: (identifier) 377 | arguments: (arguments))) 378 | declaration: (class_declaration 379 | name: (identifier) 380 | body: (class_body)))) 381 | 382 | ============================================ 383 | If statements 384 | ============================================ 385 | 386 | if (x) 387 | log(y); 388 | 389 | if (a.b) { 390 | log(c); 391 | d; 392 | } 393 | 394 | if (n-->0){} 395 | 396 | ---- 397 | 398 | (program 399 | (if_statement 400 | condition: (parenthesized_expression 401 | (identifier)) 402 | consequence: (expression_statement 403 | (call_expression 404 | function: (identifier) 405 | arguments: (arguments 406 | (identifier))))) 407 | (if_statement 408 | condition: (parenthesized_expression 409 | (member_expression 410 | object: (identifier) 411 | property: (property_identifier))) 412 | consequence: (statement_block 413 | (expression_statement 414 | (call_expression 415 | function: (identifier) 416 | arguments: (arguments 417 | (identifier)))) 418 | (expression_statement 419 | (identifier)))) 420 | (if_statement 421 | condition: (parenthesized_expression 422 | (binary_expression 423 | left: (update_expression 424 | argument: (identifier)) 425 | right: (number))) 426 | consequence: (statement_block))) 427 | 428 | ============================================ 429 | If-else statements 430 | ============================================ 431 | 432 | if (x) 433 | y; 434 | else if (a) 435 | b; 436 | 437 | if (a) { 438 | c; 439 | d; 440 | } else { 441 | e; 442 | } 443 | 444 | ---- 445 | 446 | (program 447 | (if_statement 448 | condition: (parenthesized_expression 449 | (identifier)) 450 | consequence: (expression_statement 451 | (identifier)) 452 | alternative: (else_clause 453 | (if_statement 454 | condition: (parenthesized_expression 455 | (identifier)) 456 | consequence: (expression_statement 457 | (identifier))))) 458 | (if_statement 459 | condition: (parenthesized_expression 460 | (identifier)) 461 | consequence: (statement_block 462 | (expression_statement 463 | (identifier)) 464 | (expression_statement 465 | (identifier))) 466 | alternative: (else_clause 467 | (statement_block 468 | (expression_statement 469 | (identifier)))))) 470 | 471 | ============================================ 472 | For statements 473 | ============================================ 474 | 475 | for (var a, b; c; d) 476 | e; 477 | 478 | for (i = 0, init(); i < 10; i++) 479 | log(y); 480 | 481 | for (;;) { 482 | z; 483 | continue; 484 | } 485 | 486 | for (var i = 0 487 | ; i < l 488 | ; i++) { 489 | } 490 | 491 | for (let in {}); 492 | 493 | for (let j 494 | of k); 495 | 496 | --- 497 | 498 | (program 499 | (for_statement 500 | initializer: (variable_declaration 501 | (variable_declarator 502 | name: (identifier)) 503 | (variable_declarator 504 | name: (identifier))) 505 | condition: (identifier) 506 | increment: (identifier) 507 | body: (expression_statement 508 | (identifier))) 509 | (for_statement 510 | initializer: (sequence_expression 511 | (assignment_expression 512 | left: (identifier) 513 | right: (number)) 514 | (call_expression 515 | function: (identifier) 516 | arguments: (arguments))) 517 | condition: (binary_expression 518 | left: (identifier) 519 | right: (number)) 520 | increment: (update_expression 521 | argument: (identifier)) 522 | body: (expression_statement 523 | (call_expression 524 | function: (identifier) 525 | arguments: (arguments 526 | (identifier))))) 527 | (for_statement 528 | initializer: (empty_statement) 529 | condition: (empty_statement) 530 | body: (statement_block 531 | (expression_statement 532 | (identifier)) 533 | (continue_statement))) 534 | (for_statement 535 | initializer: (variable_declaration 536 | (variable_declarator 537 | name: (identifier) 538 | value: (number))) 539 | condition: (binary_expression 540 | left: (identifier) 541 | right: (identifier)) 542 | increment: (update_expression 543 | argument: (identifier)) 544 | body: (statement_block)) 545 | (for_in_statement 546 | left: (identifier) 547 | right: (object) 548 | body: (empty_statement)) 549 | (for_in_statement 550 | left: (identifier) 551 | right: (identifier) 552 | body: (empty_statement))) 553 | 554 | ============================================ 555 | For-in statements 556 | ============================================ 557 | 558 | for (item in items) 559 | item(); 560 | 561 | for (var item in items || {}) 562 | item(); 563 | 564 | for (const {thing} in things) 565 | thing(); 566 | 567 | for (x in a, b, c) 568 | foo(); 569 | 570 | for (x[i] in a) {} 571 | 572 | for (x.y in a) {} 573 | 574 | for ([a, b] in c) {} 575 | 576 | for ((a) in b) {} 577 | 578 | for (var foo = bar in baz) {} 579 | 580 | --- 581 | 582 | (program 583 | (for_in_statement 584 | (identifier) 585 | (identifier) 586 | (expression_statement 587 | (call_expression 588 | (identifier) 589 | (arguments)))) 590 | (for_in_statement 591 | (identifier) 592 | (binary_expression 593 | (identifier) 594 | (object)) 595 | (expression_statement 596 | (call_expression 597 | (identifier) 598 | (arguments)))) 599 | (for_in_statement 600 | (object_pattern 601 | (shorthand_property_identifier_pattern)) 602 | (identifier) 603 | (expression_statement 604 | (call_expression 605 | (identifier) 606 | (arguments)))) 607 | (for_in_statement 608 | (identifier) 609 | (sequence_expression 610 | (identifier) 611 | (identifier) 612 | (identifier)) 613 | (expression_statement 614 | (call_expression 615 | (identifier) 616 | (arguments)))) 617 | (for_in_statement 618 | (subscript_expression 619 | (identifier) 620 | (identifier)) 621 | (identifier) 622 | (statement_block)) 623 | (for_in_statement 624 | (member_expression 625 | (identifier) 626 | (property_identifier)) 627 | (identifier) 628 | (statement_block)) 629 | (for_in_statement 630 | (array_pattern 631 | (identifier) 632 | (identifier)) 633 | (identifier) 634 | (statement_block)) 635 | (for_in_statement 636 | (parenthesized_expression 637 | (identifier)) 638 | (identifier) 639 | (statement_block)) 640 | (for_in_statement 641 | (identifier) 642 | (identifier) 643 | (identifier) 644 | (statement_block))) 645 | 646 | ========================================== 647 | For loops beginning with an in-expression 648 | ========================================== 649 | 650 | for (key in something && i = 0; i < n; i++) { 651 | doSomething(); 652 | } 653 | 654 | --- 655 | 656 | (program 657 | (for_statement 658 | (binary_expression 659 | (binary_expression 660 | (identifier) 661 | (identifier)) 662 | (assignment_expression 663 | (identifier) 664 | (number))) 665 | (binary_expression 666 | (identifier) 667 | (identifier)) 668 | (update_expression 669 | (identifier)) 670 | (statement_block 671 | (expression_statement 672 | (call_expression 673 | (identifier) 674 | (arguments)))))) 675 | 676 | ============================================ 677 | For-of statements 678 | ============================================ 679 | 680 | for (a of b) 681 | process(a); 682 | 683 | for (let {a, b} of items || []) 684 | process(a, b); 685 | 686 | --- 687 | 688 | (program 689 | (for_in_statement 690 | (identifier) 691 | (identifier) 692 | (expression_statement 693 | (call_expression 694 | (identifier) 695 | (arguments 696 | (identifier))))) 697 | (for_in_statement 698 | (object_pattern 699 | (shorthand_property_identifier_pattern) 700 | (shorthand_property_identifier_pattern)) 701 | (binary_expression 702 | (identifier) 703 | (array)) 704 | (expression_statement 705 | (call_expression 706 | (identifier) 707 | (arguments 708 | (identifier) 709 | (identifier)))))) 710 | 711 | ============================================ 712 | For-await-of statements 713 | ============================================ 714 | 715 | for await (const chunk of stream) { 716 | str += chunk; 717 | } 718 | 719 | --- 720 | 721 | (program 722 | (for_in_statement 723 | (identifier) 724 | (identifier) 725 | (statement_block 726 | (expression_statement 727 | (augmented_assignment_expression 728 | (identifier) 729 | (identifier)))))) 730 | 731 | ============================================ 732 | While statements 733 | ============================================ 734 | 735 | while (a) 736 | b(); 737 | 738 | while (a) { 739 | 740 | } 741 | 742 | --- 743 | 744 | (program 745 | (while_statement 746 | condition: (parenthesized_expression 747 | (identifier)) 748 | body: (expression_statement 749 | (call_expression 750 | function: (identifier) 751 | arguments: (arguments)))) 752 | (while_statement 753 | condition: (parenthesized_expression 754 | (identifier)) 755 | body: (statement_block))) 756 | 757 | ============================================ 758 | Do statements 759 | ============================================ 760 | 761 | do { 762 | a; 763 | } while (b) 764 | 765 | do a; while (b) 766 | 767 | do {} while (b) 768 | 769 | do do; while (a) while (a) 770 | 771 | for (a in b) { do ; while (a) break; } 772 | 773 | do 774 | if (true) 775 | do { 776 | } while (false); 777 | while (0); 778 | 779 | --- 780 | 781 | (program 782 | (do_statement 783 | body: (statement_block 784 | (expression_statement 785 | (identifier))) 786 | condition: (parenthesized_expression 787 | (identifier))) 788 | (do_statement 789 | body: (expression_statement 790 | (identifier)) 791 | condition: (parenthesized_expression 792 | (identifier))) 793 | (do_statement 794 | body: (statement_block) 795 | condition: (parenthesized_expression 796 | (identifier))) 797 | (do_statement 798 | body: (do_statement 799 | body: (empty_statement) 800 | condition: (parenthesized_expression 801 | (identifier))) 802 | condition: (parenthesized_expression 803 | (identifier))) 804 | (for_in_statement 805 | left: (identifier) 806 | right: (identifier) 807 | body: (statement_block 808 | (do_statement 809 | body: (empty_statement) 810 | condition: (parenthesized_expression 811 | (identifier))) 812 | (break_statement))) 813 | (do_statement 814 | body: (if_statement 815 | condition: (parenthesized_expression 816 | (true)) 817 | consequence: (do_statement 818 | body: (statement_block) 819 | condition: (parenthesized_expression 820 | (false)))) 821 | condition: (parenthesized_expression 822 | (number)))) 823 | 824 | ============================================ 825 | Return statements 826 | ============================================ 827 | 828 | return; 829 | return 5; 830 | return 1,2; 831 | return async; 832 | return a; 833 | 834 | --- 835 | 836 | (program 837 | (return_statement) 838 | (return_statement 839 | (number)) 840 | (return_statement 841 | (sequence_expression 842 | (number) 843 | (number))) 844 | (return_statement 845 | (identifier)) 846 | (return_statement 847 | (identifier))) 848 | 849 | ============================================ 850 | Variable declarations 851 | ============================================ 852 | 853 | var x = 1; 854 | var x, y = {}, z; 855 | 856 | --- 857 | 858 | (program 859 | (variable_declaration 860 | (variable_declarator 861 | (identifier) 862 | (number))) 863 | (variable_declaration 864 | (variable_declarator 865 | (identifier)) 866 | (variable_declarator 867 | (identifier) 868 | (object)) 869 | (variable_declarator 870 | (identifier)))) 871 | 872 | ============================================ 873 | Using declarations 874 | ============================================ 875 | 876 | using file = getFile(); 877 | await using resource = getResource(); 878 | using x = a, y = b; 879 | 880 | --- 881 | 882 | (program 883 | (using_declaration 884 | (variable_declarator 885 | (identifier) 886 | (call_expression 887 | (identifier) 888 | (arguments)))) 889 | (using_declaration 890 | (variable_declarator 891 | (identifier) 892 | (call_expression 893 | (identifier) 894 | (arguments)))) 895 | (using_declaration 896 | (variable_declarator 897 | (identifier) 898 | (identifier)) 899 | (variable_declarator 900 | (identifier) 901 | (identifier)))) 902 | 903 | ============================================ 904 | Using declarations in for loops 905 | ============================================ 906 | 907 | for (using item of items) { 908 | process(item); 909 | } 910 | 911 | for await (await using resource of resources) { 912 | consume(resource); 913 | } 914 | 915 | --- 916 | 917 | (program 918 | (for_in_statement 919 | (identifier) 920 | (identifier) 921 | (statement_block 922 | (expression_statement 923 | (call_expression 924 | (identifier) 925 | (arguments 926 | (identifier)))))) 927 | (for_in_statement 928 | (identifier) 929 | (identifier) 930 | (statement_block 931 | (expression_statement 932 | (call_expression 933 | (identifier) 934 | (arguments 935 | (identifier))))))) 936 | 937 | ============================================ 938 | 'of' as identifier in declarations 939 | ============================================ 940 | 941 | for (let of of []) break; 942 | for (const of = '';;) break; 943 | const of = 2; 944 | 945 | --- 946 | 947 | (program 948 | (for_in_statement 949 | (identifier) 950 | (array) 951 | (break_statement)) 952 | (for_statement 953 | (lexical_declaration 954 | (variable_declarator 955 | (identifier) 956 | (string))) 957 | (empty_statement) 958 | (break_statement)) 959 | (lexical_declaration 960 | (variable_declarator 961 | (identifier) 962 | (number)))) 963 | 964 | ============================================ 965 | Comments 966 | ============================================ 967 | 968 | { 969 | 970 | // This is a property 971 | aProperty: 1, 972 | 973 | /* 974 | * This is a method 975 | */ 976 | aMethod: function() {} 977 | }; 978 | 979 | --- 980 | 981 | (program 982 | (expression_statement 983 | (object 984 | (comment) 985 | (pair 986 | (property_identifier) 987 | (number)) 988 | (comment) 989 | (pair 990 | (property_identifier) 991 | (function_expression 992 | (formal_parameters) 993 | (statement_block)))))) 994 | 995 | ========================================== 996 | Comments between statements 997 | ========================================== 998 | 999 | // this is the beginning of the script. 1000 | // here we go. 1001 | var thing = { 1002 | 1003 | // this is a property. 1004 | // its value is a function. 1005 | key: function(x /* this is a parameter */) { 1006 | // this is one statement 1007 | one(); 1008 | // this is another statement 1009 | two(); 1010 | } 1011 | }; 1012 | 1013 | let foo = bar 1014 | // this is a comment 1015 | ? 'baz' : 'thud'; 1016 | 1017 | let a /* >_< */ = 1 1018 | 1019 | --- 1020 | 1021 | (program 1022 | (comment) 1023 | (comment) 1024 | (variable_declaration 1025 | (variable_declarator 1026 | (identifier) 1027 | (object 1028 | (comment) 1029 | (comment) 1030 | (pair 1031 | (property_identifier) 1032 | (function_expression 1033 | (formal_parameters 1034 | (identifier) 1035 | (comment)) 1036 | (statement_block 1037 | (comment) 1038 | (expression_statement 1039 | (call_expression 1040 | (identifier) 1041 | (arguments))) 1042 | (comment) 1043 | (expression_statement 1044 | (call_expression 1045 | (identifier) 1046 | (arguments))))))))) 1047 | (lexical_declaration 1048 | (variable_declarator 1049 | (identifier) 1050 | (ternary_expression 1051 | (identifier) 1052 | (comment) 1053 | (string 1054 | (string_fragment)) 1055 | (string 1056 | (string_fragment))))) 1057 | (lexical_declaration 1058 | (variable_declarator 1059 | (identifier) 1060 | (comment) 1061 | (number)))) 1062 | 1063 | ============================================ 1064 | Comments with asterisks 1065 | ============================================ 1066 | 1067 | /* a */ 1068 | const a = 1; 1069 | 1070 | /* b **/ 1071 | const b = 1; 1072 | 1073 | /* c ***/ 1074 | const c = 1; 1075 | 1076 | /* d 1077 | 1078 | ***/ 1079 | const d = 1; 1080 | 1081 | class E { 1082 | f() { 1083 | } /*** com 1084 | ment ***/ 1085 | g() { 1086 | } 1087 | } 1088 | 1089 | --- 1090 | 1091 | (program 1092 | (comment) 1093 | (lexical_declaration 1094 | (variable_declarator 1095 | (identifier) 1096 | (number))) 1097 | (comment) 1098 | (lexical_declaration 1099 | (variable_declarator 1100 | (identifier) 1101 | (number))) 1102 | (comment) 1103 | (lexical_declaration 1104 | (variable_declarator 1105 | (identifier) 1106 | (number))) 1107 | (comment) 1108 | (lexical_declaration 1109 | (variable_declarator 1110 | (identifier) 1111 | (number))) 1112 | (class_declaration 1113 | (identifier) 1114 | (class_body 1115 | (method_definition 1116 | (property_identifier) 1117 | (formal_parameters) 1118 | (statement_block)) 1119 | (comment) 1120 | (method_definition 1121 | (property_identifier) 1122 | (formal_parameters) 1123 | (statement_block))))) 1124 | 1125 | ========================================== 1126 | Comments within expressions 1127 | ========================================== 1128 | 1129 | y // comment 1130 | * z; 1131 | 1132 | --- 1133 | 1134 | (program 1135 | (expression_statement 1136 | (binary_expression 1137 | (identifier) 1138 | (comment) 1139 | (identifier)))) 1140 | 1141 | ========================================== 1142 | HTML Comments 1143 | ========================================== 1144 | 1145 | for some reason you can put text after a close comment. 1150 | 1151 | --> note that the x + 1 above should be rejected according to the spec. 1152 | --> it should instead be rejected as invalid. 1153 | --> you are supposed to only have whitespace or comments before 1154 | --> an HTML close comment. 1155 | 1156 | --- 1157 | 1158 | (program 1159 | (html_comment) 1160 | (expression_statement 1161 | (binary_expression 1162 | (identifier) 1163 | (identifier))) 1164 | (html_comment) 1165 | (expression_statement 1166 | (binary_expression 1167 | (identifier) 1168 | (number))) 1169 | (html_comment) 1170 | (html_comment) 1171 | (html_comment) 1172 | (html_comment) 1173 | (html_comment)) 1174 | 1175 | ============================================ 1176 | Switch statements 1177 | ============================================ 1178 | 1179 | switch (x) { 1180 | case 1: 1181 | case 2: 1182 | something(); 1183 | break; 1184 | case "three": 1185 | somethingElse(); 1186 | break; 1187 | default: 1188 | return 4; 1189 | } 1190 | 1191 | --- 1192 | 1193 | (program 1194 | (switch_statement 1195 | (parenthesized_expression 1196 | (identifier)) 1197 | (switch_body 1198 | (switch_case 1199 | (number)) 1200 | (switch_case 1201 | (number) 1202 | (expression_statement 1203 | (call_expression 1204 | (identifier) 1205 | (arguments))) 1206 | (break_statement)) 1207 | (switch_case 1208 | (string 1209 | (string_fragment)) 1210 | (expression_statement 1211 | (call_expression 1212 | (identifier) 1213 | (arguments))) 1214 | (break_statement)) 1215 | (switch_default 1216 | (return_statement 1217 | (number)))))) 1218 | 1219 | ============================================ 1220 | Throw statements 1221 | ============================================ 1222 | 1223 | throw new Error("uh oh"); 1224 | 1225 | --- 1226 | 1227 | (program 1228 | (throw_statement 1229 | (new_expression 1230 | (identifier) 1231 | (arguments 1232 | (string 1233 | (string_fragment)))))) 1234 | 1235 | ============================================ 1236 | Throw statements with sequence expressions 1237 | ============================================ 1238 | 1239 | throw f = 1, f; 1240 | throw g = 2, g 1241 | --- 1242 | 1243 | (program 1244 | (throw_statement 1245 | (sequence_expression 1246 | (assignment_expression 1247 | (identifier) 1248 | (number)) 1249 | (identifier))) 1250 | (throw_statement 1251 | (sequence_expression 1252 | (assignment_expression 1253 | (identifier) 1254 | (number)) 1255 | (identifier)))) 1256 | 1257 | ============================================ 1258 | Try catch finally statements 1259 | ============================================ 1260 | 1261 | try { a; } catch (b) { c; } 1262 | try { d; } finally { e; } 1263 | try { f; } catch { g; } finally { h; } 1264 | try { throw [a, b] } catch ([c, d]) { } 1265 | 1266 | --- 1267 | 1268 | (program 1269 | (try_statement 1270 | (statement_block 1271 | (expression_statement 1272 | (identifier))) 1273 | (catch_clause 1274 | (identifier) 1275 | (statement_block 1276 | (expression_statement 1277 | (identifier))))) 1278 | (try_statement 1279 | (statement_block 1280 | (expression_statement 1281 | (identifier))) 1282 | (finally_clause 1283 | (statement_block 1284 | (expression_statement 1285 | (identifier))))) 1286 | (try_statement 1287 | (statement_block 1288 | (expression_statement 1289 | (identifier))) 1290 | (catch_clause 1291 | (statement_block 1292 | (expression_statement 1293 | (identifier)))) 1294 | (finally_clause 1295 | (statement_block 1296 | (expression_statement 1297 | (identifier))))) 1298 | (try_statement 1299 | (statement_block 1300 | (throw_statement 1301 | (array 1302 | (identifier) 1303 | (identifier)))) 1304 | (catch_clause 1305 | (array_pattern 1306 | (identifier) 1307 | (identifier)) 1308 | (statement_block)))) 1309 | 1310 | ============================================ 1311 | Empty statements 1312 | ============================================ 1313 | 1314 | if (true) { ; };;; 1315 | if (true) {} else {} 1316 | 1317 | --- 1318 | 1319 | (program 1320 | (if_statement 1321 | (parenthesized_expression 1322 | (true)) 1323 | (statement_block 1324 | (empty_statement))) 1325 | (empty_statement) 1326 | (empty_statement) 1327 | (empty_statement) 1328 | (if_statement 1329 | (parenthesized_expression 1330 | (true)) 1331 | (statement_block) 1332 | (else_clause 1333 | (statement_block)))) 1334 | 1335 | ============================================ 1336 | Labeled statements 1337 | ============================================ 1338 | 1339 | theLoop: 1340 | for (;;) { 1341 | if (a) { 1342 | break theLoop; 1343 | } else { 1344 | continue theLoop; 1345 | } 1346 | } 1347 | 1348 | label 1349 | : { 1350 | break label; 1351 | } 1352 | 1353 | async: 1354 | while (true) { 1355 | continue async; 1356 | } 1357 | 1358 | --- 1359 | 1360 | (program 1361 | (labeled_statement 1362 | label: (statement_identifier) 1363 | body: (for_statement 1364 | initializer: (empty_statement) 1365 | condition: (empty_statement) 1366 | body: (statement_block 1367 | (if_statement 1368 | condition: (parenthesized_expression 1369 | (identifier)) 1370 | consequence: (statement_block 1371 | (break_statement 1372 | label: (statement_identifier))) 1373 | alternative: (else_clause 1374 | (statement_block 1375 | (continue_statement 1376 | label: (statement_identifier)))))))) 1377 | (labeled_statement 1378 | label: (statement_identifier) 1379 | body: (statement_block 1380 | (break_statement 1381 | label: (statement_identifier)))) 1382 | (labeled_statement 1383 | label: (statement_identifier) 1384 | body: (while_statement 1385 | condition: (parenthesized_expression 1386 | (true)) 1387 | body: (statement_block 1388 | (continue_statement 1389 | label: (statement_identifier)))))) 1390 | 1391 | ============================================ 1392 | Debugger statements 1393 | ============================================ 1394 | 1395 | debugger; 1396 | debugger 1397 | 1398 | --- 1399 | 1400 | (program 1401 | (debugger_statement) 1402 | (debugger_statement)) 1403 | 1404 | ============================================ 1405 | With statements 1406 | ============================================ 1407 | 1408 | with (x) { i; } 1409 | 1410 | with (x) { } 1411 | 1412 | --- 1413 | 1414 | (program 1415 | (with_statement 1416 | object: (parenthesized_expression 1417 | (identifier)) 1418 | body: (statement_block 1419 | (expression_statement 1420 | (identifier)))) 1421 | (with_statement 1422 | object: (parenthesized_expression 1423 | (identifier)) 1424 | body: (statement_block))) 1425 | 1426 | ========================================== 1427 | Hash bang lines 1428 | ========================================== 1429 | 1430 | #!/usr/bin/env node 1431 | 1432 | console.log("HI") 1433 | 1434 | --- 1435 | 1436 | (program 1437 | (hash_bang_line) 1438 | (expression_statement 1439 | (call_expression 1440 | (member_expression 1441 | (identifier) 1442 | (property_identifier)) 1443 | (arguments 1444 | (string 1445 | (string_fragment)))))) 1446 | 1447 | ========================================== 1448 | U+2028 as a line terminator 1449 | ========================================== 1450 | 1451 | let x = { a: //
 3 1452 | } 1453 | 1454 | --- 1455 | 1456 | (program 1457 | (lexical_declaration 1458 | (variable_declarator 1459 | (identifier) 1460 | (object 1461 | (pair 1462 | (property_identifier) 1463 | (comment) 1464 | (number)))))) 1465 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file JavaScript grammar for tree-sitter 3 | * @author Max Brunsfeld 4 | * @author Amaan Qureshi 5 | * @license MIT 6 | */ 7 | 8 | /// 9 | // @ts-check 10 | 11 | module.exports = grammar({ 12 | name: 'javascript', 13 | 14 | externals: $ => [ 15 | $._automatic_semicolon, 16 | $._template_chars, 17 | $._ternary_qmark, 18 | $.html_comment, 19 | '||', 20 | // We use escape sequence and regex pattern to tell the scanner if we're currently inside a string or template string, in which case 21 | // it should NOT parse html comments. 22 | $.escape_sequence, 23 | $.regex_pattern, 24 | $.jsx_text, 25 | ], 26 | 27 | extras: $ => [ 28 | $.comment, 29 | $.html_comment, 30 | /[\s\p{Zs}\uFEFF\u2028\u2029\u2060\u200B]/, 31 | ], 32 | 33 | reserved: { 34 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words 35 | global: $ => [ 36 | 'break', 37 | 'case', 38 | 'catch', 39 | 'class', 40 | 'const', 41 | 'continue', 42 | 'debugger', 43 | 'default', 44 | 'delete', 45 | 'do', 46 | 'else', 47 | 'export', 48 | 'extends', 49 | 'false', 50 | 'finally', 51 | 'for', 52 | 'function', 53 | 'if', 54 | 'import', 55 | 'in', 56 | 'instanceof', 57 | 'new', 58 | 'null', 59 | 'return', 60 | 'super', 61 | 'switch', 62 | 'this', 63 | 'throw', 64 | 'true', 65 | 'try', 66 | 'typeof', 67 | 'var', 68 | 'void', 69 | 'while', 70 | 'with', 71 | ], 72 | properties: $ => [], 73 | }, 74 | 75 | supertypes: $ => [ 76 | $.statement, 77 | $.declaration, 78 | $.expression, 79 | $.primary_expression, 80 | $.pattern, 81 | ], 82 | 83 | inline: $ => [ 84 | $._call_signature, 85 | $._formal_parameter, 86 | $._expressions, 87 | $._semicolon, 88 | $._identifier, 89 | $._reserved_identifier, 90 | $._jsx_attribute, 91 | $._jsx_element_name, 92 | $._jsx_child, 93 | $._jsx_element, 94 | $._jsx_attribute_name, 95 | $._jsx_attribute_value, 96 | $._jsx_identifier, 97 | $._lhs_expression, 98 | ], 99 | 100 | precedences: $ => [ 101 | [ 102 | 'member', 103 | 'template_call', 104 | 'call', 105 | $.update_expression, 106 | 'unary_void', 107 | 'binary_exp', 108 | 'binary_times', 109 | 'binary_plus', 110 | 'binary_shift', 111 | 'binary_compare', 112 | 'binary_relation', 113 | 'binary_equality', 114 | 'bitwise_and', 115 | 'bitwise_xor', 116 | 'bitwise_or', 117 | 'logical_and', 118 | 'logical_or', 119 | 'ternary', 120 | $.sequence_expression, 121 | $.arrow_function, 122 | ], 123 | ['assign', $.primary_expression], 124 | ['member', 'template_call', 'new', 'call', $.expression], 125 | ['declaration', 'literal'], 126 | [$.primary_expression, $.statement_block, 'object'], 127 | [$.meta_property, $.import], 128 | [$.import_statement, $.import], 129 | [$.export_statement, $.primary_expression], 130 | [$.lexical_declaration, $.primary_expression], 131 | ], 132 | 133 | conflicts: $ => [ 134 | [$.primary_expression, $._property_name], 135 | [$.primary_expression, $.await_expression], 136 | [$.primary_expression, $.await_expression, $._property_name], 137 | [$.primary_expression, $.arrow_function], 138 | [$.primary_expression, $.arrow_function, $._property_name], 139 | [$.primary_expression, $.method_definition], 140 | [$.primary_expression, $.rest_pattern], 141 | [$.primary_expression, $.pattern], 142 | [$.primary_expression, $._for_header], 143 | [$.variable_declarator, $._for_header], 144 | [$.array, $.array_pattern], 145 | [$.object, $.object_pattern], 146 | [$.assignment_expression, $.pattern], 147 | [$.assignment_expression, $.object_assignment_pattern], 148 | [$.labeled_statement, $._property_name], 149 | [$.computed_property_name, $.array], 150 | [$.binary_expression, $._initializer], 151 | [$.class_static_block, $._property_name], 152 | ], 153 | 154 | word: $ => $.identifier, 155 | 156 | rules: { 157 | program: $ => seq( 158 | optional($.hash_bang_line), 159 | repeat($.statement), 160 | ), 161 | 162 | hash_bang_line: _ => /#!.*/, 163 | 164 | // 165 | // Export declarations 166 | // 167 | 168 | export_statement: $ => choice( 169 | seq( 170 | 'export', 171 | choice( 172 | seq('*', $._from_clause), 173 | seq($.namespace_export, $._from_clause), 174 | seq($.export_clause, $._from_clause), 175 | $.export_clause, 176 | ), 177 | $._semicolon, 178 | ), 179 | seq( 180 | repeat(field('decorator', $.decorator)), 181 | 'export', 182 | choice( 183 | field('declaration', $.declaration), 184 | seq( 185 | 'default', 186 | choice( 187 | field('declaration', $.declaration), 188 | seq( 189 | field('value', $.expression), 190 | $._semicolon, 191 | ), 192 | ), 193 | ), 194 | ), 195 | ), 196 | ), 197 | 198 | namespace_export: $ => seq( 199 | '*', 'as', $._module_export_name, 200 | ), 201 | 202 | export_clause: $ => seq( 203 | '{', 204 | commaSep($.export_specifier), 205 | optional(','), 206 | '}', 207 | ), 208 | 209 | export_specifier: $ => seq( 210 | field('name', $._module_export_name), 211 | optional(seq( 212 | 'as', 213 | field('alias', $._module_export_name), 214 | )), 215 | ), 216 | 217 | _module_export_name: $ => choice( 218 | $.identifier, 219 | $.string, 220 | 'default', 221 | ), 222 | 223 | declaration: $ => choice( 224 | $.function_declaration, 225 | $.generator_function_declaration, 226 | $.class_declaration, 227 | $.lexical_declaration, 228 | $.variable_declaration, 229 | $.using_declaration, 230 | ), 231 | 232 | // 233 | // Import declarations 234 | // 235 | 236 | import: _ => token('import'), 237 | 238 | import_statement: $ => seq( 239 | 'import', 240 | choice( 241 | seq($.import_clause, $._from_clause), 242 | field('source', $.string), 243 | ), 244 | optional($.import_attribute), 245 | $._semicolon, 246 | ), 247 | 248 | import_clause: $ => choice( 249 | $.namespace_import, 250 | $.named_imports, 251 | seq( 252 | $.identifier, 253 | optional(seq( 254 | ',', 255 | choice( 256 | $.namespace_import, 257 | $.named_imports, 258 | ), 259 | )), 260 | ), 261 | ), 262 | 263 | _from_clause: $ => seq( 264 | 'from', field('source', $.string), 265 | ), 266 | 267 | namespace_import: $ => seq( 268 | '*', 'as', $.identifier, 269 | ), 270 | 271 | named_imports: $ => seq( 272 | '{', 273 | commaSep($.import_specifier), 274 | optional(','), 275 | '}', 276 | ), 277 | 278 | import_specifier: $ => choice( 279 | field('name', $.identifier), 280 | seq( 281 | field('name', $._module_export_name), 282 | 'as', 283 | field('alias', $.identifier), 284 | ), 285 | ), 286 | 287 | import_attribute: $ => seq('with', $.object), 288 | 289 | // 290 | // Statements 291 | // 292 | 293 | statement: $ => choice( 294 | $.export_statement, 295 | $.import_statement, 296 | $.debugger_statement, 297 | $.expression_statement, 298 | $.declaration, 299 | $.statement_block, 300 | 301 | $.if_statement, 302 | $.switch_statement, 303 | $.for_statement, 304 | $.for_in_statement, 305 | $.while_statement, 306 | $.do_statement, 307 | $.try_statement, 308 | $.with_statement, 309 | 310 | $.break_statement, 311 | $.continue_statement, 312 | $.return_statement, 313 | $.throw_statement, 314 | $.empty_statement, 315 | $.labeled_statement, 316 | ), 317 | 318 | expression_statement: $ => seq( 319 | $._expressions, 320 | $._semicolon, 321 | ), 322 | 323 | variable_declaration: $ => seq( 324 | 'var', 325 | commaSep1($.variable_declarator), 326 | $._semicolon, 327 | ), 328 | 329 | lexical_declaration: $ => seq( 330 | field('kind', choice('let', 'const')), 331 | commaSep1($.variable_declarator), 332 | $._semicolon, 333 | ), 334 | 335 | using_declaration: $ => seq( 336 | field('kind', choice( 337 | 'using', 338 | seq('await', 'using'), 339 | )), 340 | commaSep1($.variable_declarator), 341 | $._semicolon, 342 | ), 343 | 344 | variable_declarator: $ => seq( 345 | field('name', choice( 346 | $.identifier, 347 | alias('of', $.identifier), 348 | $._destructuring_pattern, 349 | )), 350 | optional($._initializer), 351 | ), 352 | 353 | statement_block: $ => prec.right(seq( 354 | '{', 355 | repeat($.statement), 356 | '}', 357 | optional($._automatic_semicolon), 358 | )), 359 | 360 | else_clause: $ => seq('else', $.statement), 361 | 362 | if_statement: $ => prec.right(seq( 363 | 'if', 364 | field('condition', $.parenthesized_expression), 365 | field('consequence', $.statement), 366 | optional(field('alternative', $.else_clause)), 367 | )), 368 | 369 | switch_statement: $ => seq( 370 | 'switch', 371 | field('value', $.parenthesized_expression), 372 | field('body', $.switch_body), 373 | ), 374 | 375 | for_statement: $ => seq( 376 | 'for', 377 | '(', 378 | choice( 379 | field('initializer', choice($.lexical_declaration, $.variable_declaration)), 380 | seq(field('initializer', $._expressions), ';'), 381 | field('initializer', $.empty_statement), 382 | ), 383 | field('condition', choice( 384 | seq($._expressions, ';'), 385 | $.empty_statement, 386 | )), 387 | field('increment', optional($._expressions)), 388 | ')', 389 | field('body', $.statement), 390 | ), 391 | 392 | for_in_statement: $ => seq( 393 | 'for', 394 | optional('await'), 395 | $._for_header, 396 | field('body', $.statement), 397 | ), 398 | 399 | _for_header: $ => seq( 400 | '(', 401 | choice( 402 | field('left', choice( 403 | $._lhs_expression, 404 | $.parenthesized_expression, 405 | )), 406 | seq( 407 | field('kind', 'var'), 408 | field('left', choice( 409 | $.identifier, 410 | alias('of', $.identifier), 411 | $._destructuring_pattern, 412 | )), 413 | optional($._initializer), 414 | ), 415 | seq( 416 | field('kind', choice('let', 'const')), 417 | field('left', choice( 418 | $.identifier, 419 | alias('of', $.identifier), 420 | $._destructuring_pattern, 421 | )), 422 | optional($._automatic_semicolon), 423 | ), 424 | seq( 425 | field('kind', choice( 426 | 'using', 427 | seq('await', 'using'), 428 | )), 429 | field('left', choice( 430 | $.identifier, 431 | alias('of', $.identifier), 432 | $._destructuring_pattern, 433 | )), 434 | optional($._automatic_semicolon), 435 | ), 436 | ), 437 | field('operator', choice('in', 'of')), 438 | field('right', $._expressions), 439 | ')', 440 | ), 441 | 442 | while_statement: $ => seq( 443 | 'while', 444 | field('condition', $.parenthesized_expression), 445 | field('body', $.statement), 446 | ), 447 | 448 | do_statement: $ => prec.right(seq( 449 | 'do', 450 | field('body', $.statement), 451 | 'while', 452 | field('condition', $.parenthesized_expression), 453 | optional($._semicolon), 454 | )), 455 | 456 | try_statement: $ => seq( 457 | 'try', 458 | field('body', $.statement_block), 459 | optional(field('handler', $.catch_clause)), 460 | optional(field('finalizer', $.finally_clause)), 461 | ), 462 | 463 | with_statement: $ => seq( 464 | 'with', 465 | field('object', $.parenthesized_expression), 466 | field('body', $.statement), 467 | ), 468 | 469 | break_statement: $ => seq( 470 | 'break', 471 | field('label', optional(alias($.identifier, $.statement_identifier))), 472 | $._semicolon, 473 | ), 474 | 475 | continue_statement: $ => seq( 476 | 'continue', 477 | field('label', optional(alias($.identifier, $.statement_identifier))), 478 | $._semicolon, 479 | ), 480 | 481 | debugger_statement: $ => seq( 482 | 'debugger', 483 | $._semicolon, 484 | ), 485 | 486 | return_statement: $ => seq( 487 | 'return', 488 | optional($._expressions), 489 | $._semicolon, 490 | ), 491 | 492 | throw_statement: $ => seq( 493 | 'throw', 494 | $._expressions, 495 | $._semicolon, 496 | ), 497 | 498 | empty_statement: _ => ';', 499 | 500 | labeled_statement: $ => prec.dynamic(-1, seq( 501 | field('label', alias(choice($.identifier, $._reserved_identifier), $.statement_identifier)), 502 | ':', 503 | field('body', $.statement), 504 | )), 505 | 506 | // 507 | // Statement components 508 | // 509 | 510 | switch_body: $ => seq( 511 | '{', 512 | repeat(choice($.switch_case, $.switch_default)), 513 | '}', 514 | ), 515 | 516 | switch_case: $ => seq( 517 | 'case', 518 | field('value', $._expressions), 519 | ':', 520 | field('body', repeat($.statement)), 521 | ), 522 | 523 | switch_default: $ => seq( 524 | 'default', 525 | ':', 526 | field('body', repeat($.statement)), 527 | ), 528 | 529 | catch_clause: $ => seq( 530 | 'catch', 531 | optional(seq('(', field('parameter', choice($.identifier, $._destructuring_pattern)), ')')), 532 | field('body', $.statement_block), 533 | ), 534 | 535 | finally_clause: $ => seq( 536 | 'finally', 537 | field('body', $.statement_block), 538 | ), 539 | 540 | parenthesized_expression: $ => seq( 541 | '(', 542 | $._expressions, 543 | ')', 544 | ), 545 | 546 | // 547 | // Expressions 548 | // 549 | _expressions: $ => choice( 550 | $.expression, 551 | $.sequence_expression, 552 | ), 553 | 554 | expression: $ => choice( 555 | $.primary_expression, 556 | $._jsx_element, 557 | $.assignment_expression, 558 | $.augmented_assignment_expression, 559 | $.await_expression, 560 | $.unary_expression, 561 | $.binary_expression, 562 | $.ternary_expression, 563 | $.update_expression, 564 | $.new_expression, 565 | $.yield_expression, 566 | ), 567 | 568 | primary_expression: $ => choice( 569 | $.subscript_expression, 570 | $.member_expression, 571 | $.parenthesized_expression, 572 | $._identifier, 573 | alias($._reserved_identifier, $.identifier), 574 | $.this, 575 | $.super, 576 | $.number, 577 | $.string, 578 | $.template_string, 579 | $.regex, 580 | $.true, 581 | $.false, 582 | $.null, 583 | $.object, 584 | $.array, 585 | $.function_expression, 586 | $.arrow_function, 587 | $.generator_function, 588 | $.class, 589 | $.meta_property, 590 | $.call_expression, 591 | ), 592 | 593 | yield_expression: $ => prec.right(seq( 594 | 'yield', 595 | choice( 596 | seq('*', $.expression), 597 | optional($.expression), 598 | ))), 599 | 600 | object: $ => prec('object', seq( 601 | '{', 602 | commaSep(optional(choice( 603 | $.pair, 604 | $.spread_element, 605 | $.method_definition, 606 | alias( 607 | choice($.identifier, $._reserved_identifier), 608 | $.shorthand_property_identifier, 609 | ), 610 | ))), 611 | '}', 612 | )), 613 | 614 | object_pattern: $ => prec('object', seq( 615 | '{', 616 | commaSep(optional(choice( 617 | $.pair_pattern, 618 | $.rest_pattern, 619 | $.object_assignment_pattern, 620 | alias( 621 | choice($.identifier, $._reserved_identifier), 622 | $.shorthand_property_identifier_pattern, 623 | ), 624 | ))), 625 | '}', 626 | )), 627 | 628 | assignment_pattern: $ => seq( 629 | field('left', $.pattern), 630 | '=', 631 | field('right', $.expression), 632 | ), 633 | 634 | object_assignment_pattern: $ => seq( 635 | field('left', choice( 636 | alias(choice($._reserved_identifier, $.identifier), $.shorthand_property_identifier_pattern), 637 | $._destructuring_pattern, 638 | )), 639 | '=', 640 | field('right', $.expression), 641 | ), 642 | 643 | array: $ => seq( 644 | '[', 645 | commaSep(optional(choice( 646 | $.expression, 647 | $.spread_element, 648 | ))), 649 | ']', 650 | ), 651 | 652 | array_pattern: $ => seq( 653 | '[', 654 | commaSep(optional(choice( 655 | $.pattern, 656 | $.assignment_pattern, 657 | ))), 658 | ']', 659 | ), 660 | 661 | _jsx_element: $ => choice($.jsx_element, $.jsx_self_closing_element), 662 | 663 | jsx_element: $ => seq( 664 | field('open_tag', $.jsx_opening_element), 665 | repeat($._jsx_child), 666 | field('close_tag', $.jsx_closing_element), 667 | ), 668 | 669 | // An entity can be named, numeric (decimal), or numeric (hexadecimal). The 670 | // longest entity name is 29 characters long, and the HTML spec says that 671 | // no more will ever be added. 672 | html_character_reference: _ => /&(#([xX][0-9a-fA-F]{1,6}|[0-9]{1,5})|[A-Za-z]{1,30});/, 673 | 674 | jsx_expression: $ => seq( 675 | '{', 676 | optional(choice( 677 | $.expression, 678 | $.sequence_expression, 679 | $.spread_element, 680 | )), 681 | '}', 682 | ), 683 | 684 | _jsx_child: $ => choice( 685 | $.jsx_text, 686 | $.html_character_reference, 687 | $._jsx_element, 688 | $.jsx_expression, 689 | ), 690 | 691 | jsx_opening_element: $ => prec.dynamic(-1, seq( 692 | '<', 693 | optional(seq( 694 | field('name', $._jsx_element_name), 695 | repeat(field('attribute', $._jsx_attribute)), 696 | )), 697 | '>', 698 | )), 699 | 700 | jsx_identifier: _ => /[a-zA-Z_$][a-zA-Z\d_$]*-[a-zA-Z\d_$\-]*/, 701 | 702 | _jsx_identifier: $ => choice( 703 | alias($.jsx_identifier, $.identifier), 704 | $.identifier, 705 | ), 706 | 707 | nested_identifier: $ => prec('member', seq( 708 | field('object', choice($.identifier, alias($.nested_identifier, $.member_expression))), 709 | '.', 710 | field('property', alias($.identifier, $.property_identifier)), 711 | )), 712 | 713 | jsx_namespace_name: $ => seq($._jsx_identifier, ':', $._jsx_identifier), 714 | 715 | _jsx_element_name: $ => choice( 716 | $._jsx_identifier, 717 | alias($.nested_identifier, $.member_expression), 718 | $.jsx_namespace_name, 719 | ), 720 | 721 | jsx_closing_element: $ => seq( 722 | '', 725 | ), 726 | 727 | jsx_self_closing_element: $ => seq( 728 | '<', 729 | field('name', $._jsx_element_name), 730 | repeat(field('attribute', $._jsx_attribute)), 731 | '/>', 732 | ), 733 | 734 | _jsx_attribute: $ => choice($.jsx_attribute, $.jsx_expression), 735 | 736 | _jsx_attribute_name: $ => choice(alias($._jsx_identifier, $.property_identifier), $.jsx_namespace_name), 737 | 738 | jsx_attribute: $ => seq( 739 | $._jsx_attribute_name, 740 | optional(seq( 741 | '=', 742 | $._jsx_attribute_value, 743 | )), 744 | ), 745 | 746 | _jsx_string: $ => choice( 747 | seq( 748 | '"', 749 | repeat(choice( 750 | alias($.unescaped_double_jsx_string_fragment, $.string_fragment), 751 | $.html_character_reference, 752 | )), 753 | '"', 754 | ), 755 | seq( 756 | '\'', 757 | repeat(choice( 758 | alias($.unescaped_single_jsx_string_fragment, $.string_fragment), 759 | $.html_character_reference, 760 | )), 761 | '\'', 762 | ), 763 | ), 764 | 765 | // Workaround to https://github.com/tree-sitter/tree-sitter/issues/1156 766 | // We give names to the token() constructs containing a regexp 767 | // so as to obtain a node in the CST. 768 | // 769 | unescaped_double_jsx_string_fragment: _ => token.immediate(prec(1, /([^"&]|&[^#A-Za-z])+/)), 770 | 771 | // same here 772 | unescaped_single_jsx_string_fragment: _ => token.immediate(prec(1, /([^'&]|&[^#A-Za-z])+/)), 773 | 774 | _jsx_attribute_value: $ => choice( 775 | alias($._jsx_string, $.string), 776 | $.jsx_expression, 777 | $._jsx_element, 778 | ), 779 | 780 | class: $ => prec('literal', seq( 781 | repeat(field('decorator', $.decorator)), 782 | 'class', 783 | field('name', optional($.identifier)), 784 | optional($.class_heritage), 785 | field('body', $.class_body), 786 | )), 787 | 788 | class_declaration: $ => prec('declaration', seq( 789 | repeat(field('decorator', $.decorator)), 790 | 'class', 791 | field('name', $.identifier), 792 | optional($.class_heritage), 793 | field('body', $.class_body), 794 | optional($._automatic_semicolon), 795 | )), 796 | 797 | class_heritage: $ => seq('extends', $.expression), 798 | 799 | function_expression: $ => prec('literal', seq( 800 | optional('async'), 801 | 'function', 802 | field('name', optional($.identifier)), 803 | $._call_signature, 804 | field('body', $.statement_block), 805 | )), 806 | 807 | function_declaration: $ => prec.right('declaration', seq( 808 | optional('async'), 809 | 'function', 810 | field('name', $.identifier), 811 | $._call_signature, 812 | field('body', $.statement_block), 813 | optional($._automatic_semicolon), 814 | )), 815 | 816 | generator_function: $ => prec('literal', seq( 817 | optional('async'), 818 | 'function', 819 | '*', 820 | field('name', optional($.identifier)), 821 | $._call_signature, 822 | field('body', $.statement_block), 823 | )), 824 | 825 | generator_function_declaration: $ => prec.right('declaration', seq( 826 | optional('async'), 827 | 'function', 828 | '*', 829 | field('name', $.identifier), 830 | $._call_signature, 831 | field('body', $.statement_block), 832 | optional($._automatic_semicolon), 833 | )), 834 | 835 | arrow_function: $ => seq( 836 | optional('async'), 837 | choice( 838 | field('parameter', choice( 839 | alias($._reserved_identifier, $.identifier), 840 | $.identifier, 841 | )), 842 | $._call_signature, 843 | ), 844 | '=>', 845 | field('body', choice( 846 | $.expression, 847 | $.statement_block, 848 | )), 849 | ), 850 | 851 | // Override 852 | _call_signature: $ => field('parameters', $.formal_parameters), 853 | _formal_parameter: $ => choice($.pattern, $.assignment_pattern), 854 | 855 | optional_chain: _ => '?.', 856 | 857 | call_expression: $ => choice( 858 | prec('call', seq( 859 | field('function', choice($.expression, $.import)), 860 | field('arguments', $.arguments), 861 | )), 862 | prec('template_call', seq( 863 | field('function', choice($.primary_expression, $.new_expression)), 864 | field('arguments', $.template_string), 865 | )), 866 | prec('member', seq( 867 | field('function', $.primary_expression), 868 | field('optional_chain', $.optional_chain), 869 | field('arguments', $.arguments), 870 | )), 871 | ), 872 | 873 | new_expression: $ => prec.right('new', seq( 874 | 'new', 875 | field('constructor', choice($.primary_expression, $.new_expression)), 876 | field('arguments', optional(prec.dynamic(1, $.arguments))), 877 | )), 878 | 879 | await_expression: $ => prec('unary_void', seq( 880 | 'await', 881 | $.expression, 882 | )), 883 | 884 | member_expression: $ => prec('member', seq( 885 | field('object', choice($.expression, $.primary_expression, $.import)), 886 | choice('.', field('optional_chain', $.optional_chain)), 887 | field('property', choice( 888 | $.private_property_identifier, 889 | reserved('properties', alias($.identifier, $.property_identifier)), 890 | )), 891 | )), 892 | 893 | subscript_expression: $ => prec.right('member', seq( 894 | field('object', choice($.expression, $.primary_expression)), 895 | optional(field('optional_chain', $.optional_chain)), 896 | '[', field('index', $._expressions), ']', 897 | )), 898 | 899 | _lhs_expression: $ => choice( 900 | $.member_expression, 901 | $.subscript_expression, 902 | $._identifier, 903 | alias($._reserved_identifier, $.identifier), 904 | $._destructuring_pattern, 905 | ), 906 | 907 | assignment_expression: $ => prec.right('assign', seq( 908 | field('left', choice($.parenthesized_expression, $._lhs_expression)), 909 | '=', 910 | field('right', $.expression), 911 | )), 912 | 913 | _augmented_assignment_lhs: $ => choice( 914 | $.member_expression, 915 | $.subscript_expression, 916 | alias($._reserved_identifier, $.identifier), 917 | $.identifier, 918 | $.parenthesized_expression, 919 | ), 920 | 921 | augmented_assignment_expression: $ => prec.right('assign', seq( 922 | field('left', $._augmented_assignment_lhs), 923 | field('operator', choice('+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '>>=', '>>>=', 924 | '<<=', '**=', '&&=', '||=', '??=')), 925 | field('right', $.expression), 926 | )), 927 | 928 | _initializer: $ => seq( 929 | '=', 930 | field('value', $.expression), 931 | ), 932 | 933 | _destructuring_pattern: $ => choice( 934 | $.object_pattern, 935 | $.array_pattern, 936 | ), 937 | 938 | spread_element: $ => seq('...', $.expression), 939 | 940 | ternary_expression: $ => prec.right('ternary', seq( 941 | field('condition', $.expression), 942 | alias($._ternary_qmark, '?'), 943 | field('consequence', $.expression), 944 | ':', 945 | field('alternative', $.expression), 946 | )), 947 | 948 | binary_expression: $ => choice( 949 | ...[ 950 | ['&&', 'logical_and'], 951 | ['||', 'logical_or'], 952 | ['>>', 'binary_shift'], 953 | ['>>>', 'binary_shift'], 954 | ['<<', 'binary_shift'], 955 | ['&', 'bitwise_and'], 956 | ['^', 'bitwise_xor'], 957 | ['|', 'bitwise_or'], 958 | ['+', 'binary_plus'], 959 | ['-', 'binary_plus'], 960 | ['*', 'binary_times'], 961 | ['/', 'binary_times'], 962 | ['%', 'binary_times'], 963 | ['**', 'binary_exp', 'right'], 964 | ['<', 'binary_relation'], 965 | ['<=', 'binary_relation'], 966 | ['==', 'binary_equality'], 967 | ['===', 'binary_equality'], 968 | ['!=', 'binary_equality'], 969 | ['!==', 'binary_equality'], 970 | ['>=', 'binary_relation'], 971 | ['>', 'binary_relation'], 972 | ['??', 'ternary'], 973 | ['instanceof', 'binary_relation'], 974 | ['in', 'binary_relation'], 975 | ].map(([operator, precedence, associativity]) => 976 | (associativity === 'right' ? prec.right : prec.left)(precedence, seq( 977 | field('left', operator === 'in' ? choice($.expression, $.private_property_identifier) : $.expression), 978 | field('operator', operator), 979 | field('right', $.expression), 980 | )), 981 | ), 982 | ), 983 | 984 | unary_expression: $ => prec.left('unary_void', seq( 985 | field('operator', choice('!', '~', '-', '+', 'typeof', 'void', 'delete')), 986 | field('argument', $.expression), 987 | )), 988 | 989 | update_expression: $ => prec.left(choice( 990 | seq( 991 | field('argument', $.expression), 992 | field('operator', choice('++', '--')), 993 | ), 994 | seq( 995 | field('operator', choice('++', '--')), 996 | field('argument', $.expression), 997 | ), 998 | )), 999 | 1000 | sequence_expression: $ => prec.right(commaSep1($.expression)), 1001 | 1002 | // 1003 | // Primitives 1004 | // 1005 | 1006 | string: $ => choice( 1007 | seq( 1008 | '"', 1009 | repeat(choice( 1010 | alias($.unescaped_double_string_fragment, $.string_fragment), 1011 | $.escape_sequence, 1012 | )), 1013 | '"', 1014 | ), 1015 | seq( 1016 | '\'', 1017 | repeat(choice( 1018 | alias($.unescaped_single_string_fragment, $.string_fragment), 1019 | $.escape_sequence, 1020 | )), 1021 | '\'', 1022 | ), 1023 | ), 1024 | 1025 | // Workaround to https://github.com/tree-sitter/tree-sitter/issues/1156 1026 | // We give names to the token() constructs containing a regexp 1027 | // so as to obtain a node in the CST. 1028 | // 1029 | unescaped_double_string_fragment: _ => token.immediate(prec(1, /[^"\\\r\n]+/)), 1030 | 1031 | // same here 1032 | unescaped_single_string_fragment: _ => token.immediate(prec(1, /[^'\\\r\n]+/)), 1033 | 1034 | escape_sequence: _ => token.immediate(seq( 1035 | '\\', 1036 | choice( 1037 | /[^xu0-7]/, 1038 | /[0-7]{1,3}/, 1039 | /x[0-9a-fA-F]{2}/, 1040 | /u[0-9a-fA-F]{4}/, 1041 | /u\{[0-9a-fA-F]+\}/, 1042 | /[\r?][\n\u2028\u2029]/, 1043 | ), 1044 | )), 1045 | 1046 | // http://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment/36328890#36328890 1047 | comment: _ => token(choice( 1048 | seq('//', /[^\r\n\u2028\u2029]*/), 1049 | seq( 1050 | '/*', 1051 | /[^*]*\*+([^/*][^*]*\*+)*/, 1052 | '/', 1053 | ), 1054 | )), 1055 | 1056 | template_string: $ => seq( 1057 | '`', 1058 | repeat(choice( 1059 | alias($._template_chars, $.string_fragment), 1060 | $.escape_sequence, 1061 | $.template_substitution, 1062 | )), 1063 | '`', 1064 | ), 1065 | 1066 | template_substitution: $ => seq( 1067 | '${', 1068 | $._expressions, 1069 | '}', 1070 | ), 1071 | 1072 | regex: $ => seq( 1073 | '/', 1074 | field('pattern', $.regex_pattern), 1075 | token.immediate(prec(1, '/')), 1076 | optional(field('flags', $.regex_flags)), 1077 | ), 1078 | 1079 | regex_pattern: _ => token.immediate(prec(-1, 1080 | repeat1(choice( 1081 | seq( 1082 | '[', 1083 | repeat(choice( 1084 | seq('\\', /./), // escaped character 1085 | /[^\]\n\\]/, // any character besides ']' or '\n' 1086 | )), 1087 | ']', 1088 | ), // square-bracket-delimited character class 1089 | seq('\\', /./), // escaped character 1090 | /[^/\\\[\n]/, // any character besides '[', '\', '/', '\n' 1091 | )), 1092 | )), 1093 | 1094 | regex_flags: _ => token.immediate(/[a-z]+/), 1095 | 1096 | number: _ => { 1097 | const hexLiteral = seq( 1098 | choice('0x', '0X'), 1099 | /[\da-fA-F](_?[\da-fA-F])*/, 1100 | ); 1101 | 1102 | const decimalDigits = /\d(_?\d)*/; 1103 | const signedInteger = seq(optional(choice('-', '+')), decimalDigits); 1104 | const exponentPart = seq(choice('e', 'E'), signedInteger); 1105 | 1106 | const binaryLiteral = seq(choice('0b', '0B'), /[0-1](_?[0-1])*/); 1107 | 1108 | const octalLiteral = seq(choice('0o', '0O'), /[0-7](_?[0-7])*/); 1109 | 1110 | const bigintLiteral = seq(choice(hexLiteral, binaryLiteral, octalLiteral, decimalDigits), 'n'); 1111 | 1112 | const decimalIntegerLiteral = choice( 1113 | '0', 1114 | seq(optional('0'), /[1-9]/, optional(seq(optional('_'), decimalDigits))), 1115 | ); 1116 | 1117 | const decimalLiteral = choice( 1118 | seq(decimalIntegerLiteral, '.', optional(decimalDigits), optional(exponentPart)), 1119 | seq('.', decimalDigits, optional(exponentPart)), 1120 | seq(decimalIntegerLiteral, exponentPart), 1121 | decimalDigits, 1122 | ); 1123 | 1124 | return token(choice( 1125 | hexLiteral, 1126 | decimalLiteral, 1127 | binaryLiteral, 1128 | octalLiteral, 1129 | bigintLiteral, 1130 | )); 1131 | }, 1132 | 1133 | // 'undefined' is syntactically a regular identifier in JavaScript. 1134 | // However, its main use is as the read-only global variable whose 1135 | // value is [undefined], for which there's no literal representation 1136 | // unlike 'null'. We gave it its own rule so it's easy to 1137 | // highlight in text editors and other applications. 1138 | _identifier: $ => choice( 1139 | $.undefined, 1140 | $.identifier, 1141 | ), 1142 | 1143 | identifier: _ => { 1144 | const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; 1145 | 1146 | const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; 1147 | return token(seq(alpha, repeat(alphanumeric))); 1148 | }, 1149 | 1150 | private_property_identifier: _ => { 1151 | const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; 1152 | 1153 | const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; 1154 | return token(seq('#', alpha, repeat(alphanumeric))); 1155 | }, 1156 | 1157 | meta_property: _ => choice(seq('new', '.', 'target'), seq('import', '.', 'meta')), 1158 | 1159 | this: _ => 'this', 1160 | super: _ => 'super', 1161 | true: _ => 'true', 1162 | false: _ => 'false', 1163 | null: _ => 'null', 1164 | undefined: _ => 'undefined', 1165 | 1166 | // 1167 | // Expression components 1168 | // 1169 | 1170 | arguments: $ => seq( 1171 | '(', 1172 | commaSep(optional(choice($.expression, $.spread_element))), 1173 | ')', 1174 | ), 1175 | 1176 | decorator: $ => seq( 1177 | '@', 1178 | choice( 1179 | $.identifier, 1180 | alias($.decorator_member_expression, $.member_expression), 1181 | alias($.decorator_call_expression, $.call_expression), 1182 | ), 1183 | ), 1184 | 1185 | decorator_member_expression: $ => prec('member', seq( 1186 | field('object', choice( 1187 | $.identifier, 1188 | alias($.decorator_member_expression, $.member_expression), 1189 | )), 1190 | '.', 1191 | field('property', alias($.identifier, $.property_identifier)), 1192 | )), 1193 | 1194 | decorator_call_expression: $ => prec('call', seq( 1195 | field('function', choice( 1196 | $.identifier, 1197 | alias($.decorator_member_expression, $.member_expression), 1198 | )), 1199 | field('arguments', $.arguments), 1200 | )), 1201 | 1202 | class_body: $ => seq( 1203 | '{', 1204 | repeat(choice( 1205 | seq(field('member', $.method_definition), optional(';')), 1206 | seq(field('member', $.field_definition), $._semicolon), 1207 | field('member', $.class_static_block), 1208 | ';', 1209 | )), 1210 | '}', 1211 | ), 1212 | 1213 | field_definition: $ => seq( 1214 | repeat(field('decorator', $.decorator)), 1215 | optional('static'), 1216 | field('property', $._property_name), 1217 | optional($._initializer), 1218 | ), 1219 | 1220 | formal_parameters: $ => seq( 1221 | '(', 1222 | optional(seq( 1223 | commaSep1($._formal_parameter), 1224 | optional(','), 1225 | )), 1226 | ')', 1227 | ), 1228 | 1229 | class_static_block: $ => seq( 1230 | 'static', 1231 | optional($._automatic_semicolon), 1232 | field('body', $.statement_block), 1233 | ), 1234 | 1235 | // This negative dynamic precedence ensures that during error recovery, 1236 | // unfinished constructs are generally treated as literal expressions, 1237 | // not patterns. 1238 | pattern: $ => prec.dynamic(-1, choice( 1239 | $._lhs_expression, 1240 | $.rest_pattern, 1241 | )), 1242 | 1243 | rest_pattern: $ => prec.right(seq( 1244 | '...', 1245 | $._lhs_expression, 1246 | )), 1247 | 1248 | method_definition: $ => seq( 1249 | repeat(field('decorator', $.decorator)), 1250 | optional(choice( 1251 | 'static', 1252 | alias(token(seq('static', /\s+/, 'get', /\s*\n/)), 'static get'), 1253 | )), 1254 | optional('async'), 1255 | optional(choice('get', 'set', '*')), 1256 | field('name', $._property_name), 1257 | field('parameters', $.formal_parameters), 1258 | field('body', $.statement_block), 1259 | ), 1260 | 1261 | pair: $ => seq( 1262 | field('key', $._property_name), 1263 | ':', 1264 | field('value', $.expression), 1265 | ), 1266 | 1267 | pair_pattern: $ => seq( 1268 | field('key', $._property_name), 1269 | ':', 1270 | field('value', choice($.pattern, $.assignment_pattern)), 1271 | ), 1272 | 1273 | _property_name: $ => reserved('properties', choice( 1274 | alias( 1275 | choice($.identifier, $._reserved_identifier), 1276 | $.property_identifier, 1277 | ), 1278 | $.private_property_identifier, 1279 | $.string, 1280 | $.number, 1281 | $.computed_property_name, 1282 | )), 1283 | 1284 | computed_property_name: $ => seq( 1285 | '[', 1286 | $.expression, 1287 | ']', 1288 | ), 1289 | 1290 | _reserved_identifier: _ => choice( 1291 | 'get', 1292 | 'set', 1293 | 'async', 1294 | 'await', 1295 | 'static', 1296 | 'export', 1297 | 'let', 1298 | ), 1299 | 1300 | _semicolon: $ => choice($._automatic_semicolon, ';'), 1301 | }, 1302 | }); 1303 | 1304 | /** 1305 | * Creates a rule to match one or more of the rules separated by a comma 1306 | * 1307 | * @param {Rule} rule 1308 | * 1309 | * @returns {SeqRule} 1310 | */ 1311 | function commaSep1(rule) { 1312 | return seq(rule, repeat(seq(',', rule))); 1313 | } 1314 | 1315 | /** 1316 | * Creates a rule to optionally match one or more of the rules separated by a comma 1317 | * 1318 | * @param {Rule} rule 1319 | * 1320 | * @returns {ChoiceRule} 1321 | */ 1322 | function commaSep(rule) { 1323 | return optional(commaSep1(rule)); 1324 | } 1325 | --------------------------------------------------------------------------------