├── .clang-format ├── .dockerignore ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── checkstyle.yml │ ├── reviewer.yml │ └── stale.yml ├── .gitignore ├── .gitmodules ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml └── encodings.xml ├── .vscode └── settings.json ├── CODEOWNERS ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── build.py ├── checkstyle-suppressions.xml ├── checkstyle.xml ├── include └── jni_md.h ├── lib ├── ch_usi_si_seart_treesitter.cc ├── ch_usi_si_seart_treesitter.h ├── ch_usi_si_seart_treesitter_Capture.cc ├── ch_usi_si_seart_treesitter_Capture.h ├── ch_usi_si_seart_treesitter_Language.cc ├── ch_usi_si_seart_treesitter_Language.h ├── ch_usi_si_seart_treesitter_LookaheadIterator.cc ├── ch_usi_si_seart_treesitter_LookaheadIterator.h ├── ch_usi_si_seart_treesitter_Node.cc ├── ch_usi_si_seart_treesitter_Node.h ├── ch_usi_si_seart_treesitter_Parser.cc ├── ch_usi_si_seart_treesitter_Parser.h ├── ch_usi_si_seart_treesitter_Parser_Builder.cc ├── ch_usi_si_seart_treesitter_Parser_Builder.h ├── ch_usi_si_seart_treesitter_Pattern.cc ├── ch_usi_si_seart_treesitter_Pattern.h ├── ch_usi_si_seart_treesitter_Query.cc ├── ch_usi_si_seart_treesitter_Query.h ├── ch_usi_si_seart_treesitter_QueryCursor.cc ├── ch_usi_si_seart_treesitter_QueryCursor.h ├── ch_usi_si_seart_treesitter_Query_Builder.cc ├── ch_usi_si_seart_treesitter_Query_Builder.h ├── ch_usi_si_seart_treesitter_Tree.cc ├── ch_usi_si_seart_treesitter_Tree.h ├── ch_usi_si_seart_treesitter_TreeCursor.cc ├── ch_usi_si_seart_treesitter_TreeCursor.h ├── ch_usi_si_seart_treesitter_printer_DotGraphPrinter.cc ├── ch_usi_si_seart_treesitter_printer_DotGraphPrinter.h ├── ch_usi_si_seart_treesitter_version_TreeSitter.cc └── ch_usi_si_seart_treesitter_version_TreeSitter.h ├── lombok.config ├── pom.xml ├── properties.py ├── remove.sh ├── requirements.txt ├── src ├── main │ ├── doc │ │ └── README.md │ ├── java │ │ └── ch │ │ │ └── usi │ │ │ └── si │ │ │ └── seart │ │ │ └── treesitter │ │ │ ├── Capture.java │ │ │ ├── External.java │ │ │ ├── InputEdit.java │ │ │ ├── Language.java │ │ │ ├── LibraryLoader.java │ │ │ ├── LookaheadIterator.java │ │ │ ├── Node.java │ │ │ ├── OffsetTreeCursor.java │ │ │ ├── Parser.java │ │ │ ├── Pattern.java │ │ │ ├── Point.java │ │ │ ├── Predicate.java │ │ │ ├── Quantifier.java │ │ │ ├── Query.java │ │ │ ├── QueryCursor.java │ │ │ ├── QueryMatch.java │ │ │ ├── Range.java │ │ │ ├── Symbol.java │ │ │ ├── Tree.java │ │ │ ├── TreeCursor.java │ │ │ ├── TreeCursorNode.java │ │ │ ├── error │ │ │ ├── ABIVersionError.java │ │ │ └── package-info.java │ │ │ ├── exception │ │ │ ├── ByteOffsetOutOfBoundsException.java │ │ │ ├── NodeRangeBoundaryException.java │ │ │ ├── PointOutOfBoundsException.java │ │ │ ├── TreeSitterException.java │ │ │ ├── package-info.java │ │ │ ├── parser │ │ │ │ ├── IncompatibleLanguageException.java │ │ │ │ ├── ParserException.java │ │ │ │ ├── ParsingException.java │ │ │ │ └── package-info.java │ │ │ └── query │ │ │ │ ├── QueryCaptureException.java │ │ │ │ ├── QueryException.java │ │ │ │ ├── QueryFieldException.java │ │ │ │ ├── QueryNodeTypeException.java │ │ │ │ ├── QueryStructureException.java │ │ │ │ ├── QuerySyntaxException.java │ │ │ │ └── package-info.java │ │ │ ├── function │ │ │ ├── IOExceptionThrowingConsumer.java │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ └── printer │ │ │ ├── DotGraphPrinter.java │ │ │ ├── IterativeTreePrinter.java │ │ │ ├── SymbolicExpressionPrinter.java │ │ │ ├── SyntaxTreePrinter.java │ │ │ ├── TreePrinter.java │ │ │ ├── XMLPrinter.java │ │ │ └── package-info.java │ ├── javaTemplates │ │ └── ch │ │ │ └── usi │ │ │ └── si │ │ │ └── seart │ │ │ └── treesitter │ │ │ └── version │ │ │ ├── Version.java │ │ │ └── package-info.java │ └── resources │ │ └── .keep └── test │ ├── java │ └── ch │ │ └── usi │ │ └── si │ │ └── seart │ │ └── treesitter │ │ ├── AbstractQueryTest.java │ │ ├── BaseTest.java │ │ ├── CaptureTest.java │ │ ├── LanguageTest.java │ │ ├── LookaheadIteratorTest.java │ │ ├── MultiCaptureTest.java │ │ ├── NodeTest.java │ │ ├── OffsetTreeCursorTest.java │ │ ├── ParserTest.java │ │ ├── PatternTest.java │ │ ├── PointTest.java │ │ ├── PredicateTest.java │ │ ├── QuantifierTest.java │ │ ├── QueryCursorTest.java │ │ ├── QueryTest.java │ │ ├── RangeTest.java │ │ ├── TreeCursorTest.java │ │ ├── TreeTest.java │ │ └── printer │ │ ├── DotGraphPrinterTest.java │ │ ├── PrinterTestBase.java │ │ ├── SymbolicExpressionPrinterTest.java │ │ ├── SyntaxTreePrinterTest.java │ │ └── XMLPrinterTest.java │ └── resources │ ├── deep_string_concat │ └── simplelogger.properties ├── upgrade.sh └── version.py /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: google 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !/lib 3 | !/include 4 | !/tree-sitter* 5 | !/build.py 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | target-branch: master 8 | labels: 9 | - dependencies 10 | - package-ecosystem: pip 11 | directory: / 12 | schedule: 13 | interval: daily 14 | target-branch: master 15 | labels: 16 | - dependencies 17 | - package-ecosystem: maven 18 | directory: / 19 | schedule: 20 | interval: daily 21 | target-branch: master 22 | ignore: 23 | - dependency-name: org.apache.maven:* 24 | labels: 25 | - dependencies 26 | -------------------------------------------------------------------------------- /.github/workflows/checkstyle.yml: -------------------------------------------------------------------------------- 1 | name: Check Java files with CheckStyle 2 | on: 3 | pull_request: 4 | paths: 5 | - '**/*.java' 6 | 7 | jobs: 8 | checkstyle: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout latest 12 | uses: actions/checkout@v4 13 | - name: Run CheckStyle 14 | uses: nikitasavinov/checkstyle-action@master 15 | with: 16 | level: error 17 | fail_on_error: true 18 | workdir: ./src 19 | checkstyle_config: ./checkstyle.xml 20 | github_token: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/reviewer.yml: -------------------------------------------------------------------------------- 1 | name: 'Automatically review Dependabot updates' 2 | on: 3 | pull_request_target: 4 | types: [ 'opened' ] 5 | env: 6 | PR_URL: ${{ github.event.pull_request.html_url }} 7 | GITHUB_TOKEN: ${{ secrets.API_TOKEN }} 8 | 9 | jobs: 10 | review: 11 | if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} 12 | runs-on: ubuntu-latest 13 | permissions: 14 | pull-requests: write 15 | contents: write 16 | steps: 17 | - id: dependabot-metadata 18 | name: Fetch Dependabot metadata 19 | uses: dependabot/fetch-metadata@v2 20 | - name: Enable auto-merge for Dependabot PRs 21 | run: gh pr merge --auto --merge "$PR_URL" 22 | - name: Approve patch and minor version updates 23 | if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor' }} 24 | run: gh pr review $PR_URL --approve -b "I'm **approving** this pull request because **it includes a patch or minor update**" 25 | - name: Assign reviewers for major version updates 26 | if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' }} 27 | run: | 28 | gh pr comment $PR_URL --body "I'm **not approving** this PR because **it includes a major update of a dependency**" 29 | gh pr edit $PR_URL --add-reviewer dabico 30 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues 2 | on: 3 | schedule: 4 | - cron: 0 0 * * * 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - name: Close stale issues 14 | uses: actions/stale@v9 15 | with: 16 | repo-token: ${{ secrets.API_TOKEN }} 17 | stale-issue-label: stale 18 | exempt-issue-labels: bug 19 | exempt-all-assignees: true 20 | days-before-issue-stale: 30 21 | days-before-issue-close: 14 22 | days-before-pr-stale: -1 23 | days-before-pr-close: -1 24 | stale-issue-message: This issue is stale because it has been open for 30 days with no activity. 25 | close-issue-message: This issue was closed because it has been inactive for 14 days since being marked as stale. 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Java template 2 | # Compiled class file 3 | *.class 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.nar 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | replay_pid* 26 | 27 | ### Maven template 28 | target/ 29 | pom.xml.tag 30 | pom.xml.releaseBackup 31 | pom.xml.versionsBackup 32 | pom.xml.next 33 | release.properties 34 | language.properties 35 | dependency-reduced-pom.xml 36 | buildNumber.properties 37 | .mvn/timing.properties 38 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 39 | .mvn/wrapper/maven-wrapper.jar 40 | 41 | # Eclipse m2e generated files 42 | # Eclipse Core 43 | .project 44 | # JDT-specific (Eclipse Java Development Tools) 45 | .classpath 46 | 47 | ### Python template 48 | # Byte-compiled / optimized / DLL files 49 | __pycache__/ 50 | *.py[cod] 51 | *$py.class 52 | 53 | # Distribution / packaging 54 | .Python 55 | build/ 56 | develop-eggs/ 57 | dist/ 58 | downloads/ 59 | eggs/ 60 | .eggs/ 61 | lib/ 62 | lib64/ 63 | parts/ 64 | sdist/ 65 | var/ 66 | wheels/ 67 | share/python-wheels/ 68 | *.egg-info/ 69 | .installed.cfg 70 | *.egg 71 | MANIFEST 72 | 73 | # PyInstaller 74 | *.manifest 75 | *.spec 76 | 77 | # Installer logs 78 | pip-log.txt 79 | pip-delete-this-directory.txt 80 | 81 | # Unit test / coverage reports 82 | htmlcov/ 83 | .tox/ 84 | .nox/ 85 | .coverage 86 | .coverage.* 87 | .cache 88 | nosetests.xml 89 | coverage.xml 90 | *.cover 91 | *.py,cover 92 | .hypothesis/ 93 | .pytest_cache/ 94 | cover/ 95 | 96 | # Translations 97 | *.mo 98 | *.pot 99 | 100 | # Django stuff: 101 | local_settings.py 102 | db.sqlite3 103 | db.sqlite3-journal 104 | 105 | # Flask stuff: 106 | instance/ 107 | .webassets-cache 108 | 109 | # Scrapy stuff: 110 | .scrapy 111 | 112 | # Sphinx documentation 113 | docs/_build/ 114 | 115 | # PyBuilder 116 | .pybuilder/ 117 | 118 | # Jupyter Notebook 119 | .ipynb_checkpoints 120 | 121 | # IPython 122 | profile_default/ 123 | ipython_config.py 124 | 125 | # pyenv 126 | .python-version 127 | 128 | # pipenv 129 | Pipfile.lock 130 | 131 | # poetry 132 | poetry.lock 133 | 134 | # pdm 135 | pdm.lock 136 | .pdm.toml 137 | 138 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 139 | __pypackages__/ 140 | 141 | # Celery stuff 142 | celerybeat-schedule 143 | celerybeat.pid 144 | 145 | # SageMath parsed files 146 | *.sage.py 147 | 148 | # Environments 149 | .env 150 | .venv 151 | env/ 152 | venv/ 153 | ENV/ 154 | env.bak/ 155 | venv.bak/ 156 | 157 | # Spyder project settings 158 | .spyderproject 159 | .spyproject 160 | 161 | # Rope project settings 162 | .ropeproject 163 | 164 | # mkdocs documentation 165 | /site 166 | 167 | # mypy 168 | .mypy_cache/ 169 | .dmypy.json 170 | dmypy.json 171 | 172 | # Pyre type checker 173 | .pyre/ 174 | 175 | # pytype static type analyzer 176 | .pytype/ 177 | 178 | # Cython debug symbols 179 | cython_debug/ 180 | 181 | ### C++ template 182 | # Prerequisites 183 | *.d 184 | 185 | # Compiled Object files 186 | *.slo 187 | *.lo 188 | *.o 189 | *.obj 190 | 191 | # Precompiled Headers 192 | *.gch 193 | *.pch 194 | 195 | # Compiled Dynamic libraries 196 | *.so 197 | *.dylib 198 | *.dll 199 | 200 | # Fortran module files 201 | *.mod 202 | *.smod 203 | 204 | # Compiled Static libraries 205 | *.lai 206 | *.la 207 | *.a 208 | *.lib 209 | 210 | # Executables 211 | *.exe 212 | *.out 213 | *.app 214 | 215 | ### VisualStudioCode template 216 | .vscode/* 217 | !.vscode/settings.json 218 | !.vscode/tasks.json 219 | !.vscode/launch.json 220 | !.vscode/extensions.json 221 | !.vscode/*.code-snippets 222 | 223 | # Local History for Visual Studio Code 224 | .history/ 225 | 226 | # Built Visual Studio Code Extensions 227 | *.vsix 228 | 229 | ### C template 230 | # Prerequisites 231 | 232 | # Object files 233 | *.ko 234 | *.elf 235 | 236 | # Linker output 237 | *.ilk 238 | *.map 239 | *.exp 240 | 241 | # Precompiled Headers 242 | 243 | # Libraries 244 | 245 | # Shared objects (inc. Windows DLLs) 246 | *.so.* 247 | 248 | # Executables 249 | *.i*86 250 | *.x86_64 251 | *.hex 252 | 253 | # Debug files 254 | *.dSYM/ 255 | *.su 256 | *.idb 257 | *.pdb 258 | 259 | # Kernel Module Compile Results 260 | *.mod* 261 | *.cmd 262 | .tmp_versions/ 263 | modules.order 264 | Module.symvers 265 | Mkfile.old 266 | dkms.conf 267 | 268 | ### JetBrains template 269 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 270 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 271 | 272 | # User-specific stuff 273 | .idea/**/workspace.xml 274 | .idea/**/tasks.xml 275 | .idea/**/usage.statistics.xml 276 | .idea/**/dictionaries 277 | .idea/**/shelf 278 | 279 | # AWS User-specific 280 | .idea/**/aws.xml 281 | 282 | # Generated files 283 | .idea/**/contentModel.xml 284 | 285 | # Sensitive or high-churn files 286 | .idea/**/dataSources/ 287 | .idea/**/dataSources.ids 288 | .idea/**/dataSources.local.xml 289 | .idea/**/sqlDataSources.xml 290 | .idea/**/dynamic.xml 291 | .idea/**/uiDesigner.xml 292 | .idea/**/dbnavigator.xml 293 | 294 | # Gradle 295 | .idea/**/gradle.xml 296 | .idea/**/libraries 297 | 298 | # Gradle and Maven with auto-import 299 | .idea/artifacts 300 | .idea/compiler.xml 301 | .idea/jarRepositories.xml 302 | .idea/modules.xml 303 | .idea/*.iml 304 | .idea/modules 305 | *.iml 306 | *.ipr 307 | 308 | # CMake 309 | cmake-build-*/ 310 | 311 | # Mongo Explorer plugin 312 | .idea/**/mongoSettings.xml 313 | 314 | # File-based project format 315 | *.iws 316 | 317 | # IntelliJ 318 | out/ 319 | 320 | # mpeltonen/sbt-idea plugin 321 | .idea_modules/ 322 | 323 | # JIRA plugin 324 | atlassian-ide-plugin.xml 325 | 326 | # Cursive Clojure plugin 327 | .idea/replstate.xml 328 | 329 | # SonarLint plugin 330 | .idea/sonarlint/ 331 | 332 | # Crashlytics plugin (for Android Studio and IntelliJ) 333 | com_crashlytics_export_strings.xml 334 | crashlytics.properties 335 | crashlytics-build.properties 336 | fabric.properties 337 | 338 | # Editor-based Rest Client 339 | .idea/httpRequests 340 | 341 | # Android studio 3.1+ serialized cache file 342 | .idea/caches/build_file_checksums.ser 343 | 344 | # Plugin files 345 | .idea/checkstyle-idea.xml 346 | .idea/git_toolbox_prj.xml 347 | .idea/jpa-buddy.xml 348 | .idea/misc.xml 349 | .idea/vcs.xml 350 | 351 | ### Xcode template 352 | ## User settings 353 | xcuserdata/ 354 | 355 | ## Xcode 8 and earlier 356 | *.xcscmblueprint 357 | *.xccheckout 358 | 359 | ### macOS template 360 | # General 361 | .DS_Store 362 | .AppleDouble 363 | .LSOverride 364 | 365 | # Icon must end with two \r 366 | Icon 367 | 368 | # Thumbnails 369 | ._* 370 | 371 | # Files that might appear in the root of a volume 372 | .DocumentRevisions-V100 373 | .fseventsd 374 | .Spotlight-V100 375 | .TemporaryItems 376 | .Trashes 377 | .VolumeIcon.icns 378 | .com.apple.timemachine.donotpresent 379 | 380 | # Directories potentially created on remote AFP share 381 | .AppleDB 382 | .AppleDesktop 383 | Network Trash Folder 384 | Temporary Items 385 | .apdisk 386 | 387 | ### Images template 388 | # Scalable Vector Graphics 389 | *.svg 390 | *.svgz 391 | 392 | # DOT files 393 | *.dot 394 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | @dabico 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing to `java-tree-sitter`! 4 | We appreciate your efforts to make this project better. 5 | Before you start, please take a moment to read and understand the following guidelines. 6 | 7 | ## Getting Started 8 | 9 | 1. Fork the repository and clone your fork locally. 10 | 2. Ensure the local development prerequisites are met, as outlined in the [README](README.md). 11 | 3. Create a new branch for your contribution. 12 | The branch name should follow the format of: `label/short-name`. 13 | For more information on valid labels, see the [full list](https://github.com/seart-group/java-tree-sitter/labels). 14 | 4. Test your changes thoroughly via the build system: `mvn clean package`. 15 | If new features are being added, they **must** be accompanied by tests. 16 | 5. Push your changes to your fork: `git push origin label/short-name`. 17 | 6. Open a pull request (PR) against the `master` branch of this repository. 18 | 19 | ## Pull Request Process 20 | 21 | Ensure your PR description explains the purpose of your changes and provides context. 22 | Be sure to reference related issues by using [linking keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). 23 | Ensure your PR includes only relevant changes. 24 | Large changes should be broken into smaller, manageable PRs. 25 | Be prepared to make adjustments based on feedback received both by the automated actions and reviewers. 26 | 27 | ## Style Guide 28 | 29 | We use an automated [CheckStyle action](.github/workflows/checkstyle.yml) to enforce the project code style. 30 | To get a rough idea of the style we employ, refer to the [CheckStyle configuration](checkstyle.xml). 31 | 32 | ## License 33 | 34 | By contributing to this project, you agree that your contributions will be licensed under the [project license](LICENSE). 35 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.17.7 AS build 2 | LABEL maintainer="Ozren Dabić (dabico@usi.ch)" 3 | 4 | ENV JAVA_HOME="/usr/lib/jvm/java-11-openjdk" 5 | 6 | RUN apk update && \ 7 | apk add --no-cache \ 8 | openjdk11~=11.0.23 \ 9 | python3~=3.10.14 \ 10 | py3-distutils-extra~=2.47 \ 11 | make~=4.3 \ 12 | g++~=12.2.1 13 | 14 | WORKDIR /java-tree-sitter 15 | COPY . ./ 16 | 17 | RUN python build.py 18 | 19 | FROM scratch AS export 20 | 21 | WORKDIR / 22 | 23 | COPY --from=build /java-tree-sitter/libjava-tree-sitter.so . 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-present SEART Research Group and Contributors 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 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from argparse import ArgumentParser 4 | from ctypes.util import find_library as find_cpp_library 5 | from distutils.ccompiler import new_compiler as new_c_compiler 6 | from distutils.log import set_verbosity as set_log_verbosity 7 | from glob import glob as find 8 | from os import environ 9 | from os import system as cmd 10 | from os.path import basename, dirname, exists, getmtime, realpath 11 | from os.path import join as path 12 | from os.path import split as split_path 13 | from platform import system as os_name 14 | from tempfile import TemporaryDirectory 15 | 16 | 17 | # adapted from https://github.com/tree-sitter/py-tree-sitter 18 | def build(repositories, output_path="libjava-tree-sitter", system=None, arch=None, verbose=False): 19 | here = dirname(realpath(__file__)) 20 | 21 | if not repositories: 22 | repositories = sorted([basename(repository) for repository in find(path(here, "tree-sitter-*"))]) 23 | 24 | if not repositories: 25 | raise ValueError("Library can not be compiled, no grammars were included!") 26 | 27 | if system is None: 28 | system = os_name() 29 | 30 | if arch and system != "Darwin": 31 | arch = "64" if "64" in arch else "32" 32 | if arch and system == "Darwin": 33 | arch = "arm64" if "aarch64" in arch else arch 34 | 35 | output_extension = "dylib" if system == "Darwin" else "so" 36 | output_path = f"{output_path}.{output_extension}" 37 | env = "" 38 | if arch: 39 | env += ( 40 | f"CFLAGS='-arch {arch} -mmacosx-version-min=11.0' LDFLAGS='-arch {arch}'" 41 | if system == "Darwin" 42 | else f"CFLAGS='-m{arch}' LDFLAGS='-m{arch}'" 43 | ) 44 | 45 | tree_sitter = path(here, "tree-sitter") 46 | redirect = "> /dev/null" if not verbose else "" 47 | cmd(f"make -C \"{tree_sitter}\" clean {redirect}") 48 | cmd(f"{env} make -C \"{tree_sitter}\" {redirect}") 49 | 50 | source_paths = find(path(here, "lib", "*.cc")) 51 | 52 | compiler = new_c_compiler() 53 | for repository in repositories: 54 | repository_name = split_path(repository.rstrip("/"))[1] 55 | repository_language = repository_name.split("tree-sitter-")[-1] 56 | repository_macro = f"TS_LANGUAGE_{repository_language.replace('-', '_').upper()}" 57 | compiler.define_macro(repository_macro, "1") 58 | match repository_name: 59 | case "tree-sitter-markdown": 60 | src_path = path(repository, repository_name, "src") 61 | case "tree-sitter-ocaml": 62 | src_path = path(repository, "grammars", repository_language, "src") 63 | case "tree-sitter-csv" | \ 64 | "tree-sitter-dtd" | \ 65 | "tree-sitter-ocaml" | \ 66 | "tree-sitter-php" | \ 67 | "tree-sitter-psv" | \ 68 | "tree-sitter-tsv" | \ 69 | "tree-sitter-tsx" | \ 70 | "tree-sitter-typescript" | \ 71 | "tree-sitter-xml": 72 | src_path = path(repository, repository_language, "src") 73 | case _: 74 | src_path = path(repository, "src") 75 | source_paths.append(path(src_path, "parser.c")) 76 | scanner_c = path(src_path, "scanner.c") 77 | scanner_cc = path(src_path, "scanner.cc") 78 | if exists(scanner_cc): 79 | source_paths.append(scanner_cc) 80 | elif exists(scanner_c): 81 | source_paths.append(scanner_c) 82 | 83 | source_mtimes = [getmtime(__file__)] + [getmtime(source_path) for source_path in source_paths] 84 | if find_cpp_library("stdc++"): 85 | compiler.add_library("stdc++") 86 | elif find_cpp_library("c++"): 87 | compiler.add_library("c++") 88 | 89 | output_mtime = getmtime(output_path) if exists(output_path) else 0 90 | if max(source_mtimes) <= output_mtime: 91 | return False 92 | 93 | with TemporaryDirectory(suffix="tree_sitter_language") as out_dir: 94 | object_paths = [] 95 | for source_path in source_paths: 96 | flags = ["-O3"] 97 | 98 | if system == "Linux": 99 | flags.append("-fPIC") 100 | 101 | if source_path.endswith(".c"): 102 | flags.append("-std=c11") 103 | 104 | if arch: 105 | flags += ["-arch", arch] if system == "Darwin" else [f"-m{arch}"] 106 | 107 | include_dirs = [ 108 | dirname(source_path), 109 | path("include"), 110 | path(environ["JAVA_HOME"], "include"), 111 | path(here, "tree-sitter", "lib", "include"), 112 | ] 113 | 114 | object_paths.append( 115 | compiler.compile( 116 | [source_path], 117 | output_dir=out_dir, 118 | include_dirs=include_dirs, 119 | extra_preargs=flags, 120 | )[0] 121 | ) 122 | 123 | extra_preargs = [] 124 | if system == "Darwin": 125 | extra_preargs.append("-dynamiclib") 126 | 127 | if arch: 128 | extra_preargs += ["-arch", arch] if system == "Darwin" else [f"-m{arch}"] 129 | 130 | compiler.link_shared_object( 131 | object_paths, 132 | output_path, 133 | extra_preargs=extra_preargs, 134 | extra_postargs=[path(here, "tree-sitter", "libtree-sitter.a")], 135 | library_dirs=[path(here, "tree-sitter")], 136 | ) 137 | 138 | return True 139 | 140 | 141 | if __name__ == "__main__": 142 | parser = ArgumentParser(description="Build a tree-sitter library.") 143 | parser.add_argument( 144 | "-s", 145 | "--system", 146 | help="Operating system to build for (Linux, Darwin, Windows)." 147 | ) 148 | parser.add_argument( 149 | "-a", 150 | "--arch", 151 | help="Architecture to build for (x86, x86_64, arm64, aarch64).", 152 | ) 153 | parser.add_argument( 154 | "-o", 155 | "--output", 156 | default="libjava-tree-sitter", 157 | help="Output file name.", 158 | ) 159 | parser.add_argument( 160 | "-v", 161 | "--verbose", 162 | action="store_true", 163 | help="Print verbose output.", 164 | ) 165 | parser.add_argument( 166 | "repositories", 167 | nargs="*", 168 | help=""" 169 | tree-sitter repositories to include in build. 170 | If none are specified, all directories that 171 | match ./tree-sitter-* will be included. 172 | """, 173 | ) 174 | 175 | args = parser.parse_args() 176 | set_log_verbosity(int(args.verbose)) 177 | build(args.repositories, args.output, args.system, args.arch, args.verbose) 178 | -------------------------------------------------------------------------------- /checkstyle-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /include/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | #ifndef _JAVASOFT_JNI_MD_H_ 27 | #define _JAVASOFT_JNI_MD_H_ 28 | 29 | #ifndef __has_attribute 30 | #define __has_attribute(x) 0 31 | #endif 32 | #if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) 33 | #ifdef ARM 34 | #define JNIEXPORT __attribute__((externally_visible,visibility("default"))) 35 | #define JNIIMPORT __attribute__((externally_visible,visibility("default"))) 36 | #else 37 | #define JNIEXPORT __attribute__((visibility("default"))) 38 | #define JNIIMPORT __attribute__((visibility("default"))) 39 | #endif 40 | #else 41 | #define JNIEXPORT 42 | #define JNIIMPORT 43 | #endif 44 | 45 | #define JNICALL 46 | 47 | typedef int jint; 48 | #ifdef _LP64 49 | typedef long jlong; 50 | #else 51 | typedef long long jlong; 52 | #endif 53 | 54 | typedef signed char jbyte; 55 | 56 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 57 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Capture.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Capture.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Capture_disable( 7 | JNIEnv* env, jobject thisObject) { 8 | jboolean enabled = env->GetBooleanField(thisObject, _captureEnabledField); 9 | if (!enabled) return; 10 | env->SetBooleanField(thisObject, _captureEnabledField, JNI_FALSE); 11 | jobject queryObject = env->GetObjectField(thisObject, _captureQueryField); 12 | if (queryObject == NULL) return; 13 | TSQuery* query = (TSQuery*)__getPointer(env, queryObject); 14 | if (query == NULL) return; 15 | jstring name = (jstring)env->GetObjectField(thisObject, _captureNameField); 16 | uint32_t length = env->GetStringLength(name); 17 | const char* characters = env->GetStringUTFChars(name, NULL); 18 | ts_query_disable_capture(query, characters, length); 19 | env->ReleaseStringUTFChars(name, characters); 20 | } 21 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Capture.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Capture */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Capture 6 | #define _Included_ch_usi_si_seart_treesitter_Capture 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Capture 12 | * Method: disable 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Capture_disable 16 | (JNIEnv *, jobject); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_LookaheadIterator.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_LookaheadIterator.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_LookaheadIterator_delete( 7 | JNIEnv* env, jobject thisObject) { 8 | TSLookaheadIterator* iterator = (TSLookaheadIterator*)__getPointer(env, thisObject); 9 | ts_lookahead_iterator_delete(iterator); 10 | __clearPointer(env, thisObject); 11 | } 12 | 13 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_LookaheadIterator_next( 14 | JNIEnv* env, jobject thisObject) { 15 | bool hasNext = (bool)env->GetBooleanField(thisObject, _lookaheadIteratorHasNextField); 16 | if (!hasNext) { 17 | __throwNSE(env, NULL); 18 | return NULL; 19 | } 20 | TSLookaheadIterator* iterator = (TSLookaheadIterator*)__getPointer(env, thisObject); 21 | TSSymbol symbol = ts_lookahead_iterator_current_symbol(iterator); 22 | const TSLanguage* language = ts_lookahead_iterator_language(iterator); 23 | const char* name = ts_language_symbol_name(language, symbol); 24 | TSSymbolType type = ts_language_symbol_type(language, symbol); 25 | env->SetBooleanField( 26 | thisObject, 27 | _lookaheadIteratorHasNextField, 28 | ts_lookahead_iterator_next(iterator) ? JNI_TRUE : JNI_FALSE 29 | ); 30 | return _newObject( 31 | _symbolClass, 32 | _symbolConstructor, 33 | (jint)symbol, 34 | (jint)type, 35 | env->NewStringUTF(name) 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_LookaheadIterator.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_LookaheadIterator */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_LookaheadIterator 6 | #define _Included_ch_usi_si_seart_treesitter_LookaheadIterator 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_LookaheadIterator 12 | * Method: delete 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_LookaheadIterator_delete 16 | (JNIEnv *, jobject); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_LookaheadIterator 20 | * Method: next 21 | * Signature: ()Lch/usi/si/seart/treesitter/Symbol; 22 | */ 23 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_LookaheadIterator_next 24 | (JNIEnv *, jobject); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Node.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Node */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Node 6 | #define _Included_ch_usi_si_seart_treesitter_Node 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Node 12 | * Method: getChild 13 | * Signature: (IZ)Lch/usi/si/seart/treesitter/Node; 14 | */ 15 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getChild 16 | (JNIEnv *, jobject, jint, jboolean); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_Node 20 | * Method: getChildByFieldName 21 | * Signature: (Ljava/lang/String;)Lch/usi/si/seart/treesitter/Node; 22 | */ 23 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getChildByFieldName 24 | (JNIEnv *, jobject, jstring); 25 | 26 | /* 27 | * Class: ch_usi_si_seart_treesitter_Node 28 | * Method: getChildCount 29 | * Signature: (Z)I 30 | */ 31 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Node_getChildCount 32 | (JNIEnv *, jobject, jboolean); 33 | 34 | /* 35 | * Class: ch_usi_si_seart_treesitter_Node 36 | * Method: getChildren 37 | * Signature: (Lch/usi/si/seart/treesitter/Node;Z)[Lch/usi/si/seart/treesitter/Node; 38 | */ 39 | JNIEXPORT jobjectArray JNICALL Java_ch_usi_si_seart_treesitter_Node_getChildren 40 | (JNIEnv *, jclass, jobject, jboolean); 41 | 42 | /* 43 | * Class: ch_usi_si_seart_treesitter_Node 44 | * Method: getDescendant 45 | * Signature: (IIZ)Lch/usi/si/seart/treesitter/Node; 46 | */ 47 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getDescendant__IIZ 48 | (JNIEnv *, jobject, jint, jint, jboolean); 49 | 50 | /* 51 | * Class: ch_usi_si_seart_treesitter_Node 52 | * Method: getDescendant 53 | * Signature: (Lch/usi/si/seart/treesitter/Point;Lch/usi/si/seart/treesitter/Point;Z)Lch/usi/si/seart/treesitter/Node; 54 | */ 55 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getDescendant__Lch_usi_si_seart_treesitter_Point_2Lch_usi_si_seart_treesitter_Point_2Z 56 | (JNIEnv *, jobject, jobject, jobject, jboolean); 57 | 58 | /* 59 | * Class: ch_usi_si_seart_treesitter_Node 60 | * Method: getDescendantCount 61 | * Signature: ()I 62 | */ 63 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Node_getDescendantCount 64 | (JNIEnv *, jobject); 65 | 66 | /* 67 | * Class: ch_usi_si_seart_treesitter_Node 68 | * Method: getEndByte 69 | * Signature: ()I 70 | */ 71 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Node_getEndByte 72 | (JNIEnv *, jobject); 73 | 74 | /* 75 | * Class: ch_usi_si_seart_treesitter_Node 76 | * Method: getEndPoint 77 | * Signature: ()Lch/usi/si/seart/treesitter/Point; 78 | */ 79 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getEndPoint 80 | (JNIEnv *, jobject); 81 | 82 | /* 83 | * Class: ch_usi_si_seart_treesitter_Node 84 | * Method: getFieldNameForChild 85 | * Signature: (I)Ljava/lang/String; 86 | */ 87 | JNIEXPORT jstring JNICALL Java_ch_usi_si_seart_treesitter_Node_getFieldNameForChild 88 | (JNIEnv *, jobject, jint); 89 | 90 | /* 91 | * Class: ch_usi_si_seart_treesitter_Node 92 | * Method: getFirstChildForByte 93 | * Signature: (IZ)Lch/usi/si/seart/treesitter/Node; 94 | */ 95 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getFirstChildForByte 96 | (JNIEnv *, jobject, jint, jboolean); 97 | 98 | /* 99 | * Class: ch_usi_si_seart_treesitter_Node 100 | * Method: getNextParseState 101 | * Signature: ()I 102 | */ 103 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Node_getNextParseState 104 | (JNIEnv *, jobject); 105 | 106 | /* 107 | * Class: ch_usi_si_seart_treesitter_Node 108 | * Method: getNextSibling 109 | * Signature: (Z)Lch/usi/si/seart/treesitter/Node; 110 | */ 111 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getNextSibling 112 | (JNIEnv *, jobject, jboolean); 113 | 114 | /* 115 | * Class: ch_usi_si_seart_treesitter_Node 116 | * Method: getPrevSibling 117 | * Signature: (Z)Lch/usi/si/seart/treesitter/Node; 118 | */ 119 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getPrevSibling 120 | (JNIEnv *, jobject, jboolean); 121 | 122 | /* 123 | * Class: ch_usi_si_seart_treesitter_Node 124 | * Method: getParent 125 | * Signature: ()Lch/usi/si/seart/treesitter/Node; 126 | */ 127 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getParent 128 | (JNIEnv *, jobject); 129 | 130 | /* 131 | * Class: ch_usi_si_seart_treesitter_Node 132 | * Method: getParseState 133 | * Signature: ()I 134 | */ 135 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Node_getParseState 136 | (JNIEnv *, jobject); 137 | 138 | /* 139 | * Class: ch_usi_si_seart_treesitter_Node 140 | * Method: getStartByte 141 | * Signature: ()I 142 | */ 143 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Node_getStartByte 144 | (JNIEnv *, jobject); 145 | 146 | /* 147 | * Class: ch_usi_si_seart_treesitter_Node 148 | * Method: getStartPoint 149 | * Signature: ()Lch/usi/si/seart/treesitter/Point; 150 | */ 151 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getStartPoint 152 | (JNIEnv *, jobject); 153 | 154 | /* 155 | * Class: ch_usi_si_seart_treesitter_Node 156 | * Method: getSymbol 157 | * Signature: (Z)Lch/usi/si/seart/treesitter/Symbol; 158 | */ 159 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_getSymbol 160 | (JNIEnv *, jobject, jboolean); 161 | 162 | /* 163 | * Class: ch_usi_si_seart_treesitter_Node 164 | * Method: getType 165 | * Signature: (Z)Ljava/lang/String; 166 | */ 167 | JNIEXPORT jstring JNICALL Java_ch_usi_si_seart_treesitter_Node_getType 168 | (JNIEnv *, jobject, jboolean); 169 | 170 | /* 171 | * Class: ch_usi_si_seart_treesitter_Node 172 | * Method: hasChanges 173 | * Signature: ()Z 174 | */ 175 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_hasChanges 176 | (JNIEnv *, jobject); 177 | 178 | /* 179 | * Class: ch_usi_si_seart_treesitter_Node 180 | * Method: hasError 181 | * Signature: ()Z 182 | */ 183 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_hasError 184 | (JNIEnv *, jobject); 185 | 186 | /* 187 | * Class: ch_usi_si_seart_treesitter_Node 188 | * Method: isError 189 | * Signature: ()Z 190 | */ 191 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_isError 192 | (JNIEnv *, jobject); 193 | 194 | /* 195 | * Class: ch_usi_si_seart_treesitter_Node 196 | * Method: isExtra 197 | * Signature: ()Z 198 | */ 199 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_isExtra 200 | (JNIEnv *, jobject); 201 | 202 | /* 203 | * Class: ch_usi_si_seart_treesitter_Node 204 | * Method: isMissing 205 | * Signature: ()Z 206 | */ 207 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_isMissing 208 | (JNIEnv *, jobject); 209 | 210 | /* 211 | * Class: ch_usi_si_seart_treesitter_Node 212 | * Method: isNamed 213 | * Signature: ()Z 214 | */ 215 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_isNamed 216 | (JNIEnv *, jobject); 217 | 218 | /* 219 | * Class: ch_usi_si_seart_treesitter_Node 220 | * Method: isNull 221 | * Signature: ()Z 222 | */ 223 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_isNull 224 | (JNIEnv *, jobject); 225 | 226 | /* 227 | * Class: ch_usi_si_seart_treesitter_Node 228 | * Method: walk 229 | * Signature: ()Lch/usi/si/seart/treesitter/TreeCursor; 230 | */ 231 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_walk__ 232 | (JNIEnv *, jobject); 233 | 234 | /* 235 | * Class: ch_usi_si_seart_treesitter_Node 236 | * Method: walk 237 | * Signature: (Lch/usi/si/seart/treesitter/Query;)Lch/usi/si/seart/treesitter/QueryCursor; 238 | */ 239 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Node_walk__Lch_usi_si_seart_treesitter_Query_2 240 | (JNIEnv *, jobject, jobject); 241 | 242 | /* 243 | * Class: ch_usi_si_seart_treesitter_Node 244 | * Method: equals 245 | * Signature: (Lch/usi/si/seart/treesitter/Node;Lch/usi/si/seart/treesitter/Node;)Z 246 | */ 247 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_Node_equals 248 | (JNIEnv *, jclass, jobject, jobject); 249 | 250 | #ifdef __cplusplus 251 | } 252 | #endif 253 | #endif 254 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Parser.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Parser.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_delete( 7 | JNIEnv* env, jobject thisObject) { 8 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 9 | ts_parser_delete(parser); 10 | __clearPointer(env, thisObject); 11 | } 12 | 13 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setLanguage( 14 | JNIEnv* env, jclass thisClass, jobject parserObject, jobject languageObject) { 15 | TSParser* parser = (TSParser*)__getPointer(env, parserObject); 16 | const TSLanguage* language = __unmarshalLanguage(env, languageObject); 17 | if (!ts_parser_set_language(parser, language)) { 18 | __throwILE(env, languageObject); 19 | } else { 20 | env->SetObjectField( 21 | parserObject, 22 | _parserLanguageField, 23 | languageObject 24 | ); 25 | } 26 | } 27 | 28 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_getLogger( 29 | JNIEnv* env, jobject thisObject) { 30 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 31 | TSLogger logger = ts_parser_logger(parser); 32 | jobject loggerObject = reinterpret_cast(logger.payload); 33 | return env->NewLocalRef(loggerObject); 34 | } 35 | 36 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setLogger( 37 | JNIEnv* env, jobject thisObject, jobject loggerObject) { 38 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 39 | TSLogger logger = ts_parser_logger(parser); 40 | jobject globalObject = reinterpret_cast(logger.payload); 41 | if (globalObject != NULL) env->DeleteGlobalRef(globalObject); 42 | logger.payload = reinterpret_cast(env->NewGlobalRef(loggerObject)); 43 | ts_parser_set_logger(parser, logger); 44 | } 45 | 46 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_getIncludedRanges( 47 | JNIEnv* env, jobject thisObject) { 48 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 49 | uint32_t length = 0; 50 | const TSRange* ranges = ts_parser_included_ranges(parser, &length); 51 | if ( 52 | length == 0 || (length == 1 && __isDefaultRange(ranges[0])) 53 | ) { 54 | return env->CallStaticObjectMethod(_collectionsClass, _collectionsEmptyListStaticMethod); 55 | } else { 56 | jobjectArray array = env->NewObjectArray(length, _rangeClass, NULL); 57 | for (uint32_t i = 0; i < length; i++) { 58 | TSRange range = ranges[i]; 59 | jobject rangeObject = __marshalRange(env, range); 60 | env->SetObjectArrayElement(array, i, rangeObject); 61 | } 62 | return env->CallStaticObjectMethod(_listClass, _listOfStaticMethod, array); 63 | } 64 | } 65 | 66 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setIncludedRanges( 67 | JNIEnv* env, jobject thisObject, jobjectArray rangeObjectArray, jint length) { 68 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 69 | TSRange ranges[length]; 70 | for (int i = 0; i < length; i++) { 71 | jobject rangeObject = env->GetObjectArrayElement(rangeObjectArray, i); 72 | ranges[i] = __unmarshalRange(env, rangeObject); 73 | } 74 | if (!ts_parser_set_included_ranges(parser, ranges, length)) { 75 | __throwISE(env, NULL); 76 | } 77 | } 78 | 79 | JNIEXPORT jlong JNICALL Java_ch_usi_si_seart_treesitter_Parser_getTimeout( 80 | JNIEnv* env, jobject thisObject) { 81 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 82 | return (jlong)ts_parser_timeout_micros(parser); 83 | } 84 | 85 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setTimeout( 86 | JNIEnv* env, jobject thisObject, jlong timeout) { 87 | if (timeout >= 0) { 88 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 89 | ts_parser_set_timeout_micros(parser, (uint64_t)timeout); 90 | } else { 91 | __throwIAE(env, "Timeout can not be negative!"); 92 | } 93 | } 94 | 95 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_parse( 96 | JNIEnv* env, jobject thisObject, jstring source, jbyteArray bytes, jint length, jobject treeObject) { 97 | TSParser* parser = (TSParser*)__getPointer(env, thisObject); 98 | TSTree* tree = (treeObject != NULL) ? (TSTree*)__getPointer(env, treeObject) : NULL; 99 | jbyte* elements = env->GetByteArrayElements(bytes, NULL); 100 | TSTree* result = ts_parser_parse_string_encoding( 101 | parser, tree, reinterpret_cast(elements), length, TSInputEncodingUTF16 102 | ); 103 | env->ReleaseByteArrayElements(bytes, elements, JNI_ABORT); 104 | ts_parser_reset(parser); 105 | if (result == 0) { 106 | jthrowable cause = _newThrowable( 107 | _timeoutExceptionClass, 108 | _timeoutExceptionConstructor 109 | ); 110 | jthrowable exception = _newThrowable( 111 | _parsingExceptionClass, 112 | _parsingExceptionConstructor, 113 | cause 114 | ); 115 | env->Throw(exception); 116 | return NULL; 117 | } 118 | jobject languageObject = env->GetObjectField(thisObject, _parserLanguageField); 119 | return _newObject( 120 | _treeClass, 121 | _treeConstructor, 122 | (jlong)result, 123 | languageObject, 124 | source 125 | ); 126 | } 127 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Parser.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Parser */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Parser 6 | #define _Included_ch_usi_si_seart_treesitter_Parser 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Parser 12 | * Method: delete 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_delete 16 | (JNIEnv *, jobject); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_Parser 20 | * Method: setLanguage 21 | * Signature: (Lch/usi/si/seart/treesitter/Parser;Lch/usi/si/seart/treesitter/Language;)V 22 | */ 23 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setLanguage 24 | (JNIEnv *, jclass, jobject, jobject); 25 | 26 | /* 27 | * Class: ch_usi_si_seart_treesitter_Parser 28 | * Method: getLogger 29 | * Signature: ()Lorg/slf4j/Logger; 30 | */ 31 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_getLogger 32 | (JNIEnv *, jobject); 33 | 34 | /* 35 | * Class: ch_usi_si_seart_treesitter_Parser 36 | * Method: setLogger 37 | * Signature: (Lorg/slf4j/Logger;)V 38 | */ 39 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setLogger 40 | (JNIEnv *, jobject, jobject); 41 | 42 | /* 43 | * Class: ch_usi_si_seart_treesitter_Parser 44 | * Method: getIncludedRanges 45 | * Signature: ()Ljava/util/List; 46 | */ 47 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_getIncludedRanges 48 | (JNIEnv *, jobject); 49 | 50 | /* 51 | * Class: ch_usi_si_seart_treesitter_Parser 52 | * Method: setIncludedRanges 53 | * Signature: ([Lch/usi/si/seart/treesitter/Range;I)V 54 | */ 55 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setIncludedRanges 56 | (JNIEnv *, jobject, jobjectArray, jint); 57 | 58 | /* 59 | * Class: ch_usi_si_seart_treesitter_Parser 60 | * Method: getTimeout 61 | * Signature: ()J 62 | */ 63 | JNIEXPORT jlong JNICALL Java_ch_usi_si_seart_treesitter_Parser_getTimeout 64 | (JNIEnv *, jobject); 65 | 66 | /* 67 | * Class: ch_usi_si_seart_treesitter_Parser 68 | * Method: setTimeout 69 | * Signature: (J)V 70 | */ 71 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Parser_setTimeout 72 | (JNIEnv *, jobject, jlong); 73 | 74 | /* 75 | * Class: ch_usi_si_seart_treesitter_Parser 76 | * Method: parse 77 | * Signature: (Ljava/lang/String;[BILch/usi/si/seart/treesitter/Tree;)Lch/usi/si/seart/treesitter/Tree; 78 | */ 79 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_parse 80 | (JNIEnv *, jobject, jstring, jbyteArray, jint, jobject); 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | #endif 86 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Parser_Builder.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Parser_Builder.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_00024Builder_build( 7 | JNIEnv* env, jclass thisClass, jobject languageObject, jlong timeout, jobjectArray rangeObjectArray, jint length) { 8 | TSParser* parser = ts_parser_new(); 9 | const TSLanguage* language = __unmarshalLanguage(env, languageObject); 10 | if (!ts_parser_set_language(parser, language)) { 11 | ts_parser_delete(parser); 12 | __throwILE(env, languageObject); 13 | return NULL; 14 | } 15 | if (timeout < 0) { 16 | ts_parser_delete(parser); 17 | __throwIAE(env, "Timeout can not be negative!"); 18 | return NULL; 19 | } else if (timeout > 0) { 20 | ts_parser_set_timeout_micros(parser, (uint64_t)timeout); 21 | } 22 | TSLogger logger = { 23 | .payload = NULL, 24 | .log = __log_in_java 25 | }; 26 | ts_parser_set_logger(parser, logger); 27 | TSRange ranges[length]; 28 | for (int i = 0; i < length; i++) { 29 | jobject rangeObject = env->GetObjectArrayElement(rangeObjectArray, i); 30 | ranges[i] = __unmarshalRange(env, rangeObject); 31 | } 32 | if (!ts_parser_set_included_ranges(parser, ranges, length)) { 33 | ts_parser_delete(parser); 34 | __throwISE(env, NULL); 35 | return NULL; 36 | } else { 37 | return _newObject( 38 | _parserClass, 39 | _parserConstructor, 40 | (jlong)parser, 41 | languageObject 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Parser_Builder.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Parser_Builder */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Parser_Builder 6 | #define _Included_ch_usi_si_seart_treesitter_Parser_Builder 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Parser_Builder 12 | * Method: build 13 | * Signature: (Lch/usi/si/seart/treesitter/Language;J[Lch/usi/si/seart/treesitter/Range;I)Lch/usi/si/seart/treesitter/Parser; 14 | */ 15 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Parser_00024Builder_build 16 | (JNIEnv *, jclass, jobject, jlong, jobjectArray, jint); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Pattern.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Pattern.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Pattern_disable( 7 | JNIEnv* env, jobject thisObject) { 8 | jboolean enabled = env->GetBooleanField(thisObject, _patternEnabledField); 9 | if (!enabled) return; 10 | env->SetBooleanField(thisObject, _patternEnabledField, JNI_FALSE); 11 | jobject queryObject = env->GetObjectField(thisObject, _patternQueryField); 12 | if (queryObject == NULL) return; 13 | TSQuery* query = (TSQuery*)__getPointer(env, queryObject); 14 | if (query == NULL) return; 15 | jint index = env->GetIntField(thisObject, _patternIndexField); 16 | ts_query_disable_pattern(query, (uint32_t)index); 17 | } 18 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Pattern.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Pattern */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Pattern 6 | #define _Included_ch_usi_si_seart_treesitter_Pattern 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Pattern 12 | * Method: disable 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Pattern_disable 16 | (JNIEnv *, jobject); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Query.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Query.h" 3 | #include 4 | #include 5 | #include 6 | 7 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Query_delete( 8 | JNIEnv* env, jobject thisObject) { 9 | TSQuery* query = (TSQuery*)__getPointer(env, thisObject); 10 | ts_query_delete(query); 11 | __clearPointer(env, thisObject); 12 | } 13 | 14 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Query_getQuantifier( 15 | JNIEnv* env, jobject thisObject, jint patternIndex, jint captureIndex) { 16 | TSQuery* query = (TSQuery*)__getPointer(env, thisObject); 17 | return (jint)ts_query_capture_quantifier_for_id( 18 | query, 19 | (uint32_t)patternIndex, 20 | (uint32_t)captureIndex 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Query.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Query */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Query 6 | #define _Included_ch_usi_si_seart_treesitter_Query 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Query 12 | * Method: delete 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Query_delete 16 | (JNIEnv *, jobject); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_Query 20 | * Method: getQuantifier 21 | * Signature: (II)I 22 | */ 23 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Query_getQuantifier 24 | (JNIEnv *, jobject, jint, jint); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_QueryCursor.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_QueryCursor.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_delete( 7 | JNIEnv* env, jobject thisObject) { 8 | TSQueryCursor* cursor = (TSQueryCursor*)__getPointer(env, thisObject); 9 | ts_query_cursor_delete(cursor); 10 | __clearPointer(env, thisObject); 11 | } 12 | 13 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_execute( 14 | JNIEnv* env, jobject thisObject) { 15 | jobject nodeObject = env->GetObjectField(thisObject, _queryCursorNodeField); 16 | jobject queryObject = env->GetObjectField(thisObject, _queryCursorQueryField); 17 | TSQueryCursor* cursor = (TSQueryCursor*)__getPointer(env, thisObject); 18 | TSQuery* query = (TSQuery*)__getPointer(env, queryObject); 19 | TSNode node = __unmarshalNode(env, nodeObject); 20 | ts_query_cursor_exec(cursor, query, node); 21 | env->SetBooleanField(thisObject, _queryCursorExecutedField, JNI_TRUE); 22 | } 23 | 24 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_setRange__II( 25 | JNIEnv* env, jobject thisObject, jint start, jint end) { 26 | if (start < 0 || end < 0) { 27 | __throwIAE(env, "The start and end bytes must not be negative!"); 28 | return; 29 | } 30 | if (start > end) { 31 | __throwIAE(env, "The starting byte of the range must not be greater than the ending byte!"); 32 | return; 33 | } 34 | // Not sure why I need to multiply by two, again probably because of utf-16 35 | jobject nodeObject = env->GetObjectField(thisObject, _queryCursorNodeField); 36 | TSNode node = __unmarshalNode(env, nodeObject); 37 | uint32_t nodeStart = ts_node_start_byte(node); 38 | uint32_t rangeStart = (uint32_t)start * 2; 39 | if (rangeStart < nodeStart) { 40 | __throwBOB(env, start); 41 | return; 42 | } 43 | uint32_t nodeEnd = ts_node_end_byte(node); 44 | uint32_t rangeEnd = (uint32_t)end * 2; 45 | if (rangeEnd > nodeEnd) { 46 | __throwBOB(env, end); 47 | return; 48 | } 49 | TSQueryCursor* cursor = (TSQueryCursor*)__getPointer(env, thisObject); 50 | ts_query_cursor_set_byte_range(cursor, rangeStart, rangeEnd); 51 | } 52 | 53 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_setRange__Lch_usi_si_seart_treesitter_Point_2Lch_usi_si_seart_treesitter_Point_2( 54 | JNIEnv* env, jobject thisObject, jobject startPointObject, jobject endPointObject) { 55 | if (startPointObject == NULL) { 56 | __throwNPE(env, "Start point must not be null!"); 57 | return; 58 | } 59 | if (endPointObject == NULL) { 60 | __throwNPE(env, "End point must not be null!"); 61 | return; 62 | } 63 | jobject nodeObject = env->GetObjectField(thisObject, _queryCursorNodeField); 64 | TSNode node = __unmarshalNode(env, nodeObject); 65 | TSPoint startPoint = __unmarshalPoint(env, startPointObject); 66 | TSPoint endPoint = __unmarshalPoint(env, endPointObject); 67 | if (endPoint.row < 0 || endPoint.column < 0) { 68 | __throwIAE(env, "End point can not have negative coordinates!"); 69 | return; 70 | } 71 | if (startPoint.row < 0 || startPoint.column < 0) { 72 | __throwIAE(env, "Start point can not have negative coordinates!"); 73 | return; 74 | } 75 | if (__comparePoints(startPoint, endPoint) == GT) { 76 | __throwIAE(env, "Start point can not be greater than end point!"); 77 | return; 78 | } 79 | TSPoint lowerBound = ts_node_start_point(node); 80 | if (__comparePoints(lowerBound, startPoint) == GT) { 81 | __throwPOB(env, startPointObject); 82 | return; 83 | } 84 | TSPoint upperBound = ts_node_end_point(node); 85 | if (__comparePoints(endPoint, upperBound) == GT) { 86 | __throwPOB(env, endPointObject); 87 | return; 88 | } 89 | TSQueryCursor* cursor = (TSQueryCursor*)__getPointer(env, thisObject); 90 | ts_query_cursor_set_point_range(cursor, startPoint, endPoint); 91 | } 92 | 93 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_nextMatch( 94 | JNIEnv* env, jobject thisObject) { 95 | bool executed = (bool)env->GetBooleanField(thisObject, _queryCursorExecutedField); 96 | bool found = false; 97 | if (!executed) { 98 | __throwISE(env, "Query was not executed on node!"); 99 | return NULL; 100 | } 101 | TSQueryMatch match; 102 | TSQueryCursor* cursor = (TSQueryCursor*)__getPointer(env, thisObject); 103 | found = ts_query_cursor_next_match(cursor, &match); 104 | if (!found) return NULL; 105 | jobject nodeObject = env->GetObjectField(thisObject, _queryCursorNodeField); 106 | jobject queryObject = env->GetObjectField(thisObject, _queryCursorQueryField); 107 | jobject queryCapturesList = env->GetObjectField(queryObject, _queryCapturesField); 108 | jobject queryPatternsList = env->GetObjectField(queryObject, _queryPatternsField); 109 | jobject patternObject = env->CallObjectMethod( 110 | queryPatternsList, _listGet, match.pattern_index 111 | ); 112 | jobjectArray entries = env->NewObjectArray(match.capture_count, _mapEntryClass, NULL); 113 | for (uint16_t i = 0; i < match.capture_count; i++) { 114 | TSQueryCapture capture = match.captures[i]; 115 | jobject captureObject = env->CallObjectMethod( 116 | queryCapturesList, _listGet, capture.index 117 | ); 118 | jobject matchedObject = __marshalNode(env, capture.node); 119 | __copyTree(env, nodeObject, matchedObject); 120 | jobject entryObject = env->CallStaticObjectMethod( 121 | _mapClass, 122 | _mapEntryStaticMethod, 123 | captureObject, 124 | matchedObject 125 | ); 126 | env->SetObjectArrayElement(entries, i, entryObject); 127 | } 128 | return _newObject( 129 | _queryMatchClass, 130 | _queryMatchConstructor, 131 | (jint)match.id, 132 | patternObject, 133 | entries 134 | ); 135 | } 136 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_QueryCursor.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_QueryCursor */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_QueryCursor 6 | #define _Included_ch_usi_si_seart_treesitter_QueryCursor 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_QueryCursor 12 | * Method: delete 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_delete 16 | (JNIEnv *, jobject); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_QueryCursor 20 | * Method: execute 21 | * Signature: ()V 22 | */ 23 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_execute 24 | (JNIEnv *, jobject); 25 | 26 | /* 27 | * Class: ch_usi_si_seart_treesitter_QueryCursor 28 | * Method: setRange 29 | * Signature: (II)V 30 | */ 31 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_setRange__II 32 | (JNIEnv *, jobject, jint, jint); 33 | 34 | /* 35 | * Class: ch_usi_si_seart_treesitter_QueryCursor 36 | * Method: setRange 37 | * Signature: (Lch/usi/si/seart/treesitter/Point;Lch/usi/si/seart/treesitter/Point;)V 38 | */ 39 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_setRange__Lch_usi_si_seart_treesitter_Point_2Lch_usi_si_seart_treesitter_Point_2 40 | (JNIEnv *, jobject, jobject, jobject); 41 | 42 | /* 43 | * Class: ch_usi_si_seart_treesitter_QueryCursor 44 | * Method: nextMatch 45 | * Signature: ()Lch/usi/si/seart/treesitter/QueryMatch; 46 | */ 47 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_QueryCursor_nextMatch 48 | (JNIEnv *, jobject); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | #endif 54 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Query_Builder.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Query_Builder.h" 3 | #include 4 | #include 5 | #include 6 | 7 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Query_00024Builder_build( 8 | JNIEnv* env, jclass thisClass, jobject languageObject, jstring patternString) { 9 | const TSLanguage* language = __unmarshalLanguage(env, languageObject); 10 | uint32_t length = env->GetStringLength(patternString); 11 | const char* characters = env->GetStringUTFChars(patternString, NULL); 12 | uint32_t offset; 13 | TSQueryError type = TSQueryErrorNone; 14 | TSQuery* query = ts_query_new(language, characters, length, &offset, &type); 15 | jobject result = NULL; 16 | jthrowable exception = NULL; 17 | switch (type) { 18 | case TSQueryErrorNone: 19 | { 20 | uint32_t patternsLength = ts_query_pattern_count(query); 21 | uint32_t bytePositions[patternsLength]; 22 | for (uint32_t i = 0; i < patternsLength; i++) { 23 | bytePositions[i] = ts_query_start_byte_for_pattern(query, i); 24 | } 25 | jobjectArray patterns = env->NewObjectArray(patternsLength, _patternClass, NULL); 26 | for (uint32_t i = 0; i < patternsLength; i++) { 27 | uint32_t byteLower = bytePositions[i]; 28 | uint32_t byteUpper = (i < patternsLength - 1) ? bytePositions[i + 1] : length; 29 | uint32_t byteTotal = byteUpper - byteLower; 30 | char substring[byteTotal + 1]; 31 | memcpy(substring, characters + byteLower, byteTotal); 32 | substring[byteTotal] = '\0'; 33 | bool rooted = ts_query_is_pattern_rooted(query, i); 34 | bool nonLocal = ts_query_is_pattern_non_local(query, i); 35 | uint32_t stepsLength = 0; 36 | uint32_t predicatesLength = 0; 37 | const TSQueryPredicateStep* steps = ts_query_predicates_for_pattern(query, i, &stepsLength); 38 | jobjectArray predicatesArray = __marshalPredicates(env, query, steps, &stepsLength, &predicatesLength); 39 | jobject patternObject = _newObject( 40 | _patternClass, 41 | _patternConstructor, 42 | (jint)i, 43 | (jint)byteLower, 44 | (rooted) ? JNI_TRUE : JNI_FALSE, 45 | (nonLocal) ? JNI_TRUE : JNI_FALSE, 46 | env->NewStringUTF(substring), 47 | predicatesArray 48 | ); 49 | for (uint32_t j = 0; j < predicatesLength; j++) { 50 | jobject predicateObject = env->GetObjectArrayElement(predicatesArray, j); 51 | env->SetObjectField(predicateObject, _predicatePatternField, patternObject); 52 | } 53 | env->SetObjectArrayElement(patterns, i, patternObject); 54 | } 55 | 56 | uint32_t capturesLength = ts_query_capture_count(query); 57 | jobjectArray captures = env->NewObjectArray(capturesLength, _captureClass, NULL); 58 | for (uint32_t i = 0; i < capturesLength; i++) { 59 | uint32_t ignored; 60 | const char* capture = ts_query_capture_name_for_id(query, i, &ignored); 61 | jobject captureObject = _newObject( 62 | _captureClass, 63 | _captureConstructor, 64 | (jint)i, 65 | env->NewStringUTF(capture) 66 | ); 67 | env->SetObjectArrayElement(captures, i, captureObject); 68 | } 69 | 70 | uint32_t stringsLength = ts_query_string_count(query); 71 | jobjectArray strings = env->NewObjectArray(stringsLength, _stringClass, NULL); 72 | for (uint32_t i = 0; i < stringsLength; i++) { 73 | uint32_t ignored; 74 | const char* string = ts_query_string_value_for_id(query, i, &ignored); 75 | jstring stringString = env->NewStringUTF(string); 76 | env->SetObjectArrayElement(strings, i, stringString); 77 | } 78 | 79 | jobject queryObject = _newObject( 80 | _queryClass, 81 | _queryConstructor, 82 | (jlong)query, 83 | languageObject, 84 | patterns, 85 | captures, 86 | strings 87 | ); 88 | 89 | for (uint32_t i = 0; i < capturesLength; i++) { 90 | jobject captureObject = env->GetObjectArrayElement(captures, i); 91 | env->SetObjectField(captureObject, _captureQueryField, queryObject); 92 | } 93 | 94 | for (uint32_t i = 0; i < patternsLength; i++) { 95 | jobject patternObject = env->GetObjectArrayElement(patterns, i); 96 | env->SetObjectField(patternObject, _patternQueryField, queryObject); 97 | } 98 | 99 | result = queryObject; 100 | break; 101 | } 102 | case TSQueryErrorSyntax: 103 | case TSQueryErrorNodeType: 104 | case TSQueryErrorField: 105 | case TSQueryErrorCapture: 106 | case TSQueryErrorStructure: 107 | exception = _newThrowable( 108 | __getQueryExceptionClass(type), 109 | __getQueryExceptionConstructor(type), 110 | (jint)offset 111 | ); 112 | break; 113 | case TSQueryErrorLanguage: 114 | exception = _newThrowable( 115 | __getQueryExceptionClass(type), 116 | __getQueryExceptionConstructor(type), 117 | languageObject 118 | ); 119 | break; 120 | default: 121 | exception = _newThrowable( 122 | _treeSitterExceptionClass, 123 | _treeSitterExceptionConstructor 124 | ); 125 | } 126 | env->ReleaseStringUTFChars(patternString, characters); 127 | if (exception != NULL) env->Throw(exception); 128 | return result; 129 | } 130 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Query_Builder.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Query_Builder */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Query_Builder 6 | #define _Included_ch_usi_si_seart_treesitter_Query_Builder 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Query_Builder 12 | * Method: build 13 | * Signature: (Lch/usi/si/seart/treesitter/Language;Ljava/lang/String;)Lch/usi/si/seart/treesitter/Query; 14 | */ 15 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Query_00024Builder_build 16 | (JNIEnv *, jclass, jobject, jstring); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Tree.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_Tree.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Tree_delete( 7 | JNIEnv* env, jobject thisObject) { 8 | TSTree* tree = (TSTree*)__getPointer(env, thisObject); 9 | ts_tree_delete(tree); 10 | __clearPointer(env, thisObject); 11 | } 12 | 13 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Tree_edit( 14 | JNIEnv* env, jobject thisObject, jobject inputEditObject) { 15 | TSTree* tree = (TSTree*)__getPointer(env, thisObject); 16 | if (inputEditObject == NULL) { 17 | __throwNPE(env, "Input edit must not be null!"); 18 | } else { 19 | TSInputEdit inputEdit = __unmarshalInputEdit(env, inputEditObject); 20 | ts_tree_edit(tree, &inputEdit); 21 | } 22 | } 23 | 24 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Tree_getChangedRanges( 25 | JNIEnv* env, jobject thisObject, jobject otherObject) { 26 | TSTree* old_tree = (TSTree*)__getPointer(env, thisObject); 27 | TSTree* new_tree = (TSTree*)__getPointer(env, otherObject); 28 | uint32_t length = 0; 29 | TSRange* ranges = ts_tree_get_changed_ranges(old_tree, new_tree, &length); 30 | jobjectArray array = env->NewObjectArray(length, _rangeClass, NULL); 31 | for (uint32_t i = 0; i < length; i++) { 32 | TSRange range = ranges[i]; 33 | jobject rangeObject = __marshalRange(env, range); 34 | env->SetObjectArrayElement(array, i, rangeObject); 35 | } 36 | delete[] ranges; 37 | return env->CallStaticObjectMethod(_listClass, _listOfStaticMethod, array); 38 | } 39 | 40 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Tree_getRootNode( 41 | JNIEnv* env, jobject thisObject) { 42 | TSTree* tree = (TSTree*)__getPointer(env, thisObject); 43 | TSNode node = ts_tree_root_node(tree); 44 | jobject nodeObject = __marshalNode(env, node); 45 | _setNodeTreeField(nodeObject, thisObject); 46 | return nodeObject; 47 | } 48 | 49 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Tree_clone( 50 | JNIEnv* env, jobject thisObject) { 51 | jobject languageObject = env->GetObjectField(thisObject, _treeLanguageField); 52 | jobject sourceObject = env->GetObjectField(thisObject, _treeSourceField); 53 | TSTree* tree = (TSTree*)__getPointer(env, thisObject); 54 | return _newObject( 55 | _treeClass, 56 | _treeConstructor, 57 | ts_tree_copy(tree), 58 | languageObject, 59 | sourceObject 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_Tree.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_Tree */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_Tree 6 | #define _Included_ch_usi_si_seart_treesitter_Tree 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_Tree 12 | * Method: delete 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Tree_delete 16 | (JNIEnv *, jobject); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_Tree 20 | * Method: edit 21 | * Signature: (Lch/usi/si/seart/treesitter/InputEdit;)V 22 | */ 23 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Tree_edit 24 | (JNIEnv *, jobject, jobject); 25 | 26 | /* 27 | * Class: ch_usi_si_seart_treesitter_Tree 28 | * Method: getChangedRanges 29 | * Signature: (Lch/usi/si/seart/treesitter/Tree;)Ljava/util/List; 30 | */ 31 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Tree_getChangedRanges 32 | (JNIEnv *, jobject, jobject); 33 | 34 | /* 35 | * Class: ch_usi_si_seart_treesitter_Tree 36 | * Method: getRootNode 37 | * Signature: ()Lch/usi/si/seart/treesitter/Node; 38 | */ 39 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Tree_getRootNode 40 | (JNIEnv *, jobject); 41 | 42 | /* 43 | * Class: ch_usi_si_seart_treesitter_Tree 44 | * Method: clone 45 | * Signature: ()Lch/usi/si/seart/treesitter/Tree; 46 | */ 47 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Tree_clone 48 | (JNIEnv *, jobject); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | #endif 54 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_TreeCursor.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_TreeCursor */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_TreeCursor 6 | #define _Included_ch_usi_si_seart_treesitter_TreeCursor 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_TreeCursor 12 | * Method: delete 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_delete 16 | (JNIEnv *, jobject); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_TreeCursor 20 | * Method: getCurrentDepth 21 | * Signature: ()I 22 | */ 23 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_getCurrentDepth 24 | (JNIEnv *, jobject); 25 | 26 | /* 27 | * Class: ch_usi_si_seart_treesitter_TreeCursor 28 | * Method: getCurrentNode 29 | * Signature: ()Lch/usi/si/seart/treesitter/Node; 30 | */ 31 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_getCurrentNode 32 | (JNIEnv *, jobject); 33 | 34 | /* 35 | * Class: ch_usi_si_seart_treesitter_TreeCursor 36 | * Method: getCurrentFieldName 37 | * Signature: ()Ljava/lang/String; 38 | */ 39 | JNIEXPORT jstring JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_getCurrentFieldName 40 | (JNIEnv *, jobject); 41 | 42 | /* 43 | * Class: ch_usi_si_seart_treesitter_TreeCursor 44 | * Method: getCurrentTreeCursorNode 45 | * Signature: ()Lch/usi/si/seart/treesitter/TreeCursorNode; 46 | */ 47 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_getCurrentTreeCursorNode 48 | (JNIEnv *, jobject); 49 | 50 | /* 51 | * Class: ch_usi_si_seart_treesitter_TreeCursor 52 | * Method: gotoFirstChild 53 | * Signature: ()Z 54 | */ 55 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild__ 56 | (JNIEnv *, jobject); 57 | 58 | /* 59 | * Class: ch_usi_si_seart_treesitter_TreeCursor 60 | * Method: gotoFirstChild 61 | * Signature: (I)Z 62 | */ 63 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild__I 64 | (JNIEnv *, jobject, jint); 65 | 66 | /* 67 | * Class: ch_usi_si_seart_treesitter_TreeCursor 68 | * Method: gotoFirstChild 69 | * Signature: (Lch/usi/si/seart/treesitter/Point;)Z 70 | */ 71 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild__Lch_usi_si_seart_treesitter_Point_2 72 | (JNIEnv *, jobject, jobject); 73 | 74 | /* 75 | * Class: ch_usi_si_seart_treesitter_TreeCursor 76 | * Method: gotoLastChild 77 | * Signature: ()Z 78 | */ 79 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoLastChild 80 | (JNIEnv *, jobject); 81 | 82 | /* 83 | * Class: ch_usi_si_seart_treesitter_TreeCursor 84 | * Method: gotoNextSibling 85 | * Signature: ()Z 86 | */ 87 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoNextSibling 88 | (JNIEnv *, jobject); 89 | 90 | /* 91 | * Class: ch_usi_si_seart_treesitter_TreeCursor 92 | * Method: gotoPrevSibling 93 | * Signature: ()Z 94 | */ 95 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoPrevSibling 96 | (JNIEnv *, jobject); 97 | 98 | /* 99 | * Class: ch_usi_si_seart_treesitter_TreeCursor 100 | * Method: gotoParent 101 | * Signature: ()Z 102 | */ 103 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoParent 104 | (JNIEnv *, jobject); 105 | 106 | /* 107 | * Class: ch_usi_si_seart_treesitter_TreeCursor 108 | * Method: gotoNode 109 | * Signature: (Lch/usi/si/seart/treesitter/Node;)Z 110 | */ 111 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoNode 112 | (JNIEnv *, jobject, jobject); 113 | 114 | /* 115 | * Class: ch_usi_si_seart_treesitter_TreeCursor 116 | * Method: reset 117 | * Signature: (Lch/usi/si/seart/treesitter/TreeCursor;)Z 118 | */ 119 | JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_reset 120 | (JNIEnv *, jobject, jobject); 121 | 122 | /* 123 | * Class: ch_usi_si_seart_treesitter_TreeCursor 124 | * Method: clone 125 | * Signature: ()Lch/usi/si/seart/treesitter/TreeCursor; 126 | */ 127 | JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_clone 128 | (JNIEnv *, jobject); 129 | 130 | #ifdef __cplusplus 131 | } 132 | #endif 133 | #endif 134 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_printer_DotGraphPrinter.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_printer_DotGraphPrinter.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_printer_DotGraphPrinter_write( 7 | JNIEnv* env, jobject thisObject, jobject fileObject) { 8 | jobject treeObject = env->GetObjectField(thisObject, _dotGraphPrinterTreeField); 9 | if (treeObject == NULL) { 10 | __throwNPE(env, "Tree must not be null!"); 11 | return; 12 | } 13 | jlong tree = __getPointer(env, treeObject); 14 | if (tree == 0) { 15 | __throwIAE(env, "Can not export an invalid tree!"); 16 | return; 17 | } 18 | jclass fileClass = _getClass("java/io/File"); 19 | jfieldID pathField = _getField(fileClass, "path", "Ljava/lang/String;"); 20 | jstring path = (jstring)env->GetObjectField(fileObject, pathField); 21 | const char* pathPtr = env->GetStringUTFChars(path, NULL); 22 | FILE* file = fopen(pathPtr, "w"); 23 | if (file == NULL) { 24 | __throwIOE(env, "Could not open a file for export"); 25 | env->ReleaseStringUTFChars(path, pathPtr); 26 | return; 27 | } 28 | int fd = fileno(file); 29 | if (fd == -1) { 30 | __throwIOE(env, "Could not obtain the file descriptor"); 31 | env->ReleaseStringUTFChars(path, pathPtr); 32 | return; 33 | } 34 | ts_tree_print_dot_graph((TSTree*)tree, fd); 35 | fclose(file); 36 | env->ReleaseStringUTFChars(path, pathPtr); 37 | } 38 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_printer_DotGraphPrinter.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_printer_DotGraphPrinter */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_printer_DotGraphPrinter 6 | #define _Included_ch_usi_si_seart_treesitter_printer_DotGraphPrinter 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_printer_DotGraphPrinter 12 | * Method: write 13 | * Signature: (Ljava/io/File;)V 14 | */ 15 | JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_printer_DotGraphPrinter_write 16 | (JNIEnv *, jobject, jobject); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_version_TreeSitter.cc: -------------------------------------------------------------------------------- 1 | #include "ch_usi_si_seart_treesitter.h" 2 | #include "ch_usi_si_seart_treesitter_version_TreeSitter.h" 3 | #include 4 | #include 5 | 6 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_version_TreeSitter_getCurrentABIVersion( 7 | JNIEnv* env, jclass thisClass) { 8 | return TREE_SITTER_LANGUAGE_VERSION; 9 | } 10 | 11 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_version_TreeSitter_getMinimumABIVersion( 12 | JNIEnv* env, jclass thisClass) { 13 | return TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION; 14 | } 15 | -------------------------------------------------------------------------------- /lib/ch_usi_si_seart_treesitter_version_TreeSitter.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class ch_usi_si_seart_treesitter_version_TreeSitter */ 4 | 5 | #ifndef _Included_ch_usi_si_seart_treesitter_version_TreeSitter 6 | #define _Included_ch_usi_si_seart_treesitter_version_TreeSitter 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: ch_usi_si_seart_treesitter_version_TreeSitter 12 | * Method: getCurrentABIVersion 13 | * Signature: ()I 14 | */ 15 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_version_TreeSitter_getCurrentABIVersion 16 | (JNIEnv *, jclass); 17 | 18 | /* 19 | * Class: ch_usi_si_seart_treesitter_version_TreeSitter 20 | * Method: getMinimumABIVersion 21 | * Signature: ()I 22 | */ 23 | JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_version_TreeSitter_getMinimumABIVersion 24 | (JNIEnv *, jclass); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.addLombokGeneratedAnnotation = true 3 | -------------------------------------------------------------------------------- /properties.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from argparse import ArgumentParser 4 | from git import Repo as GitRepository, Tag as GitTag 5 | from glob import glob as find 6 | from os import PathLike 7 | from os import getcwd as cwd 8 | from os.path import basename, dirname, realpath 9 | from os.path import join as path 10 | from typing import AnyStr, TextIO 11 | 12 | __location__ = realpath(path(cwd(), dirname(__file__))) 13 | 14 | 15 | class PropertyWriter: 16 | 17 | def __init__(self, submodule: AnyStr | PathLike): 18 | self.submodule = submodule 19 | self.language = basename(submodule)[12:] 20 | 21 | @staticmethod 22 | def is_semver(tag: GitTag) -> bool: 23 | first = next(iter(tag.name), "") 24 | return first.isdigit() or first == "v" 25 | 26 | def write(self, outfile: TextIO): 27 | with GitRepository(self.submodule) as repository: 28 | commit = repository.head.commit 29 | tags = [tag.name for tag in repository.tags if tag.commit == commit and self.is_semver(tag)] 30 | outfile.write( 31 | f"""\ 32 | url.{self.language}={next(repository.remotes.origin.urls)} 33 | sha.{self.language}={commit.hexsha} 34 | tag.{self.language}={next(iter(tags), "")} 35 | """) 36 | 37 | 38 | if __name__ == "__main__": 39 | parser = ArgumentParser(description="Generate tree-sitter properties file.") 40 | parser.add_argument( 41 | "-o", 42 | "--output", 43 | default=path(__location__, "language.properties"), 44 | help="Output file path.", 45 | ) 46 | args = parser.parse_args() 47 | with open(args.output, "w") as properties_file: 48 | for directory in sorted(find(f"{__location__}/tree-sitter-*")): 49 | PropertyWriter(directory).write(properties_file) 50 | -------------------------------------------------------------------------------- /remove.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # TODO: This script has not been tested yet. 4 | # If you run into an issue on the first run, 5 | # now you know why. 6 | 7 | set -e 8 | 9 | SUBMODULE=$1 10 | 11 | if [ -z "$SUBMODULE" ]; then 12 | echo "Usage: $(basename "$0") " 13 | exit 1 14 | fi 15 | 16 | git submodule deinit --force "$SUBMODULE" 17 | git rm --cached "$SUBMODULE" 18 | git config -f .gitmodules --remove-section "submodule.$SUBMODULE" 19 | git add .gitmodules 20 | rm -rf ".git/modules/$SUBMODULE" "$SUBMODULE" 21 | git commit --message "Remove \`$SUBMODULE\` submodule" 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | GitPython==3.1.44 2 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Capture.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Generated; 5 | import lombok.Getter; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.experimental.FieldDefaults; 8 | import lombok.experimental.NonFinal; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * Represents the named capture of a {@link Query}. Captures are used 17 | * to extract information from syntax trees when a query match occurs. 18 | * Each instance can be uniquely identified by the {@link Query} it 19 | * belongs to, along with its ordinal position within the same query. 20 | * 21 | * @since 1.7.0 22 | * @author Ozren Dabić 23 | * @see Pattern 24 | * @see Query 25 | */ 26 | @Getter 27 | @RequiredArgsConstructor(access = AccessLevel.PACKAGE) 28 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 29 | public class Capture { 30 | 31 | Query query; 32 | 33 | int index; 34 | 35 | String name; 36 | 37 | @NonFinal 38 | boolean enabled = true; 39 | 40 | @SuppressWarnings("unused") 41 | Capture(int index, @NotNull String name) { 42 | this(null, index, name); 43 | } 44 | 45 | /** 46 | * Disable this capture, preventing it from returning in matches. 47 | * This will eliminate any resource usage from the {@link Query} 48 | * associated with recording the capture. 49 | *

