├── .editorconfig ├── .envrc ├── .git-blame-ignore-revs ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yml └── workflows │ ├── ci.yml │ ├── fuzz.yml │ ├── publish.yml │ └── sync.yml ├── .gitignore ├── .prettierrc.json ├── CMakeLists.txt ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── binding.gyp ├── bindings ├── c │ ├── tree-sitter-scala.h │ └── tree-sitter-scala.pc.in ├── go │ ├── binding.go │ └── binding_test.go ├── node │ ├── binding.cc │ ├── binding_test.js │ ├── index.d.ts │ └── index.js ├── python │ ├── tests │ │ └── test_binding.py │ └── tree_sitter_scala │ │ ├── __init__.py │ │ ├── __init__.pyi │ │ ├── binding.c │ │ └── py.typed ├── rust │ ├── build.rs │ └── lib.rs └── swift │ ├── TreeSitterScala │ └── scala.h │ └── TreeSitterScalaTests │ └── TreeSitterScalaTests.swift ├── examples ├── Packages.scala ├── PathResolver.scala ├── RefChecks.scala ├── SyntaxAnalyer.scala └── Variance.scala ├── go.mod ├── go.sum ├── grammar.js ├── package-lock.json ├── package.json ├── pyproject.toml ├── queries ├── highlights.scm ├── indents.scm ├── locals.scm └── tags.scm ├── script ├── parse-with-scalac └── smoke_test.sh ├── setup.py ├── src ├── grammar.json ├── node-types.json ├── parser.c ├── scanner.c └── tree_sitter │ ├── alloc.h │ ├── array.h │ └── parser.h ├── test ├── corpus │ ├── annotations.txt │ ├── comments.txt │ ├── definitions-pending.txt │ ├── definitions.txt │ ├── expressions-pending.txt │ ├── expressions.txt │ ├── literals.txt │ ├── patterns-pending.txt │ ├── patterns.txt │ ├── types-pending.txt │ └── types.txt ├── highlight │ ├── basics.scala │ ├── comments.scala │ └── scala3.scala └── tags │ └── basics.scala └── tree-sitter.json /.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 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use nix 2 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Move queries to ./queries/scala sub-directory 2 | 889eb65aea8e0f9ce1694ab9ee528df1fa5f75d1 3 | 4 | # Formatting using Prettier 5 | 0a8ca4c836d7e9f0fd6a0668088c0d82874abc85 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create an issue in tree-sitter-scala 3 | body: 4 | - type: input 5 | id: commit 6 | attributes: 7 | label: Commit of tree-sitter-scala you tested this on 8 | description: | 9 | Make sure you're validating you're on the latest commit of 10 | tree-sitter-scala. Note that depending on where you're experiencing the 11 | issue there is a chance you're on an old commit. 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: code-sample 17 | attributes: 18 | label: A code sample showing the error 19 | description: Try to minimize the code sample as much as possible 20 | placeholder: | 21 | ```scala 22 | val y = x match { 23 | case A(_) if a => 1 24 | } 25 | ``` 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: error 31 | attributes: 32 | label: Show the error node 33 | description: Show us the output and where the ERROR node is appearing 34 | placeholder: | 35 | Check out [Obtaining an Error Reproduction](../../CONTRIBUTING.md#obtaining-an-error-reproduction) 36 | in the CONTRIBUTING docs if you're unsure how to get this. 37 | 38 | ``` 39 | (compilation_unit [0, 0] - [4, 0] 40 | (val_definition [0, 0] - [2, 1] 41 | pattern: (identifier [0, 4] - [0, 5]) 42 | value: (match_expression [0, 8] - [2, 1] 43 | value: (identifier [0, 8] - [0, 9]) 44 | body: (case_block [0, 16] - [2, 1] 45 | (ERROR [1, 2] - [2, 0] 46 | (case_class_pattern [1, 7] - [1, 11] 47 | type: (type_identifier [1, 7] - [1, 8]) 48 | pattern: (wildcard [1, 9] - [1, 10])) 49 | (lambda_expression [1, 15] - [2, 0] 50 | (identifier [1, 15] - [1, 16]) 51 | (integer_literal [1, 20] - [1, 21]))))))) 52 | ``` 53 | validations: 54 | required: true 55 | 56 | - type: textarea 57 | id: expectation 58 | attributes: 59 | label: What do you expect the tree to look like 60 | description: Show us what you expect the tree to look like 61 | 62 | - type: input 63 | id: where 64 | attributes: 65 | label: Where are you experiencing this error? 66 | description: Let us know where you're seeing this error (nvim-treesitter etc) 67 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build/test 2 | on: 3 | push: 4 | pull_request: 5 | branches: 6 | - master 7 | jobs: 8 | changedfiles: 9 | runs-on: ubuntu-latest 10 | outputs: 11 | all: ${{ steps.changes.outputs.all}} 12 | c: ${{ steps.changes.outputs.c }} 13 | gen: ${{ steps.changes.outputs.gen }} 14 | steps: 15 | - name: checkout tree-sitter-scala 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 10 19 | 20 | - name: Get changed files 21 | id: changes 22 | run: | 23 | echo "all=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | xargs)" >> $GITHUB_OUTPUT 24 | echo "c=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep '\.\(c\|h\)$' | xargs)" >> $GITHUB_OUTPUT 25 | # Generated C code 26 | echo "gen=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep '\.\(c\)$' | grep -v 'src/scanner.c' | grep -v 'bindings/python/tree_sitter_scala/binding.c' | xargs)" >> $GITHUB_OUTPUT 27 | 28 | test: 29 | runs-on: ${{ matrix.os }} 30 | needs: changedfiles 31 | strategy: 32 | fail-fast: true 33 | matrix: 34 | os: [ubuntu-latest, macos-latest, windows-latest] 35 | steps: 36 | - name: checkout tree-sitter-scala 37 | uses: actions/checkout@v4 38 | with: 39 | fetch-depth: 10 40 | 41 | - name: checkout scala/scala 42 | if: ${{ runner.os == 'Linux' }} 43 | uses: actions/checkout@v4 44 | with: 45 | repository: scala/scala 46 | ref: v2.13.14 47 | path: scala_scala 48 | 49 | - name: checkout scala/scala3 50 | if: ${{ runner.os == 'Linux' }} 51 | uses: actions/checkout@v4 52 | with: 53 | repository: scala/scala3 54 | ref: 3.5.0-RC2 55 | path: dotty 56 | 57 | - name: checkout lichess-org/lila 58 | if: ${{ runner.os == 'Linux' }} 59 | uses: actions/checkout@v4 60 | with: 61 | repository: lichess-org/lila 62 | ref: 83e61b9ef617164fe1d3a5112fcc611d0e5a7ea9 63 | path: lila 64 | 65 | - name: checkout nvim-treesitter/nvim-treesitter 66 | if: ${{ runner.os == 'Linux' }} 67 | uses: actions/checkout@v4 68 | with: 69 | repository: nvim-treesitter/nvim-treesitter 70 | path: nvim_treesitter 71 | 72 | - uses: actions/setup-node@v4 73 | with: 74 | node-version: 20 75 | 76 | - name: Set up tree-sitter 77 | uses: tree-sitter/setup-action/cli@v1 78 | with: 79 | tree-sitter-ref: v0.24.7 80 | 81 | - name: Generate parser from scratch and test it 82 | shell: bash 83 | run: tree-sitter generate 84 | 85 | - name: Run parser and binding tests 86 | uses: tree-sitter/parser-test-action@v2 87 | with: 88 | generate: false 89 | test-rust: true 90 | test-node: true 91 | test-python: true 92 | test-go: true 93 | test-swift: false 94 | 95 | - name: Parse sample files 96 | uses: tree-sitter/parse-action@v4 97 | id: parse-files 98 | with: 99 | files: examples/** 100 | 101 | - name: Check fidelity of checked-in C code 102 | if: ${{ runner.os == 'Linux' && needs.changedfiles.outputs.gen }} 103 | shell: bash 104 | run: | 105 | # `git diff --quiet` doesn't seem to work on Github Actions 106 | changes=$(git diff --name-only --diff-filter=ACMRT | xargs) 107 | if [ ! -z "$changes" ]; then 108 | echo "::error file=grammar.js::Generated $changes differs from the checked in version" 109 | git diff --exit-code 110 | exit 1 111 | fi 112 | 113 | - name: Smoke test 114 | if: ${{ runner.os == 'Linux' }} 115 | shell: bash 116 | env: 117 | SCALA_SCALA_DIR: scala_scala 118 | DOTTY_DIR: dotty 119 | LILA_DIR: lila 120 | run: script/smoke_test.sh 121 | 122 | - name: Check that prettier exists 123 | if: ${{ runner.os == 'Linux' }} 124 | run: | 125 | npm install 126 | export PATH=./node_modules/.bin:$PATH 127 | prettier --write --ignore-unknown grammar.js 128 | 129 | - name: copy nvim-treesitter queries 130 | if: ${{ runner.os == 'Linux' }} 131 | shell: bash 132 | run: cp ./nvim_treesitter/queries/scala/*.scm ./queries/ 133 | 134 | - name: Check if queries are out of sync with nvim-treesitter 135 | if: ${{ runner.os == 'Linux' }} 136 | uses: tj-actions/verify-changed-files@v19 137 | id: verify-changed-files 138 | with: 139 | files: | 140 | queries/*.scm 141 | 142 | # TODO: uncomment when this works 143 | # - name: Test quries if out of sync with nvim-treesitter 144 | # if: steps.verify-changed-files.outputs.files_changed == 'true' 145 | # run: | 146 | # echo "::warning Queries in ${{ steps.verify-changed-files.outputs.changed_files }} in this repo are out of sync with nvim-treesitter" 147 | # git diff queries/ 148 | # npm run test 149 | -------------------------------------------------------------------------------- /.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@v4 18 | - name: Run fuzzer 19 | uses: tree-sitter/fuzz-action@v4 20 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: "Check changes and sync" 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: 0 5 * * * 6 | 7 | jobs: 8 | check-and-sync: 9 | runs-on: ubuntu-latest 10 | if: github.repository == 'tree-sitter/tree-sitter-scala' 11 | outputs: 12 | all: ${{ steps.changes.outputs.all}} 13 | c: ${{ steps.changes.outputs.c }} 14 | steps: 15 | - name: checkout tree-sitter-scala 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 10 19 | 20 | - name: Set up tree-sitter 21 | uses: tree-sitter/setup-action/cli@v1 22 | with: 23 | tree-sitter-ref: v0.24.7 24 | 25 | - name: Generate parser from scratch 26 | shell: bash 27 | run: tree-sitter generate 28 | 29 | - name: Format Javascipt 30 | run: | 31 | npm install 32 | export PATH=./node_modules/.bin:$PATH 33 | prettier --write --ignore-unknown grammar.js 34 | 35 | - name: Check for changes 36 | uses: tj-actions/verify-changed-files@v19 37 | id: verify-changed-files 38 | with: 39 | files: | 40 | bindings/c/tree-sitter-scala.h 41 | bindings/c/tree-sitter-scala.pc.in 42 | grammar.js 43 | src/grammar.json 44 | src/node-types.json 45 | src/parser.c 46 | src/tree_sitter/alloc.h 47 | src/tree_sitter/array.h 48 | src/tree_sitter/parser.h 49 | 50 | - name: Commit changes if necessary 51 | if: steps.verify-changed-files.outputs.files_changed == 'true' 52 | run: | 53 | git config user.name "GitHub" 54 | git config user.email "noreply@github.com" 55 | git add . 56 | LAST_COMMIT=$(git log --format="%H" -n 1) 57 | git commit -m "chore: generate and sync from $LAST_COMMIT" 58 | git clean -xf 59 | 60 | - name: Create Pull Request 61 | if: steps.verify-changed-files.outputs.files_changed == 'true' 62 | uses: peter-evans/create-pull-request@v4 63 | with: 64 | title: "chore: generate and sync latest changes" 65 | branch: generation 66 | base: ${{ github.head_ref }} 67 | 68 | - name: No changes detected 69 | if: steps.verify-changed-files.outputs.files_changed == 'false' 70 | run: echo "No changes detected" 71 | -------------------------------------------------------------------------------- /.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 | 29 | # Example dirs 30 | /examples/*/ 31 | 32 | # Grammar volatiles 33 | *.wasm 34 | *.obj 35 | *.o 36 | 37 | # Archives 38 | *.tar.gz 39 | *.tgz 40 | *.zip 41 | 42 | # Scala tooling 43 | .metals 44 | .bsp 45 | .scala-build 46 | 47 | # AI assistants 48 | .aider* 49 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid" 3 | } 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(tree-sitter-scala 4 | VERSION "0.24.0" 5 | DESCRIPTION "Scala grammar for tree-sitter" 6 | HOMEPAGE_URL "https://github.com/tree-sitter/tree-sitter-scala" 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 14 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 | find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") 19 | 20 | add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 21 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" 22 | COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json 23 | --abi=${TREE_SITTER_ABI_VERSION} 24 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 25 | COMMENT "Generating parser.c") 26 | 27 | add_library(tree-sitter-scala src/parser.c) 28 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) 29 | target_sources(tree-sitter-scala PRIVATE src/scanner.c) 30 | endif() 31 | target_include_directories(tree-sitter-scala PRIVATE src) 32 | 33 | target_compile_definitions(tree-sitter-scala PRIVATE 34 | $<$:TREE_SITTER_REUSE_ALLOCATOR> 35 | $<$:TREE_SITTER_DEBUG>) 36 | 37 | set_target_properties(tree-sitter-scala 38 | PROPERTIES 39 | C_STANDARD 11 40 | POSITION_INDEPENDENT_CODE ON 41 | SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" 42 | DEFINE_SYMBOL "") 43 | 44 | configure_file(bindings/c/tree-sitter-scala.pc.in 45 | "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-scala.pc" @ONLY) 46 | 47 | include(GNUInstallDirs) 48 | 49 | install(FILES bindings/c/tree-sitter-scala.h 50 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/tree_sitter") 51 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-scala.pc" 52 | DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig") 53 | install(TARGETS tree-sitter-scala 54 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") 55 | 56 | add_custom_target(ts-test "${TREE_SITTER_CLI}" test 57 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 58 | COMMENT "tree-sitter test") 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to tree-sitter-scala 2 | 3 | First off, thanks for being willing to contribute to making syntax highlighting 4 | in Scala better! This document will hopefully help you get set up, understand 5 | how to work on the codebase, or link to places to help you understand 6 | tree-sitter. 7 | 8 | ## Requirements 9 | 10 | - [tree-sitter CLI](https://github.com/tree-sitter/tree-sitter/tree/master/cli) 11 | - Node.js version 18.0 or greater 12 | - C Compiler 13 | 14 | Refer to the [tree-sitter 15 | documentation](https://tree-sitter.github.io/tree-sitter/creating-parsers/1-getting-started.html) 16 | for more details and specifics. 17 | 18 | If you use nix you can enter a nix-shell (`nix-shell .`) which will install them 19 | for you. 20 | 21 | ## Getting Started 22 | 23 | To get started with contributing to the grammar you'll first need to install the 24 | project's dependencies: 25 | 26 | ```sh 27 | npm install 28 | ``` 29 | 30 | The general flow will often start with adding a test case to `./test/corpus`. You can 31 | find details on how testing works with tree-sitter 32 | [here](https://tree-sitter.github.io/tree-sitter/creating-parsers/5-writing-tests.html). 33 | 34 | Once you've added your test case you'll want to then make the required changes 35 | to `grammar.js`, regenerate and recompile the parser, and run the tests: 36 | 37 | ```sh 38 | tree-sitter generate 39 | tree-sitter test 40 | ``` 41 | 42 | Then adjust as necessary. Note that depending on your change you may also have 43 | to touch the `/src/scanner.c` file if you need more advanced features like 44 | look-ahead. 45 | 46 | ## Pending Tests 47 | 48 | In `./test/corpus`, there are several files named `*-pending.txt`. These contain tests labeled with `:skip`, meaning they are not currently run. Each test includes examples of valid Scala syntax that `tree-sitter-scala` is known to fail to parse. If you’d like to contribute to `tree-sitter-scala`, a good place to start is by trying to fix one of these tests. 49 | 50 | ## Syntax highlighting 51 | 52 | Right now the most common use-case for syntax highlight using tree-sitter is 53 | [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter), which 54 | means much of our testing is in relation to it. You can find the syntax 55 | highlighting tests in `test/highlight/*.scala`. You can read more about this 56 | type of testing 57 | [here](https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#unit-testing). 58 | These test will be run automatically with `tree-sitter test`. 59 | 60 | ### tree-sitter highlight 61 | 62 | Another way to test your syntax highlighting locally is to use the `tree-sitter 63 | highlight` command. Note that you'll need to have `tree-sitter` installed 64 | globally for this to work. Once you have it installed you'll want to follow the 65 | instructions [here](https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#overview) 66 | to setup a local config that points towards this repo to be used as a parser. 67 | Once done you can then do the following: 68 | 69 | ```sh 70 | tree-sitter highlight some/scala/file.scala 71 | ``` 72 | 73 | And see the syntax highlighting spit out. This is also the format used for 74 | testing, so it provides an easy way to get the output we use for testing. 75 | 76 | ## Generation 77 | 78 | In order to help not cause conflicts with multiple prs being open at the same 79 | time, we don't check in the parser on each pr. Instead, just check in the 80 | changes to the `grammar.js` and the CI will take care of the rest. Each night if 81 | changes are detected it will automatically be generated. 82 | 83 | ## Smoke testing 84 | 85 | You'll noticed that there is a part of CI that checks parsing against the Scala 86 | 2 and Scala 3 repositories to ensure we don't introduce unexpected regressions. 87 | The script for this is in `script/smoke_test.sh`. If you're change is increasing 88 | the coverage in either the library or compiler, please do update the expected 89 | percentages at the top of the file. 90 | 91 | ## Obtaining an error reproduction 92 | 93 | _With Neovim_ 94 | 95 | When creating an issue you'll want to ensure to include the `ERROR` node in it's 96 | context. There's a couple ways to do this. If you're using Neovim and utilizing 97 | treesitter, you can see the tree with `:lua vim.treesitter.show_tree()`. You can 98 | copy it directly from the open panel. 99 | 100 | _Manually_ 101 | 102 | A manual way to get this would be to ensure that when you're in the repo you 103 | have the latest parser with 104 | 105 | ```sh 106 | npm run build 107 | ``` 108 | 109 | Then you can copy your Scala code in a file and pass that file into `npm run 110 | parse`: 111 | 112 | ``` 113 | tree-sitter parse 114 | ``` 115 | 116 | Then the tree will be printed out for you to copy. 117 | 118 | ## Release 119 | 120 | 1. Update `version` in `tree-sitte.json` 121 | 2. Update `version` in `package.json`, then run `npm install` 122 | 3. Update `VERSION` in `CMakeLists.txt` 123 | 4. Update `VERSION` in `Makefile` 124 | 5. Update `version` in `Cargo.toml`, then run `cargo build` 125 | 6. Update `version` in `pyproject.toml` 126 | -------------------------------------------------------------------------------- /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.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" 19 | dependencies = [ 20 | "shlex", 21 | ] 22 | 23 | [[package]] 24 | name = "memchr" 25 | version = "2.7.4" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 28 | 29 | [[package]] 30 | name = "regex" 31 | version = "1.11.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 34 | dependencies = [ 35 | "aho-corasick", 36 | "memchr", 37 | "regex-automata", 38 | "regex-syntax", 39 | ] 40 | 41 | [[package]] 42 | name = "regex-automata" 43 | version = "0.4.9" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 46 | dependencies = [ 47 | "aho-corasick", 48 | "memchr", 49 | "regex-syntax", 50 | ] 51 | 52 | [[package]] 53 | name = "regex-syntax" 54 | version = "0.8.5" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 57 | 58 | [[package]] 59 | name = "shlex" 60 | version = "1.3.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 63 | 64 | [[package]] 65 | name = "streaming-iterator" 66 | version = "0.1.9" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" 69 | 70 | [[package]] 71 | name = "tree-sitter" 72 | version = "0.24.6" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "5f2434c86ba59ed15af56039cc5bf1acf8ba76ce301e32ef08827388ef285ec5" 75 | dependencies = [ 76 | "cc", 77 | "regex", 78 | "regex-syntax", 79 | "streaming-iterator", 80 | "tree-sitter-language", 81 | ] 82 | 83 | [[package]] 84 | name = "tree-sitter-language" 85 | version = "0.1.3" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "c199356c799a8945965bb5f2c55b2ad9d9aa7c4b4f6e587fe9dea0bc715e5f9c" 88 | 89 | [[package]] 90 | name = "tree-sitter-scala" 91 | version = "0.24.0" 92 | dependencies = [ 93 | "cc", 94 | "tree-sitter", 95 | "tree-sitter-language", 96 | ] 97 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-scala" 3 | description = "Scala grammar for tree-sitter" 4 | version = "0.24.0" 5 | authors = ["Max Brunsfeld "] 6 | license = "MIT" 7 | readme = "README.md" 8 | keywords = ["incremental", "parsing", "tree-sitter", "scala"] 9 | categories = ["parsing", "text-editors"] 10 | repository = "https://github.com/tree-sitter/tree-sitter-scala" 11 | edition = "2021" 12 | autoexamples = false 13 | 14 | build = "bindings/rust/build.rs" 15 | include = ["LICENSE", "bindings/rust/*", "grammar.js", "queries/*", "src/*", "tree-sitter.json"] 16 | 17 | [lib] 18 | path = "bindings/rust/lib.rs" 19 | 20 | [dependencies] 21 | tree-sitter-language = "0.1" 22 | 23 | [build-dependencies] 24 | cc = "1.1" 25 | 26 | [dev-dependencies] 27 | tree-sitter = "0.24" 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Max Brunsfeld and GitHub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | $(error Windows is not supported) 3 | endif 4 | 5 | LANGUAGE_NAME := tree-sitter-scala 6 | HOMEPAGE_URL := https://github.com/tree-sitter/tree-sitter-scala 7 | VERSION := 0.24.0 8 | 9 | # repository 10 | SRC_DIR := src 11 | 12 | TS ?= tree-sitter 13 | 14 | # install directory layout 15 | PREFIX ?= /usr/local 16 | INCLUDEDIR ?= $(PREFIX)/include 17 | LIBDIR ?= $(PREFIX)/lib 18 | PCLIBDIR ?= $(LIBDIR)/pkgconfig 19 | 20 | # source/object files 21 | PARSER := $(SRC_DIR)/parser.c 22 | EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c)) 23 | OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS)) 24 | 25 | # flags 26 | ARFLAGS ?= rcs 27 | override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC 28 | 29 | # ABI versioning 30 | SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER)) 31 | SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION))) 32 | 33 | # OS-specific bits 34 | ifeq ($(shell uname),Darwin) 35 | SOEXT = dylib 36 | SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT) 37 | SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT) 38 | LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks 39 | else 40 | SOEXT = so 41 | SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) 42 | SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) 43 | LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) 44 | endif 45 | ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) 46 | PCLIBDIR := $(PREFIX)/libdata/pkgconfig 47 | endif 48 | 49 | all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc 50 | 51 | lib$(LANGUAGE_NAME).a: $(OBJS) 52 | $(AR) $(ARFLAGS) $@ $^ 53 | 54 | lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS) 55 | $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@ 56 | ifneq ($(STRIP),) 57 | $(STRIP) $@ 58 | endif 59 | 60 | $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in 61 | sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \ 62 | -e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \ 63 | -e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \ 64 | -e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \ 65 | -e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \ 66 | -e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@ 67 | 68 | $(PARSER): $(SRC_DIR)/grammar.json 69 | $(TS) generate $^ 70 | 71 | install: all 72 | install -d '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)' 73 | install -m644 bindings/c/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h 74 | install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 75 | install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a 76 | install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) 77 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) 78 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) 79 | 80 | uninstall: 81 | $(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \ 82 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \ 83 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \ 84 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \ 85 | '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \ 86 | '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 87 | 88 | clean: 89 | $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) 90 | 91 | test: 92 | $(TS) test 93 | 94 | .PHONY: all install uninstall clean test 95 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "TreeSitterScala", 6 | products: [ 7 | .library(name: "TreeSitterScala", targets: ["TreeSitterScala"]), 8 | ], 9 | dependencies: [ 10 | .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.8.0"), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "TreeSitterScala", 15 | dependencies: [], 16 | path: ".", 17 | sources: [ 18 | "src/parser.c", 19 | "src/scanner.c", 20 | ], 21 | resources: [ 22 | .copy("queries") 23 | ], 24 | publicHeadersPath: "bindings/swift", 25 | cSettings: [.headerSearchPath("src")] 26 | ), 27 | .testTarget( 28 | name: "TreeSitterScalaTests", 29 | dependencies: [ 30 | "SwiftTreeSitter", 31 | "TreeSitterScala", 32 | ], 33 | path: "bindings/swift/TreeSitterScalaTests" 34 | ) 35 | ], 36 | cLanguageStandard: .c11 37 | ) 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-scala 2 | 3 | [![CI][ci]](https://github.com/tree-sitter/tree-sitter-scala/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-scala) 7 | [![npm][npm]](https://www.npmjs.com/package/tree-sitter-scala) 8 | 9 | Scala grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter) 10 | covering both Scala 2 and 3. 11 | 12 | ## References 13 | 14 | _Scala 2_ 15 | 16 | - [The Scala 2 Language Specification](https://www.scala-lang.org/files/archive/spec/2.13/) 17 | - [Scala 2 Syntax Summary](https://www.scala-lang.org/files/archive/spec/2.13/13-syntax-summary.html) 18 | 19 | _Scala 3_ 20 | 21 | - [Scala 3 Syntax Summary](https://docs.scala-lang.org/scala3/reference/syntax.html) 22 | 23 | ## Development and Contributing 24 | 25 | Please refer to the [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on 26 | getting set up. 27 | 28 | [ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter/tree-sitter-scala/ci.yml?logo=github&label=CI 29 | [discord]: https://img.shields.io/discord/1063097320771698699?logo=discord&label=discord 30 | [matrix]: https://img.shields.io/matrix/tree-sitter-chat%3Amatrix.org?logo=matrix&label=matrix 31 | [npm]: https://img.shields.io/npm/v/tree-sitter-scala?logo=npm 32 | [crates]: https://img.shields.io/crates/v/tree-sitter-scala?logo=rust 33 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_scala_binding", 5 | "dependencies": [ 6 | " 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | extern "C" TSLanguage *tree_sitter_scala(); 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 | exports["name"] = Napi::String::New(env, "scala"); 14 | auto language = Napi::External::New(env, tree_sitter_scala()); 15 | language.TypeTag(&LANGUAGE_TYPE_TAG); 16 | exports["language"] = language; 17 | return exports; 18 | } 19 | 20 | NODE_API_MODULE(tree_sitter_scala_binding, Init) 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | name: string; 23 | language: unknown; 24 | nodeTypeInfo: NodeInfo[]; 25 | }; 26 | 27 | declare const language: Language; 28 | export = language; 29 | -------------------------------------------------------------------------------- /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-scala.node`) 7 | : require("node-gyp-build")(root); 8 | 9 | try { 10 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 11 | } catch (_) {} 12 | -------------------------------------------------------------------------------- /bindings/python/tests/test_binding.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import tree_sitter, tree_sitter_scala 4 | 5 | 6 | class TestLanguage(TestCase): 7 | def test_can_load_grammar(self): 8 | try: 9 | tree_sitter.Language(tree_sitter_scala.language()) 10 | except Exception: 11 | self.fail("Error loading Scala grammar") 12 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_scala/__init__.py: -------------------------------------------------------------------------------- 1 | """Scala 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 == "LOCALS_QUERY": 18 | return _get_query("LOCALS_QUERY", "locals.scm") 19 | 20 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 21 | 22 | 23 | __all__ = [ 24 | "language", 25 | "HIGHLIGHTS_QUERY", 26 | "LOCALS_QUERY", 27 | ] 28 | 29 | 30 | def __dir__(): 31 | return sorted(__all__ + [ 32 | "__all__", "__builtins__", "__cached__", "__doc__", "__file__", 33 | "__loader__", "__name__", "__package__", "__path__", "__spec__", 34 | ]) 35 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_scala/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Final 2 | 3 | HIGHLIGHTS_QUERY: Final[str] 4 | LOCALS_QUERY: Final[str] 5 | 6 | def language() -> object: ... 7 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_scala/binding.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | TSLanguage *tree_sitter_scala(void); 6 | 7 | static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 8 | return PyCapsule_New(tree_sitter_scala(), "tree_sitter.Language", NULL); 9 | } 10 | 11 | static PyMethodDef methods[] = { 12 | {"language", _binding_language, METH_NOARGS, 13 | "Get the tree-sitter language for this grammar."}, 14 | {NULL, NULL, 0, NULL} 15 | }; 16 | 17 | static struct PyModuleDef module = { 18 | .m_base = PyModuleDef_HEAD_INIT, 19 | .m_name = "_binding", 20 | .m_doc = NULL, 21 | .m_size = -1, 22 | .m_methods = methods 23 | }; 24 | 25 | PyMODINIT_FUNC PyInit__binding(void) { 26 | return PyModule_Create(&module); 27 | } 28 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_scala/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tree-sitter/tree-sitter-scala/2d55e74b0485fe05058ffe5e8155506c9710c767/bindings/python/tree_sitter_scala/py.typed -------------------------------------------------------------------------------- /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"); 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 | c_config.file(&scanner_path); 19 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 20 | 21 | c_config.compile("tree-sitter-scala"); 22 | } 23 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides Scala 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 | //! use tree_sitter::Parser; 8 | //! 9 | //! let code = r#" 10 | //! object Hello { 11 | //! def main(args: Array[String]) = { 12 | //! println("Hello, world") 13 | //! } 14 | //! } 15 | //! "#; 16 | //! let mut parser = Parser::new(); 17 | //! let language = tree_sitter_scala::LANGUAGE; 18 | //! parser 19 | //! .set_language(&language.into()) 20 | //! .expect("Error loading Scala parser"); 21 | //! let tree = parser.parse(code, None).unwrap(); 22 | //! assert!(!tree.root_node().has_error()); 23 | //! ``` 24 | //! 25 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 26 | //! [tree-sitter]: https://tree-sitter.github.io/ 27 | 28 | use tree_sitter_language::LanguageFn; 29 | 30 | extern "C" { 31 | fn tree_sitter_scala() -> *const (); 32 | } 33 | 34 | /// The tree-sitter [`LanguageFn`][LanguageFn] for this grammar. 35 | /// 36 | /// [LanguageFn]: https://docs.rs/tree-sitter-language/*/tree_sitter_language/struct.LanguageFn.html 37 | pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_scala) }; 38 | 39 | /// The content of the [`node-types.json`][] file for this grammar. 40 | /// 41 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 42 | pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 43 | 44 | /// The syntax highlighting query for this language. 45 | pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.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 | #[cfg(test)] 51 | mod tests { 52 | #[test] 53 | fn test_can_load_grammar() { 54 | let mut parser = tree_sitter::Parser::new(); 55 | parser 56 | .set_language(&super::LANGUAGE.into()) 57 | .expect("Error loading Scala parser"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterScala/scala.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_SCALA_H_ 2 | #define TREE_SITTER_SCALA_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_scala(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_SCALA_H_ 17 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterScalaTests/TreeSitterScalaTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftTreeSitter 3 | import TreeSitterScala 4 | 5 | final class TreeSitterScalaTests: XCTestCase { 6 | func testCanLoadGrammar() throws { 7 | let parser = Parser() 8 | let language = Language(language: tree_sitter_scala()) 9 | XCTAssertNoThrow(try parser.setLanguage(language), 10 | "Error loading Scala grammar") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/Packages.scala: -------------------------------------------------------------------------------- 1 | package a.b 2 | package c { 3 | object A 4 | } 5 | package c { 6 | package object d { 7 | val hello: String = "there" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/PathResolver.scala: -------------------------------------------------------------------------------- 1 | /* NSC -- new Scala compiler 2 | * Copyright 2006-2013 LAMP/EPFL 3 | * @author Paul Phillips 4 | */ 5 | 6 | package scala 7 | package tools 8 | package util 9 | 10 | import java.net.URL 11 | import scala.tools.reflect.WrappedProperties.AccessControl 12 | import scala.tools.nsc.Settings 13 | import scala.tools.nsc.util.ClassPath 14 | import scala.reflect.io.{Directory, File, Path} 15 | import PartialFunction.condOpt 16 | import scala.tools.nsc.classpath._ 17 | 18 | // Loosely based on the draft specification at: 19 | // https://wiki.scala-lang.org/display/SIW/Classpath 20 | 21 | object PathResolver { 22 | // Imports property/environment functions which suppress security exceptions. 23 | import AccessControl._ 24 | import java.lang.System.{lineSeparator => EOL} 25 | 26 | implicit class MkLines(val t: TraversableOnce[_]) extends AnyVal { 27 | def mkLines: String = t.mkString("", EOL, EOL) 28 | def mkLines(header: String, indented: Boolean = false, embraced: Boolean = false): String = { 29 | val space = "\u0020" 30 | val sep = if (indented) EOL + space * 2 else EOL 31 | val (lbrace, rbrace) = if (embraced) (space + "{", EOL + "}") else ("", "") 32 | t.mkString(header + lbrace + sep, sep, rbrace + EOL) 33 | } 34 | } 35 | implicit class AsLines(val s: String) extends AnyVal { 36 | // sm"""...""" could do this in one pass 37 | def asLines = s.trim.stripMargin.lines.mkLines 38 | } 39 | 40 | /** pretty print class path */ 41 | def ppcp(s: String) = ClassPath.split(s) match { 42 | case Nil => "" 43 | case Seq(x) => x 44 | case xs => xs.mkString(EOL, EOL, "") 45 | } 46 | 47 | /** Values found solely by inspecting environment or property variables. 48 | */ 49 | object Environment { 50 | import scala.collection.JavaConverters._ 51 | 52 | private def searchForBootClasspath = 53 | System.getProperties.asScala collectFirst { case (k, v) if k endsWith ".boot.class.path" => v } getOrElse "" 54 | 55 | /** Environment variables which java pays attention to so it 56 | * seems we do as well. 57 | */ 58 | def sourcePathEnv = envOrElse("SOURCEPATH", "") 59 | 60 | def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath) 61 | def javaExtDirs = propOrEmpty("java.ext.dirs") 62 | def scalaHome = propOrEmpty("scala.home") 63 | def scalaExtDirs = propOrEmpty("scala.ext.dirs") 64 | 65 | /** The java classpath and whether to use it. */ 66 | def javaUserClassPath = propOrElse("java.class.path", "") 67 | def useJavaClassPath = propOrFalse("scala.usejavacp") 68 | 69 | override def toString = s""" 70 | |object Environment { 71 | | scalaHome = $scalaHome (useJavaClassPath = $useJavaClassPath) 72 | | javaBootClassPath = <${javaBootClassPath.length} chars> 73 | | javaExtDirs = ${ppcp(javaExtDirs)} 74 | | javaUserClassPath = ${ppcp(javaUserClassPath)} 75 | | scalaExtDirs = ${ppcp(scalaExtDirs)} 76 | |}""".asLines 77 | } 78 | 79 | /** Default values based on those in Environment as interpreted according 80 | * to the path resolution specification. 81 | */ 82 | object Defaults { 83 | def scalaSourcePath = Environment.sourcePathEnv 84 | def javaBootClassPath = Environment.javaBootClassPath 85 | def javaUserClassPath = Environment.javaUserClassPath 86 | def javaExtDirs = Environment.javaExtDirs 87 | def useJavaClassPath = Environment.useJavaClassPath 88 | 89 | def scalaHome = Environment.scalaHome 90 | def scalaHomeDir = Directory(scalaHome) 91 | def scalaLibDir = Directory(scalaHomeDir / "lib") 92 | def scalaClassesDir = Directory(scalaHomeDir / "classes") 93 | 94 | def scalaLibAsJar = File(scalaLibDir / "scala-library.jar") 95 | def scalaLibAsDir = Directory(scalaClassesDir / "library") 96 | 97 | def scalaLibDirFound: Option[Directory] = 98 | if (scalaLibAsJar.isFile) Some(scalaLibDir) 99 | else if (scalaLibAsDir.isDirectory) Some(scalaClassesDir) 100 | else None 101 | 102 | def scalaLibFound = 103 | if (scalaLibAsJar.isFile) scalaLibAsJar.path 104 | else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path 105 | else "" 106 | 107 | // TODO It must be time for someone to figure out what all these things 108 | // are intended to do. This is disabled here because it was causing all 109 | // the scala jars to end up on the classpath twice: one on the boot 110 | // classpath as set up by the runner (or regular classpath under -nobootcp) 111 | // and then again here. 112 | def scalaBootClassPath = "" 113 | def scalaExtDirs = Environment.scalaExtDirs 114 | def scalaPluginPath = (scalaHomeDir / "misc" / "scala-devel" / "plugins").path 115 | 116 | override def toString = s""" 117 | |object Defaults { 118 | | scalaHome = $scalaHome 119 | | javaBootClassPath = ${ppcp(javaBootClassPath)} 120 | | scalaLibDirFound = $scalaLibDirFound 121 | | scalaLibFound = $scalaLibFound 122 | | scalaBootClassPath = ${ppcp(scalaBootClassPath)} 123 | | scalaPluginPath = ${ppcp(scalaPluginPath)} 124 | |}""".asLines 125 | } 126 | 127 | /** Locations discovered by supplemental heuristics. 128 | */ 129 | object SupplementalLocations { 130 | 131 | /** The platform-specific support jar. 132 | * 133 | * Usually this is `tools.jar` in the jdk/lib directory of the platform distribution. 134 | * 135 | * The file location is determined by probing the lib directory under JDK_HOME or JAVA_HOME, 136 | * if one of those environment variables is set, then the lib directory under java.home, 137 | * and finally the lib directory under the parent of java.home. Or, as a last resort, 138 | * search deeply under those locations (except for the parent of java.home, on the notion 139 | * that if this is not a canonical installation, then that search would have little 140 | * chance of succeeding). 141 | */ 142 | def platformTools: Option[File] = { 143 | val jarName = "tools.jar" 144 | def jarPath(path: Path) = (path / "lib" / jarName).toFile 145 | def jarAt(path: Path) = { 146 | val f = jarPath(path) 147 | if (f.isFile) Some(f) else None 148 | } 149 | val jdkDir = { 150 | val d = Directory(jdkHome) 151 | if (d.isDirectory) Some(d) else None 152 | } 153 | def deeply(dir: Directory) = dir.deepFiles find (_.name == jarName) 154 | 155 | val home = envOrSome("JDK_HOME", envOrNone("JAVA_HOME")) map (p => Path(p)) 156 | val install = Some(Path(javaHome)) 157 | 158 | (home flatMap jarAt) orElse (install flatMap jarAt) orElse (install map (_.parent) flatMap jarAt) orElse 159 | (jdkDir flatMap deeply) 160 | } 161 | override def toString = s""" 162 | |object SupplementalLocations { 163 | | platformTools = $platformTools 164 | |}""".asLines 165 | } 166 | 167 | /** With no arguments, show the interesting values in Environment and Defaults. 168 | * If there are arguments, show those in Calculated as if those options had been 169 | * given to a scala runner. 170 | */ 171 | def main(args: Array[String]): Unit = 172 | if (args.isEmpty) { 173 | println(Environment) 174 | println(Defaults) 175 | } else { 176 | val settings = new Settings() 177 | val rest = settings.processArguments(args.toList, processAll = false)._2 178 | val pr = new PathResolver(settings) 179 | println("COMMAND: 'scala %s'".format(args.mkString(" "))) 180 | println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" "))) 181 | 182 | pr.result match { 183 | case cp: AggregateClassPath => 184 | println(s"ClassPath has ${cp.aggregates.size} entries and results in:\n${cp.asClassPathStrings}") 185 | } 186 | } 187 | } 188 | 189 | final class PathResolver(settings: Settings) { 190 | private val classPathFactory = new ClassPathFactory(settings) 191 | 192 | import PathResolver.{ AsLines, Defaults, ppcp } 193 | 194 | private def cmdLineOrElse(name: String, alt: String) = { 195 | (commandLineFor(name) match { 196 | case Some("") => None 197 | case x => x 198 | }) getOrElse alt 199 | } 200 | 201 | private def commandLineFor(s: String): Option[String] = condOpt(s) { 202 | case "javabootclasspath" => settings.javabootclasspath.value 203 | case "javaextdirs" => settings.javaextdirs.value 204 | case "bootclasspath" => settings.bootclasspath.value 205 | case "extdirs" => settings.extdirs.value 206 | case "classpath" | "cp" => settings.classpath.value 207 | case "sourcepath" => settings.sourcepath.value 208 | } 209 | 210 | /** Calculated values based on any given command line options, falling back on 211 | * those in Defaults. 212 | */ 213 | object Calculated { 214 | def scalaHome = Defaults.scalaHome 215 | def useJavaClassPath = settings.usejavacp.value || Defaults.useJavaClassPath 216 | def useManifestClassPath= settings.usemanifestcp.value 217 | def javaBootClassPath = cmdLineOrElse("javabootclasspath", Defaults.javaBootClassPath) 218 | def javaExtDirs = cmdLineOrElse("javaextdirs", Defaults.javaExtDirs) 219 | def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else "" 220 | def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath) 221 | def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs) 222 | 223 | /** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as: 224 | * [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect 225 | * [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg) 226 | * [scaladoc] ^ 227 | * because the bootstrapping will look at the sourcepath and create package "reflect" in "" 228 | * and then when typing relative names, instead of picking .scala.relect, typedIdentifier will pick up the 229 | * .reflect package created by the bootstrapping. Thus, no bootstrapping for scaladoc! 230 | * TODO: we should refactor this as a separate -bootstrap option to have a clean implementation, no? */ 231 | def sourcePath = if (!settings.isScaladoc) cmdLineOrElse("sourcepath", Defaults.scalaSourcePath) else "" 232 | 233 | def userClassPath = settings.classpath.value // default is specified by settings and can be overridden there 234 | 235 | import classPathFactory._ 236 | 237 | // Assemble the elements! 238 | def basis = List[Traversable[ClassPath]]( 239 | JrtClassPath.apply(), // 0. The Java 9 classpath (backed by the jrt:/ virtual system, if available) 240 | classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. 241 | contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. 242 | classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. 243 | classesInPath(scalaBootClassPath), // 4. The Scala boot class path. 244 | contentsOfDirsInPath(scalaExtDirs), // 5. The Scala extension class path. 245 | classesInExpandedPath(userClassPath), // 6. The Scala application class path. 246 | classesInManifest(useManifestClassPath), // 8. The Manifest class path. 247 | sourcesInPath(sourcePath) // 7. The Scala source path. 248 | ) 249 | 250 | lazy val containers = basis.flatten.distinct 251 | 252 | override def toString = s""" 253 | |object Calculated { 254 | | scalaHome = $scalaHome 255 | | javaBootClassPath = ${ppcp(javaBootClassPath)} 256 | | javaExtDirs = ${ppcp(javaExtDirs)} 257 | | javaUserClassPath = ${ppcp(javaUserClassPath)} 258 | | useJavaClassPath = $useJavaClassPath 259 | | scalaBootClassPath = ${ppcp(scalaBootClassPath)} 260 | | scalaExtDirs = ${ppcp(scalaExtDirs)} 261 | | userClassPath = ${ppcp(userClassPath)} 262 | | sourcePath = ${ppcp(sourcePath)} 263 | |}""".asLines 264 | } 265 | 266 | def containers = Calculated.containers 267 | 268 | import PathResolver.MkLines 269 | 270 | def result: ClassPath = { 271 | val cp = computeResult() 272 | if (settings.Ylogcp) { 273 | Console print f"Classpath built from ${settings.toConciseString} %n" 274 | Console print s"Defaults: ${PathResolver.Defaults}" 275 | Console print s"Calculated: $Calculated" 276 | 277 | val xs = (Calculated.basis drop 2).flatten.distinct 278 | Console print (xs mkLines (s"After java boot/extdirs classpath has ${xs.size} entries:", indented = true)) 279 | } 280 | cp 281 | } 282 | 283 | def resultAsURLs: Seq[URL] = result.asURLs 284 | 285 | @deprecated("Use resultAsURLs instead of this one", "2.11.5") 286 | def asURLs: List[URL] = resultAsURLs.toList 287 | 288 | private def computeResult(): ClassPath = AggregateClassPath(containers.toIndexedSeq) 289 | } 290 | -------------------------------------------------------------------------------- /examples/SyntaxAnalyer.scala: -------------------------------------------------------------------------------- 1 | /* NSC -- new Scala compiler 2 | * Copyright 2005-2013 LAMP/EPFL 3 | * @author Martin Odersky 4 | */ 5 | 6 | package scala.tools.nsc 7 | package ast.parser 8 | 9 | import javac._ 10 | 11 | /** An nsc sub-component. 12 | */ 13 | abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParsers with Scanners with JavaParsers with JavaScanners { 14 | import global._ 15 | 16 | val phaseName = "parser" 17 | def newPhase(prev: Phase): StdPhase = new ParserPhase(prev) 18 | 19 | abstract class MemberDefTraverser extends Traverser { 20 | def onMember(defn: MemberDef): Unit 21 | 22 | private var depth: Int = 0 23 | private def lower[T](body: => T): T = { 24 | depth += 1 25 | try body finally depth -= 1 26 | } 27 | def currentDepth = depth 28 | 29 | /** Prune this tree and all trees beneath it. Can be overridden. */ 30 | def prune(md: MemberDef): Boolean = ( 31 | md.mods.isSynthetic 32 | || md.mods.isParamAccessor 33 | || nme.isConstructorName(md.name) 34 | || (md.name containsName nme.ANON_CLASS_NAME) 35 | ) 36 | 37 | override def traverse(t: Tree): Unit = t match { 38 | case md: MemberDef if prune(md) => 39 | case md @ PackageDef(_, stats) => traverseTrees(stats) 40 | case md: ImplDef => onMember(md) ; lower(traverseTrees(md.impl.body)) 41 | case md: ValOrDefDef => onMember(md) ; lower(traverse(md.rhs)) 42 | case _ => super.traverse(t) 43 | } 44 | } 45 | 46 | class MemberPosReporter(unit: CompilationUnit) extends MemberDefTraverser { 47 | private var outputFn: MemberDef => String = outputForScreen 48 | val path = unit.source.file.path 49 | 50 | // If a single line, outputs the line; if it spans multiple lines 51 | // outputs NN,NN with start and end lines, e.g. 15,25. 52 | def outputPos(md: MemberDef): String = { 53 | val pos = md.pos 54 | val start = pos.focusStart.line 55 | val end = pos.focusEnd.line 56 | 57 | if (start == end) "" + start else s"$start,$end" 58 | } 59 | def outputForSed(md: MemberDef): String = { 60 | val pos_s = "%-12s" format outputPos(md) + "p" 61 | s"$pos_s $path # ${md.keyword} ${md.name}" 62 | } 63 | def outputForScreen(md: MemberDef): String = { 64 | val pos_s = "%-20s" format " " * currentDepth + outputPos(md) 65 | s"$pos_s ${md.keyword} ${md.name}" 66 | } 67 | 68 | def onMember(md: MemberDef) = println(outputFn(md)) 69 | // It recognizes "sed" and "anything else". 70 | def show(style: String) { 71 | if (style == "sed") { 72 | outputFn = outputForSed 73 | traverse(unit.body) 74 | } 75 | else { 76 | outputFn = outputForScreen 77 | println(path) 78 | traverse(unit.body) 79 | } 80 | println("") 81 | } 82 | } 83 | 84 | private def initialUnitBody(unit: CompilationUnit): Tree = { 85 | if (unit.isJava) newJavaUnitParser(unit).parse() 86 | else if (currentRun.parsing.incompleteHandled) newUnitParser(unit).parse() 87 | else newUnitParser(unit).smartParse() 88 | } 89 | 90 | class ParserPhase(prev: Phase) extends StdPhase(prev) { 91 | override val checkable = false 92 | override val keepsTypeParams = false 93 | 94 | def apply(unit: CompilationUnit) { 95 | informProgress("parsing " + unit) 96 | // if the body is already filled in, don't overwrite it 97 | // otherwise compileLate is going to overwrite bodies of synthetic source files 98 | if (unit.body == EmptyTree) 99 | unit.body = initialUnitBody(unit) 100 | 101 | if (settings.Yrangepos && !reporter.hasErrors) 102 | validatePositions(unit.body) 103 | 104 | if (settings.Ymemberpos.isSetByUser) 105 | new MemberPosReporter(unit) show (style = settings.Ymemberpos.value) 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /examples/Variance.scala: -------------------------------------------------------------------------------- 1 | class Function1[-T1, +R] 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tree-sitter/tree-sitter-scala 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 | -------------------------------------------------------------------------------- /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-javascript v0.21.5-0.20240818005344-15887341e5b5 h1:om4X9AVg3asL8gxNJDcz4e/Wp+VpQj1PY3uJXKr6EOg= 24 | github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5/go.mod h1:nNqgPoV/h9uYWk6kYEFdEAhNVOacpfpRW5SFmdaP4tU= 25 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5 h1:pfV3G3k7NCKqKk8THBmyuh2zA33lgYHS3GVrzRR8ry4= 26 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5/go.mod h1:GbMKRjLfk0H+PI7nLi1Sx5lHf5wCpLz9al8tQYSxpEk= 27 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1 h1:ZXZMDwE+IhUtGug4Brv6NjJWUU3rfkZBKpemf6RY8/g= 28 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1/go.mod h1:UKCLuYnJ312Mei+3cyTmGOHzn0YAnaPRECgJmHtzrqs= 29 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb h1:EXEM82lFM7JjJb6qiKZXkpIDaCcbV2obNn82ghwj9lw= 30 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb/go.mod h1:lXCF1nGG5Dr4J3BTS0ObN4xJCCICiSu/b+Xe/VqMV7g= 31 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d h1:fcYCvoXdcP1uRQYXqJHRy6Hec+uKScQdKVtMwK9JeCI= 32 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d/go.mod h1:T1nShQ4v5AJtozZ8YyAS4uzUtDAJj/iv4YfwXSbUHzg= 33 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447 h1:o9alBu1J/WjrcTKEthYtXmdkDc5OVXD+PqlvnEZ0Lzc= 34 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447/go.mod h1:1Oh95COkkTn6Ezp0vcMbvfhRP5gLeqqljR0BYnBzWvc= 35 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 36 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 37 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-scala", 3 | "version": "0.24.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tree-sitter-scala", 9 | "version": "0.24.0", 10 | "hasInstallScript": true, 11 | "license": "MIT", 12 | "dependencies": { 13 | "node-addon-api": "^8.2.2", 14 | "node-gyp-build": "^4.8.2" 15 | }, 16 | "devDependencies": { 17 | "prebuildify": "^6.0.1", 18 | "prettier": "3.3.3", 19 | "tree-sitter-cli": "^0.24.7" 20 | }, 21 | "peerDependencies": { 22 | "tree-sitter": "^0.21.1" 23 | }, 24 | "peerDependenciesMeta": { 25 | "tree-sitter": { 26 | "optional": true 27 | } 28 | } 29 | }, 30 | "node_modules/base64-js": { 31 | "version": "1.5.1", 32 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 33 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 34 | "dev": true, 35 | "funding": [ 36 | { 37 | "type": "github", 38 | "url": "https://github.com/sponsors/feross" 39 | }, 40 | { 41 | "type": "patreon", 42 | "url": "https://www.patreon.com/feross" 43 | }, 44 | { 45 | "type": "consulting", 46 | "url": "https://feross.org/support" 47 | } 48 | ] 49 | }, 50 | "node_modules/bl": { 51 | "version": "4.1.0", 52 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 53 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 54 | "dev": true, 55 | "dependencies": { 56 | "buffer": "^5.5.0", 57 | "inherits": "^2.0.4", 58 | "readable-stream": "^3.4.0" 59 | } 60 | }, 61 | "node_modules/buffer": { 62 | "version": "5.7.1", 63 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 64 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 65 | "dev": true, 66 | "funding": [ 67 | { 68 | "type": "github", 69 | "url": "https://github.com/sponsors/feross" 70 | }, 71 | { 72 | "type": "patreon", 73 | "url": "https://www.patreon.com/feross" 74 | }, 75 | { 76 | "type": "consulting", 77 | "url": "https://feross.org/support" 78 | } 79 | ], 80 | "dependencies": { 81 | "base64-js": "^1.3.1", 82 | "ieee754": "^1.1.13" 83 | } 84 | }, 85 | "node_modules/chownr": { 86 | "version": "1.1.4", 87 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 88 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 89 | "dev": true 90 | }, 91 | "node_modules/end-of-stream": { 92 | "version": "1.4.4", 93 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 94 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 95 | "dev": true, 96 | "dependencies": { 97 | "once": "^1.4.0" 98 | } 99 | }, 100 | "node_modules/fs-constants": { 101 | "version": "1.0.0", 102 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 103 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 104 | "dev": true 105 | }, 106 | "node_modules/ieee754": { 107 | "version": "1.2.1", 108 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 109 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 110 | "dev": true, 111 | "funding": [ 112 | { 113 | "type": "github", 114 | "url": "https://github.com/sponsors/feross" 115 | }, 116 | { 117 | "type": "patreon", 118 | "url": "https://www.patreon.com/feross" 119 | }, 120 | { 121 | "type": "consulting", 122 | "url": "https://feross.org/support" 123 | } 124 | ] 125 | }, 126 | "node_modules/inherits": { 127 | "version": "2.0.4", 128 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 129 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 130 | "dev": true 131 | }, 132 | "node_modules/minimist": { 133 | "version": "1.2.8", 134 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 135 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 136 | "dev": true, 137 | "funding": { 138 | "url": "https://github.com/sponsors/ljharb" 139 | } 140 | }, 141 | "node_modules/mkdirp-classic": { 142 | "version": "0.5.3", 143 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 144 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 145 | "dev": true 146 | }, 147 | "node_modules/node-abi": { 148 | "version": "3.67.0", 149 | "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.67.0.tgz", 150 | "integrity": "sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==", 151 | "dev": true, 152 | "dependencies": { 153 | "semver": "^7.3.5" 154 | }, 155 | "engines": { 156 | "node": ">=10" 157 | } 158 | }, 159 | "node_modules/node-addon-api": { 160 | "version": "8.2.2", 161 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.2.tgz", 162 | "integrity": "sha512-9emqXAKhVoNrQ792nLI/wpzPpJ/bj/YXxW0CvAau1+RdGBcCRF1Dmz7719zgVsQNrzHl9Tzn3ImZ4qWFarWL0A==", 163 | "engines": { 164 | "node": "^18 || ^20 || >= 21" 165 | } 166 | }, 167 | "node_modules/node-gyp-build": { 168 | "version": "4.8.2", 169 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", 170 | "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", 171 | "bin": { 172 | "node-gyp-build": "bin.js", 173 | "node-gyp-build-optional": "optional.js", 174 | "node-gyp-build-test": "build-test.js" 175 | } 176 | }, 177 | "node_modules/npm-run-path": { 178 | "version": "3.1.0", 179 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", 180 | "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", 181 | "dev": true, 182 | "dependencies": { 183 | "path-key": "^3.0.0" 184 | }, 185 | "engines": { 186 | "node": ">=8" 187 | } 188 | }, 189 | "node_modules/once": { 190 | "version": "1.4.0", 191 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 192 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 193 | "dev": true, 194 | "dependencies": { 195 | "wrappy": "1" 196 | } 197 | }, 198 | "node_modules/path-key": { 199 | "version": "3.1.1", 200 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 201 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 202 | "dev": true, 203 | "engines": { 204 | "node": ">=8" 205 | } 206 | }, 207 | "node_modules/prebuildify": { 208 | "version": "6.0.1", 209 | "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.1.tgz", 210 | "integrity": "sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw==", 211 | "dev": true, 212 | "dependencies": { 213 | "minimist": "^1.2.5", 214 | "mkdirp-classic": "^0.5.3", 215 | "node-abi": "^3.3.0", 216 | "npm-run-path": "^3.1.0", 217 | "pump": "^3.0.0", 218 | "tar-fs": "^2.1.0" 219 | }, 220 | "bin": { 221 | "prebuildify": "bin.js" 222 | } 223 | }, 224 | "node_modules/prettier": { 225 | "version": "3.3.3", 226 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", 227 | "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", 228 | "dev": true, 229 | "license": "MIT", 230 | "bin": { 231 | "prettier": "bin/prettier.cjs" 232 | }, 233 | "engines": { 234 | "node": ">=14" 235 | }, 236 | "funding": { 237 | "url": "https://github.com/prettier/prettier?sponsor=1" 238 | } 239 | }, 240 | "node_modules/pump": { 241 | "version": "3.0.0", 242 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 243 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 244 | "dev": true, 245 | "dependencies": { 246 | "end-of-stream": "^1.1.0", 247 | "once": "^1.3.1" 248 | } 249 | }, 250 | "node_modules/readable-stream": { 251 | "version": "3.6.2", 252 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 253 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 254 | "dev": true, 255 | "dependencies": { 256 | "inherits": "^2.0.3", 257 | "string_decoder": "^1.1.1", 258 | "util-deprecate": "^1.0.1" 259 | }, 260 | "engines": { 261 | "node": ">= 6" 262 | } 263 | }, 264 | "node_modules/safe-buffer": { 265 | "version": "5.2.1", 266 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 267 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 268 | "dev": true, 269 | "funding": [ 270 | { 271 | "type": "github", 272 | "url": "https://github.com/sponsors/feross" 273 | }, 274 | { 275 | "type": "patreon", 276 | "url": "https://www.patreon.com/feross" 277 | }, 278 | { 279 | "type": "consulting", 280 | "url": "https://feross.org/support" 281 | } 282 | ] 283 | }, 284 | "node_modules/semver": { 285 | "version": "7.6.3", 286 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 287 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 288 | "dev": true, 289 | "bin": { 290 | "semver": "bin/semver.js" 291 | }, 292 | "engines": { 293 | "node": ">=10" 294 | } 295 | }, 296 | "node_modules/string_decoder": { 297 | "version": "1.3.0", 298 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 299 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 300 | "dev": true, 301 | "dependencies": { 302 | "safe-buffer": "~5.2.0" 303 | } 304 | }, 305 | "node_modules/tar-fs": { 306 | "version": "2.1.3", 307 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", 308 | "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", 309 | "dev": true, 310 | "license": "MIT", 311 | "dependencies": { 312 | "chownr": "^1.1.1", 313 | "mkdirp-classic": "^0.5.2", 314 | "pump": "^3.0.0", 315 | "tar-stream": "^2.1.4" 316 | } 317 | }, 318 | "node_modules/tar-stream": { 319 | "version": "2.2.0", 320 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 321 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 322 | "dev": true, 323 | "dependencies": { 324 | "bl": "^4.0.3", 325 | "end-of-stream": "^1.4.1", 326 | "fs-constants": "^1.0.0", 327 | "inherits": "^2.0.3", 328 | "readable-stream": "^3.1.1" 329 | }, 330 | "engines": { 331 | "node": ">=6" 332 | } 333 | }, 334 | "node_modules/tree-sitter": { 335 | "version": "0.21.1", 336 | "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz", 337 | "integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==", 338 | "hasInstallScript": true, 339 | "license": "MIT", 340 | "optional": true, 341 | "peer": true, 342 | "dependencies": { 343 | "node-addon-api": "^8.0.0", 344 | "node-gyp-build": "^4.8.0" 345 | } 346 | }, 347 | "node_modules/tree-sitter-cli": { 348 | "version": "0.24.7", 349 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.24.7.tgz", 350 | "integrity": "sha512-o4gnE82pVmMMhJbWwD6+I9yr4lXii5Ci5qEQ2pFpUbVy1YiD8cizTJaqdcznA0qEbo7l2OneI1GocChPrI4YGQ==", 351 | "dev": true, 352 | "hasInstallScript": true, 353 | "license": "MIT", 354 | "bin": { 355 | "tree-sitter": "cli.js" 356 | }, 357 | "engines": { 358 | "node": ">=12.0.0" 359 | } 360 | }, 361 | "node_modules/util-deprecate": { 362 | "version": "1.0.2", 363 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 364 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 365 | "dev": true 366 | }, 367 | "node_modules/wrappy": { 368 | "version": "1.0.2", 369 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 370 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 371 | "dev": true 372 | } 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-scala", 3 | "version": "0.24.0", 4 | "description": "Scala grammar for tree-sitter", 5 | "repository": "https://github.com/tree-sitter/tree-sitter-scala", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Max Brunsfeld", 9 | "email": "maxbrunsfeld@gmail.com" 10 | }, 11 | "main": "bindings/node", 12 | "types": "bindings/node", 13 | "keywords": [ 14 | "incremental", 15 | "parsing", 16 | "tree-sitter", 17 | "scala" 18 | ], 19 | "files": [ 20 | "grammar.js", 21 | "tree-sitter.json", 22 | "binding.gyp", 23 | "prebuilds/**", 24 | "bindings/node/*", 25 | "queries/*", 26 | "src/**", 27 | "*.wasm" 28 | ], 29 | "dependencies": { 30 | "node-addon-api": "^8.2.2", 31 | "node-gyp-build": "^4.8.2" 32 | }, 33 | "devDependencies": { 34 | "prebuildify": "^6.0.1", 35 | "tree-sitter-cli": "^0.24.7", 36 | "prettier": "3.3.3" 37 | }, 38 | "peerDependencies": { 39 | "tree-sitter": "^0.21.1" 40 | }, 41 | "peerDependenciesMeta": { 42 | "tree-sitter": { 43 | "optional": true 44 | } 45 | }, 46 | "scripts": { 47 | "install": "node-gyp-build", 48 | "prestart": "tree-sitter build --wasm", 49 | "start": "tree-sitter playground", 50 | "test": "node --test bindings/node/*_test.js", 51 | "prebuildify": "prebuildify --napi --strip" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "tree-sitter-scala" 7 | description = "Scala grammar for tree-sitter" 8 | version = "0.24.0" 9 | keywords = ["incremental", "parsing", "tree-sitter", "scala"] 10 | classifiers = [ 11 | "Intended Audience :: Developers", 12 | "License :: OSI Approved :: MIT License", 13 | "Topic :: Software Development :: Compilers", 14 | "Topic :: Text Processing :: Linguistic", 15 | "Typing :: Typed", 16 | ] 17 | authors = [{ name = "Max Brunsfeld", email = "maxbrunsfeld@gmail.com" }] 18 | requires-python = ">=3.9" 19 | license.text = "MIT" 20 | readme = "README.md" 21 | 22 | [project.urls] 23 | Homepage = "https://github.com/tree-sitter/tree-sitter-scala" 24 | 25 | [project.optional-dependencies] 26 | core = ["tree-sitter~=0.22"] 27 | 28 | [tool.cibuildwheel] 29 | build = "cp39-*" 30 | build-frontend = "build" 31 | -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ; CREDITS @stumash (stuart.mashaal@gmail.com) 2 | 3 | (field_expression field: (identifier) @property) 4 | (field_expression value: (identifier) @type 5 | (#match? @type "^[A-Z]")) 6 | 7 | (type_identifier) @type 8 | 9 | (class_definition 10 | name: (identifier) @type) 11 | 12 | (enum_definition 13 | name: (identifier) @type) 14 | 15 | (object_definition 16 | name: (identifier) @type) 17 | 18 | (trait_definition 19 | name: (identifier) @type) 20 | 21 | (full_enum_case 22 | name: (identifier) @type) 23 | 24 | (simple_enum_case 25 | name: (identifier) @type) 26 | 27 | ;; variables 28 | 29 | (class_parameter 30 | name: (identifier) @parameter) 31 | 32 | (self_type (identifier) @parameter) 33 | 34 | (interpolation (identifier) @none) 35 | (interpolation (block) @none) 36 | 37 | ;; types 38 | 39 | (type_definition 40 | name: (type_identifier) @type.definition) 41 | 42 | ;; val/var definitions/declarations 43 | 44 | (val_definition 45 | pattern: (identifier) @variable) 46 | 47 | (var_definition 48 | pattern: (identifier) @variable) 49 | 50 | (val_declaration 51 | name: (identifier) @variable) 52 | 53 | (var_declaration 54 | name: (identifier) @variable) 55 | 56 | ; imports/exports 57 | 58 | (import_declaration 59 | path: (identifier) @namespace) 60 | ((stable_identifier (identifier) @namespace)) 61 | 62 | ((import_declaration 63 | path: (identifier) @type) (#match? @type "^[A-Z]")) 64 | ((stable_identifier (identifier) @type) (#match? @type "^[A-Z]")) 65 | 66 | (export_declaration 67 | path: (identifier) @namespace) 68 | ((stable_identifier (identifier) @namespace)) 69 | 70 | ((export_declaration 71 | path: (identifier) @type) (#match? @type "^[A-Z]")) 72 | ((stable_identifier (identifier) @type) (#match? @type "^[A-Z]")) 73 | 74 | ((namespace_selectors (identifier) @type) (#match? @type "^[A-Z]")) 75 | 76 | ; method invocation 77 | 78 | (call_expression 79 | function: (identifier) @function.call) 80 | 81 | (call_expression 82 | function: (operator_identifier) @function.call) 83 | 84 | (call_expression 85 | function: (field_expression 86 | field: (identifier) @method.call)) 87 | 88 | ((call_expression 89 | function: (identifier) @constructor) 90 | (#match? @constructor "^[A-Z]")) 91 | 92 | (generic_function 93 | function: (identifier) @function.call) 94 | 95 | (interpolated_string_expression 96 | interpolator: (identifier) @function.call) 97 | 98 | ; function definitions 99 | 100 | (function_definition 101 | name: (identifier) @function) 102 | 103 | (parameter 104 | name: (identifier) @parameter) 105 | 106 | (binding 107 | name: (identifier) @parameter) 108 | 109 | ; method definition 110 | 111 | (function_declaration 112 | name: (identifier) @method) 113 | 114 | (function_definition 115 | name: (identifier) @method) 116 | 117 | ; expressions 118 | 119 | (infix_expression operator: (identifier) @operator) 120 | (infix_expression operator: (operator_identifier) @operator) 121 | (infix_type operator: (operator_identifier) @operator) 122 | (infix_type operator: (operator_identifier) @operator) 123 | 124 | ; literals 125 | 126 | (boolean_literal) @boolean 127 | (integer_literal) @number 128 | (floating_point_literal) @float 129 | 130 | [ 131 | (string) 132 | (character_literal) 133 | (interpolated_string_expression) 134 | ] @string 135 | 136 | (interpolation "$" @punctuation.special) 137 | 138 | ;; keywords 139 | 140 | (opaque_modifier) @type.qualifier 141 | (infix_modifier) @keyword 142 | (transparent_modifier) @type.qualifier 143 | (open_modifier) @type.qualifier 144 | 145 | [ 146 | "case" 147 | "class" 148 | "enum" 149 | "extends" 150 | "derives" 151 | "finally" 152 | ;; `forSome` existential types not implemented yet 153 | ;; `macro` not implemented yet 154 | "object" 155 | "override" 156 | "package" 157 | "trait" 158 | "type" 159 | "val" 160 | "var" 161 | "with" 162 | "given" 163 | "using" 164 | "end" 165 | "implicit" 166 | "extension" 167 | "with" 168 | ] @keyword 169 | 170 | [ 171 | "abstract" 172 | "final" 173 | "lazy" 174 | "sealed" 175 | "private" 176 | "protected" 177 | ] @type.qualifier 178 | 179 | (inline_modifier) @storageclass 180 | 181 | (null_literal) @constant.builtin 182 | 183 | (wildcard) @parameter 184 | 185 | (annotation) @attribute 186 | 187 | ;; special keywords 188 | 189 | "new" @keyword.operator 190 | 191 | [ 192 | "else" 193 | "if" 194 | "match" 195 | "then" 196 | ] @conditional 197 | 198 | [ 199 | "(" 200 | ")" 201 | "[" 202 | "]" 203 | "{" 204 | "}" 205 | ] @punctuation.bracket 206 | 207 | [ 208 | "." 209 | "," 210 | ] @punctuation.delimiter 211 | 212 | [ 213 | "do" 214 | "for" 215 | "while" 216 | "yield" 217 | ] @repeat 218 | 219 | "def" @keyword.function 220 | 221 | [ 222 | "=>" 223 | "<-" 224 | "@" 225 | ] @operator 226 | 227 | ["import" "export"] @include 228 | 229 | [ 230 | "try" 231 | "catch" 232 | "throw" 233 | ] @exception 234 | 235 | "return" @keyword.return 236 | 237 | (comment) @spell @comment 238 | (block_comment) @spell @comment 239 | 240 | ;; `case` is a conditional keyword in case_block 241 | 242 | (case_block 243 | (case_clause ("case") @conditional)) 244 | (indented_cases 245 | (case_clause ("case") @conditional)) 246 | 247 | (operator_identifier) @operator 248 | 249 | ((identifier) @type (#match? @type "^[A-Z]")) 250 | ((identifier) @variable.builtin 251 | (#match? @variable.builtin "^this$")) 252 | 253 | ( 254 | (identifier) @function.builtin 255 | (#match? @function.builtin "^super$") 256 | ) 257 | 258 | ;; Scala CLI using directives 259 | (using_directive_key) @parameter 260 | (using_directive_value) @string 261 | -------------------------------------------------------------------------------- /queries/indents.scm: -------------------------------------------------------------------------------- 1 | ; These indent queries adhere to nvim-tree-sytter syntax. 2 | ; See `nvim-tree-sitter-indentation-mod` vim help page. 3 | 4 | [ 5 | (template_body) 6 | (block) 7 | (parameters) 8 | (arguments) 9 | (match_expression) 10 | (splice_expression) 11 | (import_declaration) 12 | (function_definition) 13 | (ERROR ":") 14 | (ERROR "=") 15 | ("match") 16 | (":") 17 | ("=") 18 | ] @indent.begin 19 | 20 | (arguments ")" @indent.end) 21 | 22 | "}" @indent.end 23 | 24 | "end" @indent.end 25 | 26 | [ 27 | ")" 28 | "]" 29 | "}" 30 | ] @indent.branch 31 | -------------------------------------------------------------------------------- /queries/locals.scm: -------------------------------------------------------------------------------- 1 | (template_body) @local.scope 2 | (lambda_expression) @local.scope 3 | 4 | 5 | (function_declaration 6 | name: (identifier) @local.definition) @local.scope 7 | 8 | (function_definition 9 | name: (identifier) @local.definition) 10 | 11 | (parameter 12 | name: (identifier) @local.definition) 13 | 14 | (binding 15 | name: (identifier) @local.definition) 16 | 17 | (val_definition 18 | pattern: (identifier) @local.definition) 19 | 20 | (var_definition 21 | pattern: (identifier) @local.definition) 22 | 23 | (val_declaration 24 | name: (identifier) @local.definition) 25 | 26 | (var_declaration 27 | name: (identifier) @local.definition) 28 | 29 | (identifier) @local.reference 30 | 31 | -------------------------------------------------------------------------------- /queries/tags.scm: -------------------------------------------------------------------------------- 1 | ; Definitions 2 | 3 | (package_clause 4 | name: (package_identifier) @name) @definition.module 5 | 6 | (trait_definition 7 | name: (identifier) @name) @definition.interface 8 | 9 | (enum_definition 10 | name: (identifier) @name) @definition.enum 11 | 12 | (simple_enum_case 13 | name: (identifier) @name) @definition.class 14 | 15 | (full_enum_case 16 | name: (identifier) @name) @definition.class 17 | 18 | (class_definition 19 | name: (identifier) @name) @definition.class 20 | 21 | (object_definition 22 | name: (identifier) @name) @definition.object 23 | 24 | (function_definition 25 | name: (identifier) @name) @definition.function 26 | 27 | (val_definition 28 | pattern: (identifier) @name) @definition.variable 29 | 30 | (given_definition 31 | name: (identifier) @name) @definition.variable 32 | 33 | (var_definition 34 | pattern: (identifier) @name) @definition.variable 35 | 36 | (val_declaration 37 | name: (identifier) @name) @definition.variable 38 | 39 | (var_declaration 40 | name: (identifier) @name) @definition.variable 41 | 42 | (type_definition 43 | name: (type_identifier) @name) @definition.type 44 | 45 | (class_parameter 46 | name: (identifier) @name) @definition.property 47 | 48 | ; References 49 | 50 | (call_expression 51 | (identifier) @name) @reference.call 52 | 53 | (instance_expression 54 | (type_identifier) @name) @reference.interface 55 | 56 | (instance_expression 57 | (generic_type 58 | (type_identifier) @name)) @reference.interface 59 | 60 | (extends_clause 61 | (type_identifier) @name) @reference.class 62 | 63 | (extends_clause 64 | (generic_type 65 | (type_identifier) @name)) @reference.class 66 | 67 | -------------------------------------------------------------------------------- /script/parse-with-scalac: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # scalac -Yshow-trees -Xprint:parser $@ 4 | scalac -Yshow-trees -Xprint:typer $@ 5 | -------------------------------------------------------------------------------- /script/smoke_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This is an integration test to generally check the quality of parsing. 4 | 5 | SCALA_SCALA_LIBRARY_EXPECTED=100 6 | SCALA_SCALA_COMPILER_EXPECTED=96 7 | DOTTY_COMPILER_EXPECTED=83 8 | LILA_MODULES_EXPECTED=84 9 | SYNTAX_COMPLEXITY_CEILING=1400 10 | 11 | if [ ! -d "$SCALA_SCALA_DIR" ]; then 12 | echo "\$SCALA_SCALA_DIR must be set" 13 | exit 1 14 | fi 15 | 16 | if [ ! -d "$DOTTY_DIR" ]; then 17 | echo "\$DOTTY_DIR must be set" 18 | exit 1 19 | fi 20 | 21 | failed=0 22 | 23 | run_tree_sitter () { 24 | local source_dir=$1 25 | local expected=$2 26 | local name=$3 27 | local files=$(find "$source_dir" -name '*.scala' -type f | tr '\n' ' ') 28 | cmd="npm exec -c 'tree-sitter parse $files --quiet --stat' | sort | sed 's%$source_dir%%g'" 29 | echo 30 | echo "Parse $source_dir: $cmd" 31 | out=$( (eval "$cmd") || true) 32 | 33 | if [ ! -e "$PRODUCE_REPORTS" ]; then 34 | local report_file="report-$name.txt" 35 | echo "$out" | sed G | sed -E 's/([0-9]+) ms//' | grep -v 'success percentage' > "report-$name.txt" 36 | echo "Report written to $report_file" 37 | fi 38 | 39 | actual=$(echo "$out" | grep 'success percentage:' | rev | cut -d' ' -f5 | rev | sed 's/;//g' | sed 's/%//g' ) 40 | echo "$actual" 41 | if (( $(echo "$actual >= $expected" |bc -l) )); then 42 | # See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error 43 | echo -e "::notice file=grammar.js,line=1::ok, ${source_dir}: ${actual}%, expected at least $expected%" 44 | else 45 | echo -e "::error file=grammar.js,line=1::${source_dir}: expected ${expected}, but got ${actual} instead" 46 | failed=$((failed + 1)) 47 | fi 48 | } 49 | 50 | check_complexity () { 51 | local expected=$1 52 | name="complexity" 53 | cmd="npm exec -c 'tree-sitter generate --report-states-for-rule compilation_unit' 2>&1 >/dev/null" 54 | echo 55 | echo "Checking syntax complexity: $cmd" 56 | out=$( (eval "$cmd") || true) 57 | 58 | if [ ! -e "$PRODUCE_REPORTS" ]; then 59 | local report_file="report-$name.txt" 60 | echo "$out" > "report-$name.txt" 61 | echo "Report written to $report_file" 62 | fi 63 | 64 | out1=$(echo "$out" | grep -v "ExperimentalWarning" | grep -v "experimental" | grep -v "node") 65 | top=$(echo "$out1" | head -n 1 | sed 's/ \+/ /g') 66 | top_definition=$(echo "$top" | cut -d' ' -f1) 67 | top_definition_line=$(grep -n "$top_definition:" grammar.js | head -n 1 | cut -d : -f 1) 68 | actual=$(echo "$top" | cut -d' ' -f2) 69 | echo "$top_definition $actual" 70 | if (( $(echo "$actual < $expected" |bc -l) )); then 71 | # See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error 72 | echo -e "::notice file=grammar.js,line=$top_definition_line::ok, complexity of the most complex definition ${top_definition}: ${actual}, lower than the allowed ceiling $expected" 73 | else 74 | echo -e "::error file=grammar.js,line=$top_definition_line::complexity of the most complex definition ${top_definition}: ${actual}, higher than the allowed ceiling $expected" 75 | failed=$((failed + 1)) 76 | fi 77 | } 78 | 79 | run_tree_sitter "$SCALA_SCALA_DIR/src/library/" $SCALA_SCALA_LIBRARY_EXPECTED scala2-library 80 | run_tree_sitter "$SCALA_SCALA_DIR/src/compiler/" $SCALA_SCALA_COMPILER_EXPECTED scala2-compiler 81 | run_tree_sitter "$DOTTY_DIR/compiler/" $DOTTY_COMPILER_EXPECTED dotty-compiler 82 | run_tree_sitter "$LILA_DIR/modules/" $LILA_MODULES_EXPECTED lila-modules 83 | 84 | check_complexity $SYNTAX_COMPLEXITY_CEILING 85 | 86 | if (( failed > 0 )); then 87 | exit 1 88 | fi 89 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os.path import isdir, join 2 | from platform import system 3 | 4 | from setuptools import Extension, find_packages, setup 5 | from setuptools.command.build import build 6 | from wheel.bdist_wheel import bdist_wheel 7 | 8 | 9 | class Build(build): 10 | def run(self): 11 | if isdir("queries"): 12 | dest = join(self.build_lib, "tree_sitter_scala", "queries") 13 | self.copy_tree("queries", dest) 14 | super().run() 15 | 16 | 17 | class BdistWheel(bdist_wheel): 18 | def get_tag(self): 19 | python, abi, platform = super().get_tag() 20 | if python.startswith("cp"): 21 | python, abi = "cp39", "abi3" 22 | return python, abi, platform 23 | 24 | 25 | setup( 26 | packages=find_packages("bindings/python"), 27 | package_dir={"": "bindings/python"}, 28 | package_data={ 29 | "tree_sitter_scala": ["*.pyi", "py.typed"], 30 | "tree_sitter_scala.queries": ["*.scm"], 31 | }, 32 | ext_package="tree_sitter_scala", 33 | ext_modules=[ 34 | Extension( 35 | name="_binding", 36 | sources=[ 37 | "bindings/python/tree_sitter_scala/binding.c", 38 | "src/parser.c", 39 | "src/scanner.c", 40 | ], 41 | extra_compile_args=[ 42 | "-std=c11", 43 | "-fvisibility=hidden", 44 | ] if system() != "Windows" else [ 45 | "/std:c11", 46 | "/utf-8", 47 | ], 48 | define_macros=[ 49 | ("Py_LIMITED_API", "0x03090000"), 50 | ("PY_SSIZE_T_CLEAN", None), 51 | ("TREE_SITTER_HIDE_SYMBOLS", None), 52 | ], 53 | include_dirs=["src"], 54 | py_limited_api=True, 55 | ) 56 | ], 57 | cmdclass={ 58 | "build": Build, 59 | "bdist_wheel": BdistWheel 60 | }, 61 | zip_safe=False 62 | ) 63 | -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/alloc.h" 2 | #include "tree_sitter/array.h" 3 | #include "tree_sitter/parser.h" 4 | 5 | #include 6 | 7 | // #define DEBUG 8 | 9 | #ifdef DEBUG 10 | #define LOG(...) fprintf(stderr, __VA_ARGS__) 11 | #else 12 | #define LOG(...) 13 | #endif 14 | 15 | enum TokenType { 16 | AUTOMATIC_SEMICOLON, 17 | INDENT, 18 | OUTDENT, 19 | SIMPLE_STRING_START, 20 | SIMPLE_STRING_MIDDLE, 21 | SIMPLE_MULTILINE_STRING_START, 22 | INTERPOLATED_STRING_MIDDLE, 23 | INTERPOLATED_MULTILINE_STRING_MIDDLE, 24 | RAW_STRING_START, 25 | RAW_STRING_MIDDLE, 26 | RAW_STRING_MULTILINE_MIDDLE, 27 | SINGLE_LINE_STRING_END, 28 | MULTILINE_STRING_END, 29 | ELSE, 30 | CATCH, 31 | FINALLY, 32 | EXTENDS, 33 | DERIVES, 34 | WITH, 35 | ERROR_SENTINEL 36 | }; 37 | 38 | const char* token_name[] = { 39 | "AUTOMATIC_SEMICOLON", 40 | "INDENT", 41 | "OUTDENT", 42 | "SIMPLE_STRING_START", 43 | "SIMPLE_STRING_MIDDLE", 44 | "SIMPLE_MULTILINE_STRING_START", 45 | "INTERPOLATED_STRING_MIDDLE", 46 | "INTERPOLATED_MULTILINE_STRING_MIDDLE", 47 | "RAW_STRING_MIDDLE", 48 | "RAW_STRING_MULTILINE_MIDDLE", 49 | "SINGLE_LINE_STRING_END", 50 | "MULTILINE_STRING_END", 51 | "ELSE", 52 | "CATCH", 53 | "FINALLY", 54 | "EXTENDS", 55 | "DERIVES", 56 | "WITH", 57 | "ERROR_SENTINEL" 58 | }; 59 | 60 | typedef struct { 61 | Array(int16_t) indents; 62 | int16_t last_indentation_size; 63 | int16_t last_newline_count; 64 | int16_t last_column; 65 | } Scanner; 66 | 67 | void *tree_sitter_scala_external_scanner_create() { 68 | Scanner *scanner = ts_calloc(1, sizeof(Scanner)); 69 | array_init(&scanner->indents); 70 | scanner->last_indentation_size = -1; 71 | scanner->last_column = -1; 72 | return scanner; 73 | } 74 | 75 | void tree_sitter_scala_external_scanner_destroy(void *payload) { 76 | Scanner *scanner = payload; 77 | array_delete(&scanner->indents); 78 | ts_free(scanner); 79 | } 80 | 81 | unsigned tree_sitter_scala_external_scanner_serialize(void *payload, char *buffer) { 82 | Scanner *scanner = (Scanner*)payload; 83 | 84 | if ((scanner->indents.size + 3) * sizeof(int16_t) > TREE_SITTER_SERIALIZATION_BUFFER_SIZE) { 85 | return 0; 86 | } 87 | 88 | size_t size = 0; 89 | memcpy(buffer + size, &scanner->last_indentation_size, sizeof(int16_t)); 90 | size += sizeof(int16_t); 91 | memcpy(buffer + size, &scanner->last_newline_count, sizeof(int16_t)); 92 | size += sizeof(int16_t); 93 | memcpy(buffer + size, &scanner->last_column, sizeof(int16_t)); 94 | size += sizeof(int16_t); 95 | 96 | for (unsigned i = 0; i < scanner->indents.size; i++) { 97 | memcpy(buffer + size, &scanner->indents.contents[i], sizeof(int16_t)); 98 | size += sizeof(int16_t); 99 | } 100 | 101 | return size; 102 | } 103 | 104 | void tree_sitter_scala_external_scanner_deserialize(void *payload, const char *buffer, 105 | unsigned length) { 106 | Scanner *scanner = (Scanner*)payload; 107 | array_clear(&scanner->indents); 108 | scanner->last_indentation_size = -1; 109 | scanner->last_column = -1; 110 | scanner->last_newline_count = 0; 111 | 112 | if (length == 0) { 113 | return; 114 | } 115 | 116 | size_t size = 0; 117 | 118 | scanner->last_indentation_size = *(int16_t *)&buffer[size]; 119 | size += sizeof(int16_t); 120 | scanner->last_newline_count = *(int16_t *)&buffer[size]; 121 | size += sizeof(int16_t); 122 | scanner->last_column = *(int16_t *)&buffer[size]; 123 | size += sizeof(int16_t); 124 | 125 | while (size < length) { 126 | array_push(&scanner->indents, *(int16_t *)&buffer[size]); 127 | size += sizeof(int16_t); 128 | } 129 | 130 | assert(size == length); 131 | } 132 | 133 | static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); } 134 | 135 | static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); } 136 | 137 | // We enumerate 3 types of strings that we need to handle differently: 138 | // 1. Simple strings, `"..."` or `"""..."""` 139 | // 2. Interpolated strings, `s"..."` or `f"..."` or `foo"..."` or foo"""...""". 140 | // 3. Raw strings, `raw"..."` 141 | typedef enum { 142 | STRING_MODE_SIMPLE, 143 | STRING_MODE_INTERPOLATED, 144 | STRING_MODE_RAW 145 | } StringMode; 146 | 147 | static bool scan_string_content(TSLexer *lexer, bool is_multiline, StringMode string_mode) { 148 | LOG("scan_string_content(%d, %d, %c)\n", is_multiline, string_mode, lexer->lookahead); 149 | unsigned closing_quote_count = 0; 150 | for (;;) { 151 | if (lexer->lookahead == '"') { 152 | advance(lexer); 153 | closing_quote_count++; 154 | if (!is_multiline) { 155 | lexer->result_symbol = SINGLE_LINE_STRING_END; 156 | lexer->mark_end(lexer); 157 | return true; 158 | } 159 | if (closing_quote_count >= 3 && lexer->lookahead != '"') { 160 | lexer->result_symbol = MULTILINE_STRING_END; 161 | lexer->mark_end(lexer); 162 | return true; 163 | } 164 | } else if (lexer->lookahead == '$' && string_mode != STRING_MODE_SIMPLE) { 165 | switch (string_mode) { 166 | case STRING_MODE_INTERPOLATED: 167 | lexer->result_symbol = is_multiline ? INTERPOLATED_MULTILINE_STRING_MIDDLE : INTERPOLATED_STRING_MIDDLE; 168 | break; 169 | case STRING_MODE_RAW: 170 | lexer->result_symbol = is_multiline ? RAW_STRING_MULTILINE_MIDDLE : RAW_STRING_MIDDLE; 171 | break; 172 | default: 173 | assert(false); 174 | } 175 | lexer->mark_end(lexer); 176 | return true; 177 | } else { 178 | closing_quote_count = 0; 179 | if (lexer->lookahead == '\\') { 180 | // Multiline strings ignore escape sequences 181 | if (is_multiline || string_mode == STRING_MODE_RAW) { 182 | // FIXME: In raw string mode, we have to jump over escaped quotes. 183 | advance(lexer); 184 | // In single-line raw strings, `\"` is not translated to `"`, but it also does 185 | // not close the string. Likewise, `\\` is not translated to `\`, but it does 186 | // stop the second `\` from stopping a double-quote from closing the string. 187 | if (!is_multiline && string_mode == STRING_MODE_RAW && 188 | (lexer->lookahead == '"' || lexer->lookahead == '\\')) { 189 | advance(lexer); 190 | } 191 | } else { 192 | lexer->result_symbol = string_mode == STRING_MODE_SIMPLE ? SIMPLE_STRING_MIDDLE : INTERPOLATED_STRING_MIDDLE; 193 | lexer->mark_end(lexer); 194 | return true; 195 | } 196 | // During error recovery and dynamic precedence resolution, the external 197 | // scanner will be invoked with all valid_symbols set to true, which means 198 | // we will be asked to scan a string token when we are not actually in a 199 | // string context. Here we detect these cases and return false. 200 | } else if (lexer->lookahead == '\n' && !is_multiline) { 201 | return false; 202 | } else if (lexer->eof(lexer)) { 203 | return false; 204 | } else { 205 | advance(lexer); 206 | } 207 | } 208 | } 209 | } 210 | 211 | static bool detect_comment_start(TSLexer *lexer) { 212 | lexer->mark_end(lexer); 213 | // Comments should not affect indentation 214 | if (lexer->lookahead == '/') { 215 | advance(lexer); 216 | if (lexer->lookahead == '/' || lexer -> lookahead == '*') { 217 | return true; 218 | } 219 | } 220 | return false; 221 | } 222 | 223 | static bool scan_word(TSLexer *lexer, const char* const word) { 224 | for (uint8_t i = 0; word[i] != '\0'; i++) { 225 | if (lexer->lookahead != word[i]) { 226 | return false; 227 | } 228 | advance(lexer); 229 | } 230 | return !iswalnum(lexer->lookahead); 231 | } 232 | 233 | static inline void debug_indents(Scanner *scanner) { 234 | LOG(" indents(%d): ", scanner->indents.size); 235 | for (unsigned i = 0; i < scanner->indents.size; i++) { 236 | LOG("%d ", scanner->indents.contents[i]); 237 | } 238 | LOG("\n"); 239 | } 240 | 241 | bool tree_sitter_scala_external_scanner_scan(void *payload, TSLexer *lexer, 242 | const bool *valid_symbols) { 243 | #ifdef DEBUG 244 | { 245 | if (valid_symbols[ERROR_SENTINEL]) { 246 | LOG("entering tree_sitter_scala_external_scanner_scan. ERROR_SENTINEL is valid\n"); 247 | } else { 248 | char debug_str[1024] = "entering tree_sitter_scala_external_scanner_scan valid symbols: "; 249 | for (unsigned i = 0; i < ERROR_SENTINEL; i++) { 250 | if (valid_symbols[i]) { 251 | strcat(debug_str, token_name[i]); 252 | strcat(debug_str, ", "); 253 | } 254 | } 255 | strcat(debug_str, "\n"); 256 | LOG("%s", debug_str); 257 | } 258 | } 259 | #endif 260 | 261 | Scanner *scanner = (Scanner *)payload; 262 | int16_t prev = scanner->indents.size > 0 ? *array_back(&scanner->indents) : -1; 263 | int16_t newline_count = 0; 264 | int16_t indentation_size = 0; 265 | 266 | while (iswspace(lexer->lookahead)) { 267 | if (lexer->lookahead == '\n') { 268 | newline_count++; 269 | indentation_size = 0; 270 | } 271 | else { 272 | indentation_size++; 273 | } 274 | skip(lexer); 275 | } 276 | 277 | // Before advancing the lexer, check if we can double outdent 278 | if ( 279 | valid_symbols[OUTDENT] && 280 | ( 281 | lexer->lookahead == 0 || 282 | ( 283 | prev != -1 && 284 | ( 285 | lexer->lookahead == ')' || 286 | lexer->lookahead == ']' || 287 | lexer->lookahead == '}' 288 | ) 289 | ) || 290 | ( 291 | scanner->last_indentation_size != -1 && 292 | prev != -1 && 293 | scanner->last_indentation_size < prev 294 | ) 295 | ) 296 | ) { 297 | if (scanner->indents.size > 0) { 298 | array_pop(&scanner->indents); 299 | } 300 | LOG(" pop\n"); 301 | LOG(" OUTDENT\n"); 302 | lexer->result_symbol = OUTDENT; 303 | return true; 304 | } 305 | scanner->last_indentation_size = -1; 306 | 307 | if ( 308 | valid_symbols[INDENT] && 309 | newline_count > 0 && 310 | ( 311 | scanner->indents.size == 0 || 312 | indentation_size > *array_back(&scanner->indents) 313 | ) 314 | ) { 315 | if (detect_comment_start(lexer)) { 316 | return false; 317 | } 318 | array_push(&scanner->indents, indentation_size); 319 | lexer->result_symbol = INDENT; 320 | LOG(" INDENT\n"); 321 | return true; 322 | } 323 | 324 | // This saves the indentation_size and newline_count so it can be used 325 | // in subsequent calls for multiple outdent or auto-semicolon. 326 | if (valid_symbols[OUTDENT] && 327 | (lexer->lookahead == 0 || 328 | ( 329 | newline_count > 0 && 330 | prev != -1 && 331 | indentation_size < prev 332 | ) 333 | ) 334 | ) { 335 | if (scanner->indents.size > 0) { 336 | array_pop(&scanner->indents); 337 | } 338 | LOG(" pop\n"); 339 | LOG(" OUTDENT\n"); 340 | lexer->result_symbol = OUTDENT; 341 | lexer->mark_end(lexer); 342 | if (detect_comment_start(lexer)) { 343 | return false; 344 | } 345 | scanner->last_indentation_size = indentation_size; 346 | scanner->last_newline_count = newline_count; 347 | if (lexer->eof(lexer)) { 348 | scanner->last_column = -1; 349 | } else { 350 | scanner->last_column = (int16_t)lexer->get_column(lexer); 351 | } 352 | return true; 353 | } 354 | 355 | // Recover newline_count from the outdent reset 356 | bool is_eof = lexer->eof(lexer); 357 | if ( 358 | ( 359 | scanner->last_newline_count > 0 && 360 | (is_eof && scanner->last_column == -1) 361 | ) || 362 | (!is_eof && lexer->get_column(lexer) == (uint32_t)scanner->last_column) 363 | ) { 364 | newline_count += scanner->last_newline_count; 365 | } 366 | scanner->last_newline_count = 0; 367 | 368 | if (valid_symbols[AUTOMATIC_SEMICOLON] && newline_count > 0) { 369 | // AUTOMATIC_SEMICOLON should not be issued in the middle of expressions 370 | // Thus, we exit this branch when encountering comments, else/catch clauses, etc. 371 | 372 | lexer->mark_end(lexer); 373 | lexer->result_symbol = AUTOMATIC_SEMICOLON; 374 | 375 | // Probably, a multi-line field expression, e.g. 376 | // a 377 | // .b 378 | // .c 379 | if (lexer->lookahead == '.') { 380 | return false; 381 | } 382 | 383 | // Single-line and multi-line comments 384 | if (lexer->lookahead == '/') { 385 | advance(lexer); 386 | if (lexer->lookahead == '/') { 387 | return false; 388 | } 389 | if (lexer->lookahead == '*') { 390 | advance(lexer); 391 | while (!lexer->eof(lexer)) { 392 | if (lexer->lookahead == '*') { 393 | advance(lexer); 394 | if (lexer->lookahead == '/') { 395 | advance(lexer); 396 | break; 397 | } 398 | } else { 399 | advance(lexer); 400 | } 401 | } 402 | while (iswspace(lexer->lookahead)) { 403 | if (lexer->lookahead == '\n' || lexer->lookahead == '\r') { 404 | return false; 405 | } 406 | skip(lexer); 407 | } 408 | // If some code is present at the same line after comment end, 409 | // we should still produce AUTOMATIC_SEMICOLON, e.g. in 410 | // val a = 1 411 | // /* comment */ val b = 2 412 | return true; 413 | } 414 | } 415 | 416 | if (valid_symbols[ELSE]) { 417 | return !scan_word(lexer, "else"); 418 | } 419 | 420 | if (valid_symbols[CATCH]) { 421 | if (scan_word(lexer, "catch")) { 422 | return false; 423 | } 424 | } 425 | 426 | if (valid_symbols[FINALLY]) { 427 | if (scan_word(lexer, "finally")) { 428 | return false; 429 | } 430 | } 431 | 432 | if (valid_symbols[EXTENDS]) { 433 | if (scan_word(lexer, "extends")) { 434 | return false; 435 | } 436 | } 437 | 438 | if (valid_symbols[WITH]) { 439 | if (scan_word(lexer, "with")) { 440 | return false; 441 | } 442 | } 443 | 444 | if (valid_symbols[DERIVES]) { 445 | if (scan_word(lexer, "derives")) { 446 | return false; 447 | } 448 | } 449 | 450 | if (newline_count > 1) { 451 | return true; 452 | } 453 | 454 | return true; 455 | } 456 | 457 | while (iswspace(lexer->lookahead)) { 458 | if (lexer->lookahead == '\n') { 459 | newline_count++; 460 | } 461 | skip(lexer); 462 | } 463 | 464 | if (valid_symbols[SIMPLE_STRING_START] && lexer->lookahead == '"') { 465 | advance(lexer); 466 | lexer->mark_end(lexer); 467 | 468 | if (lexer->lookahead == '"') { 469 | advance(lexer); 470 | if (lexer->lookahead == '"') { 471 | advance(lexer); 472 | lexer->result_symbol = SIMPLE_MULTILINE_STRING_START; 473 | lexer->mark_end(lexer); 474 | return true; 475 | } 476 | } 477 | 478 | lexer->result_symbol = SIMPLE_STRING_START; 479 | return true; 480 | } 481 | 482 | // We need two tokens of lookahead to determine if we are parsing a raw string, 483 | // the `raw` and the `"`, which is why we need to do it in the external scanner. 484 | if (valid_symbols[RAW_STRING_START] && lexer->lookahead == 'r') { 485 | advance(lexer); 486 | if (lexer->lookahead == 'a') { 487 | advance(lexer); 488 | if (lexer->lookahead == 'w') { 489 | advance(lexer); 490 | if (lexer->lookahead == '"') { 491 | lexer->mark_end(lexer); 492 | lexer->result_symbol = RAW_STRING_START; 493 | return true; 494 | } 495 | } 496 | } 497 | } 498 | 499 | if (valid_symbols[SIMPLE_STRING_MIDDLE]) { 500 | return scan_string_content(lexer, false, STRING_MODE_SIMPLE); 501 | } 502 | 503 | if (valid_symbols[INTERPOLATED_STRING_MIDDLE]) { 504 | return scan_string_content(lexer, false, STRING_MODE_INTERPOLATED); 505 | } 506 | 507 | if (valid_symbols[RAW_STRING_MIDDLE]) { 508 | return scan_string_content(lexer, false, STRING_MODE_RAW); 509 | } 510 | 511 | if (valid_symbols[RAW_STRING_MULTILINE_MIDDLE]) { 512 | return scan_string_content(lexer, true, STRING_MODE_RAW); 513 | } 514 | 515 | if (valid_symbols[INTERPOLATED_MULTILINE_STRING_MIDDLE]) { 516 | return scan_string_content(lexer, true, STRING_MODE_INTERPOLATED); 517 | } 518 | 519 | // We still need to handle the simple multiline string case, but there is 520 | // no `MULTILINE_STRING_MIDDLE` token, and `MULTILINE_STRING_END` is used 521 | // by all three of simple raw, and interpolated multiline strings. So this 522 | // check needs to come after the `INTERPOLATED_MULTILINE_STRING_MIDDLE` and 523 | // `RAW_STRING_MULTILINE_MIDDLE` check, so that we can be sure we are in a 524 | // simple multiline string context. 525 | if (valid_symbols[MULTILINE_STRING_END]) { 526 | return scan_string_content(lexer, true, STRING_MODE_SIMPLE); 527 | } 528 | 529 | return false; 530 | } 531 | 532 | // 533 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | #endif 22 | 23 | typedef struct { 24 | TSFieldId field_id; 25 | uint8_t child_index; 26 | bool inherited; 27 | } TSFieldMapEntry; 28 | 29 | typedef struct { 30 | uint16_t index; 31 | uint16_t length; 32 | } TSFieldMapSlice; 33 | 34 | typedef struct { 35 | bool visible; 36 | bool named; 37 | bool supertype; 38 | } TSSymbolMetadata; 39 | 40 | typedef struct TSLexer TSLexer; 41 | 42 | struct TSLexer { 43 | int32_t lookahead; 44 | TSSymbol result_symbol; 45 | void (*advance)(TSLexer *, bool); 46 | void (*mark_end)(TSLexer *); 47 | uint32_t (*get_column)(TSLexer *); 48 | bool (*is_at_included_range_start)(const TSLexer *); 49 | bool (*eof)(const TSLexer *); 50 | void (*log)(const TSLexer *, const char *, ...); 51 | }; 52 | 53 | typedef enum { 54 | TSParseActionTypeShift, 55 | TSParseActionTypeReduce, 56 | TSParseActionTypeAccept, 57 | TSParseActionTypeRecover, 58 | } TSParseActionType; 59 | 60 | typedef union { 61 | struct { 62 | uint8_t type; 63 | TSStateId state; 64 | bool extra; 65 | bool repetition; 66 | } shift; 67 | struct { 68 | uint8_t type; 69 | uint8_t child_count; 70 | TSSymbol symbol; 71 | int16_t dynamic_precedence; 72 | uint16_t production_id; 73 | } reduce; 74 | uint8_t type; 75 | } TSParseAction; 76 | 77 | typedef struct { 78 | uint16_t lex_state; 79 | uint16_t external_lex_state; 80 | } TSLexMode; 81 | 82 | typedef union { 83 | TSParseAction action; 84 | struct { 85 | uint8_t count; 86 | bool reusable; 87 | } entry; 88 | } TSParseActionEntry; 89 | 90 | typedef struct { 91 | int32_t start; 92 | int32_t end; 93 | } TSCharacterRange; 94 | 95 | struct TSLanguage { 96 | uint32_t version; 97 | uint32_t symbol_count; 98 | uint32_t alias_count; 99 | uint32_t token_count; 100 | uint32_t external_token_count; 101 | uint32_t state_count; 102 | uint32_t large_state_count; 103 | uint32_t production_id_count; 104 | uint32_t field_count; 105 | uint16_t max_alias_sequence_length; 106 | const uint16_t *parse_table; 107 | const uint16_t *small_parse_table; 108 | const uint32_t *small_parse_table_map; 109 | const TSParseActionEntry *parse_actions; 110 | const char * const *symbol_names; 111 | const char * const *field_names; 112 | const TSFieldMapSlice *field_map_slices; 113 | const TSFieldMapEntry *field_map_entries; 114 | const TSSymbolMetadata *symbol_metadata; 115 | const TSSymbol *public_symbol_map; 116 | const uint16_t *alias_map; 117 | const TSSymbol *alias_sequences; 118 | const TSLexMode *lex_modes; 119 | bool (*lex_fn)(TSLexer *, TSStateId); 120 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 121 | TSSymbol keyword_capture_token; 122 | struct { 123 | const bool *states; 124 | const TSSymbol *symbol_map; 125 | void *(*create)(void); 126 | void (*destroy)(void *); 127 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 128 | unsigned (*serialize)(void *, char *); 129 | void (*deserialize)(void *, const char *, unsigned); 130 | } external_scanner; 131 | const TSStateId *primary_state_ids; 132 | }; 133 | 134 | static inline bool set_contains(TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { 135 | uint32_t index = 0; 136 | uint32_t size = len - index; 137 | while (size > 1) { 138 | uint32_t half_size = size / 2; 139 | uint32_t mid_index = index + half_size; 140 | TSCharacterRange *range = &ranges[mid_index]; 141 | if (lookahead >= range->start && lookahead <= range->end) { 142 | return true; 143 | } else if (lookahead > range->end) { 144 | index = mid_index; 145 | } 146 | size -= half_size; 147 | } 148 | TSCharacterRange *range = &ranges[index]; 149 | return (lookahead >= range->start && lookahead <= range->end); 150 | } 151 | 152 | /* 153 | * Lexer Macros 154 | */ 155 | 156 | #ifdef _MSC_VER 157 | #define UNUSED __pragma(warning(suppress : 4101)) 158 | #else 159 | #define UNUSED __attribute__((unused)) 160 | #endif 161 | 162 | #define START_LEXER() \ 163 | bool result = false; \ 164 | bool skip = false; \ 165 | UNUSED \ 166 | bool eof = false; \ 167 | int32_t lookahead; \ 168 | goto start; \ 169 | next_state: \ 170 | lexer->advance(lexer, skip); \ 171 | start: \ 172 | skip = false; \ 173 | lookahead = lexer->lookahead; 174 | 175 | #define ADVANCE(state_value) \ 176 | { \ 177 | state = state_value; \ 178 | goto next_state; \ 179 | } 180 | 181 | #define ADVANCE_MAP(...) \ 182 | { \ 183 | static const uint16_t map[] = { __VA_ARGS__ }; \ 184 | for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \ 185 | if (map[i] == lookahead) { \ 186 | state = map[i + 1]; \ 187 | goto next_state; \ 188 | } \ 189 | } \ 190 | } 191 | 192 | #define SKIP(state_value) \ 193 | { \ 194 | skip = true; \ 195 | state = state_value; \ 196 | goto next_state; \ 197 | } 198 | 199 | #define ACCEPT_TOKEN(symbol_value) \ 200 | result = true; \ 201 | lexer->result_symbol = symbol_value; \ 202 | lexer->mark_end(lexer); 203 | 204 | #define END_STATE() return result; 205 | 206 | /* 207 | * Parse Table Macros 208 | */ 209 | 210 | #define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT) 211 | 212 | #define STATE(id) id 213 | 214 | #define ACTIONS(id) id 215 | 216 | #define SHIFT(state_value) \ 217 | {{ \ 218 | .shift = { \ 219 | .type = TSParseActionTypeShift, \ 220 | .state = (state_value) \ 221 | } \ 222 | }} 223 | 224 | #define SHIFT_REPEAT(state_value) \ 225 | {{ \ 226 | .shift = { \ 227 | .type = TSParseActionTypeShift, \ 228 | .state = (state_value), \ 229 | .repetition = true \ 230 | } \ 231 | }} 232 | 233 | #define SHIFT_EXTRA() \ 234 | {{ \ 235 | .shift = { \ 236 | .type = TSParseActionTypeShift, \ 237 | .extra = true \ 238 | } \ 239 | }} 240 | 241 | #define REDUCE(symbol_name, children, precedence, prod_id) \ 242 | {{ \ 243 | .reduce = { \ 244 | .type = TSParseActionTypeReduce, \ 245 | .symbol = symbol_name, \ 246 | .child_count = children, \ 247 | .dynamic_precedence = precedence, \ 248 | .production_id = prod_id \ 249 | }, \ 250 | }} 251 | 252 | #define RECOVER() \ 253 | {{ \ 254 | .type = TSParseActionTypeRecover \ 255 | }} 256 | 257 | #define ACCEPT_INPUT() \ 258 | {{ \ 259 | .type = TSParseActionTypeAccept \ 260 | }} 261 | 262 | #ifdef __cplusplus 263 | } 264 | #endif 265 | 266 | #endif // TREE_SITTER_PARSER_H_ 267 | -------------------------------------------------------------------------------- /test/corpus/annotations.txt: -------------------------------------------------------------------------------- 1 | ================================ 2 | Classes 3 | ================================ 4 | 5 | @deprecated("Use D", "1.0") class C {} 6 | 7 | --- 8 | 9 | (compilation_unit 10 | (class_definition 11 | (annotation (type_identifier) (arguments (string) (string))) 12 | (identifier) 13 | (template_body))) 14 | 15 | ================================ 16 | Declarations and definitions 17 | ================================ 18 | 19 | class A(x: String) { 20 | @transient @volatile var y: Int 21 | @transient @volatile val z = x 22 | 23 | @throws(Error) 24 | @deprecated(message = "Don't use this", since = "1.0") 25 | def foo() {} 26 | } 27 | 28 | --- 29 | 30 | (compilation_unit 31 | (class_definition (identifier) 32 | (class_parameters (class_parameter (identifier) (type_identifier))) 33 | (template_body 34 | (var_declaration 35 | (annotation (type_identifier)) 36 | (annotation (type_identifier)) 37 | (identifier) (type_identifier)) 38 | (val_definition 39 | (annotation (type_identifier)) 40 | (annotation (type_identifier)) 41 | (identifier) (identifier)) 42 | (function_definition 43 | (annotation (type_identifier) (arguments (identifier))) 44 | (annotation (type_identifier) 45 | (arguments 46 | (assignment_expression (identifier) (string)) 47 | (assignment_expression (identifier) (string)))) 48 | (identifier) (parameters) (block))))) 49 | 50 | ================================ 51 | Parameters 52 | ================================ 53 | 54 | class A(@one x: String) { 55 | def foo(@another x: Int) {} 56 | } 57 | 58 | --- 59 | 60 | (compilation_unit 61 | (class_definition (identifier) 62 | (class_parameters 63 | (class_parameter 64 | (annotation (type_identifier)) (identifier) (type_identifier))) 65 | (template_body 66 | (function_definition 67 | (identifier) 68 | (parameters (parameter 69 | (annotation (type_identifier)) 70 | (identifier) (type_identifier))) 71 | (block))))) 72 | 73 | ================================ 74 | Types 75 | ================================ 76 | 77 | trait Function0[@specialized(Unit, Int, Double) T] { 78 | def apply: T 79 | } 80 | 81 | --- 82 | 83 | (compilation_unit 84 | (trait_definition (identifier) 85 | (type_parameters 86 | (annotation (type_identifier) (arguments (identifier) (identifier) (identifier))) (identifier)) 87 | (template_body (function_declaration (identifier) (type_identifier))))) 88 | -------------------------------------------------------------------------------- /test/corpus/comments.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Single line comments 3 | ================================================================================ 4 | 5 | // comment 1 6 | // comment 2 7 | 8 | -------------------------------------------------------------------------------- 9 | 10 | (compilation_unit 11 | (comment) 12 | (comment)) 13 | 14 | ================================================================================ 15 | Block comments 16 | ================================================================================ 17 | /**/ 18 | /** comment 1 19 | * /* comment 2 20 | * /* / * * /comment 3 */ 21 | // comment 4 22 | * @param 23 | * */ 24 | */ 25 | 26 | -------------------------------------------------------------------------------- 27 | 28 | (compilation_unit 29 | (block_comment) 30 | (block_comment 31 | (block_comment 32 | (block_comment)))) 33 | 34 | ================================================================================ 35 | Single line comments with block comment 36 | ================================================================================ 37 | 38 | // /* 39 | // * This is awesome comment 40 | // */ 41 | 42 | -------------------------------------------------------------------------------- 43 | 44 | (compilation_unit 45 | (comment) 46 | (comment) 47 | (comment)) 48 | 49 | ================================================================================ 50 | Block comment with single-line comment inside 51 | ================================================================================ 52 | 53 | /* // */ 54 | 55 | -------------------------------------------------------------------------------- 56 | 57 | (compilation_unit 58 | (block_comment)) 59 | 60 | ================================================================================ 61 | Using directives 62 | ================================================================================ 63 | 64 | //> using jvm graalvm:21 65 | //> using scala 3.3.0 66 | //> using dep foo:bar:1,2,3,url=https://github.com 67 | //> using exclude "examples/*" "*/resources/*" 68 | // > just a comment 69 | 70 | -------------------------------------------------------------------------------- 71 | 72 | (compilation_unit 73 | (comment 74 | (using_directive 75 | (using_directive_key) 76 | (using_directive_value))) 77 | (comment 78 | (using_directive 79 | (using_directive_key) 80 | (using_directive_value))) 81 | (comment 82 | (using_directive 83 | (using_directive_key) 84 | (using_directive_value))) 85 | (comment 86 | (using_directive 87 | (using_directive_key) 88 | (using_directive_value))) 89 | (comment)) 90 | 91 | ================================================================================ 92 | Shebang 93 | ================================================================================ 94 | 95 | #!/usr/bin/env -S scala-cli shebang -S 3 96 | 97 | "shebang" 98 | -------------------------------------------------------------------------------- 99 | 100 | (compilation_unit 101 | (comment) 102 | (string)) 103 | -------------------------------------------------------------------------------- /test/corpus/definitions-pending.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Function without parens 3 | :skip 4 | ================================================================================ 5 | 6 | // https://github.com/scala/scala3/blob/main/tests/pos/multiLineOps.scala#L15 7 | send_! "!" 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | ================================================================================ 12 | Types of lambda parameters without parenthesis 13 | :skip 14 | ================================================================================ 15 | 16 | // https://github.com/tree-sitter/tree-sitter-scala/issues/361 17 | xs.map { x: Int => x } -------------------------------------------------------------------------------- /test/corpus/expressions-pending.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Trailing coma after for comprehension in lambda parameter of a function 3 | :skip 4 | ================================================================================ 5 | 6 | // https://github.com/tree-sitter/tree-sitter-scala/issues/449 7 | object Foo: 8 | def foo(f: Option[Int] => Option[Int]): Option[Int] = f(None) 9 | 10 | foo(o => 11 | for { 12 | a <- o 13 | } yield a, 14 | ) 15 | 16 | -------------------------------------------------------------------------------- 17 | 18 | ================================================================================ 19 | Lambda expression with semicolon 20 | :skip 21 | ================================================================================ 22 | 23 | // https://github.com/tree-sitter/tree-sitter-scala/issues/389 24 | TypeTreeWithDeferredRefCheck() { () => val tp = qual.tpe; val sym = tp.typeSymbolDirect 25 | 1 26 | } 27 | 28 | val l1 = { () => val x = 1; 2 } 29 | 30 | -------------------------------------------------------------------------------- 31 | 32 | ================================================================================ 33 | infix_expression across multiple lines 34 | :skip 35 | ================================================================================ 36 | 37 | // https://github.com/tree-sitter/tree-sitter-scala/issues/141 38 | def foo: Boolean = 39 | x 40 | || a.b(c) 41 | 42 | -------------------------------------------------------------------------------- 43 | 44 | -------------------------------------------------------------------------------- /test/corpus/literals.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Simple strings 3 | ================================================================================ 4 | 5 | val emptyString = "" 6 | 7 | val oneLineString = "I'm just on one line" 8 | 9 | val stringWithCommentLikeContent1 = "// not a comment" 10 | 11 | val stringWithCommentLikeContent2 = "/* not a comment */" 12 | 13 | val stringWithEscapeSequence = "first line\nsecond line" 14 | 15 | val multiLineString = """ 16 | a 17 | $thisIsntInterpolated 18 | ${thisEither} 19 | no escape codes in multiline strings \uD83D\uDE00 \n 20 | """ 21 | 22 | val emptyMultilineStringf = """""" 23 | 24 | val stringOfOneDoubleQuote = """"""" 25 | 26 | val multiLineString3 = """\{@inheritDoc\p{Zs}*\}""" 27 | 28 | val blackslashDoesNotEscapeClosingQuote = """\""" 29 | 30 | -------------------------------------------------------------------------------- 31 | 32 | (compilation_unit 33 | (val_definition 34 | (identifier) 35 | (string)) 36 | (val_definition 37 | (identifier) 38 | (string)) 39 | (val_definition 40 | (identifier) 41 | (string)) 42 | (val_definition 43 | (identifier) 44 | (string)) 45 | (val_definition 46 | (identifier) 47 | (string 48 | (escape_sequence))) 49 | (val_definition 50 | (identifier) 51 | (string)) 52 | (val_definition 53 | (identifier) 54 | (string)) 55 | (val_definition 56 | (identifier) 57 | (string)) 58 | (val_definition 59 | (identifier) 60 | (string)) 61 | (val_definition 62 | (identifier) 63 | (string))) 64 | 65 | ================================================================================ 66 | Escape sequences in strings 67 | ================================================================================ 68 | 69 | val singleEscapeCode = "\n" 70 | 71 | val singleCharacterEscapes = "\n\r\t\b\f\'\"\\" 72 | 73 | val unicodeEscape = "\uD83D\UDE00" 74 | 75 | val repeatedUs = "\uuuuD83D\UUDE00" 76 | 77 | -------------------------------------------------------------------------------- 78 | 79 | (compilation_unit 80 | (val_definition 81 | (identifier) 82 | (string 83 | (escape_sequence))) 84 | (val_definition 85 | (identifier) 86 | (string 87 | (escape_sequence) 88 | (escape_sequence) 89 | (escape_sequence) 90 | (escape_sequence) 91 | (escape_sequence) 92 | (escape_sequence) 93 | (escape_sequence) 94 | (escape_sequence))) 95 | (val_definition 96 | (identifier) 97 | (string 98 | (escape_sequence) 99 | (escape_sequence))) 100 | (val_definition 101 | (identifier) 102 | (string 103 | (escape_sequence) 104 | (escape_sequence)))) 105 | 106 | ================================================================================ 107 | Interpolated strings 108 | ================================================================================ 109 | 110 | val empty = s"" 111 | 112 | val emptyMultiline = s"""""" 113 | 114 | val string1 = s"a $b ${c}" 115 | 116 | val string2 = f"hi $name%s" 117 | 118 | val string3 = raw"Not a really a new line \n${ha}." 119 | 120 | val string4 = s""" 121 | works even in multiline strings, ${name} 122 | """ 123 | 124 | val string5 = s"$works${without}$spaces" 125 | 126 | val string6 = s"$a$b" 127 | 128 | val string7 = s"$$ $a" 129 | 130 | val string8 = s"$"$a" 131 | 132 | val string9 = s"$"$a\uD83D\UDE00\n" 133 | 134 | val multiline = raw""" 135 | $$ 136 | ${interp} 137 | \n 138 | \x 139 | \" 140 | \""" 141 | 142 | -------------------------------------------------------------------------------- 143 | 144 | (compilation_unit 145 | (val_definition 146 | (identifier) 147 | (interpolated_string_expression 148 | (identifier) 149 | (interpolated_string))) 150 | (val_definition 151 | (identifier) 152 | (interpolated_string_expression 153 | (identifier) 154 | (interpolated_string))) 155 | (val_definition 156 | (identifier) 157 | (interpolated_string_expression 158 | (identifier) 159 | (interpolated_string 160 | (interpolation 161 | (identifier)) 162 | (interpolation 163 | (block 164 | (identifier)))))) 165 | (val_definition 166 | (identifier) 167 | (interpolated_string_expression 168 | (identifier) 169 | (interpolated_string 170 | (interpolation 171 | (identifier))))) 172 | (val_definition 173 | (identifier) 174 | (interpolated_string_expression 175 | (identifier) 176 | (interpolated_string 177 | (interpolation 178 | (block 179 | (identifier)))))) 180 | (val_definition 181 | (identifier) 182 | (interpolated_string_expression 183 | (identifier) 184 | (interpolated_string 185 | (interpolation 186 | (block 187 | (identifier)))))) 188 | (val_definition 189 | (identifier) 190 | (interpolated_string_expression 191 | (identifier) 192 | (interpolated_string 193 | (interpolation 194 | (identifier)) 195 | (interpolation 196 | (block 197 | (identifier))) 198 | (interpolation 199 | (identifier))))) 200 | (val_definition 201 | (identifier) 202 | (interpolated_string_expression 203 | (identifier) 204 | (interpolated_string 205 | (interpolation 206 | (identifier)) 207 | (interpolation 208 | (identifier))))) 209 | (val_definition 210 | (identifier) 211 | (interpolated_string_expression 212 | (identifier) 213 | (interpolated_string 214 | (escape_sequence) 215 | (interpolation 216 | (identifier))))) 217 | (val_definition 218 | (identifier) 219 | (interpolated_string_expression 220 | (identifier) 221 | (interpolated_string 222 | (escape_sequence) 223 | (interpolation 224 | (identifier))))) 225 | (val_definition 226 | (identifier) 227 | (interpolated_string_expression 228 | (identifier) 229 | (interpolated_string 230 | (escape_sequence) 231 | (interpolation 232 | (identifier)) 233 | (escape_sequence) 234 | (escape_sequence) 235 | (escape_sequence)))) 236 | (val_definition 237 | (identifier) 238 | (interpolated_string_expression 239 | (identifier) 240 | (interpolated_string 241 | (escape_sequence) 242 | (interpolation 243 | (block 244 | (identifier))))))) 245 | 246 | ================================================================================ 247 | Raw strings 248 | ================================================================================ 249 | 250 | val emptyRaw = raw"" 251 | 252 | val emptyMultilineRaw = raw"""""" 253 | 254 | val invalidEscapeCodesAllowedAndValidEscapesIgnored = raw"\n\t\x\w\g\k" 255 | 256 | val ErasedFunctionN = raw"ErasedFunction(\d+)".r 257 | 258 | val escapedAndInterpolated = raw"Not a really a new line \n${ha}." 259 | 260 | val blackslashQuoteDoesNotCloseString = raw"\"" 261 | 262 | val doubleSlashQuoteDoesCloseString = raw"\\" 263 | 264 | val dollarEscapeInSingleLine = raw"$$" 265 | 266 | val slashDoesNotEscapeDollarSign = raw"\$$" 267 | 268 | val multiline = raw""" 269 | $$ 270 | ${interp} 271 | \n 272 | \x 273 | \" 274 | \""" 275 | 276 | val ensureIdentifierNamedRawStillWorks = someFunction(raw) 277 | 278 | val raw = raw(raw) 279 | 280 | -------------------------------------------------------------------------------- 281 | 282 | (compilation_unit 283 | (val_definition 284 | (identifier) 285 | (interpolated_string_expression 286 | (identifier) 287 | (interpolated_string))) 288 | (val_definition 289 | (identifier) 290 | (interpolated_string_expression 291 | (identifier) 292 | (interpolated_string))) 293 | (val_definition 294 | (identifier) 295 | (interpolated_string_expression 296 | (identifier) 297 | (interpolated_string))) 298 | (val_definition 299 | (identifier) 300 | (field_expression 301 | (interpolated_string_expression 302 | (identifier) 303 | (interpolated_string)) 304 | (identifier))) 305 | (val_definition 306 | (identifier) 307 | (interpolated_string_expression 308 | (identifier) 309 | (interpolated_string 310 | (interpolation 311 | (block 312 | (identifier)))))) 313 | (val_definition 314 | (identifier) 315 | (interpolated_string_expression 316 | (identifier) 317 | (interpolated_string))) 318 | (val_definition 319 | (identifier) 320 | (interpolated_string_expression 321 | (identifier) 322 | (interpolated_string))) 323 | (val_definition 324 | (identifier) 325 | (interpolated_string_expression 326 | (identifier) 327 | (interpolated_string 328 | (escape_sequence)))) 329 | (val_definition 330 | (identifier) 331 | (interpolated_string_expression 332 | (identifier) 333 | (interpolated_string 334 | (escape_sequence)))) 335 | (val_definition 336 | (identifier) 337 | (interpolated_string_expression 338 | (identifier) 339 | (interpolated_string 340 | (escape_sequence) 341 | (interpolation 342 | (block 343 | (identifier)))))) 344 | (val_definition 345 | (identifier) 346 | (call_expression 347 | (identifier) 348 | (arguments 349 | (identifier)))) 350 | (val_definition 351 | (identifier) 352 | (call_expression 353 | (identifier) 354 | (arguments 355 | (identifier))))) 356 | 357 | ================================================================================ 358 | Raw and interpolated strings have equivalent parse trees 359 | ================================================================================ 360 | 361 | val raw = raw"Foo $$ ${bar}" 362 | 363 | val raw = s"Foo $$ ${bar}" 364 | 365 | -------------------------------------------------------------------------------- 366 | 367 | (compilation_unit 368 | (val_definition 369 | (identifier) 370 | (interpolated_string_expression 371 | (identifier) 372 | (interpolated_string 373 | (escape_sequence) 374 | (interpolation 375 | (block 376 | (identifier)))))) 377 | (val_definition 378 | (identifier) 379 | (interpolated_string_expression 380 | (identifier) 381 | (interpolated_string 382 | (escape_sequence) 383 | (interpolation 384 | (block 385 | (identifier))))))) 386 | 387 | 388 | ================================================================================ 389 | Integer literals 390 | ================================================================================ 391 | 392 | val i1 = 0 393 | val i2 = 1234 394 | val i3 = -0xF2 395 | val i4 = 0XA0 396 | val l1 = -0l 397 | val l2 = 1234L 398 | val l3 = 0xF23l 399 | val l4 = 0XA03L 400 | val l5 = 150_000_000 401 | val l6 = 0xFF_FF_FF 402 | 403 | -------------------------------------------------------------------------------- 404 | 405 | (compilation_unit 406 | (val_definition 407 | (identifier) 408 | (integer_literal)) 409 | (val_definition 410 | (identifier) 411 | (integer_literal)) 412 | (val_definition 413 | (identifier) 414 | (integer_literal)) 415 | (val_definition 416 | (identifier) 417 | (integer_literal)) 418 | (val_definition 419 | (identifier) 420 | (integer_literal)) 421 | (val_definition 422 | (identifier) 423 | (integer_literal)) 424 | (val_definition 425 | (identifier) 426 | (integer_literal)) 427 | (val_definition 428 | (identifier) 429 | (integer_literal)) 430 | (val_definition 431 | (identifier) 432 | (integer_literal)) 433 | (val_definition 434 | (identifier) 435 | (integer_literal))) 436 | 437 | ================================================================================ 438 | Floating point literals 439 | ================================================================================ 440 | 441 | val f1 = 3.14 442 | val f2 = -3f 443 | val f2 = 3E-1 444 | val d1 = .314D 445 | 446 | -------------------------------------------------------------------------------- 447 | 448 | (compilation_unit 449 | (val_definition 450 | (identifier) 451 | (floating_point_literal)) 452 | (val_definition 453 | (identifier) 454 | (floating_point_literal)) 455 | (val_definition 456 | (identifier) 457 | (floating_point_literal)) 458 | (val_definition 459 | (identifier) 460 | (floating_point_literal))) 461 | 462 | ================================================================================ 463 | Boolean literals 464 | ================================================================================ 465 | 466 | val myBool = true 467 | 468 | def foo(a: Boolean = false) = a && true 469 | 470 | -------------------------------------------------------------------------------- 471 | 472 | (compilation_unit 473 | (val_definition 474 | (identifier) 475 | (boolean_literal)) 476 | (function_definition 477 | (identifier) 478 | (parameters 479 | (parameter 480 | (identifier) 481 | (type_identifier) 482 | (boolean_literal))) 483 | (infix_expression 484 | (identifier) 485 | (operator_identifier) 486 | (boolean_literal)))) 487 | 488 | ================================================================================ 489 | Character literals 490 | ================================================================================ 491 | 492 | val myChar = 'c' 493 | 494 | val otherChar = '\u0041' 495 | 496 | val otherChar2 = '\uu0041' 497 | 498 | val anotherChar = '\n' 499 | 500 | def foo(a: Char = 'c') = a + 'd' 501 | 502 | -------------------------------------------------------------------------------- 503 | 504 | (compilation_unit 505 | (val_definition 506 | (identifier) 507 | (character_literal)) 508 | (val_definition 509 | (identifier) 510 | (character_literal)) 511 | (val_definition 512 | (identifier) 513 | (character_literal)) 514 | (val_definition 515 | (identifier) 516 | (character_literal)) 517 | (function_definition 518 | (identifier) 519 | (parameters 520 | (parameter 521 | (identifier) 522 | (type_identifier) 523 | (character_literal))) 524 | (infix_expression 525 | (identifier) 526 | (operator_identifier) 527 | (character_literal)))) 528 | 529 | ================================================================================ 530 | Null 531 | ================================================================================ 532 | 533 | lazy val nullObject: String = null 534 | 535 | -------------------------------------------------------------------------------- 536 | 537 | (compilation_unit 538 | (val_definition 539 | (modifiers) 540 | (identifier) 541 | (type_identifier) 542 | (null_literal))) 543 | 544 | ================================================================================ 545 | Tuple literals 546 | ================================================================================ 547 | 548 | val x = ( 549 | 1, 550 | 2, 551 | 3, 552 | ) 553 | 554 | -------------------------------------------------------------------------------- 555 | 556 | (compilation_unit 557 | (val_definition 558 | (identifier) 559 | (tuple_expression 560 | (integer_literal) 561 | (integer_literal) 562 | (integer_literal)))) 563 | -------------------------------------------------------------------------------- /test/corpus/patterns-pending.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Infix types in match type cases 3 | :skip 4 | ================================================================================ 5 | 6 | // https://github.com/tree-sitter/tree-sitter-scala/issues/360 7 | type F[T] = T match { 8 | case t *: EmptyTuple => t 9 | case t *: rest => t 10 | } 11 | 12 | -------------------------------------------------------------------------------- 13 | -------------------------------------------------------------------------------- /test/corpus/patterns.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Alternative patterns 3 | ================================================================================ 4 | 5 | val x = y match { 6 | case 1 | a => b 7 | case "c" | "d" | "e" => f 8 | } 9 | 10 | -------------------------------------------------------------------------------- 11 | 12 | (compilation_unit 13 | (val_definition 14 | (identifier) 15 | (match_expression 16 | (identifier) 17 | (case_block 18 | (case_clause 19 | (alternative_pattern 20 | (integer_literal) 21 | (identifier)) 22 | (identifier)) 23 | (case_clause 24 | (alternative_pattern 25 | (alternative_pattern 26 | (string) 27 | (string)) 28 | (string)) 29 | (identifier)))))) 30 | 31 | ================================================================================ 32 | Typed patterns 33 | ================================================================================ 34 | 35 | val x = y match { 36 | case 1 : Int => 2 37 | case a : B with C => d 38 | case _: B | _: C => 3 39 | case Object.Constant => 3 40 | } 41 | 42 | -------------------------------------------------------------------------------- 43 | 44 | (compilation_unit 45 | (val_definition 46 | (identifier) 47 | (match_expression 48 | (identifier) 49 | (case_block 50 | (case_clause 51 | (typed_pattern 52 | (integer_literal) 53 | (type_identifier)) 54 | (integer_literal)) 55 | (case_clause 56 | (typed_pattern 57 | (identifier) 58 | (compound_type 59 | (type_identifier) 60 | (type_identifier))) 61 | (identifier)) 62 | (case_clause 63 | (alternative_pattern 64 | (typed_pattern 65 | (wildcard) 66 | (type_identifier)) 67 | (typed_pattern 68 | (wildcard) 69 | (type_identifier))) 70 | (integer_literal)) 71 | (case_clause 72 | (stable_identifier 73 | (identifier) 74 | (identifier)) 75 | (integer_literal)))))) 76 | 77 | ================================================================================ 78 | Tuple patterns 79 | ================================================================================ 80 | 81 | val (a, b) = if (c) (d, e) else (f, g) 82 | 83 | val x = y match { 84 | case (A, B) => X 85 | } 86 | 87 | -------------------------------------------------------------------------------- 88 | 89 | (compilation_unit 90 | (val_definition 91 | (tuple_pattern 92 | (identifier) 93 | (identifier)) 94 | (if_expression 95 | (parenthesized_expression 96 | (identifier)) 97 | (tuple_expression 98 | (identifier) 99 | (identifier)) 100 | (tuple_expression 101 | (identifier) 102 | (identifier)))) 103 | (val_definition 104 | (identifier) 105 | (match_expression 106 | (identifier) 107 | (case_block 108 | (case_clause 109 | (tuple_pattern 110 | (identifier) 111 | (identifier)) 112 | (identifier)))))) 113 | 114 | ================================================================================ 115 | Name tuple patterns (Scala 3 syntax) 116 | ================================================================================ 117 | 118 | val x = y match 119 | case (a = A, b = B) => ??? 120 | 121 | -------------------------------------------------------------------------------- 122 | 123 | (compilation_unit 124 | (val_definition 125 | (identifier) 126 | (match_expression 127 | (identifier) 128 | (indented_cases 129 | (case_clause 130 | (named_tuple_pattern 131 | (named_pattern 132 | (identifier) 133 | (identifier)) 134 | (named_pattern 135 | (identifier) 136 | (identifier))) 137 | (operator_identifier)))))) 138 | 139 | ================================================================================ 140 | Case class patterns 141 | ================================================================================ 142 | 143 | def showNotification(notification: Notification): String = { 144 | notification match { 145 | case Email(email, title, _) => 146 | s"You got an email from $email with title: $title" 147 | case SMS(number, message) => 148 | s"You got an SMS from $number! Message: $message" 149 | case VoiceRecording(name, link) => 150 | s"you received a Voice Recording from $name! Click the link to hear it: $link" 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- 155 | 156 | (compilation_unit 157 | (function_definition 158 | (identifier) 159 | (parameters 160 | (parameter 161 | (identifier) 162 | (type_identifier))) 163 | (type_identifier) 164 | (block 165 | (match_expression 166 | (identifier) 167 | (case_block 168 | (case_clause 169 | (case_class_pattern 170 | (type_identifier) 171 | (identifier) 172 | (identifier) 173 | (wildcard)) 174 | (interpolated_string_expression 175 | (identifier) 176 | (interpolated_string 177 | (interpolation 178 | (identifier)) 179 | (interpolation 180 | (identifier))))) 181 | (case_clause 182 | (case_class_pattern 183 | (type_identifier) 184 | (identifier) 185 | (identifier)) 186 | (interpolated_string_expression 187 | (identifier) 188 | (interpolated_string 189 | (interpolation 190 | (identifier)) 191 | (interpolation 192 | (identifier))))) 193 | (case_clause 194 | (case_class_pattern 195 | (type_identifier) 196 | (identifier) 197 | (identifier)) 198 | (interpolated_string_expression 199 | (identifier) 200 | (interpolated_string 201 | (interpolation 202 | (identifier)) 203 | (interpolation 204 | (identifier)))))))))) 205 | 206 | ================================================================================ 207 | Case class patterns (Scala 3 syntax) 208 | ================================================================================ 209 | 210 | class A: 211 | c match 212 | case c @ City(name = "Hoboken") => c.population 213 | 214 | -------------------------------------------------------------------------------- 215 | 216 | (compilation_unit 217 | (class_definition 218 | (identifier) 219 | (template_body 220 | (match_expression 221 | (identifier) 222 | (indented_cases 223 | (case_clause 224 | (capture_pattern 225 | (identifier) 226 | (case_class_pattern 227 | (type_identifier) 228 | (named_pattern 229 | (identifier) 230 | (string)))) 231 | (field_expression 232 | (identifier) 233 | (identifier)))))))) 234 | 235 | ================================================================================ 236 | Infix patterns 237 | ================================================================================ 238 | 239 | def first(x: Seq[Int]) = x match { 240 | case e :+ _ => Some(e) 241 | case _ => None 242 | } 243 | 244 | -------------------------------------------------------------------------------- 245 | 246 | (compilation_unit 247 | (function_definition 248 | (identifier) 249 | (parameters 250 | (parameter 251 | (identifier) 252 | (generic_type 253 | (type_identifier) 254 | (type_arguments 255 | (type_identifier))))) 256 | (match_expression 257 | (identifier) 258 | (case_block 259 | (case_clause 260 | (infix_pattern 261 | (identifier) 262 | (operator_identifier) 263 | (wildcard)) 264 | (call_expression 265 | (identifier) 266 | (arguments 267 | (identifier)))) 268 | (case_clause 269 | (wildcard) 270 | (identifier)))))) 271 | 272 | ================================================================================ 273 | Capture patterns 274 | ================================================================================ 275 | 276 | val x = y match { 277 | case a @ B(1) => a 278 | case b @ C(d @ (e @ X, _: Y)) => e 279 | case req @ (POST | GET) -> Root / "test" => 5 280 | case Array(a: Type, _@_*) => y 281 | } 282 | 283 | -------------------------------------------------------------------------------- 284 | 285 | (compilation_unit 286 | (val_definition 287 | (identifier) 288 | (match_expression 289 | (identifier) 290 | (case_block 291 | (case_clause 292 | (capture_pattern 293 | (identifier) 294 | (case_class_pattern 295 | (type_identifier) 296 | (integer_literal))) 297 | (identifier)) 298 | (case_clause 299 | (capture_pattern 300 | (identifier) 301 | (case_class_pattern 302 | (type_identifier) 303 | (capture_pattern 304 | (identifier) 305 | (tuple_pattern 306 | (capture_pattern 307 | (identifier) 308 | (identifier)) 309 | (typed_pattern 310 | (wildcard) 311 | (type_identifier)))))) 312 | (identifier)) 313 | (case_clause 314 | (infix_pattern 315 | (infix_pattern 316 | (capture_pattern 317 | (identifier) 318 | (tuple_pattern 319 | (alternative_pattern 320 | (identifier) 321 | (identifier)))) 322 | (operator_identifier) 323 | (identifier)) 324 | (operator_identifier) 325 | (string)) 326 | (integer_literal)) 327 | (case_clause 328 | (case_class_pattern 329 | (type_identifier) 330 | (typed_pattern 331 | (identifier) 332 | (type_identifier)) 333 | (repeat_pattern 334 | (capture_pattern 335 | (wildcard) 336 | (wildcard)))) 337 | (identifier)))))) 338 | 339 | ================================================================================ 340 | Quoted patterns (Scala 3 syntax) 341 | ================================================================================ 342 | 343 | def foo = 344 | x match 345 | case '{ $boolExpr } => Some(true) 346 | case _ => None 347 | 348 | -------------------------------------------------------------------------------- 349 | 350 | (compilation_unit 351 | (function_definition 352 | (identifier) 353 | (indented_block 354 | (match_expression 355 | (identifier) 356 | (indented_cases 357 | (case_clause 358 | (quote_expression 359 | (identifier)) 360 | (call_expression 361 | (identifier) 362 | (arguments 363 | (boolean_literal)))) 364 | (case_clause 365 | (wildcard) 366 | (identifier))))))) 367 | 368 | ================================================================================ 369 | Given pattern (Scala 3 syntax) 370 | ================================================================================ 371 | 372 | for 373 | given Int <- Some(1) 374 | yield () 375 | 376 | -------------------------------------------------------------------------------- 377 | 378 | (compilation_unit 379 | (for_expression 380 | (enumerators 381 | (enumerator 382 | (given_pattern 383 | (type_identifier)) 384 | (call_expression 385 | (identifier) 386 | (arguments 387 | (integer_literal))))) 388 | (unit))) 389 | -------------------------------------------------------------------------------- /test/corpus/types-pending.txt: -------------------------------------------------------------------------------- 1 | 2 | ================================================================================ 3 | Subclass with multiline params and unnamed after named param 4 | :skip 5 | ================================================================================ 6 | 7 | // https://github.com/tree-sitter/tree-sitter-scala/issues/465 8 | // https://github.com/scala/scala3/blob/main/tests/run/i14164.scala#L5-L9 9 | class Child extends Base( 10 | param = 11 | for x <- Seq("a") yield x 12 | "param" 13 | ) 14 | 15 | -------------------------------------------------------------------------------- 16 | 17 | ================================================================================ 18 | case class nested in an object definition 19 | :skip 20 | ================================================================================ 21 | 22 | // https://github.com/tree-sitter/tree-sitter-scala/issues/329 23 | object Merge: 24 | 25 | case class Location(runIndex: Int, locationInRun: String): 26 | end Location 27 | 28 | end Merge 29 | 30 | -------------------------------------------------------------------------------- 31 | 32 | ================================================================================ 33 | More than two argument lists in `extends` 34 | :skip 35 | ================================================================================ 36 | 37 | // https://github.com/tree-sitter/tree-sitter-scala/issues/259 38 | class A extends B(c)(d)(e) 39 | 40 | -------------------------------------------------------------------------------- 41 | -------------------------------------------------------------------------------- /test/corpus/types.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Stable type identifiers 3 | ================================================================================ 4 | 5 | object Main { 6 | type A = B.C 7 | type D = E.F.G 8 | type H = __root__.scala.Int 9 | } 10 | 11 | -------------------------------------------------------------------------------- 12 | 13 | (compilation_unit 14 | (object_definition 15 | (identifier) 16 | (template_body 17 | (type_definition 18 | (type_identifier) 19 | (stable_type_identifier 20 | (identifier) 21 | (type_identifier))) 22 | (type_definition 23 | (type_identifier) 24 | (stable_type_identifier 25 | (stable_identifier 26 | (identifier) 27 | (identifier)) 28 | (type_identifier))) 29 | (type_definition 30 | (type_identifier) 31 | (stable_type_identifier 32 | (stable_identifier 33 | (identifier) 34 | (identifier)) 35 | (type_identifier)))))) 36 | 37 | ================================================================================ 38 | Generic types 39 | ================================================================================ 40 | 41 | object Main { 42 | type A = B[ 43 | C, 44 | D, 45 | ] 46 | type E = F.G[H] 47 | } 48 | 49 | -------------------------------------------------------------------------------- 50 | 51 | (compilation_unit 52 | (object_definition 53 | (identifier) 54 | (template_body 55 | (type_definition 56 | (type_identifier) 57 | (generic_type 58 | (type_identifier) 59 | (type_arguments 60 | (type_identifier) 61 | (type_identifier)))) 62 | (type_definition 63 | (type_identifier) 64 | (generic_type 65 | (stable_type_identifier 66 | (identifier) 67 | (type_identifier)) 68 | (type_arguments 69 | (type_identifier))))))) 70 | 71 | ================================================================================ 72 | Tuple types 73 | ================================================================================ 74 | 75 | object Main { 76 | type A = (B, C) 77 | type A = ( 78 | B, 79 | C, 80 | ) 81 | } 82 | 83 | -------------------------------------------------------------------------------- 84 | 85 | (compilation_unit 86 | (object_definition 87 | (identifier) 88 | (template_body 89 | (type_definition 90 | (type_identifier) 91 | (tuple_type 92 | (type_identifier) 93 | (type_identifier))) 94 | (type_definition 95 | (type_identifier) 96 | (tuple_type 97 | (type_identifier) 98 | (type_identifier)))))) 99 | 100 | ================================================================================ 101 | Named tuple types (Scala 3 syntax) 102 | ================================================================================ 103 | 104 | object O: 105 | type A = (name: String, age: Int) 106 | 107 | -------------------------------------------------------------------------------- 108 | 109 | (compilation_unit 110 | (object_definition 111 | (identifier) 112 | (template_body 113 | (type_definition 114 | (type_identifier) 115 | (named_tuple_type 116 | (name_and_type 117 | (identifier) 118 | (type_identifier)) 119 | (name_and_type 120 | (identifier) 121 | (type_identifier))))))) 122 | 123 | ================================================================================ 124 | Function types 125 | ================================================================================ 126 | 127 | object Main { 128 | type A = (B, C) => D 129 | 130 | type A = (B, C) => (D, E) 131 | 132 | type A = B => (D, E) 133 | } 134 | 135 | -------------------------------------------------------------------------------- 136 | 137 | (compilation_unit 138 | (object_definition 139 | (identifier) 140 | (template_body 141 | (type_definition 142 | (type_identifier) 143 | (function_type 144 | (parameter_types 145 | (type_identifier) 146 | (type_identifier)) 147 | (type_identifier))) 148 | (type_definition 149 | (type_identifier) 150 | (function_type 151 | (parameter_types 152 | (type_identifier) 153 | (type_identifier)) 154 | (tuple_type 155 | (type_identifier) 156 | (type_identifier)))) 157 | (type_definition 158 | (type_identifier) 159 | (function_type 160 | (parameter_types 161 | (type_identifier)) 162 | (tuple_type 163 | (type_identifier) 164 | (type_identifier))))))) 165 | 166 | ================================================================================ 167 | Polymorphic function types (Scala 3 syntax) 168 | ================================================================================ 169 | 170 | class A: 171 | type Comparer = [X: Ord] => (X, X) => Boolean 172 | 173 | -------------------------------------------------------------------------------- 174 | 175 | (compilation_unit 176 | (class_definition 177 | (identifier) 178 | (template_body 179 | (type_definition 180 | (type_identifier) 181 | (function_type 182 | (type_parameters 183 | (identifier) 184 | (context_bound 185 | (type_identifier))) 186 | (function_type 187 | (parameter_types 188 | (type_identifier) 189 | (type_identifier)) 190 | (type_identifier))))))) 191 | 192 | ================================================================================ 193 | Context function types (Scala 3 syntax) 194 | ================================================================================ 195 | 196 | type Executable[A] = ExecutionContext ?=> A 197 | 198 | -------------------------------------------------------------------------------- 199 | 200 | (compilation_unit 201 | (type_definition 202 | (type_identifier) 203 | (type_parameters 204 | (identifier)) 205 | (function_type 206 | (parameter_types 207 | (type_identifier)) 208 | (type_identifier)))) 209 | 210 | ================================================================================ 211 | Match types (Scala 3 syntax) 212 | ================================================================================ 213 | 214 | type Elem[A] = A match 215 | case String => Char 216 | case Array[a] => a 217 | 218 | -------------------------------------------------------------------------------- 219 | 220 | (compilation_unit 221 | (type_definition 222 | (type_identifier) 223 | (type_parameters 224 | (identifier)) 225 | (match_type 226 | (type_identifier) 227 | (type_case_clause 228 | (type_identifier) 229 | (type_identifier)) 230 | (type_case_clause 231 | (generic_type 232 | (type_identifier) 233 | (type_arguments 234 | (type_identifier))) 235 | (type_identifier))))) 236 | 237 | ================================================================================ 238 | Compound types 239 | ================================================================================ 240 | 241 | def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { 242 | } 243 | 244 | class F extends Cloneable with Resetable with Serializable {} 245 | 246 | type G = H[A1] { 247 | def foo[A2 <: Stepper[_]]: A2 248 | } 249 | 250 | type I = F1[A1: 251 | def foo: A2] 252 | 253 | def a: F { val x: Int } = ??? 254 | 255 | -------------------------------------------------------------------------------- 256 | 257 | (compilation_unit 258 | (function_definition 259 | (identifier) 260 | (parameters 261 | (parameter 262 | (identifier) 263 | (compound_type 264 | (type_identifier) 265 | (type_identifier)))) 266 | (type_identifier) 267 | (block)) 268 | (class_definition 269 | (identifier) 270 | (extends_clause 271 | (type_identifier) 272 | (type_identifier) 273 | (type_identifier)) 274 | (template_body)) 275 | (type_definition 276 | (type_identifier) 277 | (compound_type 278 | (generic_type 279 | (type_identifier) 280 | (type_arguments 281 | (type_identifier))) 282 | (refinement 283 | (function_declaration 284 | (identifier) 285 | (type_parameters 286 | (identifier) 287 | (upper_bound 288 | (generic_type 289 | (type_identifier) 290 | (type_arguments 291 | (wildcard))))) 292 | (type_identifier))))) 293 | (type_definition 294 | (type_identifier) 295 | (generic_type 296 | (type_identifier) 297 | (type_arguments 298 | (compound_type 299 | (type_identifier) 300 | (refinement 301 | (function_declaration 302 | (identifier) 303 | (type_identifier))))))) 304 | (function_definition 305 | (identifier) 306 | (compound_type 307 | (type_identifier) 308 | (refinement 309 | (val_declaration 310 | (identifier) 311 | (type_identifier)))) 312 | (operator_identifier))) 313 | 314 | ================================================================================ 315 | Infix types 316 | ================================================================================ 317 | 318 | type A = B Foo C 319 | 320 | type A = B ! C or D 321 | 322 | type A = (B, C) ~ D 323 | 324 | -------------------------------------------------------------------------------- 325 | 326 | (compilation_unit 327 | (type_definition 328 | (type_identifier) 329 | (infix_type 330 | (type_identifier) 331 | (identifier) 332 | (type_identifier))) 333 | (type_definition 334 | (type_identifier) 335 | (infix_type 336 | (infix_type 337 | (type_identifier) 338 | (operator_identifier) 339 | (type_identifier)) 340 | (identifier) 341 | (type_identifier))) 342 | (type_definition 343 | (type_identifier) 344 | (infix_type 345 | (tuple_type 346 | (type_identifier) 347 | (type_identifier)) 348 | (operator_identifier) 349 | (type_identifier)))) 350 | 351 | ================================================================================ 352 | Variant Types 353 | ================================================================================ 354 | 355 | class Function1[-T1, +R] 356 | 357 | -------------------------------------------------------------------------------- 358 | 359 | (compilation_unit 360 | (class_definition 361 | (identifier) 362 | (type_parameters 363 | (contravariant_type_parameter 364 | (identifier)) 365 | (covariant_type_parameter 366 | (identifier))))) 367 | 368 | ================================================================================ 369 | Upper bound 370 | ================================================================================ 371 | 372 | class A[B <: C] 373 | 374 | -------------------------------------------------------------------------------- 375 | 376 | (compilation_unit 377 | (class_definition 378 | (identifier) 379 | (type_parameters 380 | (identifier) 381 | (upper_bound 382 | (type_identifier))))) 383 | 384 | ================================================================================ 385 | Lower bound 386 | ================================================================================ 387 | 388 | class A[B >: C] 389 | 390 | -------------------------------------------------------------------------------- 391 | 392 | (compilation_unit 393 | (class_definition 394 | (identifier) 395 | (type_parameters 396 | (identifier) 397 | (lower_bound 398 | (type_identifier))))) 399 | 400 | ================================================================================ 401 | Lower and upper bounds 402 | ================================================================================ 403 | 404 | class A[B >: C <: D] 405 | 406 | -------------------------------------------------------------------------------- 407 | 408 | (compilation_unit 409 | (class_definition 410 | (identifier) 411 | (type_parameters 412 | (identifier) 413 | (lower_bound 414 | (type_identifier)) 415 | (upper_bound 416 | (type_identifier))))) 417 | 418 | ================================================================================ 419 | View bound 420 | ================================================================================ 421 | 422 | 423 | class A[B <% C <% D] 424 | 425 | -------------------------------------------------------------------------------- 426 | 427 | (compilation_unit 428 | (class_definition 429 | (identifier) 430 | (type_parameters 431 | (identifier) 432 | (view_bound 433 | (type_identifier)) 434 | (view_bound 435 | (type_identifier))))) 436 | 437 | ================================================================================ 438 | Context bound 439 | ================================================================================ 440 | 441 | class A[B : C : D] 442 | 443 | -------------------------------------------------------------------------------- 444 | 445 | (compilation_unit 446 | (class_definition 447 | (identifier) 448 | (type_parameters 449 | (identifier) 450 | (context_bound 451 | (type_identifier)) 452 | (context_bound 453 | (type_identifier))))) 454 | 455 | ================================================================================ 456 | Context bound (Scala 3 syntax) 457 | ================================================================================ 458 | 459 | def reduce[A : Monoid as m](xs: List[A]): A = () 460 | 461 | def showMax[X : {Ordering, Show}](x: X, y: X): String = () 462 | 463 | -------------------------------------------------------------------------------- 464 | 465 | (compilation_unit 466 | (function_definition 467 | (identifier) 468 | (type_parameters 469 | (identifier) 470 | (context_bound 471 | (type_identifier) 472 | (identifier))) 473 | (parameters 474 | (parameter 475 | (identifier) 476 | (generic_type 477 | (type_identifier) 478 | (type_arguments 479 | (type_identifier))))) 480 | (type_identifier) 481 | (unit)) 482 | (function_definition 483 | (identifier) 484 | (type_parameters 485 | (identifier) 486 | (context_bound 487 | (type_identifier)) 488 | (context_bound 489 | (type_identifier))) 490 | (parameters 491 | (parameter 492 | (identifier) 493 | (type_identifier)) 494 | (parameter 495 | (identifier) 496 | (type_identifier))) 497 | (type_identifier) 498 | (unit))) 499 | 500 | ================================================================================ 501 | Projections 502 | ================================================================================ 503 | 504 | type A = B[C]#D 505 | 506 | -------------------------------------------------------------------------------- 507 | 508 | (compilation_unit 509 | (type_definition 510 | (type_identifier) 511 | (projected_type 512 | (generic_type 513 | (type_identifier) 514 | (type_arguments 515 | (type_identifier))) 516 | (type_identifier)))) 517 | 518 | ================================================================================ 519 | Complex types 520 | ================================================================================ 521 | 522 | type A = B with B1 with B2 ! C with C1 523 | 524 | -------------------------------------------------------------------------------- 525 | 526 | (compilation_unit 527 | (type_definition 528 | (type_identifier) 529 | (infix_type 530 | (compound_type 531 | (type_identifier) 532 | (type_identifier) 533 | (type_identifier)) 534 | (operator_identifier) 535 | (compound_type 536 | (type_identifier) 537 | (type_identifier))))) 538 | 539 | ================================================================================ 540 | Literal type aliases (Scala 2.13+) 541 | ================================================================================ 542 | 543 | type A = "hello" 544 | type B = 25 545 | type C = false 546 | type D = 0.5f 547 | 548 | class Test(d: "hello", b: 25) 549 | 550 | -------------------------------------------------------------------------------- 551 | 552 | (compilation_unit 553 | (type_definition 554 | (type_identifier) 555 | (literal_type 556 | (string))) 557 | (type_definition 558 | (type_identifier) 559 | (literal_type 560 | (integer_literal))) 561 | (type_definition 562 | (type_identifier) 563 | (literal_type 564 | (boolean_literal))) 565 | (type_definition 566 | (type_identifier) 567 | (literal_type 568 | (floating_point_literal))) 569 | (class_definition 570 | (identifier) 571 | (class_parameters 572 | (class_parameter 573 | (identifier) 574 | (literal_type 575 | (string))) 576 | (class_parameter 577 | (identifier) 578 | (literal_type 579 | (integer_literal)))))) 580 | 581 | ================================================================================ 582 | Singleton Types 583 | ================================================================================ 584 | 585 | class A: 586 | def foobar: this.type = this 587 | type X = A.B.type 588 | 589 | -------------------------------------------------------------------------------- 590 | 591 | (compilation_unit 592 | (class_definition 593 | (identifier) 594 | (template_body 595 | (function_definition 596 | (identifier) 597 | (singleton_type 598 | (identifier)) 599 | (identifier)) 600 | (type_definition 601 | (type_identifier) 602 | (singleton_type 603 | (stable_identifier 604 | (identifier) 605 | (identifier))))))) 606 | 607 | ================================================================================ 608 | Opaque type aliases (Scala 3) 609 | ================================================================================ 610 | 611 | opaque type A = Int 612 | private opaque type B = String 613 | opaque type B >: Test <: Help = String 614 | 615 | 616 | -------------------------------------------------------------------------------- 617 | 618 | (compilation_unit 619 | (type_definition 620 | (opaque_modifier) 621 | (type_identifier) 622 | (type_identifier)) 623 | (type_definition 624 | (modifiers 625 | (access_modifier)) 626 | (opaque_modifier) 627 | (type_identifier) 628 | (type_identifier)) 629 | (type_definition 630 | (opaque_modifier) 631 | (type_identifier) 632 | (lower_bound 633 | (type_identifier)) 634 | (upper_bound 635 | (type_identifier)) 636 | (type_identifier))) 637 | 638 | ================================================================================ 639 | Structural type 640 | ================================================================================ 641 | 642 | type A = { def fly(): Unit } 643 | 644 | -------------------------------------------------------------------------------- 645 | 646 | (compilation_unit 647 | (type_definition 648 | (type_identifier) 649 | (structural_type 650 | (function_declaration 651 | (identifier) 652 | (parameters) 653 | (type_identifier))))) 654 | 655 | ================================================================================ 656 | Anonymous structural type with projection 657 | ================================================================================ 658 | 659 | type A = B[({ type f[x] = M[S, x] })#f] 660 | 661 | -------------------------------------------------------------------------------- 662 | 663 | (compilation_unit 664 | (type_definition 665 | (type_identifier) 666 | (generic_type 667 | (type_identifier) 668 | (type_arguments 669 | (projected_type 670 | (tuple_type 671 | (structural_type 672 | (type_definition 673 | (type_identifier) 674 | (type_parameters 675 | (identifier)) 676 | (generic_type 677 | (type_identifier) 678 | (type_arguments 679 | (type_identifier) 680 | (type_identifier)))))) 681 | (type_identifier)))))) 682 | 683 | ================================================================================ 684 | Type Lambdas (Scala 3) 685 | ================================================================================ 686 | 687 | type A = [B <: C] =>> D 688 | type Z = [X, Y] =>> Map[Y, X] 689 | class A[ 690 | [B <: C] =>> D 691 | ] 692 | 693 | -------------------------------------------------------------------------------- 694 | 695 | (compilation_unit 696 | (type_definition 697 | (type_identifier) 698 | (type_lambda 699 | (identifier) 700 | (upper_bound 701 | (type_identifier)) 702 | (type_identifier))) 703 | (type_definition 704 | (type_identifier) 705 | (type_lambda 706 | (identifier) 707 | (identifier) 708 | (generic_type 709 | (type_identifier) 710 | (type_arguments 711 | (type_identifier) 712 | (type_identifier))))) 713 | (class_definition 714 | (identifier) 715 | (type_parameters 716 | (type_lambda 717 | (identifier) 718 | (upper_bound 719 | (type_identifier)) 720 | (type_identifier))))) 721 | 722 | ================================================================================ 723 | Union and Intersection Types (Scala 3) 724 | ================================================================================ 725 | 726 | type A = Foo | Bar & Buz 727 | type B = "foo" | "bar" & "buz" 728 | 729 | -------------------------------------------------------------------------------- 730 | (compilation_unit 731 | (type_definition 732 | (type_identifier) 733 | (infix_type 734 | (infix_type 735 | (type_identifier) 736 | (operator_identifier) 737 | (type_identifier)) 738 | (operator_identifier) 739 | (type_identifier))) 740 | (type_definition 741 | (type_identifier) 742 | (infix_type 743 | (infix_type 744 | (literal_type 745 | (string)) 746 | (operator_identifier) 747 | (literal_type 748 | (string))) 749 | (operator_identifier) 750 | (literal_type 751 | (string))))) 752 | -------------------------------------------------------------------------------- /test/highlight/basics.scala: -------------------------------------------------------------------------------- 1 | import java.time.Instant 2 | //^include 3 | // ^namespace 4 | // ^type 5 | object Hello { 6 | // ^ keyword 7 | // ^ type 8 | val x = if (true) (25 * 1.0) else "hello" 9 | // ^keyword 10 | // ^variable 11 | // ^conditional 12 | // ^boolean 13 | // ^number 14 | // ^float 15 | // ^string 16 | // ^conditional 17 | 18 | val y = new lower_case_intentionally 19 | // ^keyword.operator 20 | // ^type 21 | 22 | var mutation = "mutant" 23 | // ^keyword 24 | // ^variable 25 | 26 | trait Test { 27 | // ^ keyword 28 | // ^ type 29 | def meth(i: Int)(implicit x: Boolean) = ??? 30 | // ^keyword.function 31 | // ^keyword 32 | // ^type 33 | // ^method 34 | // ^parameter 35 | 36 | val anonFun: Int => Int = (a: Int) => a 37 | // ^variable 38 | // ^type 39 | // ^operator 40 | // ^type 41 | // ^parameter 42 | } 43 | 44 | protected abstract class Bla(test: String) 45 | // ^type.qualifier 46 | // ^keyword 47 | // ^type.qualifier 48 | // ^parameter 49 | // ^type 50 | 51 | type Hello = "25" 52 | // ^keyword 53 | // ^type.definition 54 | // ^string 55 | 56 | class A { 57 | // ^ keyword 58 | // ^ type 59 | self: X => 60 | // ^parameter 61 | // ^type 62 | } 63 | 64 | type A = { def fly(): Unit } 65 | // ^keyword.function 66 | // ^method 67 | // ^type 68 | 69 | type A = B[({ type f[x] = M[S, x] })#f] 70 | // ^keyword 71 | // ^type.definition 72 | 73 | val hello = c"some $mutation ${1}" 74 | // ^function.call 75 | // ^punctuation.special 76 | // ^variable 77 | // ^number 78 | def meth = ??? 79 | // ^method 80 | val hello2 = c"some $meth" 81 | // ^method 82 | val hello3 = s"$$$meth$hello2%" 83 | // ^string 84 | // ^punctuation.special 85 | // ^method 86 | // ^punctuation.special 87 | // ^variable 88 | // ^string 89 | val hello4 = s"$"$hello3" 90 | // ^string 91 | // ^punctuation.special 92 | // ^variable 93 | } 94 | 95 | -------------------------------------------------------------------------------- /test/highlight/comments.scala: -------------------------------------------------------------------------------- 1 | //> using scala 3.1.0 2 | // ^keyword 3 | // ^parameter 4 | // ^string 5 | 6 | /* 7 | * Beep boop 8 | */ 9 | // <- comment 10 | 11 | // Single line comment 12 | // <- comment 13 | -------------------------------------------------------------------------------- /test/highlight/scala3.scala: -------------------------------------------------------------------------------- 1 | // Optional braces syntax 2 | class C: 3 | // ^keyword 4 | // ^type 5 | 6 | def test(aaaa: A): Int = 7 | //^keyword.function 8 | // ^method 9 | // no curly braces, but this is still in test method 10 | val bbb = 1 11 | //^keyword 12 | // ^variable 13 | val ccc = 2 14 | //^keyword 15 | // ^variable 16 | 1 17 | 18 | object O1: 19 | //^keyword 20 | // ^type 21 | 22 | def test: Unit = () 23 | //^keyword.function 24 | // ^method 25 | 26 | object O2: 27 | type Elem[A] = A match 28 | //^keyword 29 | // ^type.definition 30 | case String => Char 31 | //^keyword 32 | // ^type ^type 33 | case Array[a] => a 34 | // ^type ^type 35 | 36 | // SIP-44 37 | class C: 38 | // ^keyword 39 | // ^type 40 | fooooo.map: x => 41 | // ^method.call 42 | x + 1 43 | 44 | xs.map: 45 | param1 => 46 | param1 + 1 47 | 48 | foooo: 49 | // ^function.call 50 | println("") 51 | 52 | foooo `++`: 53 | // ^operator 54 | val x = 1 55 | List(x) 56 | 57 | // This is an ascription 58 | val y = x: Int 59 | // ^type 60 | 61 | // This is SIP-44 62 | val y = x: 63 | Int 64 | //^type 65 | 66 | // Ascription expression 67 | class C: 68 | foooo: Int 69 | // ^type 70 | 71 | enum Simple: 72 | //^keyword 73 | // ^type 74 | case A, B, C 75 | // ^type 76 | enum Test(a: Int) derives Codec: 77 | // ^keyword 78 | // ^type 79 | // ^type 80 | // ^keyword 81 | // ^type 82 | // ^type 83 | // ^parameter 84 | case Test(b: String) 85 | // ^keyword 86 | // ^type 87 | // ^type 88 | // ^parameter 89 | case Hello, Bla 90 | // ^type 91 | // ^type 92 | case Bla extends Test(256) 93 | // ^keyword 94 | 95 | opaque type Blow <: Int = 25 96 | // ^type.qualifier 97 | // ^keyword 98 | // ^type 99 | // ^type.definition 100 | 101 | inline given Test = new Test { 102 | // ^ storageclass 103 | inline def hello(inline x: Boolean) = 104 | // ^ storageclass 105 | // ^ storageclass 106 | inline if x then "hi" else "bye" 107 | // ^storageclass 108 | // ^conditional 109 | inline x match 110 | // ^storageclass 111 | case true => 25 112 | case false => 26 113 | } 114 | 115 | object A: 116 | // ^ keyword 117 | // ^type 118 | 119 | ::(123) 120 | //^operator 121 | // ^number 122 | 123 | object bla: 124 | open class Hello(A: Int) 125 | // ^ type.qualifier 126 | transparent trait Hello 127 | // ^ type.qualifier 128 | infix def hello = 25 129 | // ^ keyword 130 | 131 | extension (s: String) 132 | // ^keyword 133 | def test = 25 134 | def test2 = 25 135 | 136 | val extension = "hello" 137 | // ^variable - to test "soft" keywords 138 | 139 | given Test with 140 | // ^keyword 141 | // ^type 142 | // ^keyword 143 | def test = 1 144 | // ^keyword.function 145 | val a = "hello" 146 | 147 | 148 | class Copier: 149 | private val printUnit = new Printer { type PrinterType = InkJet } 150 | private val scanUnit = new Scanner 151 | 152 | export scanUnit.scan 153 | // ^ include 154 | // ^variable 155 | export printUnit.{status as _, *} 156 | // ^ include 157 | // ^variable 158 | 159 | def status: List[String] = printUnit.status ++ scanUnit.status 160 | -------------------------------------------------------------------------------- /test/tags/basics.scala: -------------------------------------------------------------------------------- 1 | package com.example 2 | // ^definition.module 3 | 4 | enum Color: 5 | // ^definition.enum 6 | case Red 7 | // ^definition.class 8 | case Green 9 | // ^definition.class 10 | case Yellow 11 | // ^definition.class 12 | 13 | trait Fruit: 14 | // ^definition.interface 15 | val color: Color 16 | // ^definition.variable 17 | 18 | object Fruit: 19 | // ^definition.object 20 | case object Banana extends Fruit { 21 | // ^definition.object 22 | // ^reference.class 23 | val color = Color.Yellow 24 | // ^definition.variable 25 | } 26 | case class Apple(c: Color.Red | Color.Green) extends Fruit { 27 | // ^definition.class 28 | // ^definition.property 29 | // ^reference.class 30 | val color = c 31 | // ^definition.variable 32 | } 33 | 34 | given show: Show[Fruit] = new Show[Fruit] { } 35 | // ^definition.variable 36 | // ^reference.interface 37 | 38 | def color(fruit: Fruit): String = ??? 39 | // ^definition.function 40 | 41 | var flag: Boolean = true 42 | // ^definition.variable 43 | 44 | 45 | -------------------------------------------------------------------------------- /tree-sitter.json: -------------------------------------------------------------------------------- 1 | { 2 | "grammars": [ 3 | { 4 | "name": "scala", 5 | "camelcase": "Scala", 6 | "scope": "source.scala", 7 | "path": ".", 8 | "file-types": [ 9 | "scala", 10 | "sbt" 11 | ], 12 | "highlights": "queries/highlights.scm", 13 | "locals": "queries/locals.scm", 14 | "tags": "queries/tags.scm" 15 | } 16 | ], 17 | "metadata": { 18 | "version": "0.24.0", 19 | "license": "MIT", 20 | "description": "Scala grammar for tree-sitter", 21 | "authors": [ 22 | { 23 | "name": "Max Brunsfeld", 24 | "email": "maxbrunsfeld@gmail.com" 25 | } 26 | ], 27 | "links": { 28 | "repository": "https://github.com/tree-sitter/tree-sitter-scala" 29 | } 30 | }, 31 | "bindings": { 32 | "c": true, 33 | "go": true, 34 | "node": true, 35 | "python": true, 36 | "rust": true, 37 | "swift": true 38 | } 39 | } 40 | --------------------------------------------------------------------------------