50 | * 51 | * This can not be undone. 52 | * 53 | */ 54 | public native void disable(); 55 | 56 | /** 57 | * Get the capture quantifier for a given query {@link Pattern}. 58 | * 59 | * @param pattern the query pattern 60 | * @return the quantifier 61 | * @throws NullPointerException if pattern is {@code null} 62 | * @throws IllegalArgumentException if the pattern is not present in the query 63 | * @since 1.12.0 64 | */ 65 | public Quantifier getQuantifier(@NotNull Pattern pattern) { 66 | return query.getQuantifier(pattern, this); 67 | } 68 | 69 | /** 70 | * Get the capture quantifiers for all {@link Query} patterns. 71 | * The order of the quantifiers in the returned list corresponds 72 | * to the {@link Pattern} order in the query. 73 | * 74 | * @return the quantifiers 75 | * @since 1.12.0 76 | */ 77 | public List getQuantifiers() { 78 | return query.getPatterns().stream() 79 | .map(this::getQuantifier) 80 | .collect(Collectors.toUnmodifiableList()); 81 | } 82 | 83 | @Override 84 | @Generated 85 | public boolean equals(Object o) { 86 | if (this == o) return true; 87 | if (o == null || getClass() != o.getClass()) return false; 88 | Capture capture = (Capture) o; 89 | return Objects.equals(query, capture.query) && index == capture.index; 90 | } 91 | 92 | @Override 93 | @Generated 94 | public int hashCode() { 95 | return Objects.hash(query, index); 96 | } 97 | 98 | @Override 99 | @Generated 100 | public String toString() { 101 | return "@" + name; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/External.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.NoArgsConstructor; 6 | import lombok.experimental.FieldDefaults; 7 | 8 | import java.lang.ref.Cleaner; 9 | 10 | @FieldDefaults(makeFinal = true) 11 | @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) 12 | abstract class External implements AutoCloseable { 13 | 14 | protected long pointer; 15 | private Cleaner.Cleanable cleanable; 16 | 17 | private static final Cleaner CLEANER = Cleaner.create(); 18 | 19 | protected External(long pointer) { 20 | this.pointer = pointer; 21 | this.cleanable = CLEANER.register(this, new Action(this)); 22 | } 23 | 24 | /** 25 | * Checks whether the memory address associated 26 | * with this external resource is {@code nullptr}. 27 | * 28 | * @return {@code true} if the memory address is 0, 29 | * otherwise {@code false} 30 | */ 31 | public final boolean isNull() { 32 | return pointer == 0; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object obj) { 37 | if (obj == this) return true; 38 | if (obj == null || getClass() != obj.getClass()) return false; 39 | External other = (External) obj; 40 | return pointer == other.pointer; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Long.hashCode(pointer); 46 | } 47 | 48 | /** 49 | * Delete the external resource, freeing all the memory that it used. 50 | */ 51 | @Override 52 | public void close() { 53 | boolean requiresCleaning = cleanable != null && !isNull(); 54 | if (requiresCleaning) cleanable.clean(); 55 | } 56 | 57 | protected abstract void delete(); 58 | 59 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 60 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 61 | private static final class Action implements Runnable { 62 | 63 | External external; 64 | 65 | @Override 66 | public void run() { 67 | external.delete(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/InputEdit.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.experimental.FieldDefaults; 7 | 8 | /** 9 | * Represents an edit operation on a section of source code. 10 | * Contains information pertaining to the starting byte offset and position, 11 | * as well as the former and current end byte offsets and positions. 12 | * 13 | * @since 1.0.0 14 | * @author Ozren Dabić 15 | */ 16 | @Getter 17 | @AllArgsConstructor 18 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 19 | public class InputEdit { 20 | 21 | int startByte; 22 | int oldEndByte; 23 | int newEndByte; 24 | Point startPoint; 25 | Point oldEndPoint; 26 | Point newEndPoint; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/LibraryLoader.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import ch.usi.si.seart.treesitter.exception.TreeSitterException; 4 | import lombok.AccessLevel; 5 | import lombok.AllArgsConstructor; 6 | import lombok.experimental.FieldDefaults; 7 | import lombok.experimental.UtilityClass; 8 | import org.apache.commons.io.FileUtils; 9 | import org.apache.commons.io.IOUtils; 10 | import org.apache.commons.lang3.SystemUtils; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.OutputStream; 17 | import java.net.URL; 18 | 19 | /** 20 | * Utility for loading the native system library. 21 | * 22 | * @since 1.0.0 23 | * @author Ozren Dabić 24 | */ 25 | @UtilityClass 26 | public class LibraryLoader { 27 | 28 | private static final String LIBRARY_FILE_NAME = "libjava-tree-sitter"; 29 | 30 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 31 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 32 | private static final class SystemResource { 33 | 34 | URL url; 35 | String name; 36 | 37 | private SystemResource(String name) { 38 | this(LibraryLoader.class.getClassLoader().getResource(name), name); 39 | } 40 | } 41 | 42 | /** 43 | * Load the native library. 44 | * Call this once prior to using any of the APIs. 45 | */ 46 | public void load() { 47 | String filename = String.format("%s.%s", LIBRARY_FILE_NAME, getExtension()); 48 | SystemResource systemResource = new SystemResource(filename); 49 | String libPath = getLibPath(systemResource); 50 | System.load(libPath); 51 | } 52 | 53 | private String getLibPath(SystemResource systemResource) { 54 | String protocol = systemResource.url.getProtocol(); 55 | switch (protocol) { 56 | case "file": 57 | return systemResource.url.getPath(); 58 | case "jar": 59 | File tmpdir = FileUtils.getTempDirectory(); 60 | File tmpfile = new File(tmpdir, systemResource.name); 61 | tmpfile.deleteOnExit(); 62 | try ( 63 | InputStream input = systemResource.url.openStream(); 64 | OutputStream output = new FileOutputStream(tmpfile, false) 65 | ) { 66 | IOUtils.copy(input, output); 67 | return tmpfile.getPath(); 68 | } catch (IOException cause) { 69 | throw new TreeSitterException(cause); 70 | } 71 | default: 72 | Exception cause = new UnsupportedOperationException("Unsupported protocol: " + protocol); 73 | throw new TreeSitterException(cause); 74 | } 75 | } 76 | 77 | private String getExtension() { 78 | if (SystemUtils.IS_OS_LINUX) return "so"; 79 | else if (SystemUtils.IS_OS_MAC) return "dylib"; 80 | else throw new TreeSitterException( 81 | "The tree-sitter library was not compiled for this platform: " + SystemUtils.OS_NAME.toLowerCase() 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/LookaheadIterator.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.Getter; 4 | import lombok.experimental.FieldDefaults; 5 | 6 | import java.util.Iterator; 7 | 8 | /** 9 | * Specialized iterator that can be used to generate suggestions and improve syntax error diagnostics. 10 | * To get symbols valid in an {@code ERROR} node, use the lookahead iterator on its first leaf node state. 11 | * For {@code MISSING} nodes, a lookahead iterator created on the previous non-extra leaf node may be appropriate. 12 | * 13 | * @since 1.12.0 14 | * @author Ozren Dabić 15 | */ 16 | @FieldDefaults(level = lombok.AccessLevel.PRIVATE, makeFinal = true) 17 | public class LookaheadIterator extends External implements Iterator { 18 | 19 | boolean hasNext; 20 | 21 | @Getter 22 | Language language; 23 | 24 | LookaheadIterator(long pointer, boolean hasNext, Language language) { 25 | super(pointer); 26 | this.hasNext = hasNext; 27 | this.language = language; 28 | } 29 | 30 | @Override 31 | protected native void delete(); 32 | 33 | @Override 34 | public boolean hasNext() { 35 | return hasNext; 36 | } 37 | 38 | @Override 39 | public native Symbol next(); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Pattern.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Generated; 5 | import lombok.Getter; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.experimental.FieldDefaults; 8 | import lombok.experimental.NonFinal; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Represents a single symbolic expression (s-expression) pattern of a {@link Query}. 16 | * Said pattern is a structured representation of a syntax tree fragment, which can 17 | * be used to query subtrees. Each instance can be uniquely identified by the query 18 | * it belongs to, along with its ordinal position within the same query. 19 | * 20 | * @since 1.7.0 21 | * @author Ozren Dabić 22 | * @see Capture 23 | * @see Query 24 | */ 25 | @Getter 26 | @RequiredArgsConstructor(access = AccessLevel.PACKAGE) 27 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 28 | public class Pattern { 29 | 30 | Query query; 31 | 32 | int index; 33 | 34 | int startOffset; 35 | 36 | boolean rooted; 37 | boolean nonLocal; 38 | 39 | String value; 40 | 41 | List predicates; 42 | 43 | @NonFinal 44 | boolean enabled = true; 45 | 46 | @SuppressWarnings("unused") 47 | Pattern( 48 | int index, 49 | int startOffset, 50 | boolean rooted, 51 | boolean nonLocal, 52 | @NotNull String value, 53 | @NotNull Predicate[] predicates 54 | ) { 55 | this(null, index, startOffset, rooted, nonLocal, value.stripTrailing(), List.of(predicates)); 56 | } 57 | 58 | /** 59 | * Disable this pattern, preventing it from further matching. 60 | * This will eliminate any resource usage from the {@link Query} 61 | * associated with the pattern. 62 | *

63 | * 64 | * This can not be undone. 65 | * 66 | */ 67 | public native void disable(); 68 | 69 | @Generated 70 | public int getEndOffset() { 71 | return startOffset + value.length(); 72 | } 73 | 74 | @Override 75 | @Generated 76 | public boolean equals(Object o) { 77 | if (this == o) return true; 78 | if (o == null || getClass() != o.getClass()) return false; 79 | Pattern pattern = (Pattern) o; 80 | return Objects.equals(query, pattern.query) && index == pattern.index; 81 | } 82 | 83 | @Override 84 | @Generated 85 | public int hashCode() { 86 | return Objects.hash(query, index); 87 | } 88 | 89 | @Override 90 | @Generated 91 | public String toString() { 92 | return value; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Point.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Generated; 7 | import lombok.Getter; 8 | import lombok.experimental.Accessors; 9 | import lombok.experimental.FieldDefaults; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Objects; 13 | 14 | /** 15 | * Represents a two-dimensional point with row and column coordinates. 16 | * Points are an alternative to byte ranges, and as such are used to 17 | * represent more human-friendly positions of {@link Tree} nodes within 18 | * source code. Although {@link Node} positions within files should never 19 | * be negative, instances of this class can be created with negative row 20 | * and column values for other purposes, such as representing relative 21 | * positions within a file or snippet. 22 | *

23 | * Points are immutable, and operations on them will either yield a new 24 | * instance, or existing instances under certain conditions. For example, 25 | * adding the origin point to any other point will return the same instance. 26 | * 27 | * @since 1.0.0 28 | * @author Ozren Dabić 29 | */ 30 | @Getter 31 | @AllArgsConstructor 32 | @EqualsAndHashCode 33 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 34 | public class Point implements Comparable { 35 | 36 | @Getter(lazy = true) 37 | @Accessors(fluent = true, makeFinal = true) 38 | private static final Point ORIGIN = new Point(0, 0); 39 | 40 | int row; 41 | int column; 42 | 43 | /** 44 | * Returns a string representation of this point in the format: 45 | *

{@code
 46 |      *      row:column
 47 |      * }
48 | * 49 | * @return a string representation of this point 50 | */ 51 | @Override 52 | @Generated 53 | public String toString() { 54 | return row + ":" + column; 55 | } 56 | 57 | /** 58 | * Checks if this point represents the origin, 59 | * which is when both the row and the column are zero. 60 | * In byte range terms, this point also corresponds to zero. 61 | * 62 | * @return {@code true} if this is an origin point, 63 | * {@code false} otherwise 64 | */ 65 | public boolean isOrigin() { 66 | return equals(ORIGIN()); 67 | } 68 | 69 | /** 70 | * Compares this point with another point for positional order. 71 | * Points are compared by their row values first, and if those 72 | * are equal they are then compared by their column values. 73 | * 74 | * @param other the object to be compared 75 | * @return the row comparison result, or the column comparison result if the rows are equal 76 | * @throws NullPointerException if the other point is null 77 | * @since 1.5.1 78 | */ 79 | @Override 80 | public int compareTo(@NotNull Point other) { 81 | Objects.requireNonNull(other, "Other point must not be null!"); 82 | int compare = Integer.compare(row, other.row); 83 | return compare != 0 ? compare : Integer.compare(column, other.column); 84 | } 85 | 86 | /** 87 | * Adds another point to this point, 88 | * resulting in a new point with coordinates 89 | * equal to the sum of the coordinates 90 | * of this point and the other point. 91 | * 92 | * @param other the point to be added to this point 93 | * @return the resulting point 94 | * @throws NullPointerException if {@code other} is null 95 | * @since 1.5.1 96 | */ 97 | public Point add(@NotNull Point other) { 98 | Objects.requireNonNull(other, "Other point must not be null!"); 99 | if (isOrigin()) return other; 100 | if (other.isOrigin()) return this; 101 | return add(other.row, other.column); 102 | } 103 | 104 | /** 105 | * Subtracts another point from this point, resulting 106 | * in a new point with coordinates equal to the difference 107 | * between the coordinates of this point and the other point. 108 | * 109 | * @param other the point to be subtracted from this point 110 | * @return the resulting point 111 | * @throws NullPointerException if {@code other} is null 112 | * @since 1.5.1 113 | */ 114 | public Point subtract(@NotNull Point other) { 115 | Objects.requireNonNull(other, "Other point must not be null!"); 116 | if (other.isOrigin()) return this; 117 | if (equals(other)) return ORIGIN(); 118 | return add(-other.row, -other.column); 119 | } 120 | 121 | private Point add(int row, int column) { 122 | return new Point(this.row + row, this.column + column); 123 | } 124 | 125 | /** 126 | * Multiplies the coordinates of this point by a scalar value, 127 | * resulting in a new point with scaled coordinates. 128 | * 129 | * @param value the scalar value by which to multiply the coordinates of this point 130 | * @return the resulting point 131 | * @since 1.5.1 132 | */ 133 | public Point multiply(int value) { 134 | switch (value) { 135 | case 0: return ORIGIN(); 136 | case 1: return this; 137 | default: return new Point(row * value, column * value); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Predicate.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.experimental.FieldDefaults; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * A specialised symbolic expression (s-expression) that can 12 | * appear anywhere within a {@link Pattern}, instances of this 13 | * class represent predicates within a pattern. They consist of 14 | * arbitrary metadata and conditions which dictate the matching 15 | * behavior of a {@link Query}. 16 | *

17 | * Note that the actual matching behavior is currently not 18 | * implemented in this binding. In spite of this, one can 19 | * still use this class to apply matching logic manually. 20 | * 21 | * @since 1.12.0 22 | * @author Ozren Dabić 23 | * @see Pattern 24 | * @see Query 25 | */ 26 | @Getter 27 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 28 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 29 | public class Predicate { 30 | 31 | Pattern pattern; 32 | List steps; 33 | 34 | @SuppressWarnings("unused") 35 | Predicate(Pattern pattern, Step[] steps) { 36 | this(pattern, List.of(steps)); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | StringBuilder builder = new StringBuilder(); 42 | for (int i = 0; i < steps.size() - 1; i++) { 43 | Step step = steps.get(i); 44 | String value = step.getValue(); 45 | if (i == 0) { 46 | builder.append(value); 47 | continue; 48 | } 49 | builder.append(" "); 50 | switch (step.getType()) { 51 | case CAPTURE: 52 | builder.append("@").append(value); 53 | break; 54 | case STRING: 55 | builder.append('"').append(value).append('"'); 56 | break; 57 | default: 58 | } 59 | } 60 | 61 | return "(#" + builder + ")"; 62 | } 63 | 64 | /** 65 | * Represents a single step in a {@link Predicate}. 66 | * Each step is characterized by a {@link Type Type}, 67 | * and an optional value. 68 | * 69 | * @since 1.12.0 70 | * @author Ozren Dabić 71 | */ 72 | @Getter 73 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 74 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 75 | public static class Step { 76 | 77 | Type type; 78 | String value; 79 | 80 | public Step(int type, String value) { 81 | this(Type.get(type), value); 82 | } 83 | 84 | /** 85 | * Represents the type of {@link Step Step}. 86 | * 87 | * @since 1.12.0 88 | * @author Ozren Dabić 89 | */ 90 | public enum Type { 91 | 92 | /** 93 | * Steps with this type are sentinels that 94 | * represent the end of an individual predicate. 95 | * Only one such step is allowed per predicate. 96 | */ 97 | DONE, 98 | 99 | /** 100 | * Steps with this type represent names of captures. 101 | */ 102 | CAPTURE, 103 | 104 | /** 105 | * Steps with this type represent literal strings. 106 | */ 107 | STRING; 108 | 109 | private static final Type[] VALUES = values(); 110 | 111 | private static Type get(int ordinal) { 112 | return VALUES[ordinal]; 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Quantifier.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | /** 4 | * Represents the {@link Capture} quantifier in a {@link Pattern}, 5 | * i.e. the number of nodes that a capture should contain. Within a 6 | * query, a capture can have different quantifiers for each pattern. 7 | * 8 | * @since 1.12.0 9 | * @author Ozren Dabić 10 | */ 11 | public enum Quantifier { 12 | 13 | /** 14 | * The capture will not match any nodes, 15 | * as said capture is not present in a 16 | * specific pattern. 17 | */ 18 | ZERO, 19 | /** 20 | * The capture will match at most one node. 21 | * Example: 22 | *

{@code
23 |      * ((_)? @capture)
24 |      * }
25 | */ 26 | ZERO_OR_ONE, 27 | /** 28 | * The capture will match any number of nodes. 29 | * Example: 30 | *
{@code
31 |      * ((_)* @capture)
32 |      * }
33 | */ 34 | ZERO_OR_MORE, 35 | /** 36 | * The capture will match exactly one node. 37 | * Example: 38 | *
{@code
39 |      * ((_) @capture)
40 |      * }
41 | */ 42 | ONE, 43 | /** 44 | * The capture will match at least one node. 45 | * Example: 46 | *
{@code
47 |      * ((_)+ @capture)
48 |      * }
49 | */ 50 | ONE_OR_MORE 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/QueryCursor.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Generated; 5 | import lombok.Getter; 6 | import lombok.experimental.FieldDefaults; 7 | import lombok.experimental.NonFinal; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Iterator; 11 | import java.util.NoSuchElementException; 12 | 13 | /** 14 | * Cursor used for executing queries, carrying the state needed to process them. 15 | *

16 | * The query cursor should not be shared between threads, but can be reused for many query executions. 17 | * 18 | * @since 1.0.0 19 | * @author Ozren Dabić 20 | */ 21 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 22 | public class QueryCursor extends External implements Iterable { 23 | 24 | Node node; 25 | Query query; 26 | 27 | @Getter 28 | @NonFinal 29 | boolean executed = false; 30 | 31 | @SuppressWarnings("unused") 32 | QueryCursor(long pointer, @NotNull Node node, @NotNull Query query) { 33 | super(pointer); 34 | this.node = node; 35 | this.query = query; 36 | } 37 | 38 | @Override 39 | protected native void delete(); 40 | 41 | /** 42 | * Start running a query against a node. 43 | */ 44 | public native void execute(); 45 | 46 | /** 47 | * Set the range of bytes positions in which the query will be executed. 48 | * 49 | * @param startByte The start byte of the query range 50 | * @param endByte The end byte of the query range 51 | * @throws ch.usi.si.seart.treesitter.exception.ByteOffsetOutOfBoundsException 52 | * if either argument is outside the queried node's byte range 53 | * @throws IllegalArgumentException if: 54 | *

    55 | *
  • {@code startByte} < 0
  • 56 | *
  • {@code endByte} < 0
  • 57 | *
  • {@code startByte} > {@code endByte}
  • 58 | *
59 | * @since 1.9.0 60 | */ 61 | public native void setRange(int startByte, int endByte); 62 | 63 | /** 64 | * Set the range of row-column coordinates in which the query will be executed. 65 | * 66 | * @param startPoint The start point of the query range 67 | * @param endPoint The end point of the query range 68 | * @throws NullPointerException if either argument is null 69 | * @throws IllegalArgumentException if any point coordinates are negative, 70 | * or if {@code startPoint} is a position that comes after {@code endPoint} 71 | * @throws ch.usi.si.seart.treesitter.exception.PointOutOfBoundsException 72 | * if any of the arguments is outside the queried node's position range 73 | * @since 1.9.0 74 | */ 75 | public native void setRange(@NotNull Point startPoint, @NotNull Point endPoint); 76 | 77 | /** 78 | * Advance to the next match of the currently running query. 79 | * 80 | * @return A match if there is one, null otherwise 81 | * @throws IllegalStateException if the query was not executed beforehand 82 | * @see #execute() 83 | */ 84 | public native QueryMatch nextMatch(); 85 | 86 | /** 87 | * Returns an iterator over the query matches, 88 | * starting from the first {@link QueryMatch}. 89 | * Implicitly calls {@link #execute()}. 90 | * 91 | * @return an iterator over query cursor matches 92 | */ 93 | @Override 94 | public @NotNull Iterator iterator() { 95 | execute(); 96 | return new Iterator<>() { 97 | 98 | private QueryMatch current = nextMatch(); 99 | 100 | @Override 101 | public boolean hasNext() { 102 | return current != null; 103 | } 104 | 105 | @Override 106 | public QueryMatch next() { 107 | if (!hasNext()) throw new NoSuchElementException(); 108 | QueryMatch match = current; 109 | current = nextMatch(); 110 | return match; 111 | } 112 | }; 113 | } 114 | 115 | @Override 116 | @Generated 117 | public String toString() { 118 | return String.format("QueryCursor(node: %s, query: %s)", node, query); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/QueryMatch.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Generated; 5 | import lombok.Getter; 6 | import lombok.experimental.FieldDefaults; 7 | import org.apache.commons.collections4.MultiValuedMap; 8 | import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; 9 | import org.apache.commons.collections4.multimap.UnmodifiableMultiValuedMap; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Collection; 13 | import java.util.Map; 14 | import java.util.stream.Collectors; 15 | import java.util.stream.Stream; 16 | 17 | /** 18 | * Represents a collection of captured nodes, matched with a single query {@link Pattern}. 19 | * 20 | * @since 1.0.0 21 | * @author Ozren Dabić 22 | */ 23 | @Getter 24 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 25 | public class QueryMatch { 26 | 27 | int id; 28 | Pattern pattern; 29 | MultiValuedMap captures; 30 | 31 | @SuppressWarnings("unused") 32 | QueryMatch(int id, @NotNull Pattern pattern, @NotNull Map.Entry[] captures) { 33 | this.id = id; 34 | this.pattern = pattern; 35 | this.captures = UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap( 36 | Stream.of(captures).collect( 37 | ArrayListValuedHashMap::new, 38 | (map, entry) -> map.put(entry.getKey(), entry.getValue()), 39 | MultiValuedMap::putAll 40 | ) 41 | ); 42 | } 43 | 44 | /** 45 | * Retrieves a mapping between the captures and captured nodes in this match. 46 | * 47 | * @return a map of captures and the nodes they captured 48 | * @since 1.7.0 49 | */ 50 | public Map> getCaptures() { 51 | return captures.asMap(); 52 | } 53 | 54 | /** 55 | * Retrieves all the captured nodes from this match. 56 | * 57 | * @return a collection of captured nodes 58 | * @since 1.7.0 59 | */ 60 | public Collection getNodes() { 61 | return captures.values(); 62 | } 63 | 64 | /** 65 | * Retrieves all nodes captured under a specific capture. 66 | * 67 | * @param capture the query capture 68 | * @return a collection of captured nodes 69 | * @since 1.7.0 70 | */ 71 | public Collection getNodes(Capture capture) { 72 | return captures.get(capture); 73 | } 74 | 75 | /** 76 | * Retrieves all nodes captured under a specific capture with the given name. 77 | * 78 | * @param name the name of the query capture 79 | * @return a collection of captured nodes 80 | * @since 1.7.0 81 | */ 82 | public Collection getNodes(String name) { 83 | return captures.entries().stream() 84 | .filter(entry -> { 85 | Capture capture = entry.getKey(); 86 | return name.equals(capture.getName()); 87 | }) 88 | .map(Map.Entry::getValue) 89 | .collect(Collectors.toUnmodifiableList()); 90 | } 91 | 92 | @Override 93 | @Generated 94 | public String toString() { 95 | String joined = captures.entries().stream() 96 | .map(entry -> entry.getKey() + "=" + entry.getValue()) 97 | .collect(Collectors.joining(", ", "{", "}")); 98 | return String.format("QueryMatch(id: %d, pattern: '%s', captures: [%s])", id, pattern, joined); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Range.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Generated; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.experimental.FieldDefaults; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Objects; 13 | 14 | /** 15 | * Represents the portions of source code taken up by a node within a file or snippet. 16 | * Each range consists of a: 17 | *
    18 | *
  • start byte offset
  • 19 | *
  • end byte offset
  • 20 | *
  • start point
  • 21 | *
  • end point
  • 22 | *
23 | * 24 | * @since 1.0.0 25 | * @author Ozren Dabić 26 | */ 27 | @Getter 28 | @EqualsAndHashCode 29 | @AllArgsConstructor(access = AccessLevel.PROTECTED) 30 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 31 | public class Range { 32 | 33 | int startByte; 34 | int endByte; 35 | Point startPoint; 36 | Point endPoint; 37 | 38 | /** 39 | * Obtain a new {@link Builder} instance for constructing a range. 40 | * 41 | * @return a new range builder 42 | */ 43 | public static Builder builder() { 44 | return new Builder(); 45 | } 46 | 47 | /** 48 | * Obtain a new {@link Builder} initialized with the current range's attributes. 49 | * 50 | * @return a new range builder 51 | * @since 1.12.0 52 | */ 53 | public Builder toBuilder() { 54 | return builder().startByte(startByte) 55 | .endByte(endByte) 56 | .startPoint(startPoint) 57 | .endPoint(endPoint); 58 | } 59 | 60 | /** 61 | * Facilitates the construction of {@link Range} instances. 62 | * It allows for the step-by-step creation of these objects 63 | * by providing methods for setting individual attributes. 64 | * Input validations are performed at each build step. 65 | * 66 | * @since 1.12.0 67 | */ 68 | @FieldDefaults(level = AccessLevel.PRIVATE) 69 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 70 | public static final class Builder { 71 | 72 | int startByte = 0; 73 | int endByte = Integer.MAX_VALUE; 74 | Point startPoint = Point.ORIGIN(); 75 | Point endPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); 76 | 77 | /** 78 | * Sets the start byte offset for this range. 79 | * 80 | * @param value byte value offset 81 | * @return this builder 82 | * @throws IllegalArgumentException if the value is negative 83 | */ 84 | public Builder startByte(int value) { 85 | if (value < 0) throw new IllegalArgumentException("Start byte cannot be negative"); 86 | startByte = value; 87 | return this; 88 | } 89 | 90 | /** 91 | * Sets the end byte offset for this range. 92 | * 93 | * @param value byte value offset 94 | * @return this builder 95 | * @throws IllegalArgumentException if the value is negative 96 | */ 97 | public Builder endByte(int value) { 98 | if (value < 0) throw new IllegalArgumentException("End byte cannot be negative"); 99 | endByte = value; 100 | return this; 101 | } 102 | 103 | /** 104 | * Sets the start point for this range. 105 | * 106 | * @param point the point 107 | * @return this builder 108 | * @throws NullPointerException if the point is {@code null} 109 | * @throws IllegalArgumentException if the point has negative coordinates 110 | */ 111 | public Builder startPoint(@NotNull Point point) { 112 | Objects.requireNonNull(point, "Start point cannot be null"); 113 | if (point.getRow() < 0 || point.getColumn() < 0) 114 | throw new IllegalArgumentException("Start point cannot have negative coordinates"); 115 | startPoint = point; 116 | return this; 117 | } 118 | 119 | /** 120 | * Sets the end point for this range. 121 | * 122 | * @param point the point 123 | * @return this builder 124 | * @throws NullPointerException if the point is {@code null} 125 | * @throws IllegalArgumentException if the point has negative coordinates 126 | */ 127 | public Builder endPoint(@NotNull Point point) { 128 | Objects.requireNonNull(point, "End point cannot be null"); 129 | if (point.getRow() < 0 || point.getColumn() < 0) 130 | throw new IllegalArgumentException("End point cannot have negative coordinates"); 131 | endPoint = point; 132 | return this; 133 | } 134 | 135 | /** 136 | * Builds a new range instance with the 137 | * attributes specified in this builder. 138 | * 139 | * @return a new range instance 140 | * @throws IllegalArgumentException 141 | * if the start byte is greater than the end byte, 142 | * or if the start point is greater than the end point 143 | * @see Point#compareTo(Point) 144 | */ 145 | public Range build() { 146 | if (Integer.compareUnsigned(startByte, endByte) > 0) 147 | throw new IllegalArgumentException("Start byte cannot be greater than end byte"); 148 | if (startPoint.compareTo(endPoint) > 0) 149 | throw new IllegalArgumentException("Start point cannot be greater than end point"); 150 | return new Range(startByte, endByte, startPoint, endPoint); 151 | } 152 | } 153 | 154 | Range(@NotNull Node node) { 155 | this(node.getStartByte(), node.getEndByte(), node.getStartPoint(), node.getEndPoint()); 156 | } 157 | 158 | /** 159 | * Returns a string representation of this range in the format: 160 | *
{@code
161 |      *      [startPoint] - [endPoint]
162 |      * }
163 | * 164 | * @return A string representation of this range 165 | */ 166 | @Override 167 | @Generated 168 | public String toString() { 169 | return String.format("[%s] - [%s]", startPoint, endPoint); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Symbol.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Generated; 6 | import lombok.Getter; 7 | import lombok.experimental.FieldDefaults; 8 | 9 | import java.util.Objects; 10 | 11 | /** 12 | * Represents a symbol in an abstract syntax {@link Tree}. 13 | * Symbols are used to identify nodes in the AST. 14 | * Each symbol has an associated ID, {@link Type Type}, and name. 15 | * 16 | * @author Ozren Dabić 17 | * @since 1.6.0 18 | */ 19 | @Getter 20 | @AllArgsConstructor(access = AccessLevel.PACKAGE) 21 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 22 | public class Symbol { 23 | 24 | int id; 25 | Type type; 26 | String name; 27 | 28 | @SuppressWarnings("unused") 29 | Symbol(int id, int ordinal, String name) { 30 | this(id, Type.get(ordinal), name); 31 | } 32 | 33 | @Override 34 | @Generated 35 | public String toString() { 36 | return String.format("Symbol(id: %d, type: %s, name: '%s')", id, type, name); 37 | } 38 | 39 | @Override 40 | @Generated 41 | public boolean equals(Object obj) { 42 | if (this == obj) return true; 43 | if (obj == null || getClass() != obj.getClass()) return false; 44 | Symbol symbol = (Symbol) obj; 45 | return id == symbol.id && type == symbol.type && Objects.equals(name, symbol.name); 46 | } 47 | 48 | @Override 49 | @Generated 50 | public int hashCode() { 51 | return Objects.hash(id, type, name); 52 | } 53 | 54 | /** 55 | * Enumeration representing the possible types of symbols. 56 | * This includes: 57 | *
    58 | *
  • Named nodes ({@link #REGULAR})
  • 59 | *
  • Anonymous nodes ({@link #ANONYMOUS})
  • 60 | *
  • Hidden nodes ({@link #AUXILIARY})
  • 61 | *
62 | * 63 | * @author Ozren Dabić 64 | * @since 1.6.0 65 | */ 66 | public enum Type { 67 | 68 | REGULAR, 69 | ANONYMOUS, 70 | AUXILIARY; 71 | 72 | private static final Type[] VALUES = values(); 73 | 74 | private static Type get(int ordinal) { 75 | return VALUES[ordinal]; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/Tree.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Getter; 5 | import lombok.experimental.FieldDefaults; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.nio.charset.Charset; 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.Arrays; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | 14 | /** 15 | * A Tree represents the syntax tree of an entire source code file. 16 | * It contains {@link Node} instances that indicate the structure of the source code. 17 | * 18 | * @since 1.0.0 19 | * @author Tommy MacWilliam 20 | * @author Ozren Dabić 21 | */ 22 | @Getter 23 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 24 | public class Tree extends External implements Iterable, Cloneable { 25 | 26 | private static final Charset CHARSET = StandardCharsets.UTF_16LE; 27 | 28 | Language language; 29 | String source; 30 | 31 | Tree(long pointer, @NotNull Language language, @NotNull String source) { 32 | super(pointer); 33 | this.language = language; 34 | this.source = source; 35 | } 36 | 37 | @Override 38 | protected native void delete(); 39 | 40 | /** 41 | * Edit the syntax tree to keep it in sync with source code that has been edited. 42 | * 43 | * @param edit changes made to the source code in terms of 44 | * both byte offsets and row/column coordinates 45 | */ 46 | public native void edit(@NotNull InputEdit edit); 47 | 48 | /** 49 | * Compare this old edited syntax tree to a new syntax tree representing the same document, 50 | * returning a sequence of {@link Range} instances, their coordinates corresponding to changes 51 | * made to the syntactic structure. 52 | *

53 | * For this to work correctly, this syntax tree must have been edited such that its 54 | * ranges match up to the new tree. Generally, you'll want to call this method right 55 | * after calling one of the {@link Parser} methods. 56 | * 57 | * @param other the tree to compare with 58 | * @return a list of ranges that have been changed 59 | * @throws NullPointerException if {@code other} is null 60 | * @since 1.12.0 61 | * @see Parser#parse(String, Tree) 62 | * @see Parser#parse(java.nio.file.Path, Tree) Parser.parse(Path, Tree) 63 | */ 64 | public native List getChangedRanges(@NotNull Tree other); 65 | 66 | /** 67 | * Get the topmost {@link Node} of the syntax tree. 68 | * 69 | * @return the root node of the syntax tree 70 | */ 71 | public native Node getRootNode(); 72 | 73 | /** 74 | * Returns an iterator over the entire syntax tree, starting from the root. 75 | * 76 | * @return an iterator over syntax tree nodes 77 | * @see Node#iterator() 78 | */ 79 | @Override 80 | public @NotNull Iterator iterator() { 81 | return getRootNode().iterator(); 82 | } 83 | 84 | /** 85 | * Clone this tree, creating a separate, independent instance. 86 | * 87 | * @return a clone of this instance 88 | * @since 1.6.0 89 | */ 90 | @Override 91 | public native Tree clone(); 92 | 93 | String getSource(int startByte, int endByte) { 94 | byte[] bytes = source.getBytes(CHARSET); 95 | byte[] copy = Arrays.copyOfRange(bytes, startByte * 2, endByte * 2); 96 | return new String(copy, CHARSET); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/TreeCursorNode.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Generated; 6 | import lombok.Getter; 7 | import lombok.experimental.FieldDefaults; 8 | 9 | /** 10 | * Special type of node returned during tree traversals with {@link TreeCursor}. 11 | * 12 | * @since 1.0.0 13 | * @author Tommy MacWilliam 14 | * @author Ozren Dabić 15 | */ 16 | @Getter 17 | @AllArgsConstructor(access = AccessLevel.PACKAGE) 18 | @FieldDefaults(level = AccessLevel.PRIVATE) 19 | public class TreeCursorNode { 20 | 21 | String name; 22 | String type; 23 | String content; 24 | int startByte; 25 | int endByte; 26 | Point startPoint; 27 | Point endPoint; 28 | boolean isNamed; 29 | 30 | @SuppressWarnings("unused") 31 | TreeCursorNode(String name, Node node) { 32 | this( 33 | name, 34 | node.getType(), 35 | node.getContent(), 36 | node.getStartByte(), 37 | node.getEndByte(), 38 | node.getStartPoint(), 39 | node.getEndPoint(), 40 | node.isNamed() 41 | ); 42 | } 43 | 44 | @Override 45 | @Generated 46 | public String toString() { 47 | String field = name != null ? name + ": " : ""; 48 | return String.format("%s%s [%s] - [%s]", field, type, startPoint, endPoint); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/error/ABIVersionError.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.error; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * An error raised whenever there is an ABI version mismatch. 7 | *

8 | * These usually occur as a result of a language being generated 9 | * with an incompatible version of the Tree-sitter CLI. 10 | * 11 | * @since 1.0.0 12 | * @author Ozren Dabić 13 | */ 14 | @StandardException 15 | public class ABIVersionError extends LinkageError { 16 | 17 | private static final String TEMPLATE = "Incompatible language version: %d"; 18 | 19 | public ABIVersionError(int version) { 20 | super(String.format(TEMPLATE, version)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/error/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides {@link java.lang.Error} subclasses related to tree-sitter. 3 | * 4 | * @since 1.0.0 5 | */ 6 | package ch.usi.si.seart.treesitter.error; 7 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/ByteOffsetOutOfBoundsException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception; 2 | 3 | /** 4 | * Thrown to indicate that a specified byte offset is outside a node's byte range. 5 | * 6 | * @since 1.7.0 7 | * @author Ozren Dabić 8 | */ 9 | public class ByteOffsetOutOfBoundsException extends NodeRangeBoundaryException { 10 | 11 | public ByteOffsetOutOfBoundsException(int offset) { 12 | super("Byte offset outside node range: " + offset); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/NodeRangeBoundaryException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.experimental.StandardException; 5 | 6 | /** 7 | * The base exception type for all exceptions involving invalid node positional offsets. 8 | * 9 | * @since 1.7.0 10 | * @author Ozren Dabić 11 | */ 12 | @StandardException(access = AccessLevel.PROTECTED) 13 | abstract class NodeRangeBoundaryException extends TreeSitterException { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/PointOutOfBoundsException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception; 2 | 3 | import ch.usi.si.seart.treesitter.Point; 4 | 5 | /** 6 | * Thrown to indicate that a specified point is outside a node's point range. 7 | * 8 | * @since 1.7.0 9 | * @author Ozren Dabić 10 | */ 11 | public class PointOutOfBoundsException extends NodeRangeBoundaryException { 12 | 13 | public PointOutOfBoundsException(Point point) { 14 | super("Point outside node range: " + point); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/TreeSitterException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * The base exception type for all tree-sitter exceptions. 7 | * 8 | * @since 1.0.0 9 | * @author Ozren Dabić 10 | */ 11 | @StandardException 12 | public class TreeSitterException extends RuntimeException { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides {@link java.lang.Exception} subclasses related to tree-sitter. 3 | * 4 | * @since 1.0.0 5 | */ 6 | package ch.usi.si.seart.treesitter.exception; 7 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/parser/IncompatibleLanguageException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.parser; 2 | 3 | import ch.usi.si.seart.treesitter.Language; 4 | import lombok.experimental.StandardException; 5 | 6 | /** 7 | * Thrown when attempts to set the parser language result in failure. 8 | * 9 | * @since 1.6.0 10 | * @author Ozren Dabić 11 | */ 12 | @StandardException 13 | public class IncompatibleLanguageException extends ParserException { 14 | 15 | private static final String TEMPLATE = "Could not assign language to parser: %s"; 16 | 17 | @SuppressWarnings("unused") 18 | public IncompatibleLanguageException(Language language) { 19 | super(String.format(TEMPLATE, language)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/parser/ParserException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.parser; 2 | 3 | import ch.usi.si.seart.treesitter.exception.TreeSitterException; 4 | import lombok.AccessLevel; 5 | import lombok.experimental.StandardException; 6 | 7 | /** 8 | * The base exception type for all exceptions related to tree-sitter parsers. 9 | * 10 | * @since 1.6.0 11 | * @author Ozren Dabić 12 | */ 13 | @StandardException(access = AccessLevel.PROTECTED) 14 | public abstract class ParserException extends TreeSitterException { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/parser/ParsingException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.parser; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * Thrown when there is an error during parsing. 7 | * 8 | * @since 1.6.0 9 | * @author Ozren Dabić 10 | */ 11 | @StandardException 12 | public class ParsingException extends ParserException { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/parser/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides parser-specific exception class hierarchy. 3 | * 4 | * @since 1.6.0 5 | */ 6 | package ch.usi.si.seart.treesitter.exception.parser; 7 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/QueryCaptureException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.query; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * Thrown when a query string has incorrect captures. 7 | * 8 | * @since 1.0.0 9 | * @author Ozren Dabić 10 | */ 11 | @SuppressWarnings("unused") 12 | @StandardException 13 | public class QueryCaptureException extends QueryException { 14 | 15 | public QueryCaptureException(int offset) { 16 | super("Bad capture at offset " + offset); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/QueryException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.query; 2 | 3 | import ch.usi.si.seart.treesitter.exception.TreeSitterException; 4 | import lombok.experimental.StandardException; 5 | 6 | /** 7 | * The base exception type for all exceptions related to tree-sitter queries. 8 | * 9 | * @since 1.0.0 10 | * @author Ozren Dabić 11 | */ 12 | @StandardException 13 | public abstract class QueryException extends TreeSitterException { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/QueryFieldException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.query; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * Thrown when the targeted node does not have a field with the specified name. 7 | * 8 | * @since 1.0.0 9 | * @author Ozren Dabić 10 | */ 11 | @SuppressWarnings("unused") 12 | @StandardException 13 | public class QueryFieldException extends QueryException { 14 | 15 | public QueryFieldException(int offset) { 16 | super("Bad field name at offset " + offset); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/QueryNodeTypeException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.query; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * Thrown when a specified node type does not exist in the language grammar. 7 | * 8 | * @since 1.0.0 9 | * @author Ozren Dabić 10 | */ 11 | @SuppressWarnings("unused") 12 | @StandardException 13 | public class QueryNodeTypeException extends QueryException { 14 | 15 | public QueryNodeTypeException(int offset) { 16 | super("Bad node name at offset " + offset); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/QueryStructureException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.query; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * Thrown when the node structure in the query does not adhere to the grammar. 7 | * 8 | * @since 1.0.0 9 | * @author Ozren Dabić 10 | */ 11 | @SuppressWarnings("unused") 12 | @StandardException 13 | public class QueryStructureException extends QueryException { 14 | 15 | public QueryStructureException(int offset) { 16 | super("Bad pattern structure at offset " + offset); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/QuerySyntaxException.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.exception.query; 2 | 3 | import lombok.experimental.StandardException; 4 | 5 | /** 6 | * Thrown when a query has incorrect syntax. 7 | * 8 | * @since 1.0.0 9 | * @author Ozren Dabić 10 | */ 11 | @SuppressWarnings("unused") 12 | @StandardException 13 | public class QuerySyntaxException extends QueryException { 14 | 15 | public QuerySyntaxException(int offset) { 16 | super("Bad syntax at offset " + offset); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/exception/query/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides query-specific exception class hierarchy. 3 | * 4 | * @since 1.0.0 5 | */ 6 | package ch.usi.si.seart.treesitter.exception.query; 7 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/function/IOExceptionThrowingConsumer.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.function; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.io.IOException; 6 | import java.io.UncheckedIOException; 7 | import java.util.Objects; 8 | import java.util.function.Consumer; 9 | 10 | /** 11 | * Represents an operation that accepts a single input argument and returns no result. 12 | * Can potentially throw an {@link IOException} while doing so. 13 | * 14 | * @since 1.2.0 15 | * @see java.util.function.Consumer Consumer 16 | * @param the type of the input to the operation 17 | * @author Ozren Dabić 18 | */ 19 | @FunctionalInterface 20 | public interface IOExceptionThrowingConsumer { 21 | 22 | /** 23 | * Performs this operation on the given argument. 24 | * 25 | * @param t the input argument 26 | * @throws IOException if an I/O error occurs 27 | */ 28 | void accept(T t) throws IOException; 29 | 30 | /** 31 | * Returns a wrapped {@code Consumer} that performs this operation. 32 | * If performing said operation results in an {@code IOException}, 33 | * it is re-thrown as its unchecked counterpart: {@code UncheckedIOException}. 34 | * 35 | * @param consumer the operation to wrap 36 | * @param the type of the input to the operation 37 | * @return and identical {@code Consumer} that throws 38 | * {@code UncheckedIOException} instead 39 | * @throws NullPointerException if {@code consumer} is null 40 | */ 41 | static Consumer toUnchecked(@NotNull IOExceptionThrowingConsumer consumer) { 42 | Objects.requireNonNull(consumer, "Throwing consumer must not be null!"); 43 | return t -> { 44 | try { 45 | consumer.accept(t); 46 | } catch (IOException ex) { 47 | throw new UncheckedIOException(ex); 48 | } 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/function/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides functional interface utilities. 3 | * 4 | * @since 1.2.0 5 | */ 6 | package ch.usi.si.seart.treesitter.function; 7 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides all the core classes for interfacing with the tree-sitter API. 3 | * 4 | * @since 1.0.0 5 | */ 6 | package ch.usi.si.seart.treesitter; 7 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/DotGraphPrinter.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.Tree; 4 | import lombok.AccessLevel; 5 | import lombok.experimental.FieldDefaults; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.UncheckedIOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.util.Objects; 14 | 15 | /** 16 | * Printer used for generating DOT graph representations of trees. 17 | * Unlike its sister classes, it does not rely on an iterative approach, 18 | * relying instead on the internal {@code tree-sitter} API. 19 | * 20 | * @since 1.2.0 21 | * @author Ozren Dabić 22 | */ 23 | @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) 24 | public class DotGraphPrinter implements TreePrinter { 25 | 26 | @SuppressWarnings({"FieldCanBeLocal", "unused"}) 27 | Tree tree; 28 | 29 | public DotGraphPrinter(@NotNull Tree tree) { 30 | this.tree = Objects.requireNonNull(tree, "Tree must not be null!"); 31 | } 32 | 33 | /** 34 | * Generates a DOT graph of the tree. 35 | * 36 | * @return A DOT graph string of the tree 37 | */ 38 | @Override 39 | public String print() { 40 | try { 41 | File file = export(); 42 | Path path = file.toPath(); 43 | String contents = Files.readString(path); 44 | Files.delete(path); 45 | return contents; 46 | } catch (IOException ex) { 47 | throw new UncheckedIOException(ex); 48 | } 49 | } 50 | 51 | /** 52 | * Generates a DOT graph of the tree, 53 | * writing it directly to a file. 54 | * 55 | * @return A file containing the DOT graph of the tree 56 | * @throws IOException if an I/O error occurs 57 | */ 58 | @Override 59 | public File export() throws IOException { 60 | File file = Files.createTempFile("ts-export-", ".dot").toFile(); 61 | write(file); 62 | return file; 63 | } 64 | 65 | private native void write(File file) throws IOException; 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/IterativeTreePrinter.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.TreeCursor; 4 | import ch.usi.si.seart.treesitter.function.IOExceptionThrowingConsumer; 5 | import lombok.AccessLevel; 6 | import lombok.experimental.FieldDefaults; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.io.BufferedWriter; 10 | import java.io.File; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.io.UncheckedIOException; 14 | import java.io.Writer; 15 | import java.nio.file.Files; 16 | import java.util.Objects; 17 | import java.util.function.Consumer; 18 | 19 | @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) 20 | abstract class IterativeTreePrinter implements TreePrinter { 21 | 22 | TreeCursor cursor; 23 | 24 | protected IterativeTreePrinter(@NotNull TreeCursor cursor) { 25 | this.cursor = Objects.requireNonNull(cursor, "Cursor must not be null!"); 26 | } 27 | 28 | /** 29 | * Generates a string representation of the Abstract Syntax Tree (AST), 30 | * starting from the node currently pointed to by the cursor. 31 | * 32 | * @return A string representation of the tree 33 | */ 34 | public final String print() { 35 | StringBuilder stringBuilder = new StringBuilder(getPreamble()); 36 | write(stringBuilder::append); 37 | return stringBuilder.toString(); 38 | } 39 | 40 | /** 41 | * Generates a string representation of the Abstract Syntax Tree (AST), 42 | * starting from the node currently pointed to by the cursor, 43 | * while writing outputs directly to a file. 44 | * 45 | * @return A file containing the string of the tree 46 | * @throws IOException if an I/O error occurs 47 | */ 48 | public final File export() throws IOException { 49 | File file = Files.createTempFile("ts-export-", getFileExtension()).toFile(); 50 | try (Writer writer = new BufferedWriter(new FileWriter(file))) { 51 | writer.append(getPreamble()); 52 | Consumer appender = IOExceptionThrowingConsumer.toUnchecked(writer::append); 53 | write(appender); 54 | return file; 55 | } catch (UncheckedIOException ex) { 56 | throw ex.getCause(); 57 | } 58 | } 59 | 60 | protected String getPreamble() { 61 | return ""; 62 | } 63 | 64 | protected String getFileExtension() { 65 | return ""; 66 | } 67 | 68 | protected abstract void write(Consumer appender); 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/SymbolicExpressionPrinter.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.TreeCursor; 4 | import ch.usi.si.seart.treesitter.TreeCursorNode; 5 | import lombok.AccessLevel; 6 | import lombok.experimental.FieldDefaults; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Consumer; 10 | 11 | /** 12 | * Printer used for generating Symbolic Expression (S-expression) 13 | * representations of trees using an iterative approach. 14 | * Note that unlike the CLI counterpart, the resulting symbolic 15 | * expression does not contain the positional information of the 16 | * node. 17 | * 18 | * @since 1.4.0 19 | * @see Rust implementation 20 | * @author Ozren Dabić 21 | */ 22 | @FieldDefaults(level = AccessLevel.PRIVATE) 23 | public class SymbolicExpressionPrinter extends IterativeTreePrinter { 24 | 25 | public SymbolicExpressionPrinter(@NotNull TreeCursor cursor) { 26 | super(cursor); 27 | } 28 | 29 | @Override 30 | protected String getFileExtension() { 31 | return ".scm"; 32 | } 33 | 34 | protected void write(Consumer appender) { 35 | boolean needsSpace = false; 36 | boolean visitedChildren = false; 37 | for (;;) { 38 | TreeCursorNode cursorNode = cursor.getCurrentTreeCursorNode(); 39 | boolean isNamed = cursorNode.isNamed(); 40 | String type = cursorNode.getType(); 41 | String name = cursorNode.getName(); 42 | if (visitedChildren) { 43 | if (isNamed) { 44 | appender.accept(")"); 45 | needsSpace = true; 46 | } 47 | if (cursor.gotoNextSibling()) visitedChildren = false; 48 | else if (cursor.gotoParent()) visitedChildren = true; 49 | else return; 50 | } else { 51 | if (isNamed) { 52 | if (needsSpace) 53 | appender.accept(" "); 54 | if (name != null) { 55 | appender.accept(name); 56 | appender.accept(": "); 57 | } 58 | appender.accept("("); 59 | appender.accept(type); 60 | needsSpace = true; 61 | } 62 | visitedChildren = !cursor.gotoFirstChild(); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/SyntaxTreePrinter.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.TreeCursor; 4 | import ch.usi.si.seart.treesitter.TreeCursorNode; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.function.Consumer; 8 | 9 | /** 10 | * Printer used for generating human-readable 11 | * representations of trees using an iterative approach. 12 | * Each node of the subtree is represented as follows: 13 | *

{@code
14 |  *      optional_field_name: node_name [start_column:start_row] - [end_column:end_row]
15 |  * }
16 | * While indentation is used to represent the tree structure. 17 | * 18 | * @since 1.2.0 19 | * @see Syntax Tree Playground 20 | * @author Ozren Dabić 21 | */ 22 | public class SyntaxTreePrinter extends IterativeTreePrinter { 23 | 24 | public SyntaxTreePrinter(@NotNull TreeCursor cursor) { 25 | super(cursor); 26 | } 27 | 28 | @Override 29 | protected String getFileExtension() { 30 | return ".txt"; 31 | } 32 | 33 | protected void write(Consumer appender) { 34 | for (;;) { 35 | TreeCursorNode cursorNode = cursor.getCurrentTreeCursorNode(); 36 | if (cursorNode.isNamed()) { 37 | int depth = cursor.getCurrentDepth(); 38 | String indent = " ".repeat(depth); 39 | appender.accept(indent); 40 | appender.accept(cursorNode.toString()); 41 | appender.accept("\n"); 42 | } 43 | if (cursor.gotoFirstChild() || cursor.gotoNextSibling()) continue; 44 | do { 45 | if (!cursor.gotoParent()) return; 46 | } while (!cursor.gotoNextSibling()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/TreePrinter.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Contract for classes that can generate string 8 | * representations of Abstract Syntax Trees (ASTs). 9 | * Implementations of this interface are responsible 10 | * for traversing trees and producing a specific representation. 11 | * 12 | * @since 1.2.0 13 | * @author Ozren Dabić 14 | */ 15 | public interface TreePrinter { 16 | 17 | /** 18 | * Generates a string representation of an Abstract Syntax Tree (AST). 19 | * 20 | * @return A string representation of the tree 21 | */ 22 | String print(); 23 | 24 | /** 25 | * Generates a file containing the string 26 | * representation of an Abstract Syntax Tree (AST). 27 | * This method should be preferred over {@link #print()} 28 | * when dealing with extremely wide, or deep trees. 29 | * 30 | * @return A file containing the string representation of the tree 31 | * @throws IOException if an I/O error occurs 32 | */ 33 | File export() throws IOException; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/XMLPrinter.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.Point; 4 | import ch.usi.si.seart.treesitter.TreeCursor; 5 | import ch.usi.si.seart.treesitter.TreeCursorNode; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.ArrayDeque; 9 | import java.util.Deque; 10 | import java.util.function.Consumer; 11 | 12 | /** 13 | * Printer used for generating Extensible Markup Language (XML) 14 | * representations of trees using an iterative approach. 15 | * Note that unlike the CLI counterpart, the resulting XML 16 | * document does not contain the actual source code contents. 17 | * 18 | * @since 1.2.0 19 | * @see Rust implementation 20 | * @author Ozren Dabić 21 | */ 22 | public class XMLPrinter extends IterativeTreePrinter { 23 | 24 | public static final String PROLOG = ""; 25 | 26 | public XMLPrinter(@NotNull TreeCursor cursor) { 27 | super(cursor); 28 | } 29 | 30 | @Override 31 | protected String getPreamble() { 32 | return PROLOG; 33 | } 34 | 35 | @Override 36 | protected String getFileExtension() { 37 | return ".xml"; 38 | } 39 | 40 | protected void write(Consumer appender) { 41 | boolean visitedChildren = false; 42 | Deque tags = new ArrayDeque<>(); 43 | for (;;) { 44 | TreeCursorNode cursorNode = cursor.getCurrentTreeCursorNode(); 45 | boolean isNamed = cursorNode.isNamed(); 46 | String type = cursorNode.getType(); 47 | String name = cursorNode.getName(); 48 | Point start = cursorNode.getStartPoint(); 49 | Point end = cursorNode.getEndPoint(); 50 | if (visitedChildren) { 51 | if (isNamed) { 52 | appender.accept(""); 55 | } 56 | if (cursor.gotoNextSibling()) visitedChildren = false; 57 | else if (cursor.gotoParent()) visitedChildren = true; 58 | else return; 59 | } else { 60 | if (isNamed) { 61 | appender.accept("<"); 62 | appender.accept(type); 63 | appender.accept(" "); 64 | if (name != null) { 65 | appender.accept("name=\""); 66 | appender.accept(name); 67 | appender.accept("\" "); 68 | } 69 | appender.accept("start=\""); 70 | appender.accept(start.toString()); 71 | appender.accept("\" "); 72 | appender.accept("end=\""); 73 | appender.accept(end.toString()); 74 | appender.accept("\">"); 75 | tags.push(type); 76 | } 77 | visitedChildren = !cursor.gotoFirstChild(); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/ch/usi/si/seart/treesitter/printer/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides parse tree serialization utilities. 3 | * 4 | * @since 1.2.0 5 | */ 6 | package ch.usi.si.seart.treesitter.printer; 7 | -------------------------------------------------------------------------------- /src/main/javaTemplates/ch/usi/si/seart/treesitter/version/Version.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.version; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | /** 6 | * Utility used for obtaining the current version of the {@code ${project.artifactId}} codebase. 7 | * Unlike attempting to access the "Implementation-Version" manifest attribute from the JAR file, 8 | * this utility will work even if the {@link ClassLoader} does not expose the manifest metadata, 9 | * or if the code is not packaged in a JAR file. 10 | * 11 | * @author Ozren Dabić 12 | * @since 1.11.0 13 | */ 14 | @UtilityClass 15 | public final class Version { 16 | 17 | private static final String VALUE = "${project.version}"; 18 | 19 | /** 20 | * Get the current version of {@code ${project.artifactId}}. 21 | * 22 | * @return the semantic version string 23 | */ 24 | public String getVersion() { 25 | return VALUE; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/javaTemplates/ch/usi/si/seart/treesitter/version/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides version information for the {@code tree-sitter} API and this library. 3 | */ 4 | package ch.usi.si.seart.treesitter.version; 5 | -------------------------------------------------------------------------------- /src/main/resources/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seart-group/java-tree-sitter/b06ef81c0233487be4c9ef51b65aaa07734e5567/src/main/resources/.keep -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/AbstractQueryTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.Cleanup; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public abstract class AbstractQueryTest extends BaseTest { 7 | 8 | protected Language getLanguage() { 9 | return Language.PYTHON; 10 | } 11 | 12 | protected String getSource() { 13 | return 14 | "@pure\n" + 15 | "@property\n" + 16 | "def foo(x):\n" + 17 | " pass\n" + 18 | "\n" + 19 | "@pure\n" + 20 | "@property\n" + 21 | "def bar(x):\n" + 22 | " pass\n"; 23 | } 24 | 25 | protected String getPattern() { 26 | return "(_ (decorator)* @additional (function_definition) @target)"; 27 | } 28 | 29 | protected abstract void assertions(Node node, Query query); 30 | 31 | @Test 32 | void test() { 33 | String source = getSource(); 34 | Language language = getLanguage(); 35 | @Cleanup Query query = Query.getFor(language, getPattern()); 36 | @Cleanup Parser parser = Parser.getFor(language); 37 | @Cleanup Tree tree = parser.parse(source); 38 | Node root = tree.getRootNode(); 39 | assertions(root, query); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/BaseTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import org.mockito.Mockito; 4 | 5 | import java.util.List; 6 | 7 | public abstract class BaseTest { 8 | 9 | protected static final Language invalid; 10 | 11 | protected static final Node empty = new Node.Null(); 12 | protected static final Node treeless = new Node(1, 1, 1, 1, 1L, null); 13 | 14 | protected static final Point _0_0_ = new Point(0, 0); 15 | protected static final Point _0_1_ = new Point(0, 1); 16 | protected static final Point _1_0_ = new Point(1, 0); 17 | protected static final Point _1_1_ = new Point(1, 1); 18 | protected static final Point _2_2_ = new Point(2, 2); 19 | 20 | static { 21 | LibraryLoader.load(); 22 | invalid = Mockito.mock(Language.class); 23 | 24 | Mockito.when(invalid.name()).thenReturn("INVALID"); 25 | Mockito.when(invalid.ordinal()).thenReturn(Language.values().length); 26 | 27 | Mockito.when(invalid.getId()).thenReturn(0L); 28 | Mockito.when(invalid.getVersion()).thenReturn(0); 29 | Mockito.when(invalid.getTotalStates()).thenReturn(0); 30 | Mockito.when(invalid.getSymbols()).thenReturn(List.of()); 31 | Mockito.when(invalid.getFields()).thenReturn(List.of()); 32 | Mockito.when(invalid.getExtensions()).thenReturn(List.of()); 33 | 34 | Mockito.when(invalid.nextState(Mockito.any())).thenCallRealMethod(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/CaptureTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | 5 | import java.util.List; 6 | 7 | class CaptureTest extends AbstractQueryTest { 8 | 9 | @Override 10 | protected void assertions(Node node, Query query) { 11 | List captures = query.getCaptures(); 12 | Capture additional = captures.get(0); 13 | Assertions.assertTrue(additional.isEnabled()); 14 | additional.disable(); 15 | Assertions.assertFalse(additional.isEnabled()); 16 | try (QueryCursor cursor = node.walk(query)) { 17 | for (QueryMatch match: cursor) { 18 | Assertions.assertEquals(1, match.getNodes().size()); 19 | Assertions.assertEquals(0, match.getNodes(additional).size()); 20 | } 21 | } 22 | Assertions.assertFalse(additional.isEnabled()); 23 | additional.disable(); 24 | Assertions.assertFalse(additional.isEnabled()); 25 | Capture target = captures.get(1); 26 | Assertions.assertTrue(target.isEnabled()); 27 | target.disable(); 28 | Assertions.assertFalse(target.isEnabled()); 29 | try (QueryCursor cursor = node.walk(query)) { 30 | for (QueryMatch match: cursor) { 31 | Assertions.assertEquals(0, match.getNodes().size()); 32 | } 33 | } 34 | Assertions.assertFalse(target.isEnabled()); 35 | target.disable(); 36 | Assertions.assertFalse(target.isEnabled()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/LanguageTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import ch.usi.si.seart.treesitter.error.ABIVersionError; 4 | import ch.usi.si.seart.treesitter.version.TreeSitter; 5 | import lombok.Cleanup; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtensionContext; 9 | import org.junit.jupiter.api.io.TempDir; 10 | import org.junit.jupiter.params.ParameterizedTest; 11 | import org.junit.jupiter.params.provider.Arguments; 12 | import org.junit.jupiter.params.provider.ArgumentsProvider; 13 | import org.junit.jupiter.params.provider.ArgumentsSource; 14 | import org.junit.jupiter.params.provider.EnumSource; 15 | import org.junit.jupiter.params.provider.MethodSource; 16 | import org.mockito.Mockito; 17 | 18 | import java.net.URL; 19 | import java.nio.file.Path; 20 | import java.util.Collection; 21 | import java.util.List; 22 | import java.util.stream.Stream; 23 | 24 | class LanguageTest extends BaseTest { 25 | 26 | @TempDir 27 | private static Path tmp; 28 | private static final Language language = Language.PYTHON; 29 | 30 | @ParameterizedTest 31 | @EnumSource(Language.class) 32 | void testValidate(Language language) { 33 | Assertions.assertDoesNotThrow(() -> Language.validate(language)); 34 | } 35 | 36 | private static class ValidateExceptionProvider implements ArgumentsProvider { 37 | 38 | @Override 39 | public Stream provideArguments(ExtensionContext extensionContext) { 40 | int min = TreeSitter.getMinimumABIVersion(); 41 | int max = TreeSitter.getCurrentABIVersion(); 42 | Language outdated = Mockito.mock(Language.class); 43 | Mockito.when(outdated.getId()).thenReturn(1L); 44 | Mockito.when(outdated.getVersion()).thenReturn(min - 1); 45 | Language unsupported = Mockito.mock(Language.class); 46 | Mockito.when(unsupported.getId()).thenReturn(1L); 47 | Mockito.when(unsupported.getVersion()).thenReturn(max + 1); 48 | return Stream.of( 49 | Arguments.of(NullPointerException.class, null), 50 | Arguments.of(UnsatisfiedLinkError.class, invalid), 51 | Arguments.of(ABIVersionError.class, outdated), 52 | Arguments.of(ABIVersionError.class, unsupported) 53 | ); 54 | } 55 | } 56 | 57 | @ParameterizedTest(name = "[{index}] {0}") 58 | @ArgumentsSource(ValidateExceptionProvider.class) 59 | void testValidateThrows(Class throwableType, Language language) { 60 | Assertions.assertThrows(throwableType, () -> Language.validate(language)); 61 | } 62 | 63 | @ParameterizedTest 64 | @EnumSource(Language.class) 65 | void testGetMetadata(Language language) { 66 | Language.Metadata metadata = language.getMetadata(); 67 | Assertions.assertNotNull(metadata); 68 | String sha = metadata.getSHA(); 69 | Assertions.assertNotNull(sha); 70 | Assertions.assertEquals(40, sha.length()); 71 | URL url = metadata.getURL(); 72 | Assertions.assertNotNull(url); 73 | } 74 | 75 | private static class AssociatedWithProvider implements ArgumentsProvider { 76 | 77 | @Override 78 | public Stream provideArguments(ExtensionContext extensionContext) { 79 | return Stream.of( 80 | Arguments.of(".keep", List.of()), 81 | Arguments.of("requirements.txt", List.of(Language.REQUIREMENTS)), 82 | Arguments.of(".py", List.of(Language.PYTHON)), 83 | Arguments.of(".gitattributes", List.of(Language.GITATTRIBUTES)), 84 | Arguments.of("__init__.py", List.of(Language.PYTHON)), 85 | Arguments.of(".gitignore", List.of(Language.GITIGNORE)), 86 | Arguments.of("Main.java", List.of(Language.JAVA)), 87 | Arguments.of("Dockerfile", List.of(Language.DOCKERFILE)), 88 | Arguments.of("example.h", List.of( 89 | Language.C, 90 | Language.CPP, 91 | Language.OBJECTIVE_C 92 | )) 93 | ); 94 | } 95 | } 96 | 97 | @ParameterizedTest(name = "[{index}] {0}") 98 | @ArgumentsSource(AssociatedWithProvider.class) 99 | void testAssociatedWith(String name, List expected) { 100 | Path path = Path.of(tmp.toString(), name); 101 | Collection actual = Language.associatedWith(path); 102 | Assertions.assertNotNull(actual); 103 | Assertions.assertEquals(expected.size(), actual.size()); 104 | Assertions.assertEquals(expected, actual); 105 | Assertions.assertThrows(UnsupportedOperationException.class, () -> actual.add(null)); 106 | } 107 | 108 | private static class AssociatedWithExceptionProvider implements ArgumentsProvider { 109 | 110 | @Override 111 | public Stream provideArguments(ExtensionContext extensionContext) { 112 | return Stream.of( 113 | Arguments.of(NullPointerException.class, null), 114 | Arguments.of(IllegalArgumentException.class, tmp) 115 | ); 116 | } 117 | } 118 | 119 | @ParameterizedTest(name = "[{index}] {0}") 120 | @ArgumentsSource(AssociatedWithExceptionProvider.class) 121 | void testAssociatedWithThrows(Class throwableType, Path path) { 122 | Assertions.assertThrows(throwableType, () -> Language.associatedWith(path)); 123 | } 124 | 125 | @Test 126 | void testNextState() { 127 | @Cleanup Parser parser = Parser.getFor(language); 128 | @Cleanup Tree tree = parser.parse("pass"); 129 | Node root = tree.getRootNode(); 130 | Assertions.assertEquals(0, language.nextState(root)); 131 | } 132 | 133 | private static Stream invalidNodes() { 134 | return Stream.of( 135 | Arguments.of(NullPointerException.class, null), 136 | Arguments.of(IllegalArgumentException.class, empty) 137 | ); 138 | } 139 | 140 | @ParameterizedTest(name = "[{index}] {0}") 141 | @MethodSource("invalidNodes") 142 | void testNextStateThrows(Class throwableType, Node node) { 143 | Assertions.assertThrows(throwableType, () -> language.nextState(node)); 144 | Assertions.assertThrows(throwableType, () -> invalid.nextState(node)); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/LookaheadIteratorTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.List; 7 | import java.util.NoSuchElementException; 8 | import java.util.Spliterator; 9 | import java.util.Spliterators; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.StreamSupport; 12 | 13 | class LookaheadIteratorTest extends BaseTest { 14 | 15 | private static final Language language = Language.JAVA; 16 | 17 | @Test 18 | void testIterator() { 19 | try (LookaheadIterator iterator = language.iterator(0)) { 20 | Spliterator spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED); 21 | List symbols = StreamSupport.stream(spliterator, false).collect(Collectors.toUnmodifiableList()); 22 | Assertions.assertFalse(symbols.isEmpty()); 23 | Assertions.assertTrue(language.getSymbols().containsAll(symbols)); 24 | Assertions.assertThrows(NoSuchElementException.class, iterator::next); 25 | } catch (NoSuchElementException ignored) { 26 | Assertions.fail(); 27 | } 28 | } 29 | 30 | @Test 31 | @SuppressWarnings("resource") 32 | void testIteratorThrows() { 33 | int states = language.getTotalStates(); 34 | Assertions.assertThrows(IllegalArgumentException.class, () -> language.iterator(Integer.MIN_VALUE)); 35 | Assertions.assertThrows(IllegalArgumentException.class, () -> language.iterator(-1)); 36 | Assertions.assertThrows(IllegalArgumentException.class, () -> language.iterator(states)); 37 | Assertions.assertThrows(IllegalArgumentException.class, () -> language.iterator(Integer.MAX_VALUE)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/MultiCaptureTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.Cleanup; 4 | import org.junit.jupiter.api.Assertions; 5 | 6 | import java.util.List; 7 | 8 | class MultiCaptureTest extends AbstractQueryTest { 9 | 10 | @Override 11 | protected void assertions(Node node, Query query) { 12 | List captures = query.getCaptures(); 13 | Capture additional = captures.get(0); 14 | Capture target = captures.get(1); 15 | @Cleanup QueryCursor cursor = node.walk(query); 16 | for (QueryMatch match: cursor) { 17 | Assertions.assertEquals(3, match.getNodes().size()); 18 | Assertions.assertEquals(2, match.getNodes(additional.getName()).size()); 19 | Assertions.assertEquals(1, match.getNodes(target.getName()).size()); 20 | Assertions.assertEquals(2, match.getNodes(additional).size()); 21 | Assertions.assertEquals(1, match.getNodes(target).size()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/OffsetTreeCursorTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import ch.usi.si.seart.treesitter.printer.PrinterTestBase; 4 | import ch.usi.si.seart.treesitter.printer.SyntaxTreePrinter; 5 | import ch.usi.si.seart.treesitter.printer.TreePrinter; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Disabled; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class OffsetTreeCursorTest extends PrinterTestBase { 11 | 12 | @Test 13 | @SuppressWarnings("resource") 14 | void testCursorThrows() { 15 | Assertions.assertThrows(NullPointerException.class, () -> new OffsetTreeCursor(null, _1_1_)); 16 | Assertions.assertThrows(NullPointerException.class, () -> new OffsetTreeCursor(treeless, null)); 17 | Assertions.assertThrows(IllegalStateException.class, () -> new OffsetTreeCursor(treeless, _1_1_)); 18 | } 19 | 20 | @Disabled("No need test the throw here") 21 | @Override 22 | protected void testPrinterThrows() { 23 | } 24 | 25 | @Override 26 | protected TreeCursor getCursor(Tree tree) { 27 | Node root = tree.getRootNode(); 28 | Node method = root.getChild(1).getChildByFieldName("body").getChild(1); 29 | return new OffsetTreeCursor(method, new Point(-1, -2)); 30 | } 31 | 32 | @Override 33 | protected String getExpected() { 34 | return "method_declaration [2:2] - [5:3]\n" + 35 | " modifiers [2:2] - [2:15]\n" + 36 | " type: void_type [2:16] - [2:20]\n" + 37 | " name: identifier [2:21] - [2:25]\n" + 38 | " parameters: formal_parameters [2:25] - [2:40]\n" + 39 | " formal_parameter [2:26] - [2:39]\n" + 40 | " type: array_type [2:26] - [2:34]\n" + 41 | " element: type_identifier [2:26] - [2:32]\n" + 42 | " dimensions: dimensions [2:32] - [2:34]\n" + 43 | " name: identifier [2:35] - [2:39]\n" + 44 | " body: block [2:41] - [5:3]\n" + 45 | " line_comment [3:6] - [3:20]\n" + 46 | " expression_statement [4:6] - [4:42]\n" + 47 | " method_invocation [4:6] - [4:41]\n" + 48 | " object: field_access [4:6] - [4:16]\n" + 49 | " object: identifier [4:6] - [4:12]\n" + 50 | " field: identifier [4:13] - [4:16]\n" + 51 | " name: identifier [4:17] - [4:24]\n" + 52 | " arguments: argument_list [4:24] - [4:41]\n" + 53 | " string_literal [4:25] - [4:40]\n" + 54 | " string_fragment [4:26] - [4:39]\n"; 55 | } 56 | 57 | @Override 58 | protected TreePrinter getPrinter(TreeCursor cursor) { 59 | return new SyntaxTreePrinter(cursor); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/PatternTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.Cleanup; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.ValueSource; 8 | 9 | import java.util.List; 10 | 11 | class PatternTest extends BaseTest { 12 | 13 | @ParameterizedTest(name = "[{index}] {0}") 14 | @ValueSource(strings = { 15 | "(line_comment)(block_comment)", 16 | "(line_comment) (block_comment)", 17 | "(line_comment ) (block_comment )", 18 | "(line_comment ) ( block_comment)", 19 | "( line_comment) (block_comment )", 20 | "( line_comment ) ( block_comment )", 21 | "(line_comment) (block_comment)", 22 | "(line_comment )( block_comment)", 23 | " (line_comment) (block_comment) ", 24 | }) 25 | void test(String string) { 26 | List parts = List.of("(line_comment)", "(block_comment)"); 27 | @Cleanup Query query = Query.builder() 28 | .language(Language.JAVA) 29 | .pattern(string) 30 | .build(); 31 | Assertions.assertEquals(String.join(" ", parts), query.getPattern()); 32 | List patterns = query.getPatterns(); 33 | Assertions.assertFalse(patterns.isEmpty()); 34 | Assertions.assertEquals(2, patterns.size()); 35 | for (int i = 0; i < patterns.size(); i++) { 36 | Pattern pattern = patterns.get(i); 37 | Assertions.assertTrue(pattern.getStartOffset() >= 0); 38 | Assertions.assertTrue(pattern.getStartOffset() < pattern.getEndOffset()); 39 | String actual = pattern.getValue(); 40 | String expected = parts.get(i); 41 | Assertions.assertEquals(expected, actual); 42 | } 43 | } 44 | 45 | @Test 46 | void testDisable() { 47 | @Cleanup Parser parser = Parser.getFor(Language.PYTHON); 48 | @Cleanup Tree tree = parser.parse("pass"); 49 | @Cleanup Query query = Query.builder() 50 | .language(Language.PYTHON) 51 | .pattern("(module) @capture") 52 | .build(); 53 | Node root = tree.getRootNode(); 54 | Pattern pattern = query.getPatterns().stream() 55 | .findFirst() 56 | .orElseGet(Assertions::fail); 57 | Assertions.assertTrue(pattern.isEnabled()); 58 | pattern.disable(); 59 | Assertions.assertFalse(pattern.isEnabled()); 60 | @Cleanup QueryCursor cursor = root.walk(query); 61 | for (QueryMatch ignored: cursor) Assertions.fail(); 62 | Assertions.assertFalse(pattern.isEnabled()); 63 | pattern.disable(); 64 | Assertions.assertFalse(pattern.isEnabled()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/PointTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | class PointTest extends BaseTest { 11 | 12 | @Test 13 | void testIsOrigin() { 14 | Assertions.assertTrue(_0_0_.isOrigin()); 15 | } 16 | 17 | @Test 18 | void testIsNotOrigin() { 19 | Assertions.assertFalse(_1_0_.isOrigin()); 20 | Assertions.assertFalse(_0_1_.isOrigin()); 21 | Assertions.assertFalse(_1_1_.isOrigin()); 22 | Assertions.assertFalse(_2_2_.isOrigin()); 23 | } 24 | 25 | @Test 26 | void testCompareTo() { 27 | List sorted = List.of(new Point(-1, -1), new Point(-1, 0), _0_0_, _0_1_, _1_0_, _1_1_); 28 | ArrayList unsorted = new ArrayList<>(sorted); 29 | Collections.shuffle(unsorted); 30 | Collections.sort(unsorted); 31 | Assertions.assertEquals(sorted, unsorted); 32 | } 33 | 34 | @Test 35 | void testCompareToThrows() { 36 | Assertions.assertThrows(NullPointerException.class, () -> _0_0_.compareTo(null)); 37 | } 38 | 39 | @Test 40 | void testAdd() { 41 | Assertions.assertEquals(_1_1_, _0_0_.add(_1_1_)); 42 | Assertions.assertEquals(_1_1_, _1_1_.add(_0_0_)); 43 | Assertions.assertEquals(_0_0_, new Point(-1, -1).add(_1_1_)); 44 | Assertions.assertEquals(_2_2_, _1_1_.add(_1_1_)); 45 | } 46 | 47 | @Test 48 | void testSubtract() { 49 | Assertions.assertEquals(_0_0_, _1_1_.subtract(_1_1_)); 50 | Assertions.assertEquals(_1_1_, _1_1_.subtract(_0_0_)); 51 | Assertions.assertEquals(new Point(-1, -1), _0_0_.subtract(_1_1_)); 52 | Assertions.assertEquals(new Point(-2, -2), new Point(-1, -1).subtract(_1_1_)); 53 | } 54 | 55 | @Test 56 | void testMultiply() { 57 | Assertions.assertEquals(_0_0_, _0_0_.multiply(2)); 58 | Assertions.assertEquals(_0_0_, _1_1_.multiply(0)); 59 | Assertions.assertEquals(_1_1_, _1_1_.multiply(1)); 60 | Assertions.assertEquals(_2_2_, _1_1_.multiply(2)); 61 | } 62 | 63 | @Test 64 | void testThrows() { 65 | Assertions.assertThrows(NullPointerException.class, () -> _0_0_.add(null)); 66 | Assertions.assertThrows(NullPointerException.class, () -> _0_0_.subtract(null)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/PredicateTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import org.junit.jupiter.api.AfterAll; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | 9 | class PredicateTest extends BaseTest { 10 | 11 | private static final String expected = "(#any-of? @variable \"document\" \"window\" \"console\")"; 12 | 13 | private static final Query query = Query.builder() 14 | .language(Language.JAVASCRIPT) 15 | .pattern("((identifier) @variable "+expected+")") 16 | .build(); 17 | 18 | private static final Pattern pattern = query.getPatterns().stream() 19 | .findFirst() 20 | .orElseThrow(); 21 | 22 | @AfterAll 23 | static void afterAll() { 24 | query.close(); 25 | } 26 | 27 | @Test 28 | void test() { 29 | Predicate predicate = pattern.getPredicates().stream() 30 | .findFirst() 31 | .orElseGet(Assertions::fail); 32 | List steps = predicate.getSteps(); 33 | Assertions.assertFalse(steps.isEmpty()); 34 | Assertions.assertEquals(6, steps.size()); 35 | Assertions.assertEquals( 36 | Predicate.Step.Type.STRING, 37 | steps.stream() 38 | .map(Predicate.Step::getType) 39 | .findFirst() 40 | .orElse(null) 41 | ); 42 | Assertions.assertEquals(expected, predicate.toString()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/QuantifierTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.Cleanup; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtensionContext; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.Arguments; 9 | import org.junit.jupiter.params.provider.ArgumentsProvider; 10 | import org.junit.jupiter.params.provider.ArgumentsSource; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.stream.Stream; 15 | 16 | class QuantifierTest extends BaseTest { 17 | 18 | private static final Language language = Language.JAVA; 19 | 20 | private static final Map symbols = Map.of( 21 | Quantifier.ONE, "", 22 | Quantifier.ONE_OR_MORE, "+", 23 | Quantifier.ZERO_OR_ONE, "?", 24 | Quantifier.ZERO_OR_MORE, "*" 25 | ); 26 | 27 | private static final class QuantifierArgumentsProvider implements ArgumentsProvider { 28 | 29 | @Override 30 | public Stream provideArguments(ExtensionContext context) { 31 | return symbols.entrySet().stream() 32 | .map(entry -> { 33 | Quantifier quantifier = entry.getKey(); 34 | String symbol = entry.getValue(); 35 | String pattern = "((_)" + symbol + " @capture)"; 36 | Query query = Query.getFor(language, pattern); 37 | List captures = query.getCaptures(); 38 | Capture capture = captures.stream().findFirst().orElseThrow(); 39 | return Arguments.of(quantifier, capture, query); 40 | }); 41 | } 42 | } 43 | 44 | @ParameterizedTest(name = "[{index}] {0}") 45 | @ArgumentsSource(QuantifierArgumentsProvider.class) 46 | void testGetQuantifiers(Quantifier expected, Capture capture, Query query) { 47 | List quantifiers = capture.getQuantifiers(); 48 | Assertions.assertNotNull(quantifiers); 49 | Assertions.assertFalse(quantifiers.isEmpty()); 50 | Assertions.assertEquals(1, quantifiers.size()); 51 | Quantifier actual = quantifiers.stream() 52 | .findFirst() 53 | .orElseGet(Assertions::fail); 54 | Assertions.assertEquals(expected, actual); 55 | query.close(); 56 | } 57 | 58 | @Test 59 | void testGetQuantifier() { 60 | @Cleanup Query query = Query.builder() 61 | .language(language) 62 | .pattern("((_) @capture)") 63 | .pattern("(identifier)") 64 | .build(); 65 | List captures = query.getCaptures(); 66 | List patterns = query.getPatterns(); 67 | Capture capture = captures.stream().findFirst().orElseThrow(); 68 | Pattern pattern = patterns.stream().skip(1).findFirst().orElseThrow(); 69 | Quantifier quantifier = capture.getQuantifier(pattern); 70 | Assertions.assertEquals(Quantifier.ZERO, quantifier); 71 | } 72 | 73 | @Test 74 | void testGetQuantifierThrows() { 75 | @Cleanup Query original = Query.getFor(language, "((_) @capture)"); 76 | @Cleanup Query copy = Query.getFor(language, "((_) @capture)"); 77 | List captures = original.getCaptures(); 78 | Capture capture = captures.stream().findFirst().orElseThrow(); 79 | List patterns = copy.getPatterns(); 80 | Pattern pattern = patterns.stream().findFirst().orElseThrow(); 81 | Assertions.assertThrows(NullPointerException.class, () -> capture.getQuantifier(null)); 82 | Assertions.assertThrows(IllegalArgumentException.class, () -> capture.getQuantifier(pattern)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/QueryTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import ch.usi.si.seart.treesitter.exception.query.QueryCaptureException; 4 | import ch.usi.si.seart.treesitter.exception.query.QueryFieldException; 5 | import ch.usi.si.seart.treesitter.exception.query.QueryNodeTypeException; 6 | import ch.usi.si.seart.treesitter.exception.query.QueryStructureException; 7 | import ch.usi.si.seart.treesitter.exception.query.QuerySyntaxException; 8 | import lombok.Cleanup; 9 | import org.junit.jupiter.api.Assertions; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtensionContext; 12 | import org.junit.jupiter.params.ParameterizedTest; 13 | import org.junit.jupiter.params.provider.Arguments; 14 | import org.junit.jupiter.params.provider.ArgumentsProvider; 15 | import org.junit.jupiter.params.provider.ArgumentsSource; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.function.Supplier; 21 | import java.util.stream.Stream; 22 | 23 | class QueryTest extends BaseTest { 24 | 25 | private static final Language language = Language.JAVA; 26 | 27 | private static class QueryProvider implements ArgumentsProvider { 28 | 29 | @Override 30 | public Stream provideArguments(ExtensionContext extensionContext) { 31 | 32 | String pattern1 = "(_)"; 33 | String pattern2 = "(_) @capture"; 34 | String pattern3 = "\"return\" @capture"; 35 | String pattern4 = "\"private\" @capture.first"; 36 | String pattern5 = "\"public\" @capture.second"; 37 | 38 | return Stream.of( 39 | Arguments.of("", Query.builder().language(language).build(), 0, 0, 0), 40 | Arguments.of(pattern1, Query.getFor(language, pattern1), 1, 0, 0), 41 | Arguments.of(pattern2, Query.getFor(language, pattern2), 1, 1, 0), 42 | Arguments.of(pattern3, Query.getFor(language, pattern3), 1, 1, 0), 43 | Arguments.of(pattern4, Query.getFor(language, pattern4, pattern5), 2, 2, 0) 44 | ); 45 | } 46 | } 47 | 48 | @ParameterizedTest(name = "[{index}] {0}") 49 | @ArgumentsSource(QueryProvider.class) 50 | void testQuery(String ignored, Query query, int patterns, int captures, int strings) { 51 | Assertions.assertNotNull(query); 52 | Assertions.assertFalse(query.isNull()); 53 | Assertions.assertEquals(patterns, query.getPatterns().size()); 54 | Assertions.assertEquals(captures, query.getCaptures().size()); 55 | Assertions.assertEquals(strings, query.getStrings().size()); 56 | Assertions.assertEquals(captures > 0, query.hasCaptures()); 57 | query.close(); 58 | Assertions.assertTrue(query.isNull()); 59 | } 60 | 61 | private static class QueryExceptionProvider implements ArgumentsProvider { 62 | 63 | @Override 64 | public Stream provideArguments(ExtensionContext extensionContext) { 65 | return Stream.of( 66 | Arguments.of(UnsatisfiedLinkError.class, invalid, "(_)"), 67 | Arguments.of(QueryCaptureException.class, Language.JAVA, "(#eq? @key @value)"), 68 | Arguments.of(QueryFieldException.class, Language.JAVA, "(program unknown: (_))"), 69 | Arguments.of(QueryNodeTypeException.class, Language.JAVA, "(null)"), 70 | Arguments.of(QueryStructureException.class, Language.JAVA, "(program (program))"), 71 | Arguments.of(QuerySyntaxException.class, Language.JAVA, "()") 72 | ); 73 | } 74 | } 75 | 76 | @ParameterizedTest(name = "[{index}] {0}") 77 | @ArgumentsSource(QueryExceptionProvider.class) 78 | void testQueryException(Class throwableType, Language language, String pattern) { 79 | Assertions.assertThrows(throwableType, () -> Query.getFor(language, pattern)); 80 | } 81 | 82 | private static class QuerySupplierExceptionProvider implements ArgumentsProvider { 83 | 84 | @Override 85 | public Stream provideArguments(ExtensionContext extensionContext) { 86 | Query.Builder builder = Query.builder(); 87 | Map> map = Map.of( 88 | "Nothing specified", builder::build, 89 | "No language specified", () -> builder.pattern("(_)").build(), 90 | "Single null pattern string", () -> builder.pattern(null).build(), 91 | "String array as null", () -> builder.patterns((String[]) null).build(), 92 | "String list as null", () -> builder.patterns((List) null).build(), 93 | "String array contains null", () -> builder.patterns("(_)", null).build(), 94 | "String list contains null", () -> builder.patterns(Arrays.asList("(_)", null)).build() 95 | ); 96 | return map.entrySet().stream().map(entry -> Arguments.of(entry.getKey(), entry.getValue())); 97 | } 98 | } 99 | 100 | @ParameterizedTest(name = "[{index}] {0}") 101 | @ArgumentsSource(QuerySupplierExceptionProvider.class) 102 | void testQueryException(String ignored, Supplier supplier) { 103 | Assertions.assertThrows(NullPointerException.class, supplier::get); 104 | } 105 | 106 | @Test 107 | void testQueryToBuilder() { 108 | @Cleanup Query original = Query.builder() 109 | .language(language) 110 | .pattern("(line_comment)") 111 | .build(); 112 | Query.Builder builder = original.toBuilder(); 113 | @Cleanup Query modified = builder 114 | .pattern("(block_comment)") 115 | .build(); 116 | Assertions.assertNotEquals(original, modified); 117 | Assertions.assertEquals(1, original.getPatterns().size()); 118 | Assertions.assertEquals(2, modified.getPatterns().size()); 119 | Assertions.assertEquals(original.getLanguage(), modified.getLanguage()); 120 | @Cleanup Query overwrite = builder 121 | .patterns(List.of("(_)")) 122 | .build(); 123 | Assertions.assertNotEquals(original, overwrite); 124 | Assertions.assertEquals(original.getPatterns().size(), overwrite.getPatterns().size()); 125 | Assertions.assertNotEquals(original.getPattern(), overwrite.getPattern()); 126 | Assertions.assertEquals(original.getLanguage(), modified.getLanguage()); 127 | @Cleanup Query empty = builder.pattern().build(); 128 | Assertions.assertNotEquals(original, empty); 129 | Assertions.assertEquals(1, original.getPatterns().size()); 130 | Assertions.assertEquals(0, empty.getPatterns().size()); 131 | Assertions.assertEquals(original.getLanguage(), empty.getLanguage()); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/RangeTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtensionContext; 6 | import org.junit.jupiter.api.function.Executable; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.Arguments; 9 | import org.junit.jupiter.params.provider.ArgumentsProvider; 10 | import org.junit.jupiter.params.provider.ArgumentsSource; 11 | 12 | import java.util.stream.Stream; 13 | 14 | class RangeTest extends BaseTest { 15 | 16 | @Test 17 | void testBuilder() { 18 | Range expected = new Range(0, 1, _0_0_, _1_1_); 19 | Range actual = Range.builder() 20 | .startByte(0) 21 | .endByte(1) 22 | .startPoint(_0_0_) 23 | .endPoint(_1_1_) 24 | .build(); 25 | Assertions.assertEquals(expected, actual); 26 | Range modified = actual.toBuilder() 27 | .endByte(2) 28 | .endPoint(_2_2_) 29 | .build(); 30 | Assertions.assertNotEquals(expected, modified); 31 | } 32 | 33 | private static class BuilderExceptionProvider implements ArgumentsProvider { 34 | 35 | @Override 36 | public Stream provideArguments(ExtensionContext extensionContext) { 37 | Point negativeRow = new Point(-1, 0); 38 | Point negativeCol = new Point(0, -1); 39 | Executable setNegativeStartByte = () -> Range.builder().startByte(-1); 40 | Executable setNegativeEndByte = () -> Range.builder().endByte(-1); 41 | Executable setNullStartPoint = () -> Range.builder().startPoint(null); 42 | Executable setNullEndPoint = () -> Range.builder().endPoint(null); 43 | Executable setNegativeStartPointRow = () -> Range.builder().startPoint(negativeRow); 44 | Executable setNegativeEndPointRow = () -> Range.builder().endPoint(negativeRow); 45 | Executable setNegativeStartPointCol = () -> Range.builder().startPoint(negativeCol); 46 | Executable setNegativeEndPointCol = () -> Range.builder().endPoint(negativeCol); 47 | Executable buildReversedByteRange = () -> Range.builder().startByte(2).endByte(0).build(); 48 | Executable buildReversedPointRange = () -> Range.builder().startPoint(_1_1_).endPoint(_0_0_).build(); 49 | return Stream.of( 50 | Arguments.of(IllegalArgumentException.class, setNegativeStartByte), 51 | Arguments.of(IllegalArgumentException.class, setNegativeEndByte), 52 | Arguments.of(NullPointerException.class, setNullStartPoint), 53 | Arguments.of(NullPointerException.class, setNullEndPoint), 54 | Arguments.of(IllegalArgumentException.class, setNegativeStartPointRow), 55 | Arguments.of(IllegalArgumentException.class, setNegativeEndPointRow), 56 | Arguments.of(IllegalArgumentException.class, setNegativeStartPointCol), 57 | Arguments.of(IllegalArgumentException.class, setNegativeEndPointCol), 58 | Arguments.of(IllegalArgumentException.class, buildReversedByteRange), 59 | Arguments.of(IllegalArgumentException.class, buildReversedPointRange) 60 | ); 61 | } 62 | } 63 | 64 | @ParameterizedTest(name = "[{index}] {0}") 65 | @ArgumentsSource(BuilderExceptionProvider.class) 66 | void testBuilderThrows(Class exception, Executable runnable) { 67 | Assertions.assertThrows(exception, runnable); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/TreeTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter; 2 | 3 | import lombok.Cleanup; 4 | import org.junit.jupiter.api.AfterAll; 5 | import org.junit.jupiter.api.AfterEach; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.BeforeAll; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtensionContext; 11 | import org.junit.jupiter.params.ParameterizedTest; 12 | import org.junit.jupiter.params.provider.Arguments; 13 | import org.junit.jupiter.params.provider.ArgumentsProvider; 14 | import org.junit.jupiter.params.provider.ArgumentsSource; 15 | 16 | import java.util.List; 17 | import java.util.stream.Stream; 18 | 19 | class TreeTest extends BaseTest { 20 | 21 | private static final String source = "class Main {\n // This is a line comment\n}\n"; 22 | private static final String target = "class Main {\n}\n"; 23 | private static Parser parser; 24 | private Tree tree; 25 | private Node root; 26 | 27 | @BeforeAll 28 | static void beforeAll() { 29 | parser = Parser.getFor(Language.JAVA); 30 | } 31 | 32 | @BeforeEach 33 | void setUp() { 34 | tree = parser.parse(source); 35 | root = tree.getRootNode(); 36 | } 37 | 38 | @AfterEach 39 | void tearDown() { 40 | tree.close(); 41 | } 42 | 43 | @AfterAll 44 | static void afterAll() { 45 | parser.close(); 46 | } 47 | 48 | @Test 49 | void testGetSource() { 50 | Assertions.assertEquals(source, tree.getSource()); 51 | } 52 | 53 | private static class ByteRangeContentProvider implements ArgumentsProvider { 54 | 55 | @Override 56 | public Stream provideArguments(ExtensionContext extensionContext) { 57 | Tree tree = parser.parse(source); 58 | Node root = tree.getRootNode(); 59 | Node name = root.getChild(0).getChildByFieldName("name"); 60 | Node body = root.getChild(0).getChildByFieldName("body"); 61 | Node leftCurly = body.getChild(0); 62 | Node comment = body.getChild(1); 63 | Node rightCurly = body.getChild(2); 64 | return Stream.of( 65 | Arguments.of(0, 45, root), 66 | Arguments.of(6, 10, name), 67 | Arguments.of(11, 12, leftCurly), 68 | Arguments.of(17, 42, comment), 69 | Arguments.of(43, 44, rightCurly) 70 | ); 71 | } 72 | } 73 | 74 | @ParameterizedTest(name = "[{index}] {0} - {1}") 75 | @ArgumentsSource(ByteRangeContentProvider.class) 76 | void testGetSourceStartEnd(int beginIndex, int endIndex, Node node) { 77 | String expected = source.substring(beginIndex, endIndex); 78 | String actual = tree.getSource(node.getStartByte(), node.getEndByte()); 79 | Assertions.assertEquals(expected, actual); 80 | } 81 | 82 | @Test 83 | void testEdit() { 84 | Assertions.assertEquals(new Point(0, 0), root.getStartPoint()); 85 | Assertions.assertEquals(new Point(3, 0), root.getEndPoint()); 86 | Assertions.assertFalse(root.hasChanges()); 87 | InputEdit inputEdit = new InputEdit( 88 | source.indexOf("// This is a line comment"), 89 | source.length(), 90 | target.length(), 91 | new Point(1, 4), // comment start 92 | new Point(3, 0), // old root end 93 | new Point(2, 0) // new root end 94 | ); 95 | tree.edit(inputEdit); 96 | Assertions.assertTrue(root.hasChanges()); 97 | Tree modified = parser.parse(target, tree); 98 | List ranges = tree.getChangedRanges(modified); 99 | Assertions.assertNotNull(ranges); 100 | Assertions.assertEquals(1, ranges.size()); 101 | Range range = ranges.stream().findFirst().orElseGet(Assertions::fail); 102 | Assertions.assertEquals(new Point(1, 0), range.getStartPoint()); 103 | Assertions.assertEquals(new Point(2, 0), range.getEndPoint()); 104 | root = modified.getRootNode(); 105 | Assertions.assertEquals("program", root.getType()); 106 | Assertions.assertEquals(new Point(0, 0), root.getStartPoint()); 107 | Assertions.assertEquals(new Point(2, 0), root.getEndPoint()); 108 | Assertions.assertFalse(root.hasChanges()); 109 | } 110 | 111 | @Test 112 | void testClone() { 113 | @Cleanup Tree copy = tree.clone(); 114 | Assertions.assertNotEquals(tree, copy); 115 | } 116 | 117 | @Test 118 | void testConstructorThrows() { 119 | @Cleanup Tree tree = new Tree(0L, Language.JAVA, ""); 120 | Assertions.assertThrows(NullPointerException.class, () -> tree.edit(null)); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/printer/DotGraphPrinterTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.BaseTest; 4 | import ch.usi.si.seart.treesitter.Language; 5 | import ch.usi.si.seart.treesitter.Parser; 6 | import ch.usi.si.seart.treesitter.Tree; 7 | import lombok.Cleanup; 8 | import lombok.SneakyThrows; 9 | import org.junit.jupiter.api.Assertions; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | 17 | class DotGraphPrinterTest extends BaseTest { 18 | 19 | private static final String source = 20 | "package ch.usi.si;\n" + 21 | "\n" + 22 | "public class Main {\n" + 23 | " public static void main(String[] args) {\n" + 24 | " //line comment\n" + 25 | " System.out.println(\"Hello, World!\");\n" + 26 | " }\n" + 27 | "}"; 28 | 29 | @Test 30 | void testPrint() { 31 | @Cleanup Parser parser = Parser.getFor(Language.JAVA); 32 | @Cleanup Tree tree = parser.parse(source); 33 | TreePrinter printer = new DotGraphPrinter(tree); 34 | String actual = printer.print(); 35 | assertion(actual); 36 | } 37 | 38 | @Test 39 | @SneakyThrows(IOException.class) 40 | void testExport() { 41 | @Cleanup Parser parser = Parser.getFor(Language.JAVA); 42 | @Cleanup Tree tree = parser.parse(source); 43 | TreePrinter printer = new DotGraphPrinter(tree); 44 | File file = printer.export(); 45 | Path path = file.toPath(); 46 | String actual = Files.readString(path); 47 | Files.delete(file.toPath()); 48 | assertion(actual); 49 | } 50 | 51 | @Test 52 | void testPrinterThrows() { 53 | Assertions.assertThrows(NullPointerException.class, () -> new DotGraphPrinter(null)); 54 | } 55 | 56 | private void assertion(String result) { 57 | Assertions.assertEquals(704, result.lines().count()); 58 | Assertions.assertTrue(result.startsWith("digraph tree {")); 59 | Assertions.assertTrue(result.endsWith("}\n")); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/printer/PrinterTestBase.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.BaseTest; 4 | import ch.usi.si.seart.treesitter.Language; 5 | import ch.usi.si.seart.treesitter.Parser; 6 | import ch.usi.si.seart.treesitter.Tree; 7 | import ch.usi.si.seart.treesitter.TreeCursor; 8 | import lombok.Cleanup; 9 | import lombok.SneakyThrows; 10 | import org.junit.jupiter.api.Assertions; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | 18 | public abstract class PrinterTestBase extends BaseTest { 19 | 20 | @Test 21 | protected void testPrint() { 22 | String source = getSource(); 23 | @Cleanup Parser parser = getParser(); 24 | @Cleanup Tree tree = parser.parse(source); 25 | @Cleanup TreeCursor cursor = getCursor(tree); 26 | TreePrinter printer = getPrinter(cursor); 27 | String expected = getExpected(); 28 | String actual = printer.print(); 29 | Assertions.assertEquals(expected, actual); 30 | } 31 | 32 | @Test 33 | @SneakyThrows(IOException.class) 34 | protected void testExport() { 35 | String source = getSource(); 36 | @Cleanup Parser parser = getParser(); 37 | @Cleanup Tree tree = parser.parse(source); 38 | @Cleanup TreeCursor cursor = getCursor(tree); 39 | TreePrinter printer = getPrinter(cursor); 40 | String expected = getExpected(); 41 | File file = printer.export(); 42 | Path path = file.toPath(); 43 | String actual = Files.readString(path); 44 | Files.delete(path); 45 | Assertions.assertEquals(expected, actual); 46 | } 47 | 48 | @Test 49 | protected void testPrinterThrows() { 50 | Assertions.assertThrows(NullPointerException.class, () -> getPrinter(null)); 51 | } 52 | 53 | protected Language getLanguage() { 54 | return Language.JAVA; 55 | } 56 | 57 | protected Parser getParser() { 58 | return Parser.builder() 59 | .language(getLanguage()) 60 | .build(); 61 | } 62 | 63 | protected TreeCursor getCursor(Tree tree) { 64 | return tree.getRootNode().walk(); 65 | } 66 | 67 | protected String getSource() { 68 | return "package ch.usi.si;\n" + 69 | "\n" + 70 | "public class Main {\n" + 71 | " public static void main(String[] args) {\n" + 72 | " //line comment\n" + 73 | " System.out.println(\"Hello, World!\");\n" + 74 | " }\n" + 75 | "}"; 76 | } 77 | 78 | protected abstract String getExpected(); 79 | 80 | protected abstract TreePrinter getPrinter(TreeCursor cursor); 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/printer/SymbolicExpressionPrinterTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.TreeCursor; 4 | 5 | class SymbolicExpressionPrinterTest extends PrinterTestBase { 6 | 7 | @Override 8 | protected String getExpected() { 9 | return 10 | "(program " + 11 | "(package_declaration " + 12 | "(scoped_identifier " + 13 | "scope: (scoped_identifier " + 14 | "scope: (identifier) " + 15 | "name: (identifier)" + 16 | ") " + 17 | "name: (identifier)" + 18 | ")" + 19 | ") " + 20 | "(class_declaration " + 21 | "(modifiers) " + 22 | "name: (identifier) " + 23 | "body: (class_body " + 24 | "(method_declaration " + 25 | "(modifiers) " + 26 | "type: (void_type) " + 27 | "name: (identifier) " + 28 | "parameters: (formal_parameters " + 29 | "(formal_parameter " + 30 | "type: (array_type " + 31 | "element: (type_identifier) " + 32 | "dimensions: (dimensions)" + 33 | ") " + 34 | "name: (identifier)" + 35 | ")" + 36 | ") " + 37 | "body: (block " + 38 | "(line_comment) " + 39 | "(expression_statement " + 40 | "(method_invocation " + 41 | "object: (field_access " + 42 | "object: (identifier) " + 43 | "field: (identifier)" + 44 | ") " + 45 | "name: (identifier) " + 46 | "arguments: (argument_list " + 47 | "(string_literal " + 48 | "(string_fragment)" + 49 | ")" + 50 | ")" + 51 | ")" + 52 | ")" + 53 | ")" + 54 | ")" + 55 | ")" + 56 | ")" + 57 | ")"; 58 | } 59 | 60 | @Override 61 | protected TreePrinter getPrinter(TreeCursor cursor) { 62 | return new SymbolicExpressionPrinter(cursor); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/printer/SyntaxTreePrinterTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.TreeCursor; 4 | 5 | class SyntaxTreePrinterTest extends PrinterTestBase { 6 | 7 | @Override 8 | protected String getExpected() { 9 | return "program [0:0] - [7:1]\n" + 10 | " package_declaration [0:0] - [0:18]\n" + 11 | " scoped_identifier [0:8] - [0:17]\n" + 12 | " scope: scoped_identifier [0:8] - [0:14]\n" + 13 | " scope: identifier [0:8] - [0:10]\n" + 14 | " name: identifier [0:11] - [0:14]\n" + 15 | " name: identifier [0:15] - [0:17]\n" + 16 | " class_declaration [2:0] - [7:1]\n" + 17 | " modifiers [2:0] - [2:6]\n" + 18 | " name: identifier [2:13] - [2:17]\n" + 19 | " body: class_body [2:18] - [7:1]\n" + 20 | " method_declaration [3:4] - [6:5]\n" + 21 | " modifiers [3:4] - [3:17]\n" + 22 | " type: void_type [3:18] - [3:22]\n" + 23 | " name: identifier [3:23] - [3:27]\n" + 24 | " parameters: formal_parameters [3:27] - [3:42]\n" + 25 | " formal_parameter [3:28] - [3:41]\n" + 26 | " type: array_type [3:28] - [3:36]\n" + 27 | " element: type_identifier [3:28] - [3:34]\n" + 28 | " dimensions: dimensions [3:34] - [3:36]\n" + 29 | " name: identifier [3:37] - [3:41]\n" + 30 | " body: block [3:43] - [6:5]\n" + 31 | " line_comment [4:8] - [4:22]\n" + 32 | " expression_statement [5:8] - [5:44]\n" + 33 | " method_invocation [5:8] - [5:43]\n" + 34 | " object: field_access [5:8] - [5:18]\n" + 35 | " object: identifier [5:8] - [5:14]\n" + 36 | " field: identifier [5:15] - [5:18]\n" + 37 | " name: identifier [5:19] - [5:26]\n" + 38 | " arguments: argument_list [5:26] - [5:43]\n" + 39 | " string_literal [5:27] - [5:42]\n" + 40 | " string_fragment [5:28] - [5:41]\n"; 41 | } 42 | 43 | @Override 44 | protected TreePrinter getPrinter(TreeCursor cursor) { 45 | return new SyntaxTreePrinter(cursor); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/ch/usi/si/seart/treesitter/printer/XMLPrinterTest.java: -------------------------------------------------------------------------------- 1 | package ch.usi.si.seart.treesitter.printer; 2 | 3 | import ch.usi.si.seart.treesitter.TreeCursor; 4 | 5 | class XMLPrinterTest extends PrinterTestBase { 6 | 7 | @Override 8 | protected String getExpected() { 9 | return "" + 10 | "" + 11 | "" + 12 | "" + 13 | "" + 14 | "" + 15 | "" + 16 | "" + 17 | "" + 18 | "" + 19 | "" + 20 | "" + 21 | "" + 22 | "" + 23 | "" + 24 | "" + 25 | "" + 26 | "" + 27 | "" + 28 | "" + 29 | "" + 30 | "" + 31 | "" + 32 | "" + 33 | "" + 34 | "" + 35 | "" + 36 | "" + 37 | "" + 38 | "" + 39 | "" + 40 | "" + 41 | "" + 42 | "" + 43 | "" + 44 | "" + 45 | "" + 46 | "" + 47 | "" + 48 | "" + 49 | "" + 50 | "" + 51 | "" + 52 | "" + 53 | "" + 54 | "" + 55 | "" + 56 | "" + 57 | "" + 58 | "" + 59 | "" + 60 | "" + 61 | "" + 62 | "" + 63 | "" + 64 | "" + 65 | "" + 66 | "" + 67 | "" + 68 | "" + 69 | "" + 70 | "" + 71 | "" + 72 | "" + 73 | ""; 74 | } 75 | 76 | @Override 77 | protected TreePrinter getPrinter(TreeCursor cursor) { 78 | return new XMLPrinter(cursor); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.logFile=System.out 2 | org.slf4j.simpleLogger.defaultLogLevel=debug 3 | org.slf4j.simpleLogger.showThreadName=false 4 | org.slf4j.simpleLogger.showDateTime=true 5 | org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss.SSS 6 | -------------------------------------------------------------------------------- /upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function get_default_branch() { 4 | TARGET="$1" 5 | HEAD=refs/remotes/origin/HEAD 6 | RESULT=$(git -C "$TARGET" symbolic-ref "$HEAD" --short) 7 | echo "$RESULT" 8 | } 9 | 10 | set -e 11 | 12 | SUBMODULE=$1 13 | 14 | if [ -z "$SUBMODULE" ]; then 15 | echo "Usage: $(basename "$0") [checkout_target]" 16 | exit 1 17 | fi 18 | 19 | CHECKOUT_TARGET="${2:-$(get_default_branch "$SUBMODULE")}" 20 | 21 | git -C "$SUBMODULE" fetch --all 22 | git -C "$SUBMODULE" checkout "$CHECKOUT_TARGET" 23 | git add "$SUBMODULE" 24 | -------------------------------------------------------------------------------- /version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from argparse import ArgumentParser 4 | from git import Repo as GitRepository 5 | from os import getcwd as cwd 6 | from os.path import dirname, realpath 7 | from os.path import join as path 8 | 9 | __location__ = realpath(path(cwd(), dirname(__file__))) 10 | 11 | if __name__ == "__main__": 12 | parser = ArgumentParser(description="Generate tree-sitter API version class.") 13 | parser.add_argument( 14 | "-o", 15 | "--output", 16 | default=path(__location__, "TreeSitter.java"), 17 | help="Output file path.", 18 | ) 19 | args = parser.parse_args() 20 | tree_sitter = path(__location__, "tree-sitter") 21 | with open(args.output, "w") as output, GitRepository(tree_sitter) as repository: 22 | commit = repository.head.commit 23 | tags = [tag for tag in repository.tags if tag.commit == commit] 24 | output.write(f"""\ 25 | /* 26 | * MIT License 27 | * 28 | * Copyright (c) 2022-present SEART Research Group and Contributors 29 | * 30 | * Permission is hereby granted, free of charge, to any person obtaining a copy 31 | * of this software and associated documentation files (the "Software"), to deal 32 | * in the Software without restriction, including without limitation the rights 33 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | * copies of the Software, and to permit persons to whom the Software is 35 | * furnished to do so, subject to the following conditions: 36 | * 37 | * The above copyright notice and this permission notice shall be included in all 38 | * copies or substantial portions of the Software. 39 | * 40 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 46 | * SOFTWARE. 47 | */ 48 | package ch.usi.si.seart.treesitter.version; 49 | 50 | import lombok.experimental.UtilityClass; 51 | 52 | /** 53 | * Utility used for obtaining the current version of the {{@code tree-sitter}} API. 54 | * 55 | * @author Ozren Dabić 56 | * @since 1.11.0 57 | */ 58 | @UtilityClass 59 | public class TreeSitter {{ 60 | 61 | public static final String SHA = \"{commit.hexsha}\"; 62 | 63 | public static final String TAG = \"{next((tag.name for tag in tags), "")}\"; 64 | 65 | /** 66 | * Get the current version of {{@code tree-sitter}}. 67 | * 68 | * @return the semantic version string, along with a commit SHA 69 | */ 70 | public String getVersion() {{ 71 | return TAG + \" (\" + SHA + \")\"; 72 | }} 73 | 74 | /** 75 | * The latest ABI version that is supported by the current version of the library. 76 | * When languages are generated by the {{@code tree-sitter}} CLI, they are assigned 77 | * an ABI version number that corresponds to the current CLI version. 78 | * 79 | * @return current ABI version number 80 | * @since 1.12.0 81 | */ 82 | public native int getCurrentABIVersion(); 83 | 84 | /** 85 | * The earliest ABI version that is supported by the current version of the library. 86 | * The {{@code tree-sitter}} library is generally backwards-compatible with languages 87 | * generated using older CLI versions, but is not forwards-compatible. 88 | * 89 | * @return earliest supported ABI version number 90 | * @since 1.12.0 91 | */ 92 | public native int getMinimumABIVersion(); 93 | }} 94 | """) 95 | --------------------------------------------------------------------------------