├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── build_publish.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── CMakeLists.txt ├── Cargo.toml ├── LICENSE ├── Makefile ├── Package.swift ├── README.md ├── binding.gyp ├── bindings ├── c │ ├── tree-sitter-gdscript.h │ └── tree-sitter-gdscript.pc.in ├── go │ ├── binding.go │ └── binding_test.go ├── nim │ └── treesittergdscript.nim ├── node │ ├── binding.cc │ ├── binding_test.js │ ├── index.d.ts │ └── index.js ├── python │ ├── tests │ │ └── test_binding.py │ └── tree_sitter_gdscript │ │ ├── __init__.py │ │ ├── __init__.pyi │ │ ├── binding.c │ │ └── py.typed ├── rust │ ├── build.rs │ └── lib.rs └── swift │ ├── TreeSitterGDScript │ └── gdscript.h │ └── TreeSitterGDScriptTests │ └── TreeSitterGDScriptTests.swift ├── examples ├── attributes.gd ├── basic_blocks.gd ├── gdscript-style-guide.gd ├── gdscript2 │ ├── pattern_guard.gd │ ├── static_vars.gd │ └── typed_for_loop.gd ├── indent.gd ├── inheriting_constructor.gd ├── lambda.gd ├── match_statement.gd └── test.gd ├── go.mod ├── grammar.js ├── package-lock.json ├── package.json ├── pyproject.toml ├── setup.py ├── src ├── grammar.json ├── node-types.json ├── parser.c ├── scanner.c └── tree_sitter │ ├── alloc.h │ ├── array.h │ └── parser.h ├── test └── corpus │ ├── issues.txt │ ├── issues │ ├── godot.txt │ └── setget.txt │ ├── lambdas.txt │ ├── match.txt │ ├── sample.txt │ ├── source.txt │ └── source2.txt └── tree-sitter.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.{json,toml,yml,gyp}] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [*.js] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.scm] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [*.{c,cc,h}] 19 | indent_style = space 20 | indent_size = 4 21 | 22 | [*.rs] 23 | indent_style = space 24 | indent_size = 4 25 | 26 | [*.{py,pyi}] 27 | indent_style = space 28 | indent_size = 4 29 | 30 | [*.swift] 31 | indent_style = space 32 | indent_size = 4 33 | 34 | [*.go] 35 | indent_style = tab 36 | indent_size = 8 37 | 38 | [Makefile] 39 | indent_style = tab 40 | indent_size = 8 41 | 42 | [parser.c] 43 | indent_size = 2 44 | 45 | [{alloc,array,parser}.h] 46 | indent_size = 2 47 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | # Generated source files 4 | src/*.json linguist-generated 5 | src/parser.c linguist-generated 6 | src/tree_sitter/* linguist-generated 7 | 8 | # C bindings 9 | bindings/c/* linguist-generated 10 | CMakeLists.txt linguist-generated 11 | Makefile linguist-generated 12 | 13 | # Rust bindings 14 | bindings/rust/* linguist-generated 15 | Cargo.toml linguist-generated 16 | Cargo.lock linguist-generated 17 | 18 | # Node.js bindings 19 | bindings/node/* linguist-generated 20 | binding.gyp linguist-generated 21 | package.json linguist-generated 22 | package-lock.json linguist-generated 23 | 24 | # Python bindings 25 | bindings/python/** linguist-generated 26 | setup.py linguist-generated 27 | pyproject.toml linguist-generated 28 | 29 | # Go bindings 30 | bindings/go/* linguist-generated 31 | go.mod linguist-generated 32 | go.sum linguist-generated 33 | 34 | # Swift bindings 35 | bindings/swift/** linguist-generated 36 | Package.swift linguist-generated 37 | Package.resolved linguist-generated 38 | -------------------------------------------------------------------------------- /.github/workflows/build_publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will 2 | # - test tree sitter grammar 3 | # - upload build native binaries as an artifact for each major platform 4 | # - download artifacts for each major platform and bundle them to be published 5 | # to npm 6 | # when a new version tag is pushed to the master branch. 7 | name: Test Build Publish 8 | 9 | on: 10 | push: 11 | tags: [v*] 12 | 13 | jobs: 14 | 15 | build_native_binaries: 16 | strategy: 17 | matrix: 18 | # Use macos-14 for arm, however the artifact upload name conflicts with 19 | # macos-latest. There's probably a way to crossbuild for arm with 20 | # prebuildify. 21 | os: [macos-latest, ubuntu-latest, windows-latest] 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: actions/setup-node@v4 26 | with: 27 | node-version: lts/* 28 | # Why is npm only occassionally installing peer dependencies? 29 | # I cannot get consistent peer dependency installs with the same command runs. 30 | # Should I always explicitly run npm i tree-sitter? The general consensus is yes. 31 | # But then, why does it install peer deps sometimes? 32 | - run: | 33 | node --version 34 | npm --version 35 | - run: npm ci --include=peer --include=optional --include=dev 36 | - run: npm i tree-sitter 37 | - run: npm run versions 38 | - run: npm test 39 | - run: npm run prebuild 40 | # upload-artifact@v4 requires each artifact name to be unique. 41 | - uses: actions/upload-artifact@v4 42 | with: 43 | name: prebuilds-${{ matrix.os }} 44 | path: prebuilds 45 | retention-days: 1 46 | 47 | 48 | publish_npm: 49 | needs: build_native_binaries 50 | runs-on: ubuntu-latest 51 | steps: 52 | - uses: actions/checkout@v4 53 | - uses: actions/setup-node@v4 54 | with: 55 | node-version: lts/* 56 | # https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-nodejs-packages#publishing-packages-to-the-npm-registry 57 | # https://github.com/actions/setup-node/issues/342 58 | # This is required to publish to npm. 59 | registry-url: 'https://registry.npmjs.org' 60 | - run: | 61 | node --version 62 | npm --version 63 | - run: npm ci 64 | # Download all artifacts and merge into prebuilds dir. 65 | - uses: actions/download-artifact@v4 66 | with: 67 | path: prebuilds 68 | pattern: prebuilds-* 69 | merge-multiple: true 70 | - run: ls -R prebuilds 71 | - run: npm publish 72 | env: 73 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 74 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'grammar.js' 7 | - 'corpus/**' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: lts/* 18 | - run: | 19 | node --version 20 | npm --version 21 | - run: npm ci --include=dev --include=optional --include=peer 22 | - run: npm test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust artifacts 2 | target/ 3 | 4 | # Node artifacts 5 | build/ 6 | prebuilds/ 7 | node_modules/ 8 | 9 | # Swift artifacts 10 | .build/ 11 | 12 | # Go artifacts 13 | _obj/ 14 | 15 | # Python artifacts 16 | .venv/ 17 | dist/ 18 | *.egg-info 19 | *.whl 20 | 21 | # C artifacts 22 | *.a 23 | *.so 24 | *.so.* 25 | *.dylib 26 | *.dll 27 | *.pc 28 | parser.exp 29 | parser.lib 30 | parser.obj 31 | scanner.obj 32 | 33 | # Grammar volatiles 34 | *.wasm 35 | *.obj 36 | *.o 37 | 38 | # Archives 39 | *.tar.gz 40 | *.tgz 41 | *.zip 42 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | corpus 2 | examples 3 | build 4 | script 5 | parser.exp 6 | parser.lib 7 | parser.obj 8 | scanner.obj 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(tree-sitter-gdscript 4 | VERSION "5.0.1" 5 | DESCRIPTION "Grammar for Godot's built-in scripting language." 6 | HOMEPAGE_URL "https://github.com/prestonknopp/tree-sitter-gdscript" 7 | LANGUAGES C) 8 | 9 | option(BUILD_SHARED_LIBS "Build using shared libraries" ON) 10 | option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF) 11 | 12 | set(TREE_SITTER_ABI_VERSION 14 CACHE STRING "Tree-sitter ABI version") 13 | if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$") 14 | unset(TREE_SITTER_ABI_VERSION CACHE) 15 | message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer") 16 | endif() 17 | 18 | find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") 19 | 20 | add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 21 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" 22 | COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json 23 | --abi=${TREE_SITTER_ABI_VERSION} 24 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 25 | COMMENT "Generating parser.c") 26 | 27 | add_library(tree-sitter-gdscript src/parser.c) 28 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) 29 | target_sources(tree-sitter-gdscript PRIVATE src/scanner.c) 30 | endif() 31 | target_include_directories(tree-sitter-gdscript PRIVATE src) 32 | 33 | target_compile_definitions(tree-sitter-gdscript PRIVATE 34 | $<$:TREE_SITTER_REUSE_ALLOCATOR> 35 | $<$:TREE_SITTER_DEBUG>) 36 | 37 | set_target_properties(tree-sitter-gdscript 38 | PROPERTIES 39 | C_STANDARD 11 40 | POSITION_INDEPENDENT_CODE ON 41 | SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" 42 | DEFINE_SYMBOL "") 43 | 44 | configure_file(bindings/c/tree-sitter-gdscript.pc.in 45 | "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-gdscript.pc" @ONLY) 46 | 47 | include(GNUInstallDirs) 48 | 49 | install(FILES bindings/c/tree-sitter-gdscript.h 50 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/tree_sitter") 51 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-gdscript.pc" 52 | DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig") 53 | install(TARGETS tree-sitter-gdscript 54 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") 55 | 56 | add_custom_target(ts-test "${TREE_SITTER_CLI}" test 57 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 58 | COMMENT "tree-sitter test") 59 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-gdscript" 3 | description = "Grammar for Godot's built-in scripting language." 4 | version = "5.0.1" 5 | authors = ["Preston Knopp "] 6 | license = "MIT" 7 | readme = "README.md" 8 | keywords = ["incremental", "parsing", "tree-sitter", "gdscript"] 9 | categories = ["parsing", "text-editors"] 10 | repository = "https://github.com/prestonknopp/tree-sitter-gdscript" 11 | edition = "2021" 12 | autoexamples = false 13 | 14 | build = "bindings/rust/build.rs" 15 | include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*", "tree-sitter.json"] 16 | 17 | [lib] 18 | path = "bindings/rust/lib.rs" 19 | 20 | [dependencies] 21 | tree-sitter-language = "0.1" 22 | 23 | [build-dependencies] 24 | cc = "1.1.22" 25 | 26 | [dev-dependencies] 27 | tree-sitter = "0.24.6" 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Max Brunsfeld 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | $(error Windows is not supported) 3 | endif 4 | 5 | LANGUAGE_NAME := tree-sitter-gdscript 6 | HOMEPAGE_URL := https://github.com/prestonknopp/tree-sitter-gdscript 7 | VERSION := 5.0.1 8 | 9 | # repository 10 | SRC_DIR := src 11 | 12 | TS ?= tree-sitter 13 | 14 | # install directory layout 15 | PREFIX ?= /usr/local 16 | INCLUDEDIR ?= $(PREFIX)/include 17 | LIBDIR ?= $(PREFIX)/lib 18 | PCLIBDIR ?= $(LIBDIR)/pkgconfig 19 | 20 | # source/object files 21 | PARSER := $(SRC_DIR)/parser.c 22 | EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c)) 23 | OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS)) 24 | 25 | # flags 26 | ARFLAGS ?= rcs 27 | override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC 28 | 29 | # ABI versioning 30 | SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER)) 31 | SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION))) 32 | 33 | # OS-specific bits 34 | ifeq ($(shell uname),Darwin) 35 | SOEXT = dylib 36 | SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT) 37 | SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT) 38 | LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks 39 | else 40 | SOEXT = so 41 | SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) 42 | SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) 43 | LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) 44 | endif 45 | ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) 46 | PCLIBDIR := $(PREFIX)/libdata/pkgconfig 47 | endif 48 | 49 | all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc 50 | 51 | lib$(LANGUAGE_NAME).a: $(OBJS) 52 | $(AR) $(ARFLAGS) $@ $^ 53 | 54 | lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS) 55 | $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@ 56 | ifneq ($(STRIP),) 57 | $(STRIP) $@ 58 | endif 59 | 60 | $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in 61 | sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \ 62 | -e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \ 63 | -e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \ 64 | -e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \ 65 | -e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \ 66 | -e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@ 67 | 68 | $(PARSER): $(SRC_DIR)/grammar.json 69 | $(TS) generate $^ 70 | 71 | install: all 72 | install -d '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)' 73 | install -m644 bindings/c/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h 74 | install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 75 | install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a 76 | install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) 77 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) 78 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) 79 | 80 | uninstall: 81 | $(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \ 82 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \ 83 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \ 84 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \ 85 | '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \ 86 | '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 87 | 88 | clean: 89 | $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) 90 | 91 | test: 92 | $(TS) test 93 | 94 | .PHONY: all install uninstall clean test 95 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "TreeSitterGDScript", 6 | products: [ 7 | .library(name: "TreeSitterGDScript", targets: ["TreeSitterGDScript"]), 8 | ], 9 | dependencies: [ 10 | .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.8.0"), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "TreeSitterGDScript", 15 | dependencies: [], 16 | path: ".", 17 | sources: [ 18 | "src/parser.c", 19 | "src/scanner.c", 20 | ], 21 | resources: [ 22 | .copy("queries") 23 | ], 24 | publicHeadersPath: "bindings/swift", 25 | cSettings: [.headerSearchPath("src")] 26 | ), 27 | .testTarget( 28 | name: "TreeSitterGDScriptTests", 29 | dependencies: [ 30 | "SwiftTreeSitter", 31 | "TreeSitterGDScript", 32 | ], 33 | path: "bindings/swift/TreeSitterGDScriptTests" 34 | ) 35 | ], 36 | cLanguageStandard: .c11 37 | ) 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tree-sitter-gdscript 2 | ==================== 3 | 4 | GDScript grammar for [tree-sitter][]. 5 | 6 | ## Latest Godot Commit Syntactically Synced 7 | 8 | Note: *Some commits may have been missed.* 9 | 10 | ```bash 11 | git log --oneline --no-merges modules/gdscript 12 | ``` 13 | 14 | [6ae54fd787](https://github.com/godotengine/godot/commits/6ae54fd787) 15 | 16 | ## How To 17 | 18 | - Test grammar 19 | 1. `npm run genTest` 20 | - Test scanner 21 | 1. Edit "src/scanner.c" 22 | 1. `npm run test`, no need to generate. 23 | - Build prebuilds 24 | 1. `npm run genTest` 25 | 1. `npm run prebuild` 26 | - Build with node-gyp 27 | 1. `npm run genTest` 28 | 1. `npm install node-gyp` 29 | 1. `node-gyp rebuild` 30 | - Edit 31 | 1. Write tests in corpus to express behavior. 32 | 1. Make grammar or scanner edits. 33 | 1. See above for running tests. 34 | 1. `npm run format` 35 | 1. Commit changes. 36 | - If commit is an issue fix, prefix message with `fix(#):` 37 | - List the rules changed in commit message. 38 | - Note what rules need to be updated in [nvim-treesitter][] queries. 39 | 1. Commit generated files with the latest non-wip commit. 40 | 1. Push 41 | - Release 42 | 1. Manually edit version in package files: CMakeLists.txt, Cargo.toml, 43 | Makefile, pyproject.toml, tree-sitter.json 44 | 1. `npm version -m "<> version bump"` 45 | 1. `git push --follow-tags` 46 | 47 | Note: `node-gyp-build` will check for binaries in both `build` and `prebuilds` 48 | directories. 49 | 50 | [tree-sitter]: https://github.com/tree-sitter/tree-sitter 51 | [nvim-treesitter]: https://github.com/nvim-treesitter/nvim-treesitter 52 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_gdscript_binding", 5 | "dependencies": [ 6 | " 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | extern "C" TSLanguage *tree_sitter_gdscript(); 6 | 7 | // "tree-sitter", "language" hashed with BLAKE2 8 | const napi_type_tag LANGUAGE_TYPE_TAG = { 9 | 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16 10 | }; 11 | 12 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 13 | exports["name"] = Napi::String::New(env, "gdscript"); 14 | auto language = Napi::External::New(env, tree_sitter_gdscript()); 15 | language.TypeTag(&LANGUAGE_TYPE_TAG); 16 | exports["language"] = language; 17 | return exports; 18 | } 19 | 20 | NODE_API_MODULE(tree_sitter_gdscript_binding, Init) 21 | -------------------------------------------------------------------------------- /bindings/node/binding_test.js: -------------------------------------------------------------------------------- 1 | const assert = require("node:assert"); 2 | const { test } = require("node:test"); 3 | 4 | const Parser = require("tree-sitter"); 5 | 6 | test("can load grammar", () => { 7 | const parser = new Parser(); 8 | assert.doesNotThrow(() => parser.setLanguage(require("."))); 9 | }); 10 | -------------------------------------------------------------------------------- /bindings/node/index.d.ts: -------------------------------------------------------------------------------- 1 | type BaseNode = { 2 | type: string; 3 | named: boolean; 4 | }; 5 | 6 | type ChildNode = { 7 | multiple: boolean; 8 | required: boolean; 9 | types: BaseNode[]; 10 | }; 11 | 12 | type NodeInfo = 13 | | (BaseNode & { 14 | subtypes: BaseNode[]; 15 | }) 16 | | (BaseNode & { 17 | fields: { [name: string]: ChildNode }; 18 | children: ChildNode[]; 19 | }); 20 | 21 | type Language = { 22 | name: string; 23 | language: unknown; 24 | nodeTypeInfo: NodeInfo[]; 25 | }; 26 | 27 | declare const language: Language; 28 | export = language; 29 | -------------------------------------------------------------------------------- /bindings/node/index.js: -------------------------------------------------------------------------------- 1 | const root = require("path").join(__dirname, "..", ".."); 2 | 3 | module.exports = 4 | typeof process.versions.bun === "string" 5 | // Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time 6 | ? require(`../../prebuilds/${process.platform}-${process.arch}/tree-sitter-gdscript.node`) 7 | : require("node-gyp-build")(root); 8 | 9 | try { 10 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 11 | } catch (_) {} 12 | -------------------------------------------------------------------------------- /bindings/python/tests/test_binding.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import tree_sitter, tree_sitter_gdscript 4 | 5 | 6 | class TestLanguage(TestCase): 7 | def test_can_load_grammar(self): 8 | try: 9 | tree_sitter.Language(tree_sitter_gdscript.language()) 10 | except Exception: 11 | self.fail("Error loading GDScript grammar") 12 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gdscript/__init__.py: -------------------------------------------------------------------------------- 1 | """Grammar for Godot's built-in scripting language.""" 2 | 3 | from importlib.resources import files as _files 4 | 5 | from ._binding import language 6 | 7 | 8 | def _get_query(name, file): 9 | query = _files(f"{__package__}.queries") / file 10 | globals()[name] = query.read_text() 11 | return globals()[name] 12 | 13 | 14 | def __getattr__(name): 15 | # NOTE: uncomment these to include any queries that this grammar contains: 16 | 17 | # if name == "HIGHLIGHTS_QUERY": 18 | # return _get_query("HIGHLIGHTS_QUERY", "highlights.scm") 19 | # if name == "INJECTIONS_QUERY": 20 | # return _get_query("INJECTIONS_QUERY", "injections.scm") 21 | # if name == "LOCALS_QUERY": 22 | # return _get_query("LOCALS_QUERY", "locals.scm") 23 | # if name == "TAGS_QUERY": 24 | # return _get_query("TAGS_QUERY", "tags.scm") 25 | 26 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 27 | 28 | 29 | __all__ = [ 30 | "language", 31 | # "HIGHLIGHTS_QUERY", 32 | # "INJECTIONS_QUERY", 33 | # "LOCALS_QUERY", 34 | # "TAGS_QUERY", 35 | ] 36 | 37 | 38 | def __dir__(): 39 | return sorted(__all__ + [ 40 | "__all__", "__builtins__", "__cached__", "__doc__", "__file__", 41 | "__loader__", "__name__", "__package__", "__path__", "__spec__", 42 | ]) 43 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gdscript/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Final 2 | 3 | # NOTE: uncomment these to include any queries that this grammar contains: 4 | 5 | # HIGHLIGHTS_QUERY: Final[str] 6 | # INJECTIONS_QUERY: Final[str] 7 | # LOCALS_QUERY: Final[str] 8 | # TAGS_QUERY: Final[str] 9 | 10 | def language() -> object: ... 11 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gdscript/binding.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | TSLanguage *tree_sitter_gdscript(void); 6 | 7 | static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 8 | return PyCapsule_New(tree_sitter_gdscript(), "tree_sitter.Language", NULL); 9 | } 10 | 11 | static PyMethodDef methods[] = { 12 | {"language", _binding_language, METH_NOARGS, 13 | "Get the tree-sitter language for this grammar."}, 14 | {NULL, NULL, 0, NULL} 15 | }; 16 | 17 | static struct PyModuleDef module = { 18 | .m_base = PyModuleDef_HEAD_INIT, 19 | .m_name = "_binding", 20 | .m_doc = NULL, 21 | .m_size = -1, 22 | .m_methods = methods 23 | }; 24 | 25 | PyMODINIT_FUNC PyInit__binding(void) { 26 | return PyModule_Create(&module); 27 | } 28 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gdscript/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrestonKnopp/tree-sitter-gdscript/2ed3ef9e30a7998a98eea4dfbcd308678f93b88f/bindings/python/tree_sitter_gdscript/py.typed -------------------------------------------------------------------------------- /bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | 4 | let mut c_config = cc::Build::new(); 5 | c_config.std("c11").include(src_dir); 6 | 7 | #[cfg(target_env = "msvc")] 8 | c_config.flag("-utf-8"); 9 | 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 13 | 14 | let scanner_path = src_dir.join("scanner.c"); 15 | c_config.file(&scanner_path); 16 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 17 | 18 | c_config.compile("tree-sitter-gdscript"); 19 | } 20 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides GDScript language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [LANGUAGE][] constant to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = r#" 8 | //! "#; 9 | //! let mut parser = tree_sitter::Parser::new(); 10 | //! let language = tree_sitter_gdscript::LANGUAGE; 11 | //! parser 12 | //! .set_language(&language.into()) 13 | //! .expect("Error loading GDScript parser"); 14 | //! let tree = parser.parse(code, None).unwrap(); 15 | //! assert!(!tree.root_node().has_error()); 16 | //! ``` 17 | //! 18 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 19 | //! [tree-sitter]: https://tree-sitter.github.io/ 20 | 21 | use tree_sitter_language::LanguageFn; 22 | 23 | extern "C" { 24 | fn tree_sitter_gdscript() -> *const (); 25 | } 26 | 27 | /// The tree-sitter [`LanguageFn`][LanguageFn] for this grammar. 28 | /// 29 | /// [LanguageFn]: https://docs.rs/tree-sitter-language/*/tree_sitter_language/struct.LanguageFn.html 30 | pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_gdscript) }; 31 | 32 | /// The content of the [`node-types.json`][] file for this grammar. 33 | /// 34 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 35 | pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 36 | 37 | // NOTE: uncomment these to include any queries that this grammar contains: 38 | 39 | // pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); 40 | // pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm"); 41 | // pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm"); 42 | // pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm"); 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | #[test] 47 | fn test_can_load_grammar() { 48 | let mut parser = tree_sitter::Parser::new(); 49 | parser 50 | .set_language(&super::LANGUAGE.into()) 51 | .expect("Error loading GDScript parser"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterGDScript/gdscript.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_GDSCRIPT_H_ 2 | #define TREE_SITTER_GDSCRIPT_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_gdscript(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_GDSCRIPT_H_ 17 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterGDScriptTests/TreeSitterGDScriptTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftTreeSitter 3 | import TreeSitterGDScript 4 | 5 | final class TreeSitterGDScriptTests: XCTestCase { 6 | func testCanLoadGrammar() throws { 7 | let parser = Parser() 8 | let language = Language(language: tree_sitter_gdscript()) 9 | XCTAssertNoThrow(try parser.setLanguage(language), 10 | "Error loading GDScript grammar") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/attributes.gd: -------------------------------------------------------------------------------- 1 | (V(1, 2) + V(2, 3)).b().c().d = ('Hello' + 'World').b.c.d() 2 | -------------------------------------------------------------------------------- /examples/basic_blocks.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | func _ready(): 4 | var x := 2 5 | for i in range(x): 6 | prints(i) 7 | 8 | while x > 0: 9 | print(x) 10 | 11 | if x > 0: 12 | print("if test") 13 | elif x < 0: 14 | print("if test") 15 | else: 16 | print("if test") 17 | -------------------------------------------------------------------------------- /examples/gdscript-style-guide.gd: -------------------------------------------------------------------------------- 1 | # Excerpted from: 2 | # https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html 3 | class_name StateMachine 4 | extends Node 5 | ## Hierarchical State machine for the player. 6 | ## 7 | ## Initializes states and delegates engine callbacks ([method Node._physics_process], 8 | ## [method Node._unhandled_input]) to the state. 9 | 10 | signal state_changed(previous, new) 11 | 12 | @export var initial_state: Node 13 | var is_active = true: 14 | set = set_is_active 15 | 16 | @onready var _state = initial_state: 17 | set = set_state 18 | @onready var _state_name = _state.name 19 | 20 | 21 | func _init(): 22 | add_to_group("state_machine") 23 | 24 | 25 | func _enter_tree(): 26 | print("this happens before the ready method!") 27 | 28 | 29 | func _ready(): 30 | state_changed.connect(_on_state_changed) 31 | _state.enter() 32 | 33 | 34 | func _unhandled_input(event): 35 | _state.unhandled_input(event) 36 | 37 | 38 | func _physics_process(delta): 39 | _state.physics_process(delta) 40 | 41 | 42 | func transition_to(target_state_path, msg={}): 43 | if not has_node(target_state_path): 44 | return 45 | 46 | var target_state = get_node(target_state_path) 47 | assert(target_state.is_composite == false) 48 | 49 | _state.exit() 50 | self._state = target_state 51 | _state.enter(msg) 52 | Events.player_state_changed.emit(_state.name) 53 | 54 | 55 | func set_is_active(value): 56 | is_active = value 57 | set_physics_process(value) 58 | set_process_unhandled_input(value) 59 | set_block_signals(not value) 60 | 61 | 62 | func set_state(value): 63 | _state = value 64 | _state_name = _state.name 65 | 66 | 67 | func _on_state_changed(previous, new): 68 | print("state changed") 69 | state_changed.emit() 70 | 71 | 72 | class State: 73 | var foo = 0 74 | 75 | func _init(): 76 | print("Hello!") 77 | -------------------------------------------------------------------------------- /examples/gdscript2/pattern_guard.gd: -------------------------------------------------------------------------------- 1 | func hello(): 2 | match 0: 3 | 0 when false: print("does not run") 4 | ## Invalid: 5 | # 1 when true, 2 when false: print("This does not parse") 6 | -------------------------------------------------------------------------------- /examples/gdscript2/static_vars.gd: -------------------------------------------------------------------------------- 1 | # Error: @export cannot be applied to static var 2 | # but annotations are still parsed. 3 | # @export static var hello: int 4 | 5 | static var foo = 1 6 | 7 | class Foo: 8 | static var bar 9 | 10 | static var name := "Me" 11 | static var value: int 12 | -------------------------------------------------------------------------------- /examples/gdscript2/typed_for_loop.gd: -------------------------------------------------------------------------------- 1 | func hello(): 2 | for i: int in [1,2,3]: 3 | pass 4 | -------------------------------------------------------------------------------- /examples/indent.gd: -------------------------------------------------------------------------------- 1 | take(func name(): 2 | print('hello') 3 | return 1 4 | pass 5 | 1 + 1 6 | , 7 | "" 8 | ) 9 | 10 | take(func name(): 11 | 1 + 1; pass 12 | , "") 13 | 14 | take(func name(): 15 | pass 16 | pass, "") 17 | 18 | take(func name(): "") 19 | -------------------------------------------------------------------------------- /examples/inheriting_constructor.gd: -------------------------------------------------------------------------------- 1 | func _init(arg1, arg2).(arg1, arg2): 2 | pass 3 | -------------------------------------------------------------------------------- /examples/lambda.gd: -------------------------------------------------------------------------------- 1 | # Verified with v4.0.beta17.official [c40020513] 2 | 3 | # FIXME: tree-sitter-gdscript can't parse this yet. 4 | var p = func(): if true: pass else: pass 5 | # If stmts can be inline. (They do not eval to their last expr.) 6 | t(func(): if true: 'string' else: 100) 7 | 8 | 9 | 10 | # GDScript v2 cannot parse this: 11 | #if true: print(1); print(2) else: print(3); print(4) 12 | 13 | # But, can parse this: 14 | # prints: 15 | # 1 16 | # 2 17 | var p = func(): if true: print(1); print(2) else: print(3); print(4) 18 | 19 | # prints: 20 | # outer 3 21 | #var c = func(): if true: if false: print('inner 1'); print('inner 2') else: print('outer 3') else: print(4) 22 | 23 | # some whitespace handling between statement groups. 24 | # returns: 2 25 | var x = func hello(): var b = 2; return b; 26 | 27 | if true: print('a') ; print('b'); 28 | else: print('c'); var a: int = 0; a + 1; return a 29 | # prints: 30 | # a 31 | # b 32 | 33 | # func bodies end at a comma. remaining args can follow. 34 | # no newline needed. 35 | take(func(): var a = 2; return x, 1) 36 | take(func(): 37 | print("hello world") 38 | pass, "") 39 | 40 | # func bodies also end at a paren. 41 | t(func(): 42 | print('hello') 43 | 1 + 1) 44 | 45 | # These are valid. 46 | t(func(): 47 | pass 48 | ) 49 | 50 | t( 51 | func(): pass, 52 | func(): 53 | pass 54 | ) 55 | 56 | # types can be specified 57 | var d = func(hello: int, string: String) -> int: 58 | return string.length() + hello 59 | 60 | t(func name(a: int, b: int) -> int: return a + b) 61 | t(func name(a: int, b: int) -> int: 62 | return a + b, "last argument") 63 | -------------------------------------------------------------------------------- /examples/match_statement.gd: -------------------------------------------------------------------------------- 1 | match x: 2 | 1: pass 3 | _: pass 4 | TYPE_ARRAY: pass 5 | var new_var: pass 6 | 7 | []: pass 8 | [1, 3, "test", null]: pass 9 | [var start, _]: pass 10 | [42, ..]: pass 11 | 12 | {}: pass 13 | {"name": "Dennis"}: pass 14 | {"name": "Dennis", "age": var age}: pass 15 | {"key": "godotisawesome", ..}: pass 16 | 17 | 1, 2, 3: pass 18 | -------------------------------------------------------------------------------- /examples/test.gd: -------------------------------------------------------------------------------- 1 | extends SceneTree 2 | 3 | # interesting, godot looks for a get_node() func when '$' is used on a non node? 4 | func get_node(a): 5 | return null 6 | 7 | # func hello(one,): 8 | # print('hello') 9 | 10 | class n extends Node: 11 | 12 | # no: remote static func rfunc(): 13 | # no: static remote func rfunc(): 14 | static func rfunc(): 15 | pass 16 | 17 | # no: master export var world = 'world' 18 | remotesync var hello = 'world' 19 | export remote var butt = 'world' 20 | 21 | 22 | var x setget setter,getter 23 | var y setget setter 24 | var a setget ,getter 25 | 26 | func setter(a):pass 27 | func getter():pass 28 | 29 | func _initialize(): 30 | var b: String = "hello" 31 | print(b) 32 | var butt = 'buttface' 33 | var next = 'next' 34 | var d = { 35 | 'hello': 'world', 36 | world = 'hello', 37 | butt: 'buttt', 38 | butt + next: 'buttnext', 39 | } 40 | var d2 = {hello='hello',} 41 | print(1 % 5) 42 | print(d) 43 | 44 | var n = $node 45 | n = $ node 46 | n = $'1/a' 47 | 48 | # var x = n is get_node(a) 49 | 50 | var res = 'Response' 51 | 52 | match 'Response': 53 | res: print('response') 54 | # no: 55 | # match {'Response':'ok'}: 56 | # {res: 'ok'}: print('okay') 57 | 58 | # no: 59 | # match {'Response':'ok'}: 60 | # {Response = 'ok'}: print('okay') 61 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/prestonknopp/tree-sitter-gdscript 2 | 3 | go 1.22 4 | 5 | require github.com/tree-sitter/go-tree-sitter v0.24.0 6 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | const PREC = { 2 | typed_parameter: -1, 3 | conditional: -1, 4 | 5 | parenthesized_expression: 1, 6 | or: 3, 7 | and: 4, 8 | in: 5, 9 | compare: 6, 10 | bitwise_or: 7, 11 | bitwise_and: 8, 12 | xor: 9, 13 | shift: 10, 14 | plus: 11, 15 | times: 12, 16 | power: 13, 17 | unary: 14, 18 | is: 15, 19 | as: 16, 20 | call: 17, 21 | attribute: 18, 22 | attribute_expression: 19, 23 | type: 20, 24 | }; 25 | 26 | module.exports = grammar({ 27 | name: "gdscript", 28 | 29 | word: ($) => $._identifier, 30 | 31 | extras: ($) => [$.comment, /[\s\uFEFF\u2060\u200B]|\\\r?\n/], 32 | 33 | externals: ($) => [ 34 | $._newline, 35 | $._indent, 36 | $._dedent, 37 | $._string_start, 38 | $._string_content, 39 | $._string_end, 40 | 41 | // Mark comments as external tokens so that the external scanner is always 42 | // invoked, even if no external token is expected. This allows for better 43 | // error recovery, because the external scanner can maintain the overall 44 | // structure by returning dedent tokens whenever a dedent occurs, even 45 | // if no dedent is expected. 46 | $.comment, 47 | 48 | // Allow the external scanner to check for the validity of closing brackets 49 | // so that it can avoid returning dedent tokens between brackets. 50 | "]", 51 | ")", 52 | "}", 53 | 54 | // Allow the external scanner to check for valid comma and colon tokens when 55 | // scanning for a $._body_end token. 56 | ",", 57 | // Allowing the scanner to check if colon is a valid token when 58 | // parsing body ends works for expected cases. One case is using a lambda as 59 | // a dictionary key e.g. `{func(): pass: 'value'}`. 60 | // However, it breaks nested if else chains. 61 | /* ":", */ 62 | $._body_end, 63 | ], 64 | 65 | inline: ($) => [$._simple_statement, $._compound_statement], 66 | 67 | supertypes: ($) => [ 68 | $._compound_statement, 69 | $._pattern, 70 | $._expression, 71 | $._primary_expression, 72 | $._attribute_expression, 73 | $._parameters, 74 | ], 75 | 76 | rules: { 77 | source: ($) => repeat($._statement), 78 | 79 | // ----------------------------------------------------------------------------- 80 | // - Atoms - 81 | // ----------------------------------------------------------------------------- 82 | 83 | _identifier: ($) => /[a-zA-Z_][a-zA-Z_0-9]*/, 84 | // any "symbol" 85 | identifier: ($) => $._identifier, 86 | // named symbol of a statement 87 | // such as a function name or class name 88 | name: ($) => $._identifier, 89 | comment: ($) => token(seq("#", /.*/)), 90 | true: ($) => "true", 91 | false: ($) => "false", 92 | null: ($) => "null", 93 | static_keyword: ($) => "static", 94 | remote_keyword: ($) => 95 | choice( 96 | "remote", 97 | "master", 98 | "puppet", 99 | "remotesync", 100 | "mastersync", 101 | "puppetsync" 102 | ), 103 | 104 | escape_sequence: ($) => 105 | token( 106 | seq( 107 | "\\", 108 | choice( 109 | /u[a-fA-F\d]{4}/, 110 | /U[a-fA-F\d]{8}/, 111 | /x[a-fA-F\d]{2}/, 112 | /o\d{3}/, 113 | /\r\n/, 114 | /[^uxo]/ 115 | ) 116 | ) 117 | ), 118 | 119 | string: ($) => 120 | seq( 121 | alias($._string_start, '"'), 122 | repeat(choice($.escape_sequence, $._string_content)), 123 | alias($._string_end, '"') 124 | ), 125 | 126 | float: ($) => { 127 | const digits = repeat1(/[0-9]+_?/); 128 | const exponent = seq(/[eE][\+-]?/, digits); 129 | 130 | return token( 131 | choice( 132 | seq(digits, ".", optional(digits), optional(exponent)), 133 | seq(optional(digits), ".", digits, optional(exponent)), 134 | seq(digits, exponent) 135 | ) 136 | ); 137 | }, 138 | 139 | integer: ($) => 140 | token( 141 | choice( 142 | seq(choice("0x", "0X"), repeat1(/_?[A-Fa-f0-9]+/)), 143 | seq(choice("0o", "0O"), repeat1(/_?[0-7]+/)), 144 | seq(choice("0b", "0B"), repeat1(/_?[0-1]+/)), 145 | repeat1(/[0-9]+_?/) 146 | ) 147 | ), 148 | 149 | // This should be a token. 150 | string_name: ($) => seq("&", alias($.string, "value")), 151 | node_path: ($) => token(seq(choice("@", "^"), nodePathString())), 152 | get_node: ($) => 153 | token( 154 | seq( 155 | choice("$", "%"), 156 | choice(nodePathString(), /[a-zA-Z_][a-zA-Z_/0-9]*/) 157 | ) 158 | ), 159 | 160 | // ----------------------------------------------------------------------------- 161 | // - Type - 162 | // ----------------------------------------------------------------------------- 163 | 164 | // Higher precedence is required to avoid conflicts with the "in" keyword in 165 | // $.for_statement. 166 | type: ($) => 167 | prec(PREC.type, choice($.attribute, $.identifier, $.subscript)), 168 | 169 | // ----------------------------------------------------------------------------- 170 | // - Statements - 171 | // ----------------------------------------------------------------------------- 172 | 173 | _statement: ($) => choice($._simple_statements, $._compound_statement), 174 | 175 | body: ($) => 176 | choice( 177 | $._simple_statements, 178 | seq( 179 | $._indent, 180 | seq(repeat($._statement), choice($._body_end, $._dedent)) 181 | ), 182 | choice($._newline, $._body_end) 183 | ), 184 | 185 | // Simple statements 186 | 187 | _simple_statements: ($) => 188 | seq(trailSep1($._simple_statement, ";"), choice($._newline, $._body_end)), 189 | 190 | _simple_statement: ($) => 191 | choice( 192 | $._annotations, 193 | $.tool_statement, 194 | $.signal_statement, 195 | $.class_name_statement, 196 | $.extends_statement, 197 | $.expression_statement, 198 | $.export_variable_statement, 199 | $.onready_variable_statement, 200 | $.variable_statement, 201 | $.const_statement, 202 | $.return_statement, 203 | $.pass_statement, 204 | $.break_statement, 205 | $.breakpoint_statement, 206 | $.continue_statement 207 | ), 208 | 209 | expression_statement: ($) => 210 | choice($._expression, $.assignment, $.augmented_assignment), 211 | 212 | // -- Annotation 213 | 214 | annotation: ($) => seq("@", $.identifier, optional($.arguments)), 215 | 216 | // The syntax tree looks better when annotations are grouped in a container 217 | // node in contexts like variable_statement and function_definition. 218 | _annotations: ($) => repeat1($.annotation), 219 | annotations: ($) => $._annotations, 220 | 221 | // -- Variables 222 | 223 | inferred_type: ($) => choice(":=", seq(":", "=")), 224 | 225 | _variable_assignment: ($) => seq("=", field("value", $._rhs_expression)), 226 | _variable_inferred_type_assignment: ($) => 227 | seq(field("type", $.inferred_type), field("value", $._rhs_expression)), 228 | _variable_typed_assignment: ($) => 229 | seq(":", field("type", $.type), "=", field("value", $._rhs_expression)), 230 | 231 | _variable_typed_definition: ($) => 232 | choice(seq(":", field("type", $.type)), $._variable_typed_assignment), 233 | 234 | // -- SetGet 235 | 236 | set_body: ($) => seq("set", $.parameters, ":", alias($.body, "body")), 237 | get_body: ($) => 238 | seq( 239 | "get", 240 | // Let's alias parameters as an un-named node since 241 | // get does not take any parameters. 242 | optional(alias($.parameters, "()")), 243 | ":", 244 | alias($.body, "body") 245 | ), 246 | 247 | _set_assign: ($) => seq("set", "=", field("set", $.setter)), 248 | _get_assign: ($) => seq("get", "=", field("get", $.getter)), 249 | 250 | _setget_body: ($) => 251 | seq( 252 | ":", 253 | seq( 254 | $._indent, 255 | choice( 256 | seq(field("set", $.set_body), optional(field("get", $.get_body))), 257 | seq(field("get", $.get_body), optional(field("set", $.set_body))), 258 | seq($._set_assign, optional(seq(",", $._get_assign))), 259 | seq($._get_assign, optional(seq(",", $._set_assign))) 260 | ), 261 | $._dedent 262 | ) 263 | ), 264 | 265 | setter: ($) => $._identifier, 266 | getter: ($) => $._identifier, 267 | setget: ($) => 268 | choice( 269 | $._setget_body, 270 | seq( 271 | "setget", 272 | choice($.setter, seq($.setter, ",", $.getter), seq(",", $.getter)) 273 | ) 274 | ), 275 | 276 | _variable_statement: ($) => 277 | seq( 278 | optional($.annotations), 279 | optional(field("static", $.static_keyword)), 280 | "var", 281 | field("name", $.name), 282 | optional( 283 | choice( 284 | $._variable_typed_definition, 285 | $._variable_inferred_type_assignment, 286 | $._variable_assignment 287 | ) 288 | ), 289 | optional(field("setget", $.setget)) 290 | ), 291 | 292 | variable_statement: ($) => 293 | seq(optional($.remote_keyword), $._variable_statement), 294 | 295 | export_variable_statement: ($) => 296 | seq( 297 | "export", 298 | optional(field("export_arguments", $.arguments)), 299 | optional(choice("onready", $.remote_keyword)), 300 | $._variable_statement 301 | ), 302 | 303 | onready_variable_statement: ($) => seq("onready", $._variable_statement), 304 | 305 | const_statement: ($) => 306 | seq( 307 | "const", 308 | field("name", $.name), 309 | choice( 310 | $._variable_inferred_type_assignment, 311 | $._variable_typed_assignment, 312 | $._variable_assignment 313 | ) 314 | ), 315 | 316 | return_statement: ($) => seq("return", optional($._rhs_expression)), 317 | 318 | pass_statement: ($) => prec.left("pass"), 319 | break_statement: ($) => prec.left("break"), 320 | breakpoint_statement: ($) => "breakpoint", 321 | continue_statement: ($) => prec.left("continue"), 322 | tool_statement: ($) => "tool", 323 | 324 | signal_statement: ($) => seq("signal", $.name, optional($.parameters)), 325 | 326 | class_name_statement: ($) => 327 | seq( 328 | "class_name", 329 | $.name, 330 | optional(seq(",", field("icon_path", $.string))) 331 | ), 332 | 333 | extends_statement: ($) => seq("extends", choice($.string, $.type)), 334 | 335 | _compound_statement: ($) => 336 | choice( 337 | $.if_statement, 338 | $.for_statement, 339 | $.while_statement, 340 | $.function_definition, 341 | $.constructor_definition, 342 | $.class_definition, 343 | $.enum_definition, 344 | $.match_statement 345 | ), 346 | 347 | if_statement: ($) => 348 | seq( 349 | "if", 350 | field("condition", $._expression), 351 | ":", 352 | field("body", $.body), 353 | repeat(field("alternative", $.elif_clause)), 354 | optional(field("alternative", $.else_clause)) 355 | ), 356 | 357 | elif_clause: ($) => 358 | seq( 359 | "elif", 360 | field("condition", $._expression), 361 | ":", 362 | field("body", $.body) 363 | ), 364 | 365 | else_clause: ($) => seq("else", ":", field("body", $.body)), 366 | 367 | for_statement: ($) => 368 | seq( 369 | "for", 370 | field("left", $.identifier), 371 | optional(seq(":", field("type", $.type))), 372 | "in", 373 | field("right", $._expression), 374 | ":", 375 | field("body", $.body) 376 | ), 377 | 378 | while_statement: ($) => 379 | seq( 380 | "while", 381 | field("condition", $._expression), 382 | ":", 383 | field("body", $.body) 384 | ), 385 | 386 | class_definition: ($) => 387 | seq( 388 | "class", 389 | field("name", $.name), 390 | optional(field("extends", $.extends_statement)), 391 | ":", 392 | field("body", $.body) 393 | ), 394 | 395 | // -- Enum 396 | enum_definition: ($) => 397 | seq( 398 | "enum", 399 | optional(field("name", $.name)), 400 | field("body", $.enumerator_list) 401 | ), 402 | 403 | enumerator_list: ($) => seq("{", trailCommaSep1($.enumerator), "}"), 404 | 405 | _enumerator_expression: ($) => 406 | choice( 407 | $.integer, 408 | $.binary_operator, 409 | $.identifier, 410 | $.unary_operator, 411 | $.attribute, 412 | $.subscript, 413 | $.call, 414 | $.parenthesized_expression 415 | ), 416 | 417 | enumerator: ($) => 418 | seq( 419 | field("left", $.identifier), 420 | optional(seq("=", field("right", $._enumerator_expression))) 421 | ), 422 | 423 | // ----------------------------------------------------------------------------- 424 | // - Match - 425 | // ----------------------------------------------------------------------------- 426 | 427 | match_statement: ($) => 428 | seq( 429 | "match", 430 | field("value", $._expression), 431 | ":", 432 | field("body", $.match_body) 433 | ), 434 | 435 | match_body: ($) => seq($._indent, repeat1($.pattern_section), $._dedent), 436 | 437 | // Sources: 438 | // - https://github.com/godotengine/godot-proposals/issues/4775 439 | // - https://github.com/godotengine/godot/pull/80085 440 | // 441 | // One guard per section. Meaning Comma separated patterns cannot each have 442 | // a guard. 443 | pattern_guard: ($) => seq("when", $._expression), 444 | 445 | pattern_section: ($) => 446 | seq( 447 | commaSep1($._pattern), 448 | optional($.pattern_guard), 449 | ":", 450 | field("body", $.body) 451 | ), 452 | 453 | _pattern: ($) => choice($._primary_expression, $.pattern_binding), 454 | 455 | // Rather than creating distinct pattern array, dictionary, and expression 456 | // rules, we insert $.pattern_binding and $.pattern_open_ending into the 457 | // $.array and $.dictionary rules. Although, they are only valid in the 458 | // context of a pattern, this keeps the grammar simpler and allows us to 459 | // have arbitrary expressions in patterns. 460 | pattern_binding: ($) => seq("var", $.identifier), 461 | pattern_open_ending: ($) => "..", 462 | 463 | // ----------------------------------------------------------------------------- 464 | // - Expressions - 465 | // ----------------------------------------------------------------------------- 466 | 467 | _expression: ($) => 468 | choice( 469 | $._primary_expression, 470 | $.conditional_expression, 471 | $.await_expression 472 | ), 473 | 474 | _primary_expression: ($) => 475 | choice( 476 | $.binary_operator, 477 | $.identifier, 478 | $.string, 479 | $.integer, 480 | $.float, 481 | $.true, 482 | $.false, 483 | $.null, 484 | $.unary_operator, 485 | $.string_name, 486 | $.node_path, 487 | $.get_node, 488 | $.attribute, 489 | $.subscript, 490 | $.base_call, 491 | $.call, 492 | $.array, 493 | $.dictionary, 494 | $.parenthesized_expression 495 | ), 496 | 497 | _rhs_expression: ($) => choice($._expression, $.lambda), 498 | 499 | // This makes an attribute's ast linear 500 | // When attribute is used inside $.attribute it becomes recursive spaghetti 501 | _attribute_expression: ($) => 502 | prec( 503 | PREC.attribute_expression, 504 | choice( 505 | $.binary_operator, 506 | $.identifier, 507 | $.string, 508 | $.integer, 509 | $.float, 510 | $.true, 511 | $.false, 512 | $.null, 513 | $.unary_operator, 514 | $.node_path, 515 | $.get_node, 516 | $.subscript, 517 | $.base_call, 518 | $.call, 519 | $.array, 520 | $.dictionary, 521 | $.parenthesized_expression 522 | ) 523 | ), 524 | 525 | // -- Operators 526 | 527 | binary_operator: ($) => { 528 | // Inspired by tree-sitter-c 529 | const operators = [ 530 | ["in", PREC.in], 531 | ["and", PREC.and], 532 | ["&&", PREC.and], 533 | ["or", PREC.or], 534 | ["||", PREC.or], 535 | ["+", PREC.plus], 536 | ["-", PREC.plus], 537 | ["*", PREC.times], 538 | ["/", PREC.times], 539 | ["**", PREC.times], 540 | ["%", PREC.times], 541 | ["|", PREC.bitwise_or], 542 | ["&", PREC.bitwise_and], 543 | ["^", PREC.xor], 544 | ["<<", PREC.shift], 545 | [">>", PREC.shift], 546 | ["<", PREC.compare], 547 | ["<=", PREC.compare], 548 | ["==", PREC.compare], 549 | ["!=", PREC.compare], 550 | [">=", PREC.compare], 551 | [">", PREC.compare], 552 | ["as", PREC.as], 553 | [seq("is", optional("not")), PREC.is], 554 | ]; 555 | 556 | const choices = operators.map(([operator, precedence]) => { 557 | return prec.left( 558 | precedence, 559 | seq( 560 | field("left", $._primary_expression), 561 | field("op", operator), 562 | field("right", $._primary_expression) 563 | ) 564 | ); 565 | }); 566 | 567 | return choice(...choices); 568 | }, 569 | 570 | unary_operator: ($) => 571 | choice( 572 | prec(PREC.unary, seq(choice("not", "!"), $._primary_expression)), 573 | prec(PREC.unary, seq("-", $._primary_expression)), 574 | prec(PREC.unary, seq("+", $._primary_expression)), 575 | prec(PREC.unary, seq("~", $._primary_expression)) 576 | ), 577 | 578 | // -- Accessors 579 | subscript: ($) => seq($._primary_expression, "[", $._expression, "]"), 580 | 581 | attribute_call: ($) => prec(PREC.attribute, seq($.identifier, $.arguments)), 582 | attribute_subscript: ($) => 583 | prec(PREC.attribute, seq($.identifier, "[", $._primary_expression, "]")), 584 | attribute: ($) => 585 | prec( 586 | PREC.attribute, 587 | seq( 588 | $._attribute_expression, 589 | repeat1( 590 | seq( 591 | ".", 592 | choice($.attribute_subscript, $.attribute_call, $.identifier) 593 | ) 594 | ) 595 | ) 596 | ), 597 | 598 | conditional_expression: ($) => 599 | prec.right( 600 | PREC.conditional, 601 | seq($._expression, "if", $._expression, "else", $._expression) 602 | ), 603 | 604 | parenthesized_expression: ($) => 605 | prec(PREC.parenthesized_expression, seq("(", $._rhs_expression, ")")), 606 | 607 | // ----------------------------------------------------------------------------- 608 | // - Await - 609 | // ----------------------------------------------------------------------------- 610 | 611 | await_expression: ($) => seq("await", $._expression), 612 | 613 | // ----------------------------------------------------------------------------- 614 | // - Assignment - 615 | // ----------------------------------------------------------------------------- 616 | 617 | assignment: ($) => 618 | seq(field("left", $._expression), "=", field("right", $._rhs_expression)), 619 | 620 | augmented_assignment: ($) => 621 | seq( 622 | field("left", $._expression), 623 | choice( 624 | "+=", 625 | "-=", 626 | "*=", 627 | "/=", 628 | "**=", 629 | "%=", 630 | ">>=", 631 | "<<=", 632 | "&=", 633 | "^=", 634 | "|=" 635 | ), 636 | field("right", $._rhs_expression) 637 | ), 638 | 639 | // ----------------------------------------------------------------------------- 640 | // - Data Structs - 641 | // ----------------------------------------------------------------------------- 642 | 643 | pair: ($) => 644 | seq( 645 | choice( 646 | seq(field("key", $._rhs_expression), ":"), // Lambdas are allowed here. 647 | seq(field("key", $.identifier), "=") 648 | ), 649 | field("value", choice($._rhs_expression, $.pattern_binding)) 650 | ), 651 | 652 | dictionary: ($) => 653 | seq( 654 | "{", 655 | optional(trailCommaSep1($.pair)), 656 | optional($.pattern_open_ending), 657 | "}" 658 | ), 659 | 660 | array: ($) => 661 | seq( 662 | "[", 663 | optional(trailCommaSep1(choice($._rhs_expression, $.pattern_binding))), 664 | optional($.pattern_open_ending), 665 | "]" 666 | ), 667 | 668 | // ----------------------------------------------------------------------------- 669 | // - Function Definition - 670 | // ----------------------------------------------------------------------------- 671 | 672 | typed_parameter: ($) => 673 | prec(PREC.typed_parameter, seq($.identifier, ":", field("type", $.type))), 674 | 675 | default_parameter: ($) => 676 | seq($.identifier, "=", field("value", $._rhs_expression)), 677 | 678 | typed_default_parameter: ($) => 679 | prec( 680 | PREC.typed_parameter, 681 | seq( 682 | $.identifier, 683 | ":", 684 | field("type", $.type), 685 | "=", 686 | field("value", $._rhs_expression) 687 | ) 688 | ), 689 | 690 | _parameters: ($) => 691 | choice( 692 | $.identifier, 693 | $.typed_parameter, 694 | $.default_parameter, 695 | $.typed_default_parameter 696 | ), 697 | 698 | parameters: ($) => seq("(", optional(trailCommaSep1($._parameters)), ")"), 699 | 700 | _return_type: ($) => seq("->", field("return_type", $.type)), 701 | 702 | function_definition: ($) => 703 | seq( 704 | optional(choice($.static_keyword, $.remote_keyword)), 705 | optional($.annotations), 706 | "func", 707 | optional(field("name", $.name)), 708 | field("parameters", $.parameters), 709 | optional($._return_type), 710 | ":", 711 | field("body", $.body) 712 | ), 713 | 714 | lambda: ($) => 715 | seq( 716 | "func", 717 | optional($.name), 718 | $.parameters, 719 | optional($._return_type), 720 | ":", 721 | $.body 722 | ), 723 | 724 | constructor_definition: ($) => 725 | seq( 726 | "func", 727 | "_init", 728 | field("parameters", $.parameters), 729 | optional(seq(".", field("constructor_arguments", $.arguments))), 730 | optional(seq("->", field("return_type", $.type))), 731 | ":", 732 | field("body", $.body) 733 | ), 734 | 735 | // ----------------------------------------------------------------------------- 736 | // - Function Call - 737 | // ----------------------------------------------------------------------------- 738 | 739 | arguments: ($) => 740 | seq("(", optional(trailCommaSep1($._rhs_expression)), ")"), 741 | 742 | base_call: ($) => prec(PREC.call, seq(".", $.identifier, $.arguments)), 743 | 744 | call: ($) => prec(PREC.call, seq($._primary_expression, $.arguments)), 745 | }, // end rules 746 | }); 747 | 748 | function sep1(rule, separator) { 749 | return seq(rule, repeat(seq(separator, rule))); 750 | } 751 | 752 | function trailSep1(rule, sep) { 753 | return seq(sep1(rule, sep), optional(sep)); 754 | } 755 | 756 | function commaSep1(rule) { 757 | return sep1(rule, ","); 758 | } 759 | 760 | function trailCommaSep1(rule) { 761 | return trailSep1(rule, ","); 762 | } 763 | 764 | // This is a function instead of a rule since it's is used more than once and 765 | // token body must be made of terminals. This can be defined as a rule and 766 | // specify it as inlined, but this is fine. 767 | function nodePathString() { 768 | return choice( 769 | seq('"', /[0-9a-zA-Z_/\- .]*/, '"'), 770 | seq("'", /[0-9a-zA-Z_/\- .]*/, "'") 771 | ); 772 | } 773 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-gdscript", 3 | "version": "5.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tree-sitter-gdscript", 9 | "version": "5.0.1", 10 | "hasInstallScript": true, 11 | "license": "MIT", 12 | "dependencies": { 13 | "node-addon-api": "^8.3.1", 14 | "node-gyp-build": "^4.8.4" 15 | }, 16 | "devDependencies": { 17 | "prebuildify": "^6.0.1", 18 | "prettier": "^2.8.8", 19 | "tree-sitter-cli": "^0.24.7" 20 | }, 21 | "peerDependencies": { 22 | "tree-sitter": "^0.21.1" 23 | }, 24 | "peerDependenciesMeta": { 25 | "tree-sitter": { 26 | "optional": true 27 | } 28 | } 29 | }, 30 | "node_modules/base64-js": { 31 | "version": "1.5.1", 32 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 33 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 34 | "dev": true, 35 | "funding": [ 36 | { 37 | "type": "github", 38 | "url": "https://github.com/sponsors/feross" 39 | }, 40 | { 41 | "type": "patreon", 42 | "url": "https://www.patreon.com/feross" 43 | }, 44 | { 45 | "type": "consulting", 46 | "url": "https://feross.org/support" 47 | } 48 | ], 49 | "license": "MIT" 50 | }, 51 | "node_modules/bl": { 52 | "version": "4.1.0", 53 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 54 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 55 | "dev": true, 56 | "license": "MIT", 57 | "dependencies": { 58 | "buffer": "^5.5.0", 59 | "inherits": "^2.0.4", 60 | "readable-stream": "^3.4.0" 61 | } 62 | }, 63 | "node_modules/buffer": { 64 | "version": "5.7.1", 65 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 66 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 67 | "dev": true, 68 | "funding": [ 69 | { 70 | "type": "github", 71 | "url": "https://github.com/sponsors/feross" 72 | }, 73 | { 74 | "type": "patreon", 75 | "url": "https://www.patreon.com/feross" 76 | }, 77 | { 78 | "type": "consulting", 79 | "url": "https://feross.org/support" 80 | } 81 | ], 82 | "license": "MIT", 83 | "dependencies": { 84 | "base64-js": "^1.3.1", 85 | "ieee754": "^1.1.13" 86 | } 87 | }, 88 | "node_modules/chownr": { 89 | "version": "1.1.4", 90 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 91 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 92 | "dev": true, 93 | "license": "ISC" 94 | }, 95 | "node_modules/end-of-stream": { 96 | "version": "1.4.4", 97 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 98 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 99 | "dev": true, 100 | "license": "MIT", 101 | "dependencies": { 102 | "once": "^1.4.0" 103 | } 104 | }, 105 | "node_modules/fs-constants": { 106 | "version": "1.0.0", 107 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 108 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 109 | "dev": true, 110 | "license": "MIT" 111 | }, 112 | "node_modules/ieee754": { 113 | "version": "1.2.1", 114 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 115 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 116 | "dev": true, 117 | "funding": [ 118 | { 119 | "type": "github", 120 | "url": "https://github.com/sponsors/feross" 121 | }, 122 | { 123 | "type": "patreon", 124 | "url": "https://www.patreon.com/feross" 125 | }, 126 | { 127 | "type": "consulting", 128 | "url": "https://feross.org/support" 129 | } 130 | ], 131 | "license": "BSD-3-Clause" 132 | }, 133 | "node_modules/inherits": { 134 | "version": "2.0.4", 135 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 136 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 137 | "dev": true, 138 | "license": "ISC" 139 | }, 140 | "node_modules/minimist": { 141 | "version": "1.2.8", 142 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 143 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 144 | "dev": true, 145 | "license": "MIT", 146 | "funding": { 147 | "url": "https://github.com/sponsors/ljharb" 148 | } 149 | }, 150 | "node_modules/mkdirp-classic": { 151 | "version": "0.5.3", 152 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 153 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 154 | "dev": true, 155 | "license": "MIT" 156 | }, 157 | "node_modules/node-abi": { 158 | "version": "3.75.0", 159 | "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", 160 | "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", 161 | "dev": true, 162 | "license": "MIT", 163 | "dependencies": { 164 | "semver": "^7.3.5" 165 | }, 166 | "engines": { 167 | "node": ">=10" 168 | } 169 | }, 170 | "node_modules/node-addon-api": { 171 | "version": "8.3.1", 172 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", 173 | "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", 174 | "license": "MIT", 175 | "engines": { 176 | "node": "^18 || ^20 || >= 21" 177 | } 178 | }, 179 | "node_modules/node-gyp-build": { 180 | "version": "4.8.4", 181 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", 182 | "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", 183 | "license": "MIT", 184 | "bin": { 185 | "node-gyp-build": "bin.js", 186 | "node-gyp-build-optional": "optional.js", 187 | "node-gyp-build-test": "build-test.js" 188 | } 189 | }, 190 | "node_modules/npm-run-path": { 191 | "version": "3.1.0", 192 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", 193 | "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", 194 | "dev": true, 195 | "license": "MIT", 196 | "dependencies": { 197 | "path-key": "^3.0.0" 198 | }, 199 | "engines": { 200 | "node": ">=8" 201 | } 202 | }, 203 | "node_modules/once": { 204 | "version": "1.4.0", 205 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 206 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 207 | "dev": true, 208 | "license": "ISC", 209 | "dependencies": { 210 | "wrappy": "1" 211 | } 212 | }, 213 | "node_modules/path-key": { 214 | "version": "3.1.1", 215 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 216 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 217 | "dev": true, 218 | "license": "MIT", 219 | "engines": { 220 | "node": ">=8" 221 | } 222 | }, 223 | "node_modules/prebuildify": { 224 | "version": "6.0.1", 225 | "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.1.tgz", 226 | "integrity": "sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw==", 227 | "dev": true, 228 | "license": "MIT", 229 | "dependencies": { 230 | "minimist": "^1.2.5", 231 | "mkdirp-classic": "^0.5.3", 232 | "node-abi": "^3.3.0", 233 | "npm-run-path": "^3.1.0", 234 | "pump": "^3.0.0", 235 | "tar-fs": "^2.1.0" 236 | }, 237 | "bin": { 238 | "prebuildify": "bin.js" 239 | } 240 | }, 241 | "node_modules/prettier": { 242 | "version": "2.8.8", 243 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", 244 | "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", 245 | "dev": true, 246 | "license": "MIT", 247 | "bin": { 248 | "prettier": "bin-prettier.js" 249 | }, 250 | "engines": { 251 | "node": ">=10.13.0" 252 | }, 253 | "funding": { 254 | "url": "https://github.com/prettier/prettier?sponsor=1" 255 | } 256 | }, 257 | "node_modules/pump": { 258 | "version": "3.0.2", 259 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", 260 | "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", 261 | "dev": true, 262 | "license": "MIT", 263 | "dependencies": { 264 | "end-of-stream": "^1.1.0", 265 | "once": "^1.3.1" 266 | } 267 | }, 268 | "node_modules/readable-stream": { 269 | "version": "3.6.2", 270 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 271 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 272 | "dev": true, 273 | "license": "MIT", 274 | "dependencies": { 275 | "inherits": "^2.0.3", 276 | "string_decoder": "^1.1.1", 277 | "util-deprecate": "^1.0.1" 278 | }, 279 | "engines": { 280 | "node": ">= 6" 281 | } 282 | }, 283 | "node_modules/safe-buffer": { 284 | "version": "5.2.1", 285 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 286 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 287 | "dev": true, 288 | "funding": [ 289 | { 290 | "type": "github", 291 | "url": "https://github.com/sponsors/feross" 292 | }, 293 | { 294 | "type": "patreon", 295 | "url": "https://www.patreon.com/feross" 296 | }, 297 | { 298 | "type": "consulting", 299 | "url": "https://feross.org/support" 300 | } 301 | ], 302 | "license": "MIT" 303 | }, 304 | "node_modules/semver": { 305 | "version": "7.7.2", 306 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", 307 | "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", 308 | "dev": true, 309 | "license": "ISC", 310 | "bin": { 311 | "semver": "bin/semver.js" 312 | }, 313 | "engines": { 314 | "node": ">=10" 315 | } 316 | }, 317 | "node_modules/string_decoder": { 318 | "version": "1.3.0", 319 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 320 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 321 | "dev": true, 322 | "license": "MIT", 323 | "dependencies": { 324 | "safe-buffer": "~5.2.0" 325 | } 326 | }, 327 | "node_modules/tar-fs": { 328 | "version": "2.1.2", 329 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", 330 | "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", 331 | "dev": true, 332 | "license": "MIT", 333 | "dependencies": { 334 | "chownr": "^1.1.1", 335 | "mkdirp-classic": "^0.5.2", 336 | "pump": "^3.0.0", 337 | "tar-stream": "^2.1.4" 338 | } 339 | }, 340 | "node_modules/tar-stream": { 341 | "version": "2.2.0", 342 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 343 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 344 | "dev": true, 345 | "license": "MIT", 346 | "dependencies": { 347 | "bl": "^4.0.3", 348 | "end-of-stream": "^1.4.1", 349 | "fs-constants": "^1.0.0", 350 | "inherits": "^2.0.3", 351 | "readable-stream": "^3.1.1" 352 | }, 353 | "engines": { 354 | "node": ">=6" 355 | } 356 | }, 357 | "node_modules/tree-sitter": { 358 | "version": "0.21.1", 359 | "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz", 360 | "integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==", 361 | "hasInstallScript": true, 362 | "license": "MIT", 363 | "optional": true, 364 | "peer": true, 365 | "dependencies": { 366 | "node-addon-api": "^8.0.0", 367 | "node-gyp-build": "^4.8.0" 368 | } 369 | }, 370 | "node_modules/tree-sitter-cli": { 371 | "version": "0.24.7", 372 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.24.7.tgz", 373 | "integrity": "sha512-o4gnE82pVmMMhJbWwD6+I9yr4lXii5Ci5qEQ2pFpUbVy1YiD8cizTJaqdcznA0qEbo7l2OneI1GocChPrI4YGQ==", 374 | "dev": true, 375 | "hasInstallScript": true, 376 | "license": "MIT", 377 | "bin": { 378 | "tree-sitter": "cli.js" 379 | }, 380 | "engines": { 381 | "node": ">=12.0.0" 382 | } 383 | }, 384 | "node_modules/util-deprecate": { 385 | "version": "1.0.2", 386 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 387 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 388 | "dev": true, 389 | "license": "MIT" 390 | }, 391 | "node_modules/wrappy": { 392 | "version": "1.0.2", 393 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 394 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 395 | "dev": true, 396 | "license": "ISC" 397 | } 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-gdscript", 3 | "version": "5.0.1", 4 | "description": "Grammar for Godot's built-in scripting language.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/prestonknopp/tree-sitter-gdscript.git" 8 | }, 9 | "license": "MIT", 10 | "author": "Preston Knopp", 11 | "main": "bindings/node", 12 | "keywords": [ 13 | "incremental", 14 | "parsing", 15 | "tree-sitter", 16 | "gdscript", 17 | "godot" 18 | ], 19 | "files": [ 20 | "grammar.js", 21 | "tree-sitter.json", 22 | "binding.gyp", 23 | "prebuilds/**", 24 | "bindings/node/*", 25 | "queries/*", 26 | "src/**", 27 | "*.wasm" 28 | ], 29 | "dependencies": { 30 | "node-addon-api": "^8.3.1", 31 | "node-gyp-build": "^4.8.4" 32 | }, 33 | "devDependencies": { 34 | "prebuildify": "^6.0.1", 35 | "prettier": "^2.8.8", 36 | "tree-sitter-cli": "^0.24.7" 37 | }, 38 | "peerDependencies": { 39 | "tree-sitter": "^0.21.1" 40 | }, 41 | "peerDependenciesMeta": { 42 | "tree-sitter": { 43 | "optional": true 44 | } 45 | }, 46 | "scripts": { 47 | "install": "node-gyp-build", 48 | "prebuild": "prebuildify --strip -t electron@34.5.5 -t electron@35.3.0 -t electron@36.2.0 -t v20.19.1 -t v22.15.0 -t v23.11.0 -t v24.0.1", 49 | "prestart": "tree-sitter build --wasm", 50 | "start": "tree-sitter playground", 51 | "versions": "node --version && npm --version && tree-sitter --version", 52 | "test": "tree-sitter test && node --test bindings/node/*_test.js", 53 | "generate": "tree-sitter generate", 54 | "genTest": "tree-sitter generate && npm test", 55 | "parse": "tree-sitter parse", 56 | "ts": "tree-sitter", 57 | "format": "prettier -w grammar.js package.json" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "tree-sitter-gdscript" 7 | description = "Grammar for Godot's built-in scripting language." 8 | version = "5.0.1" 9 | keywords = ["incremental", "parsing", "tree-sitter", "gdscript"] 10 | classifiers = [ 11 | "Intended Audience :: Developers", 12 | "Topic :: Software Development :: Compilers", 13 | "Topic :: Text Processing :: Linguistic", 14 | "Typing :: Typed", 15 | ] 16 | authors = [{ name = "Preston Knopp", email = "prestonknopp@gmail.com" }] 17 | requires-python = ">=3.9" 18 | license.text = "MIT" 19 | readme = "README.md" 20 | 21 | [project.urls] 22 | Homepage = "https://github.com/prestonknopp/tree-sitter-gdscript" 23 | 24 | [project.optional-dependencies] 25 | core = ["tree-sitter~=0.22"] 26 | 27 | [tool.cibuildwheel] 28 | build = "cp39-*" 29 | build-frontend = "build" 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os.path import isdir, join 2 | from platform import system 3 | 4 | from setuptools import Extension, find_packages, setup 5 | from setuptools.command.build import build 6 | from wheel.bdist_wheel import bdist_wheel 7 | 8 | 9 | class Build(build): 10 | def run(self): 11 | if isdir("queries"): 12 | dest = join(self.build_lib, "tree_sitter_gdscript", "queries") 13 | self.copy_tree("queries", dest) 14 | super().run() 15 | 16 | 17 | class BdistWheel(bdist_wheel): 18 | def get_tag(self): 19 | python, abi, platform = super().get_tag() 20 | if python.startswith("cp"): 21 | python, abi = "cp39", "abi3" 22 | return python, abi, platform 23 | 24 | 25 | setup( 26 | packages=find_packages("bindings/python"), 27 | package_dir={"": "bindings/python"}, 28 | package_data={ 29 | "tree_sitter_gdscript": ["*.pyi", "py.typed"], 30 | "tree_sitter_gdscript.queries": ["*.scm"], 31 | }, 32 | ext_package="tree_sitter_gdscript", 33 | ext_modules=[ 34 | Extension( 35 | name="_binding", 36 | sources=[ 37 | "bindings/python/tree_sitter_gdscript/binding.c", 38 | "src/parser.c", 39 | "src/scanner.c", 40 | ], 41 | extra_compile_args=[ 42 | "-std=c11", 43 | "-fvisibility=hidden", 44 | ] if system() != "Windows" else [ 45 | "/std:c11", 46 | "/utf-8", 47 | ], 48 | define_macros=[ 49 | ("Py_LIMITED_API", "0x03090000"), 50 | ("PY_SSIZE_T_CLEAN", None), 51 | ("TREE_SITTER_HIDE_SYMBOLS", None), 52 | ], 53 | include_dirs=["src"], 54 | py_limited_api=True, 55 | ) 56 | ], 57 | cmdclass={ 58 | "build": Build, 59 | "bdist_wheel": BdistWheel 60 | }, 61 | zip_safe=False 62 | ) 63 | -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 8 | 9 | #define VEC_RESIZE(vec, _cap) \ 10 | void *tmp = realloc((vec)->data, (_cap) * sizeof((vec)->data[0])); \ 11 | assert(tmp != NULL); \ 12 | (vec)->data = tmp; \ 13 | (vec)->cap = (_cap); 14 | 15 | #define VEC_GROW(vec, _cap) \ 16 | if ((vec)->cap < (_cap)) { \ 17 | VEC_RESIZE((vec), (_cap)); \ 18 | } 19 | 20 | #define VEC_PUSH(vec, el) \ 21 | if ((vec)->cap == (vec)->len) { \ 22 | VEC_RESIZE((vec), MAX(16, (vec)->len * 2)); \ 23 | } \ 24 | (vec)->data[(vec)->len++] = (el); 25 | 26 | #define VEC_POP(vec) (vec)->len--; 27 | 28 | #define VEC_NEW \ 29 | { .len = 0, .cap = 0, .data = NULL } 30 | 31 | #define VEC_BACK(vec) ((vec)->data[(vec)->len - 1]) 32 | 33 | #define VEC_FREE(vec) \ 34 | { \ 35 | if ((vec)->data != NULL) \ 36 | free((vec)->data); \ 37 | } 38 | 39 | #define VEC_CLEAR(vec) \ 40 | { (vec)->len = 0; } 41 | 42 | enum TokenType { 43 | NEWLINE, 44 | INDENT, 45 | DEDENT, 46 | STRING_START, 47 | STRING_CONTENT, 48 | STRING_END, 49 | COMMENT, 50 | CLOSE_PAREN, 51 | CLOSE_BRACKET, 52 | CLOSE_BRACE, 53 | COMMA, 54 | /* COLON, // See grammar.js externals */ 55 | BODY_END, 56 | }; 57 | 58 | typedef enum { 59 | SingleQuote = 1 << 0, 60 | DoubleQuote = 1 << 1, 61 | BackQuote = 1 << 2, 62 | Raw = 1 << 3, 63 | Format = 1 << 4, 64 | Triple = 1 << 5, 65 | Bytes = 1 << 6, 66 | } Flags; 67 | 68 | typedef struct { 69 | char flags; 70 | } Delimiter; 71 | 72 | static inline Delimiter new_delimiter() { return (Delimiter){0}; } 73 | 74 | static inline bool is_format(Delimiter *delimiter) { 75 | return delimiter->flags & Format; 76 | } 77 | 78 | static inline bool is_raw(Delimiter *delimiter) { 79 | return delimiter->flags & Raw; 80 | } 81 | 82 | static inline bool is_triple(Delimiter *delimiter) { 83 | return delimiter->flags & Triple; 84 | } 85 | 86 | static inline bool is_bytes(Delimiter *delimiter) { 87 | return delimiter->flags & Bytes; 88 | } 89 | 90 | static inline int32_t end_character(Delimiter *delimiter) { 91 | if (delimiter->flags & SingleQuote) { 92 | return '\''; 93 | } 94 | if (delimiter->flags & DoubleQuote) { 95 | return '"'; 96 | } 97 | if (delimiter->flags & BackQuote) { 98 | return '`'; 99 | } 100 | return 0; 101 | } 102 | 103 | static inline void set_format(Delimiter *delimiter) { 104 | delimiter->flags |= Format; 105 | } 106 | 107 | static inline void set_raw(Delimiter *delimiter) { delimiter->flags |= Raw; } 108 | 109 | static inline void set_triple(Delimiter *delimiter) { 110 | delimiter->flags |= Triple; 111 | } 112 | 113 | static inline void set_bytes(Delimiter *delimiter) { 114 | delimiter->flags |= Bytes; 115 | } 116 | 117 | static inline void set_end_character(Delimiter *delimiter, int32_t character) { 118 | switch (character) { 119 | case '\'': 120 | delimiter->flags |= SingleQuote; 121 | break; 122 | case '"': 123 | delimiter->flags |= DoubleQuote; 124 | break; 125 | case '`': 126 | delimiter->flags |= BackQuote; 127 | break; 128 | default: 129 | assert(false); 130 | } 131 | } 132 | 133 | static inline const char *delimiter_string(Delimiter *delimiter) { 134 | if (delimiter->flags & SingleQuote) { 135 | return "\'"; 136 | } 137 | if (delimiter->flags & DoubleQuote) { 138 | return "\""; 139 | } 140 | if (delimiter->flags & BackQuote) { 141 | return "`"; 142 | } 143 | return ""; 144 | } 145 | 146 | typedef struct { 147 | uint32_t len; 148 | uint32_t cap; 149 | uint16_t *data; 150 | } indent_vec; 151 | 152 | typedef struct { 153 | uint32_t len; 154 | uint32_t cap; 155 | Delimiter *data; 156 | } delimiter_vec; 157 | 158 | typedef struct { 159 | indent_vec *indents; 160 | delimiter_vec *delimiters; 161 | } Scanner; 162 | 163 | static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); } 164 | 165 | static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); } 166 | 167 | bool tree_sitter_gdscript_external_scanner_scan(void *payload, TSLexer *lexer, 168 | const bool *valid_symbols) { 169 | Scanner *scanner = (Scanner *)payload; 170 | 171 | bool error_recovery_mode = 172 | valid_symbols[STRING_CONTENT] && valid_symbols[INDENT]; 173 | bool within_brackets = valid_symbols[CLOSE_BRACE] || 174 | valid_symbols[CLOSE_PAREN] || 175 | valid_symbols[CLOSE_BRACKET]; 176 | 177 | if (valid_symbols[STRING_CONTENT] && scanner->delimiters->len > 0 && 178 | !error_recovery_mode) { 179 | Delimiter delimiter = VEC_BACK(scanner->delimiters); 180 | int32_t end_char = end_character(&delimiter); 181 | bool has_content = false; 182 | while (lexer->lookahead) { 183 | if ((lexer->lookahead == '{' || lexer->lookahead == '}') && 184 | is_format(&delimiter)) { 185 | lexer->mark_end(lexer); 186 | lexer->result_symbol = STRING_CONTENT; 187 | return has_content; 188 | } 189 | if (lexer->lookahead == '\\') { 190 | if (is_raw(&delimiter)) { 191 | // Step over the backslash. 192 | lexer->advance(lexer, false); 193 | // Step over any escaped quotes. 194 | if (lexer->lookahead == end_character(&delimiter) || 195 | lexer->lookahead == '\\') { 196 | lexer->advance(lexer, false); 197 | } 198 | continue; 199 | } 200 | if (is_bytes(&delimiter)) { 201 | lexer->mark_end(lexer); 202 | lexer->advance(lexer, false); 203 | if (lexer->lookahead == 'N' || lexer->lookahead == 'u' || 204 | lexer->lookahead == 'U') { 205 | // In bytes string, \N{...}, \uXXXX and \UXXXXXXXX are 206 | // not escape sequences 207 | // https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals 208 | lexer->advance(lexer, false); 209 | } else { 210 | lexer->result_symbol = STRING_CONTENT; 211 | return has_content; 212 | } 213 | } else { 214 | lexer->mark_end(lexer); 215 | lexer->result_symbol = STRING_CONTENT; 216 | return has_content; 217 | } 218 | } else if (lexer->lookahead == end_char) { 219 | if (is_triple(&delimiter)) { 220 | lexer->mark_end(lexer); 221 | lexer->advance(lexer, false); 222 | if (lexer->lookahead == end_char) { 223 | lexer->advance(lexer, false); 224 | if (lexer->lookahead == end_char) { 225 | if (has_content) { 226 | lexer->result_symbol = STRING_CONTENT; 227 | } else { 228 | lexer->advance(lexer, false); 229 | lexer->mark_end(lexer); 230 | VEC_POP(scanner->delimiters); 231 | lexer->result_symbol = STRING_END; 232 | } 233 | return true; 234 | } 235 | lexer->mark_end(lexer); 236 | lexer->result_symbol = STRING_CONTENT; 237 | return true; 238 | } 239 | lexer->mark_end(lexer); 240 | lexer->result_symbol = STRING_CONTENT; 241 | return true; 242 | } 243 | if (has_content) { 244 | lexer->result_symbol = STRING_CONTENT; 245 | } else { 246 | lexer->advance(lexer, false); 247 | VEC_POP(scanner->delimiters); 248 | lexer->result_symbol = STRING_END; 249 | } 250 | lexer->mark_end(lexer); 251 | return true; 252 | 253 | } else if (lexer->lookahead == '\n' && has_content && 254 | !is_triple(&delimiter)) { 255 | return false; 256 | } 257 | advance(lexer); 258 | has_content = true; 259 | } 260 | } 261 | 262 | lexer->mark_end(lexer); 263 | 264 | bool found_end_of_line = false; 265 | uint32_t indent_length = 0; 266 | int32_t first_comment_indent_length = -1; 267 | for (;;) { 268 | if (lexer->lookahead == '\n') { 269 | found_end_of_line = true; 270 | indent_length = 0; 271 | skip(lexer); 272 | } else if (lexer->lookahead == ' ') { 273 | indent_length++; 274 | skip(lexer); 275 | } else if (lexer->lookahead == '\r' || lexer->lookahead == '\f') { 276 | indent_length = 0; 277 | skip(lexer); 278 | } else if (lexer->lookahead == '\t') { 279 | indent_length += 8; 280 | skip(lexer); 281 | } else if (lexer->lookahead == '#') { 282 | if (first_comment_indent_length == -1) { 283 | first_comment_indent_length = (int32_t)indent_length; 284 | } 285 | while (lexer->lookahead && lexer->lookahead != '\n') { 286 | skip(lexer); 287 | } 288 | skip(lexer); 289 | indent_length = 0; 290 | } else if (lexer->lookahead == '\\') { 291 | skip(lexer); 292 | if (lexer->lookahead == '\r') { 293 | skip(lexer); 294 | } 295 | if (lexer->lookahead == '\n' || lexer->eof(lexer)) { 296 | skip(lexer); 297 | } else { 298 | return false; 299 | } 300 | } else if (lexer->eof(lexer)) { 301 | indent_length = 0; 302 | found_end_of_line = true; 303 | break; 304 | } else { 305 | break; 306 | } 307 | } 308 | 309 | if (found_end_of_line) { 310 | if (scanner->indents->len > 0) { 311 | uint16_t current_indent_length = VEC_BACK(scanner->indents); 312 | 313 | if (valid_symbols[INDENT] && 314 | indent_length > current_indent_length) { 315 | VEC_PUSH(scanner->indents, indent_length); 316 | lexer->result_symbol = INDENT; 317 | return true; 318 | } 319 | 320 | if ((valid_symbols[DEDENT] || 321 | (!valid_symbols[NEWLINE] && !valid_symbols[STRING_START] && 322 | !within_brackets)) && 323 | indent_length < current_indent_length && 324 | 325 | // Wait to create a dedent token until we've consumed any 326 | // comments 327 | // whose indentation matches the current block. 328 | first_comment_indent_length < (int32_t)current_indent_length) { 329 | VEC_POP(scanner->indents); 330 | lexer->result_symbol = DEDENT; 331 | return true; 332 | } 333 | } 334 | 335 | if (valid_symbols[NEWLINE] && !error_recovery_mode) { 336 | lexer->result_symbol = NEWLINE; 337 | return true; 338 | } 339 | } 340 | 341 | // This if statement can be placed before the above if statement that 342 | // handles newlines. However, it feels safer to give indentation and 343 | // newlines higher precedence. 344 | if ( 345 | // Guard against BODY_END tokens overriding valid tokens. 346 | !valid_symbols[COMMA] && 347 | /* !valid_symbols[COLON] && */ 348 | !valid_symbols[CLOSE_PAREN] && !valid_symbols[CLOSE_BRACE] && 349 | !valid_symbols[CLOSE_BRACKET] && 350 | 351 | // Body ends occur in error recovery mode since the grammar does not 352 | // (cannot?) specify that a body can end with the below characters 353 | // without consuming them itself. 354 | (error_recovery_mode || valid_symbols[BODY_END])) { 355 | if (lexer->lookahead == ',' || // separator 356 | lexer->lookahead == ')' || // args, params, paren expr 357 | lexer->lookahead == '}' || // dictionary (may not be needed) 358 | lexer->lookahead == ']' // array 359 | /* lexer->lookahead == ':' // key-value pairs (breaks if 360 | elses) */ 361 | ) { 362 | // BODY_END tokens can take the place of a dedent. Therefore, we 363 | // should pop the stack when DEDENT is valid. 364 | if (valid_symbols[DEDENT] && scanner->indents->len > 0) { 365 | VEC_POP(scanner->indents); 366 | } 367 | lexer->result_symbol = BODY_END; 368 | return true; 369 | } 370 | } 371 | 372 | if (first_comment_indent_length == -1 && valid_symbols[STRING_START]) { 373 | Delimiter delimiter = new_delimiter(); 374 | 375 | bool has_flags = false; 376 | while (lexer->lookahead) { 377 | if (lexer->lookahead == 'f' || lexer->lookahead == 'F') { 378 | set_format(&delimiter); 379 | } else if (lexer->lookahead == 'r' || lexer->lookahead == 'R') { 380 | set_raw(&delimiter); 381 | } else if (lexer->lookahead == 'b' || lexer->lookahead == 'B') { 382 | set_bytes(&delimiter); 383 | } else if (lexer->lookahead != 'u' && lexer->lookahead != 'U') { 384 | break; 385 | } 386 | has_flags = true; 387 | advance(lexer); 388 | } 389 | 390 | if (lexer->lookahead == '`') { 391 | set_end_character(&delimiter, '`'); 392 | advance(lexer); 393 | lexer->mark_end(lexer); 394 | } else if (lexer->lookahead == '\'') { 395 | set_end_character(&delimiter, '\''); 396 | advance(lexer); 397 | lexer->mark_end(lexer); 398 | if (lexer->lookahead == '\'') { 399 | advance(lexer); 400 | if (lexer->lookahead == '\'') { 401 | advance(lexer); 402 | lexer->mark_end(lexer); 403 | set_triple(&delimiter); 404 | } 405 | } 406 | } else if (lexer->lookahead == '"') { 407 | set_end_character(&delimiter, '"'); 408 | advance(lexer); 409 | lexer->mark_end(lexer); 410 | if (lexer->lookahead == '"') { 411 | advance(lexer); 412 | if (lexer->lookahead == '"') { 413 | advance(lexer); 414 | lexer->mark_end(lexer); 415 | set_triple(&delimiter); 416 | } 417 | } 418 | } 419 | 420 | if (end_character(&delimiter)) { 421 | VEC_PUSH(scanner->delimiters, delimiter); 422 | lexer->result_symbol = STRING_START; 423 | 424 | return true; 425 | } 426 | if (has_flags) { 427 | return false; 428 | } 429 | } 430 | 431 | return false; 432 | } 433 | 434 | unsigned tree_sitter_gdscript_external_scanner_serialize(void *payload, 435 | char *buffer) { 436 | Scanner *scanner = (Scanner *)payload; 437 | 438 | size_t size = 0; 439 | 440 | size_t delimiter_count = scanner->delimiters->len; 441 | if (delimiter_count > UINT8_MAX) { 442 | delimiter_count = UINT8_MAX; 443 | } 444 | buffer[size++] = (char)delimiter_count; 445 | 446 | if (delimiter_count > 0) { 447 | memcpy(&buffer[size], scanner->delimiters->data, delimiter_count); 448 | } 449 | size += delimiter_count; 450 | 451 | for (int iter = 1; (uint32_t)iter < scanner->indents->len && 452 | size < TREE_SITTER_SERIALIZATION_BUFFER_SIZE; 453 | ++iter) { 454 | buffer[size++] = (char)scanner->indents->data[iter]; 455 | } 456 | 457 | return size; 458 | } 459 | 460 | void tree_sitter_gdscript_external_scanner_deserialize(void *payload, 461 | const char *buffer, 462 | unsigned length) { 463 | Scanner *scanner = (Scanner *)payload; 464 | 465 | VEC_CLEAR(scanner->delimiters); 466 | VEC_CLEAR(scanner->indents); 467 | VEC_PUSH(scanner->indents, 0); 468 | 469 | if (length > 0) { 470 | size_t size = 0; 471 | 472 | size_t delimiter_count = (uint8_t)buffer[size++]; 473 | if (delimiter_count > 0) { 474 | VEC_GROW(scanner->delimiters, delimiter_count); 475 | scanner->delimiters->len = delimiter_count; 476 | memcpy(scanner->delimiters->data, &buffer[size], delimiter_count); 477 | size += delimiter_count; 478 | } 479 | 480 | for (; size < length; size++) { 481 | VEC_PUSH(scanner->indents, (unsigned char)buffer[size]); 482 | } 483 | 484 | assert(size == length); 485 | } 486 | } 487 | 488 | void *tree_sitter_gdscript_external_scanner_create() { 489 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) 490 | _Static_assert(sizeof(Delimiter) == sizeof(char), ""); 491 | #else 492 | assert(sizeof(Delimiter) == sizeof(char)); 493 | #endif 494 | Scanner *scanner = calloc(1, sizeof(Scanner)); 495 | if (!scanner) { 496 | // What is the tree-sitter idiomatic way to handle this? 497 | // fprintf(stderr, "Failed to allocate memory for scanner\n"); 498 | return NULL; 499 | } 500 | 501 | scanner->indents = calloc(1, sizeof(indent_vec)); 502 | scanner->delimiters = calloc(1, sizeof(delimiter_vec)); 503 | tree_sitter_gdscript_external_scanner_deserialize(scanner, NULL, 0); 504 | return scanner; 505 | } 506 | 507 | void tree_sitter_gdscript_external_scanner_destroy(void *payload) { 508 | Scanner *scanner = (Scanner *)payload; 509 | VEC_FREE(scanner->indents); 510 | VEC_FREE(scanner->delimiters); 511 | free(scanner->indents); 512 | free(scanner->delimiters); 513 | free(scanner); 514 | } 515 | -------------------------------------------------------------------------------- /src/tree_sitter/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ALLOC_H_ 2 | #define TREE_SITTER_ALLOC_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // Allow clients to override allocation functions 13 | #ifdef TREE_SITTER_REUSE_ALLOCATOR 14 | 15 | extern void *(*ts_current_malloc)(size_t size); 16 | extern void *(*ts_current_calloc)(size_t count, size_t size); 17 | extern void *(*ts_current_realloc)(void *ptr, size_t size); 18 | extern void (*ts_current_free)(void *ptr); 19 | 20 | #ifndef ts_malloc 21 | #define ts_malloc ts_current_malloc 22 | #endif 23 | #ifndef ts_calloc 24 | #define ts_calloc ts_current_calloc 25 | #endif 26 | #ifndef ts_realloc 27 | #define ts_realloc ts_current_realloc 28 | #endif 29 | #ifndef ts_free 30 | #define ts_free ts_current_free 31 | #endif 32 | 33 | #else 34 | 35 | #ifndef ts_malloc 36 | #define ts_malloc malloc 37 | #endif 38 | #ifndef ts_calloc 39 | #define ts_calloc calloc 40 | #endif 41 | #ifndef ts_realloc 42 | #define ts_realloc realloc 43 | #endif 44 | #ifndef ts_free 45 | #define ts_free free 46 | #endif 47 | 48 | #endif 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // TREE_SITTER_ALLOC_H_ 55 | -------------------------------------------------------------------------------- /src/tree_sitter/array.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ARRAY_H_ 2 | #define TREE_SITTER_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./alloc.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef _MSC_VER 17 | #pragma warning(push) 18 | #pragma warning(disable : 4101) 19 | #elif defined(__GNUC__) || defined(__clang__) 20 | #pragma GCC diagnostic push 21 | #pragma GCC diagnostic ignored "-Wunused-variable" 22 | #endif 23 | 24 | #define Array(T) \ 25 | struct { \ 26 | T *contents; \ 27 | uint32_t size; \ 28 | uint32_t capacity; \ 29 | } 30 | 31 | /// Initialize an array. 32 | #define array_init(self) \ 33 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 34 | 35 | /// Create an empty array. 36 | #define array_new() \ 37 | { NULL, 0, 0 } 38 | 39 | /// Get a pointer to the element at a given `index` in the array. 40 | #define array_get(self, _index) \ 41 | (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) 42 | 43 | /// Get a pointer to the first element in the array. 44 | #define array_front(self) array_get(self, 0) 45 | 46 | /// Get a pointer to the last element in the array. 47 | #define array_back(self) array_get(self, (self)->size - 1) 48 | 49 | /// Clear the array, setting its size to zero. Note that this does not free any 50 | /// memory allocated for the array's contents. 51 | #define array_clear(self) ((self)->size = 0) 52 | 53 | /// Reserve `new_capacity` elements of space in the array. If `new_capacity` is 54 | /// less than the array's current capacity, this function has no effect. 55 | #define array_reserve(self, new_capacity) \ 56 | _array__reserve((Array *)(self), array_elem_size(self), new_capacity) 57 | 58 | /// Free any memory allocated for this array. Note that this does not free any 59 | /// memory allocated for the array's contents. 60 | #define array_delete(self) _array__delete((Array *)(self)) 61 | 62 | /// Push a new `element` onto the end of the array. 63 | #define array_push(self, element) \ 64 | (_array__grow((Array *)(self), 1, array_elem_size(self)), \ 65 | (self)->contents[(self)->size++] = (element)) 66 | 67 | /// Increase the array's size by `count` elements. 68 | /// New elements are zero-initialized. 69 | #define array_grow_by(self, count) \ 70 | do { \ 71 | if ((count) == 0) break; \ 72 | _array__grow((Array *)(self), count, array_elem_size(self)); \ 73 | memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ 74 | (self)->size += (count); \ 75 | } while (0) 76 | 77 | /// Append all elements from one array to the end of another. 78 | #define array_push_all(self, other) \ 79 | array_extend((self), (other)->size, (other)->contents) 80 | 81 | /// Append `count` elements to the end of the array, reading their values from the 82 | /// `contents` pointer. 83 | #define array_extend(self, count, contents) \ 84 | _array__splice( \ 85 | (Array *)(self), array_elem_size(self), (self)->size, \ 86 | 0, count, contents \ 87 | ) 88 | 89 | /// Remove `old_count` elements from the array starting at the given `index`. At 90 | /// the same index, insert `new_count` new elements, reading their values from the 91 | /// `new_contents` pointer. 92 | #define array_splice(self, _index, old_count, new_count, new_contents) \ 93 | _array__splice( \ 94 | (Array *)(self), array_elem_size(self), _index, \ 95 | old_count, new_count, new_contents \ 96 | ) 97 | 98 | /// Insert one `element` into the array at the given `index`. 99 | #define array_insert(self, _index, element) \ 100 | _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) 101 | 102 | /// Remove one element from the array at the given `index`. 103 | #define array_erase(self, _index) \ 104 | _array__erase((Array *)(self), array_elem_size(self), _index) 105 | 106 | /// Pop the last element off the array, returning the element by value. 107 | #define array_pop(self) ((self)->contents[--(self)->size]) 108 | 109 | /// Assign the contents of one array to another, reallocating if necessary. 110 | #define array_assign(self, other) \ 111 | _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) 112 | 113 | /// Swap one array with another 114 | #define array_swap(self, other) \ 115 | _array__swap((Array *)(self), (Array *)(other)) 116 | 117 | /// Get the size of the array contents 118 | #define array_elem_size(self) (sizeof *(self)->contents) 119 | 120 | /// Search a sorted array for a given `needle` value, using the given `compare` 121 | /// callback to determine the order. 122 | /// 123 | /// If an existing element is found to be equal to `needle`, then the `index` 124 | /// out-parameter is set to the existing value's index, and the `exists` 125 | /// out-parameter is set to true. Otherwise, `index` is set to an index where 126 | /// `needle` should be inserted in order to preserve the sorting, and `exists` 127 | /// is set to false. 128 | #define array_search_sorted_with(self, compare, needle, _index, _exists) \ 129 | _array__search_sorted(self, 0, compare, , needle, _index, _exists) 130 | 131 | /// Search a sorted array for a given `needle` value, using integer comparisons 132 | /// of a given struct field (specified with a leading dot) to determine the order. 133 | /// 134 | /// See also `array_search_sorted_with`. 135 | #define array_search_sorted_by(self, field, needle, _index, _exists) \ 136 | _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists) 137 | 138 | /// Insert a given `value` into a sorted array, using the given `compare` 139 | /// callback to determine the order. 140 | #define array_insert_sorted_with(self, compare, value) \ 141 | do { \ 142 | unsigned _index, _exists; \ 143 | array_search_sorted_with(self, compare, &(value), &_index, &_exists); \ 144 | if (!_exists) array_insert(self, _index, value); \ 145 | } while (0) 146 | 147 | /// Insert a given `value` into a sorted array, using integer comparisons of 148 | /// a given struct field (specified with a leading dot) to determine the order. 149 | /// 150 | /// See also `array_search_sorted_by`. 151 | #define array_insert_sorted_by(self, field, value) \ 152 | do { \ 153 | unsigned _index, _exists; \ 154 | array_search_sorted_by(self, field, (value) field, &_index, &_exists); \ 155 | if (!_exists) array_insert(self, _index, value); \ 156 | } while (0) 157 | 158 | // Private 159 | 160 | typedef Array(void) Array; 161 | 162 | /// This is not what you're looking for, see `array_delete`. 163 | static inline void _array__delete(Array *self) { 164 | if (self->contents) { 165 | ts_free(self->contents); 166 | self->contents = NULL; 167 | self->size = 0; 168 | self->capacity = 0; 169 | } 170 | } 171 | 172 | /// This is not what you're looking for, see `array_erase`. 173 | static inline void _array__erase(Array *self, size_t element_size, 174 | uint32_t index) { 175 | assert(index < self->size); 176 | char *contents = (char *)self->contents; 177 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 178 | (self->size - index - 1) * element_size); 179 | self->size--; 180 | } 181 | 182 | /// This is not what you're looking for, see `array_reserve`. 183 | static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { 184 | if (new_capacity > self->capacity) { 185 | if (self->contents) { 186 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 187 | } else { 188 | self->contents = ts_malloc(new_capacity * element_size); 189 | } 190 | self->capacity = new_capacity; 191 | } 192 | } 193 | 194 | /// This is not what you're looking for, see `array_assign`. 195 | static inline void _array__assign(Array *self, const Array *other, size_t element_size) { 196 | _array__reserve(self, element_size, other->size); 197 | self->size = other->size; 198 | memcpy(self->contents, other->contents, self->size * element_size); 199 | } 200 | 201 | /// This is not what you're looking for, see `array_swap`. 202 | static inline void _array__swap(Array *self, Array *other) { 203 | Array swap = *other; 204 | *other = *self; 205 | *self = swap; 206 | } 207 | 208 | /// This is not what you're looking for, see `array_push` or `array_grow_by`. 209 | static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { 210 | uint32_t new_size = self->size + count; 211 | if (new_size > self->capacity) { 212 | uint32_t new_capacity = self->capacity * 2; 213 | if (new_capacity < 8) new_capacity = 8; 214 | if (new_capacity < new_size) new_capacity = new_size; 215 | _array__reserve(self, element_size, new_capacity); 216 | } 217 | } 218 | 219 | /// This is not what you're looking for, see `array_splice`. 220 | static inline void _array__splice(Array *self, size_t element_size, 221 | uint32_t index, uint32_t old_count, 222 | uint32_t new_count, const void *elements) { 223 | uint32_t new_size = self->size + new_count - old_count; 224 | uint32_t old_end = index + old_count; 225 | uint32_t new_end = index + new_count; 226 | assert(old_end <= self->size); 227 | 228 | _array__reserve(self, element_size, new_size); 229 | 230 | char *contents = (char *)self->contents; 231 | if (self->size > old_end) { 232 | memmove( 233 | contents + new_end * element_size, 234 | contents + old_end * element_size, 235 | (self->size - old_end) * element_size 236 | ); 237 | } 238 | if (new_count > 0) { 239 | if (elements) { 240 | memcpy( 241 | (contents + index * element_size), 242 | elements, 243 | new_count * element_size 244 | ); 245 | } else { 246 | memset( 247 | (contents + index * element_size), 248 | 0, 249 | new_count * element_size 250 | ); 251 | } 252 | } 253 | self->size += new_count - old_count; 254 | } 255 | 256 | /// A binary search routine, based on Rust's `std::slice::binary_search_by`. 257 | /// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. 258 | #define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ 259 | do { \ 260 | *(_index) = start; \ 261 | *(_exists) = false; \ 262 | uint32_t size = (self)->size - *(_index); \ 263 | if (size == 0) break; \ 264 | int comparison; \ 265 | while (size > 1) { \ 266 | uint32_t half_size = size / 2; \ 267 | uint32_t mid_index = *(_index) + half_size; \ 268 | comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ 269 | if (comparison <= 0) *(_index) = mid_index; \ 270 | size -= half_size; \ 271 | } \ 272 | comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \ 273 | if (comparison == 0) *(_exists) = true; \ 274 | else if (comparison < 0) *(_index) += 1; \ 275 | } while (0) 276 | 277 | /// Helper macro for the `_sorted_by` routines below. This takes the left (existing) 278 | /// parameter by reference in order to work with the generic sorting function above. 279 | #define _compare_int(a, b) ((int)*(a) - (int)(b)) 280 | 281 | #ifdef _MSC_VER 282 | #pragma warning(pop) 283 | #elif defined(__GNUC__) || defined(__clang__) 284 | #pragma GCC diagnostic pop 285 | #endif 286 | 287 | #ifdef __cplusplus 288 | } 289 | #endif 290 | 291 | #endif // TREE_SITTER_ARRAY_H_ 292 | -------------------------------------------------------------------------------- /src/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | #ifndef TREE_SITTER_API_H_ 17 | typedef uint16_t TSStateId; 18 | typedef uint16_t TSSymbol; 19 | typedef uint16_t TSFieldId; 20 | typedef struct TSLanguage TSLanguage; 21 | #endif 22 | 23 | typedef struct { 24 | TSFieldId field_id; 25 | uint8_t child_index; 26 | bool inherited; 27 | } TSFieldMapEntry; 28 | 29 | typedef struct { 30 | uint16_t index; 31 | uint16_t length; 32 | } TSFieldMapSlice; 33 | 34 | typedef struct { 35 | bool visible; 36 | bool named; 37 | bool supertype; 38 | } TSSymbolMetadata; 39 | 40 | typedef struct TSLexer TSLexer; 41 | 42 | struct TSLexer { 43 | int32_t lookahead; 44 | TSSymbol result_symbol; 45 | void (*advance)(TSLexer *, bool); 46 | void (*mark_end)(TSLexer *); 47 | uint32_t (*get_column)(TSLexer *); 48 | bool (*is_at_included_range_start)(const TSLexer *); 49 | bool (*eof)(const TSLexer *); 50 | void (*log)(const TSLexer *, const char *, ...); 51 | }; 52 | 53 | typedef enum { 54 | TSParseActionTypeShift, 55 | TSParseActionTypeReduce, 56 | TSParseActionTypeAccept, 57 | TSParseActionTypeRecover, 58 | } TSParseActionType; 59 | 60 | typedef union { 61 | struct { 62 | uint8_t type; 63 | TSStateId state; 64 | bool extra; 65 | bool repetition; 66 | } shift; 67 | struct { 68 | uint8_t type; 69 | uint8_t child_count; 70 | TSSymbol symbol; 71 | int16_t dynamic_precedence; 72 | uint16_t production_id; 73 | } reduce; 74 | uint8_t type; 75 | } TSParseAction; 76 | 77 | typedef struct { 78 | uint16_t lex_state; 79 | uint16_t external_lex_state; 80 | } TSLexMode; 81 | 82 | typedef union { 83 | TSParseAction action; 84 | struct { 85 | uint8_t count; 86 | bool reusable; 87 | } entry; 88 | } TSParseActionEntry; 89 | 90 | typedef struct { 91 | int32_t start; 92 | int32_t end; 93 | } TSCharacterRange; 94 | 95 | struct TSLanguage { 96 | uint32_t version; 97 | uint32_t symbol_count; 98 | uint32_t alias_count; 99 | uint32_t token_count; 100 | uint32_t external_token_count; 101 | uint32_t state_count; 102 | uint32_t large_state_count; 103 | uint32_t production_id_count; 104 | uint32_t field_count; 105 | uint16_t max_alias_sequence_length; 106 | const uint16_t *parse_table; 107 | const uint16_t *small_parse_table; 108 | const uint32_t *small_parse_table_map; 109 | const TSParseActionEntry *parse_actions; 110 | const char * const *symbol_names; 111 | const char * const *field_names; 112 | const TSFieldMapSlice *field_map_slices; 113 | const TSFieldMapEntry *field_map_entries; 114 | const TSSymbolMetadata *symbol_metadata; 115 | const TSSymbol *public_symbol_map; 116 | const uint16_t *alias_map; 117 | const TSSymbol *alias_sequences; 118 | const TSLexMode *lex_modes; 119 | bool (*lex_fn)(TSLexer *, TSStateId); 120 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 121 | TSSymbol keyword_capture_token; 122 | struct { 123 | const bool *states; 124 | const TSSymbol *symbol_map; 125 | void *(*create)(void); 126 | void (*destroy)(void *); 127 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 128 | unsigned (*serialize)(void *, char *); 129 | void (*deserialize)(void *, const char *, unsigned); 130 | } external_scanner; 131 | const TSStateId *primary_state_ids; 132 | }; 133 | 134 | static inline bool set_contains(TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { 135 | uint32_t index = 0; 136 | uint32_t size = len - index; 137 | while (size > 1) { 138 | uint32_t half_size = size / 2; 139 | uint32_t mid_index = index + half_size; 140 | TSCharacterRange *range = &ranges[mid_index]; 141 | if (lookahead >= range->start && lookahead <= range->end) { 142 | return true; 143 | } else if (lookahead > range->end) { 144 | index = mid_index; 145 | } 146 | size -= half_size; 147 | } 148 | TSCharacterRange *range = &ranges[index]; 149 | return (lookahead >= range->start && lookahead <= range->end); 150 | } 151 | 152 | /* 153 | * Lexer Macros 154 | */ 155 | 156 | #ifdef _MSC_VER 157 | #define UNUSED __pragma(warning(suppress : 4101)) 158 | #else 159 | #define UNUSED __attribute__((unused)) 160 | #endif 161 | 162 | #define START_LEXER() \ 163 | bool result = false; \ 164 | bool skip = false; \ 165 | UNUSED \ 166 | bool eof = false; \ 167 | int32_t lookahead; \ 168 | goto start; \ 169 | next_state: \ 170 | lexer->advance(lexer, skip); \ 171 | start: \ 172 | skip = false; \ 173 | lookahead = lexer->lookahead; 174 | 175 | #define ADVANCE(state_value) \ 176 | { \ 177 | state = state_value; \ 178 | goto next_state; \ 179 | } 180 | 181 | #define ADVANCE_MAP(...) \ 182 | { \ 183 | static const uint16_t map[] = { __VA_ARGS__ }; \ 184 | for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \ 185 | if (map[i] == lookahead) { \ 186 | state = map[i + 1]; \ 187 | goto next_state; \ 188 | } \ 189 | } \ 190 | } 191 | 192 | #define SKIP(state_value) \ 193 | { \ 194 | skip = true; \ 195 | state = state_value; \ 196 | goto next_state; \ 197 | } 198 | 199 | #define ACCEPT_TOKEN(symbol_value) \ 200 | result = true; \ 201 | lexer->result_symbol = symbol_value; \ 202 | lexer->mark_end(lexer); 203 | 204 | #define END_STATE() return result; 205 | 206 | /* 207 | * Parse Table Macros 208 | */ 209 | 210 | #define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT) 211 | 212 | #define STATE(id) id 213 | 214 | #define ACTIONS(id) id 215 | 216 | #define SHIFT(state_value) \ 217 | {{ \ 218 | .shift = { \ 219 | .type = TSParseActionTypeShift, \ 220 | .state = (state_value) \ 221 | } \ 222 | }} 223 | 224 | #define SHIFT_REPEAT(state_value) \ 225 | {{ \ 226 | .shift = { \ 227 | .type = TSParseActionTypeShift, \ 228 | .state = (state_value), \ 229 | .repetition = true \ 230 | } \ 231 | }} 232 | 233 | #define SHIFT_EXTRA() \ 234 | {{ \ 235 | .shift = { \ 236 | .type = TSParseActionTypeShift, \ 237 | .extra = true \ 238 | } \ 239 | }} 240 | 241 | #define REDUCE(symbol_name, children, precedence, prod_id) \ 242 | {{ \ 243 | .reduce = { \ 244 | .type = TSParseActionTypeReduce, \ 245 | .symbol = symbol_name, \ 246 | .child_count = children, \ 247 | .dynamic_precedence = precedence, \ 248 | .production_id = prod_id \ 249 | }, \ 250 | }} 251 | 252 | #define RECOVER() \ 253 | {{ \ 254 | .type = TSParseActionTypeRecover \ 255 | }} 256 | 257 | #define ACCEPT_INPUT() \ 258 | {{ \ 259 | .type = TSParseActionTypeAccept \ 260 | }} 261 | 262 | #ifdef __cplusplus 263 | } 264 | #endif 265 | 266 | #endif // TREE_SITTER_PARSER_H_ 267 | -------------------------------------------------------------------------------- /test/corpus/issues.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | #21 Parse condition with comparison_operator and binary_operator wrong 3 | ============================================ 4 | 5 | 1 == 2 or 3 == 4 6 | 7 | --- 8 | 9 | (source 10 | (expression_statement 11 | (binary_operator 12 | (binary_operator 13 | (integer) 14 | (integer)) 15 | (binary_operator 16 | (integer) 17 | (integer))))) 18 | 19 | ============================================ 20 | Variable right after lambda 21 | ============================================ 22 | 23 | func _on_pressed_button() -> void: 24 | var root = func() -> Node: 25 | var node: Node 26 | var node_type: String = lib.editor.root.get_class() 27 | 28 | match node_type: 29 | "Node3D": 30 | node = Node3D.new() 31 | "Node2D": 32 | node = Node2D.new() 33 | 34 | return node 35 | 36 | --- 37 | 38 | (source 39 | (function_definition 40 | (name) 41 | (parameters) 42 | (type (identifier)) 43 | (body 44 | (variable_statement 45 | (name) 46 | (lambda 47 | (parameters) 48 | (type (identifier)) 49 | (body 50 | (variable_statement 51 | (name) 52 | (type (identifier))) 53 | (variable_statement 54 | (name) 55 | (type (identifier)) 56 | (attribute 57 | (identifier) 58 | (identifier) 59 | (identifier) 60 | (attribute_call 61 | (identifier) 62 | (arguments)))) 63 | (match_statement 64 | (identifier) 65 | (match_body 66 | (pattern_section 67 | (string) 68 | (body 69 | (expression_statement 70 | (assignment 71 | (identifier) 72 | (attribute 73 | (identifier) 74 | (attribute_call 75 | (identifier) 76 | (arguments))))))) 77 | (pattern_section 78 | (string) 79 | (body 80 | (expression_statement 81 | (assignment 82 | (identifier) 83 | (attribute 84 | (identifier) 85 | (attribute_call 86 | (identifier) 87 | (arguments))))))))) 88 | (return_statement 89 | (identifier)))))))) 90 | 91 | ============================================ 92 | Highlight incorrect when just setting get 93 | ============================================ 94 | 95 | var selection: 96 | get: 97 | return interface.get_selection() 98 | 99 | var children: 100 | get: 101 | return selected[0].get_children() 102 | 103 | --- 104 | 105 | (source 106 | (variable_statement 107 | (name) 108 | (setget 109 | (get_body 110 | (return_statement 111 | (attribute 112 | (identifier) 113 | (attribute_call 114 | (identifier) 115 | (arguments))))))) 116 | (variable_statement 117 | (name) 118 | (setget 119 | (get_body 120 | (return_statement 121 | (attribute 122 | (subscript 123 | (identifier) 124 | (integer)) 125 | (attribute_call 126 | (identifier) 127 | (arguments)))))))) 128 | 129 | ============================================ 130 | Export Var Stmt can be onready 131 | ============================================ 132 | 133 | export(NodePath) onready var path = get_node(path) 134 | 135 | --- 136 | 137 | (source 138 | (export_variable_statement 139 | (arguments 140 | (identifier)) 141 | (name) 142 | (call 143 | (identifier) 144 | (arguments 145 | (identifier))))) 146 | 147 | ============================================ 148 | Literals bugs with [get_node] 149 | ============================================ 150 | 151 | $"../inspectorHeader/inspectorHeaderHBoxContainer/CurrentBlock" 152 | $"../../CommandsSettings" 153 | $'../../Node' 154 | 155 | --- 156 | 157 | (source 158 | (expression_statement 159 | (get_node)) 160 | (expression_statement 161 | (get_node)) 162 | (expression_statement 163 | (get_node))) 164 | 165 | ============================================ 166 | Fails to parse basic blocks 167 | ============================================ 168 | 169 | extends Node 170 | 171 | func _ready(): 172 | var x := 2 173 | for i in range(x): 174 | prints(i) 175 | 176 | while x > 0: 177 | print(x) 178 | 179 | if x > 0: 180 | print("if test") 181 | elif x < 0: 182 | print("if test") 183 | else: 184 | print("if test") 185 | 186 | --- 187 | 188 | (source 189 | (extends_statement 190 | (type (identifier))) 191 | (function_definition 192 | (name) 193 | (parameters) 194 | (body 195 | (variable_statement 196 | (name) 197 | (inferred_type) 198 | (integer)) 199 | (for_statement 200 | (identifier) 201 | (call 202 | (identifier) 203 | (arguments 204 | (identifier))) 205 | (body 206 | (expression_statement 207 | (call 208 | (identifier) 209 | (arguments 210 | (identifier)))))) 211 | (while_statement 212 | (binary_operator 213 | (identifier) 214 | (integer)) 215 | (body 216 | (expression_statement 217 | (call 218 | (identifier) 219 | (arguments 220 | (identifier)))))) 221 | (if_statement 222 | (binary_operator 223 | (identifier) 224 | (integer)) 225 | (body 226 | (expression_statement 227 | (call 228 | (identifier) 229 | (arguments 230 | (string))))) 231 | (elif_clause 232 | (binary_operator 233 | (identifier) 234 | (integer)) 235 | (body 236 | (expression_statement 237 | (call 238 | (identifier) 239 | (arguments 240 | (string)))))) 241 | (else_clause 242 | (body 243 | (expression_statement 244 | (call 245 | (identifier) 246 | (arguments 247 | (string)))))))))) 248 | -------------------------------------------------------------------------------- /test/corpus/issues/godot.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | #73273 Lambda assign after if 3 | ============================================ 4 | 5 | if true: v = func(): test() 6 | if true: v = func(): test() 7 | 8 | --- 9 | 10 | (source 11 | (if_statement 12 | (true) 13 | (body 14 | (expression_statement 15 | (assignment 16 | (identifier) 17 | (lambda 18 | (parameters) 19 | (body 20 | (expression_statement 21 | (call 22 | (identifier) 23 | (arguments))))))))) 24 | (if_statement 25 | (true) 26 | (body 27 | (expression_statement 28 | (assignment 29 | (identifier) 30 | (lambda 31 | (parameters) 32 | (body 33 | (expression_statement 34 | (call 35 | (identifier) 36 | (arguments)))))))))) 37 | -------------------------------------------------------------------------------- /test/corpus/issues/setget.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Setget parse failure 3 | ============================================ 4 | 5 | var is_active = true: 6 | set = set_is_active 7 | 8 | @onready var _state = initial_state: 9 | set = set_state 10 | 11 | --- 12 | 13 | (source 14 | (variable_statement 15 | name: (name) 16 | value: (true) 17 | setget: (setget 18 | set: (setter))) 19 | (variable_statement 20 | (annotations 21 | (annotation 22 | (identifier))) 23 | name: (name) 24 | value: (identifier) 25 | setget: (setget 26 | set: (setter)))) 27 | -------------------------------------------------------------------------------- /test/corpus/lambdas.txt: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Lambdas Assignment 3 | ===================================== 4 | 5 | var x = func hello(): 6 | pass 7 | 8 | var x = func(): 9 | pass 10 | 11 | var x = func hello(): pass 12 | 13 | --- 14 | 15 | (source 16 | (variable_statement 17 | (name) 18 | (lambda 19 | (name) 20 | (parameters) 21 | (body 22 | (pass_statement)))) 23 | (variable_statement 24 | (name) 25 | (lambda 26 | (parameters) 27 | (body 28 | (pass_statement)))) 29 | (variable_statement 30 | (name) 31 | (lambda 32 | (name) 33 | (parameters) 34 | (body 35 | (pass_statement))))) 36 | 37 | ===================================== 38 | Lambdas in Dictionary 39 | ===================================== 40 | 41 | {key = func(): pass; pass, key = 1} 42 | {key = 1, 'key': func(): 43 | pass 44 | pass} 45 | {key = 1, 'key': func(): 46 | pass 47 | pass, 'key': 1} 48 | 49 | --- 50 | 51 | (source 52 | (expression_statement 53 | (dictionary 54 | (pair (identifier) 55 | (lambda 56 | (parameters) 57 | (body 58 | (pass_statement) 59 | (pass_statement)))) 60 | (pair (identifier) (integer)))) 61 | (expression_statement 62 | (dictionary 63 | (pair (identifier) (integer)) 64 | (pair (string) 65 | (lambda 66 | (parameters) 67 | (body 68 | (pass_statement) 69 | (pass_statement)))))) 70 | (expression_statement 71 | (dictionary 72 | (pair 73 | (identifier) 74 | (integer)) 75 | (pair 76 | (string) 77 | (lambda 78 | (parameters) 79 | (body 80 | (pass_statement) 81 | (pass_statement)))) 82 | (pair 83 | (string) 84 | (integer))))) 85 | 86 | ; ===================================== 87 | ; Lambdas as Dictionary Keys 88 | ; ===================================== 89 | 90 | ; {func(): 91 | ; pass 92 | ; pass: 'value'} 93 | 94 | ; --- 95 | 96 | ; (source 97 | ; (expression_statement 98 | ; (dictionary 99 | ; (pair 100 | ; (lambda 101 | ; (parameters) 102 | ; (body 103 | ; (pass_statement) 104 | ; (pass_statement))) 105 | ; (string))))) 106 | 107 | ===================================== 108 | Lambdas in Array 109 | ===================================== 110 | 111 | [func x(): pass; pass] 112 | [func x(): pass; pass,] 113 | [func x(): 114 | pass 115 | pass] 116 | [func x(): 117 | pass 118 | pass,] 119 | [ 120 | func(): 121 | pass 122 | pass, 123 | func(): 124 | pass 125 | pass, 126 | ] 127 | 128 | func x(): 129 | if true: 130 | var a = [func(): pass ; pass,] 131 | var a = [func(): 132 | pass 133 | pass,] 134 | 135 | --- 136 | 137 | (source 138 | (expression_statement 139 | (array 140 | (lambda 141 | (name) 142 | (parameters) 143 | (body 144 | (pass_statement) 145 | (pass_statement))))) 146 | (expression_statement 147 | (array 148 | (lambda 149 | (name) 150 | (parameters) 151 | (body 152 | (pass_statement) 153 | (pass_statement))))) 154 | (expression_statement 155 | (array 156 | (lambda 157 | (name) 158 | (parameters) 159 | (body 160 | (pass_statement) 161 | (pass_statement))))) 162 | (expression_statement 163 | (array 164 | (lambda 165 | (name) 166 | (parameters) 167 | (body 168 | (pass_statement) 169 | (pass_statement))))) 170 | (expression_statement 171 | (array 172 | (lambda 173 | (parameters) 174 | (body 175 | (pass_statement) 176 | (pass_statement))) 177 | (lambda 178 | (parameters) 179 | (body 180 | (pass_statement) 181 | (pass_statement))))) 182 | (function_definition 183 | (name) 184 | (parameters) 185 | (body 186 | (if_statement 187 | (true) 188 | (body 189 | (variable_statement 190 | (name) 191 | (array 192 | (lambda 193 | (parameters) 194 | (body 195 | (pass_statement) 196 | (pass_statement))))) 197 | (variable_statement 198 | (name) 199 | (array 200 | (lambda 201 | (parameters) 202 | (body 203 | (pass_statement) 204 | (pass_statement)))))))))) 205 | 206 | ===================================== 207 | Lambdas Nested 208 | ===================================== 209 | 210 | var x = func(): (func hello(): pass) ; pass 211 | 212 | --- 213 | 214 | (source 215 | (variable_statement 216 | (name) 217 | (lambda 218 | (parameters) 219 | (body 220 | (expression_statement 221 | (parenthesized_expression 222 | (lambda 223 | (name) 224 | (parameters) 225 | (body 226 | (pass_statement))))) 227 | (pass_statement))))) 228 | 229 | ===================================== 230 | Lambdas as Params 231 | ===================================== 232 | 233 | func hello(p = func(p: int) -> int: pass; pass, x: int): 234 | pass 235 | 236 | func hello(p = func(p: int) -> int: 237 | pass 238 | pass, x: int): 239 | pass 240 | --- 241 | 242 | (source 243 | (function_definition 244 | (name) 245 | (parameters 246 | (default_parameter 247 | (identifier) 248 | (lambda 249 | (parameters 250 | (typed_parameter 251 | (identifier) 252 | (type (identifier)))) 253 | (type (identifier)) 254 | (body 255 | (pass_statement) 256 | (pass_statement)))) 257 | (typed_parameter 258 | (identifier) 259 | (type (identifier)))) 260 | (body 261 | (pass_statement))) 262 | (function_definition 263 | (name) 264 | (parameters 265 | (default_parameter 266 | (identifier) 267 | (lambda 268 | (parameters 269 | (typed_parameter 270 | (identifier) 271 | (type (identifier)))) 272 | (type (identifier)) 273 | (body 274 | (pass_statement) 275 | (pass_statement)))) 276 | (typed_parameter 277 | (identifier) 278 | (type (identifier)))) 279 | (body 280 | (pass_statement)))) 281 | 282 | ===================================== 283 | Lambdas as Args 284 | ===================================== 285 | 286 | hello(func(): pass) 287 | 288 | --- 289 | 290 | (source 291 | (expression_statement 292 | (call 293 | (identifier) 294 | (arguments 295 | (lambda 296 | (parameters) 297 | (body 298 | (pass_statement))))))) 299 | 300 | 301 | ===================================== 302 | Lambdas in Statements 303 | ===================================== 304 | 305 | func x(p: int = 1) -> int: 306 | if p == 1: 307 | var v = func(p: int) -> int: 308 | var result = v + p 309 | return result 310 | return x(p) 311 | else: 312 | return (func(v: int) -> int: 313 | var result = v + p 314 | return result)(p) 315 | 316 | --- 317 | 318 | (source 319 | (function_definition 320 | (name) 321 | (parameters 322 | (typed_default_parameter 323 | (identifier) 324 | (type (identifier)) 325 | (integer))) 326 | (type (identifier)) 327 | (body 328 | (if_statement 329 | (binary_operator 330 | (identifier) 331 | (integer)) 332 | (body 333 | (variable_statement 334 | (name) 335 | (lambda 336 | (parameters 337 | (typed_parameter 338 | (identifier) 339 | (type (identifier)))) 340 | (type (identifier)) 341 | (body 342 | (variable_statement 343 | (name) 344 | (binary_operator 345 | (identifier) 346 | (identifier))) 347 | (return_statement 348 | (identifier))))) 349 | (return_statement 350 | (call 351 | (identifier) 352 | (arguments 353 | (identifier))))) 354 | (else_clause 355 | (body 356 | (return_statement 357 | (call 358 | (parenthesized_expression 359 | (lambda 360 | (parameters 361 | (typed_parameter 362 | (identifier) 363 | (type (identifier)))) 364 | (type (identifier)) 365 | (body 366 | (variable_statement 367 | (name) 368 | (binary_operator 369 | (identifier) 370 | (identifier))) 371 | (return_statement 372 | (identifier))))) 373 | (arguments 374 | (identifier)))))))))) 375 | 376 | ===================================== 377 | Lambdas Return Stmt 378 | ===================================== 379 | 380 | var x = func(): 381 | pass 382 | return func x(t: T) -> T: 383 | pass 384 | return t 385 | 386 | --- 387 | 388 | (source 389 | (variable_statement 390 | (name) 391 | (lambda 392 | (parameters) 393 | (body 394 | (pass_statement) 395 | (return_statement 396 | (lambda 397 | (name) 398 | (parameters 399 | (typed_parameter 400 | (identifier) 401 | (type (identifier)))) 402 | (type (identifier)) 403 | (body 404 | (pass_statement) 405 | (return_statement 406 | (identifier))))))))) 407 | 408 | ===================================== 409 | Lambdas Nested Ifs 410 | ===================================== 411 | 412 | func x(): 413 | x(func(): 414 | if true: 415 | y(func(): 416 | if true: 417 | pass 418 | else: 419 | var x = 1 + 1 , 420 | "END y") 421 | else: 422 | pass, "END x") 423 | 424 | --- 425 | 426 | (source 427 | (function_definition 428 | (name) 429 | (parameters) 430 | (body 431 | (expression_statement 432 | (call 433 | (identifier) 434 | (arguments 435 | (lambda 436 | (parameters) 437 | (body 438 | (if_statement 439 | (true) 440 | (body 441 | (expression_statement 442 | (call 443 | (identifier) 444 | (arguments 445 | (lambda 446 | (parameters) 447 | (body 448 | (if_statement 449 | (true) 450 | (body 451 | (pass_statement)) 452 | (else_clause 453 | (body 454 | (variable_statement 455 | (name) 456 | (binary_operator 457 | (integer) 458 | (integer)))))))) 459 | (string))))) 460 | (else_clause 461 | (body 462 | (pass_statement)))))) 463 | (string))))))) 464 | 465 | -------------------------------------------------------------------------------- /test/corpus/match.txt: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Single 3 | ===================================== 4 | 5 | match x: 6 | 1: pass 7 | _: pass 8 | TYPE_ARRAY: 9 | pass 10 | var new_var: 11 | pass 12 | 13 | --- 14 | 15 | (source 16 | (match_statement (identifier) (match_body 17 | (pattern_section (integer) (body (pass_statement))) 18 | (pattern_section (identifier) (body (pass_statement))) 19 | (pattern_section (identifier) (body (pass_statement))) 20 | (pattern_section (pattern_binding (identifier)) (body (pass_statement))) 21 | ))) 22 | 23 | ===================================== 24 | Multiple 25 | ===================================== 26 | 27 | match x: 28 | _: 29 | pass 30 | 1, 2, 3: pass 31 | 32 | --- 33 | 34 | (source 35 | (match_statement (identifier) (match_body 36 | (pattern_section (identifier) (body (pass_statement))) 37 | (pattern_section (integer) (integer) (integer) 38 | (body (pass_statement))) 39 | ))) 40 | 41 | ===================================== 42 | Arrays 43 | ===================================== 44 | 45 | match x: 46 | []: pass 47 | [1, 3, "test", null]: 48 | pass 49 | [var start, _]: pass 50 | [42, ..]: 51 | pass 52 | 53 | --- 54 | 55 | (source 56 | (match_statement (identifier) (match_body 57 | (pattern_section (array) (body (pass_statement))) 58 | (pattern_section 59 | (array (integer) (integer) (string) (null)) 60 | (body (pass_statement))) 61 | (pattern_section 62 | (array (pattern_binding (identifier)) (identifier)) 63 | (body (pass_statement))) 64 | (pattern_section 65 | (array (integer) (pattern_open_ending)) 66 | (body (pass_statement))) 67 | ))) 68 | 69 | ===================================== 70 | Dicts 71 | ===================================== 72 | 73 | match x: 74 | {}: pass 75 | {"name": "Dennis"}: pass 76 | {"name": "Dennis", "age": var age}: pass 77 | {"key": "godotisawesome", ..}: pass 78 | 79 | --- 80 | 81 | (source 82 | (match_statement (identifier) (match_body 83 | (pattern_section (dictionary) (body (pass_statement))) 84 | (pattern_section 85 | (dictionary (pair (string) (string))) 86 | (body (pass_statement))) 87 | (pattern_section 88 | (dictionary 89 | (pair (string) (string)) 90 | (pair (string) (pattern_binding (identifier)))) 91 | (body (pass_statement))) 92 | (pattern_section 93 | (dictionary 94 | (pair (string) (string)) 95 | (pattern_open_ending)) 96 | (body (pass_statement))) 97 | ))) 98 | 99 | ===================================== 100 | Expressions 101 | ===================================== 102 | 103 | match x: 104 | Hello.World: pass 105 | Hello.World(): pass 106 | function(): pass 107 | Color().a: pass 108 | [var hello, _, ..]: pass 109 | 110 | --- 111 | 112 | (source 113 | (match_statement (identifier) (match_body 114 | (pattern_section 115 | (attribute (identifier) (identifier)) 116 | (body (pass_statement))) 117 | (pattern_section 118 | (attribute (identifier) (attribute_call (identifier) (arguments))) 119 | (body (pass_statement))) 120 | (pattern_section 121 | (call (identifier) (arguments)) 122 | (body (pass_statement))) 123 | (pattern_section 124 | (attribute 125 | (call (identifier) (arguments)) 126 | (identifier)) 127 | (body (pass_statement))) 128 | (pattern_section 129 | (array 130 | (pattern_binding (identifier)) 131 | (identifier) 132 | (pattern_open_ending)) 133 | (body (pass_statement)))))) -------------------------------------------------------------------------------- /test/corpus/sample.txt: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Sample - GDScript 1 3 | ===================================== 4 | 5 | tool 6 | extends Node2D 7 | class_name CustomNode, "icon_path" 8 | 9 | class Data extends Resource: 10 | var source: DataSource 11 | 12 | func process_source() -> ProcessedSource: 13 | if source.is_processable(): 14 | var processed = ProcessedSource.new(source) 15 | return processed 16 | return null 17 | 18 | func _ready(): 19 | for i in range(100): 20 | var child = Node.new() 21 | child.name = str("Name-", i) 22 | add_child(child) 23 | 24 | --- 25 | 26 | (source 27 | (tool_statement) 28 | (extends_statement (type (identifier))) 29 | (class_name_statement (name) (string)) 30 | (class_definition (name) (extends_statement (type (identifier))) 31 | (body 32 | (variable_statement (name) (type (identifier))) 33 | (function_definition (name) (parameters) (type (identifier)) 34 | (body 35 | (if_statement (attribute (identifier) (attribute_call (identifier) (arguments))) 36 | (body 37 | (variable_statement (name) 38 | (attribute (identifier) (attribute_call (identifier) (arguments (identifier))))) 39 | (return_statement (identifier)))) 40 | (return_statement (null)))))) 41 | (function_definition (name) (parameters) 42 | (body 43 | (for_statement (identifier) (call (identifier) (arguments (integer))) 44 | (body 45 | (variable_statement (name) 46 | (attribute (identifier) (attribute_call (identifier) (arguments)))) 47 | (expression_statement 48 | (assignment 49 | (attribute (identifier) (identifier)) 50 | (call (identifier) (arguments (string) (identifier))))) 51 | (expression_statement 52 | (call (identifier) (arguments (identifier))))))))) 53 | 54 | -------------------------------------------------------------------------------- /test/corpus/source.txt: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Math Expressions 3 | ===================================== 4 | 5 | 1 + x + 1 6 | 0xDEAD * x / 0.01 7 | 0xDEAD * (x / 0.01) 8 | 2 ** 4 9 | 10 | --- 11 | 12 | (source 13 | (expression_statement 14 | (binary_operator (binary_operator (integer) (identifier)) (integer))) 15 | (expression_statement 16 | (binary_operator (binary_operator (integer) (identifier)) (float))) 17 | (expression_statement 18 | (binary_operator (integer) (parenthesized_expression 19 | (binary_operator (identifier) (float))))) 20 | (expression_statement 21 | (binary_operator (integer) (integer)))) 22 | 23 | ===================================== 24 | Operators Boolean 25 | ===================================== 26 | 27 | x and (x && true) 28 | x or (x || true) 29 | not x and (!x) 30 | 31 | --- 32 | 33 | (source 34 | (expression_statement 35 | (binary_operator 36 | (identifier) 37 | (parenthesized_expression 38 | (binary_operator 39 | (identifier) 40 | (true))))) 41 | (expression_statement 42 | (binary_operator 43 | (identifier) 44 | (parenthesized_expression 45 | (binary_operator 46 | (identifier) 47 | (true))))) 48 | (expression_statement 49 | (binary_operator 50 | (unary_operator (identifier)) 51 | (parenthesized_expression 52 | (unary_operator (identifier)))))) 53 | 54 | ===================================== 55 | Operators Is 56 | ===================================== 57 | 58 | x is y 59 | x is int() 60 | (1 + 1) is Name 61 | 62 | --- 63 | 64 | (source 65 | (expression_statement (binary_operator (identifier) (identifier))) 66 | (expression_statement (binary_operator (identifier) (call (identifier) (arguments)))) 67 | (expression_statement (binary_operator 68 | (parenthesized_expression 69 | (binary_operator (integer) (integer))) 70 | (identifier)))) 71 | 72 | ===================================== 73 | Operators As 74 | ===================================== 75 | 76 | x as Y 77 | x() as Y 78 | 79 | --- 80 | 81 | (source 82 | (expression_statement (binary_operator (identifier) (identifier))) 83 | (expression_statement (binary_operator (call (identifier) (arguments)) (identifier)))) 84 | 85 | ===================================== 86 | Mixed Operators 87 | ===================================== 88 | 89 | # Should parse as: 90 | # ((1 - 2) != (-1 << 2)) or "hello" 91 | 1 - 2 != -1 << 2 or "hello" 92 | 93 | --- 94 | 95 | (source (comment) (comment) 96 | (expression_statement 97 | (binary_operator 98 | (binary_operator 99 | (binary_operator 100 | (integer) 101 | (integer)) 102 | (binary_operator 103 | (unary_operator 104 | (integer)) 105 | (integer))) 106 | (string)))) 107 | 108 | ===================================== 109 | Array 110 | ===================================== 111 | 112 | [1] 113 | [1, 2] 114 | [1, "a", 2] 115 | [ 116 | 1, 117 | ] 118 | [,] 119 | 120 | --- 121 | 122 | (source 123 | (expression_statement (array (integer))) 124 | (expression_statement (array (integer) (integer))) 125 | (expression_statement (array (integer) (string) (integer))) 126 | (expression_statement (array (integer))) 127 | (expression_statement (array (ERROR))) 128 | ) 129 | 130 | ===================================== 131 | Dictionary 132 | ===================================== 133 | 134 | {a = 0.1, c:1, "e":$hello} 135 | {a = 0xBEEF, c:1 + 1, "e":f,} 136 | {(1 + 1) : "a"} 137 | {(1 + 1) = "a"} 138 | {,} 139 | 140 | --- 141 | 142 | (source 143 | (expression_statement 144 | (dictionary 145 | (pair (identifier) (float)) 146 | (pair (identifier) (integer)) 147 | (pair (string) (get_node)))) 148 | (expression_statement 149 | (dictionary 150 | (pair (identifier) (integer)) 151 | (pair (identifier) (binary_operator (integer) (integer))) 152 | (pair (string) (identifier)))) 153 | (expression_statement 154 | (dictionary 155 | (pair 156 | (parenthesized_expression (binary_operator (integer) (integer))) 157 | (string)))) 158 | (expression_statement 159 | (dictionary 160 | (ERROR 161 | (parenthesized_expression (binary_operator (integer) (integer))) 162 | (string)))) 163 | (expression_statement 164 | (dictionary (ERROR)))) 165 | 166 | ===================================== 167 | StringName 168 | ===================================== 169 | 170 | &"string name" 171 | &'string name' 172 | 173 | --- 174 | 175 | (source 176 | (expression_statement (string_name)) 177 | (expression_statement (string_name))) 178 | 179 | ===================================== 180 | NodePath 181 | ===================================== 182 | 183 | @"Node/Path" 184 | @'Node/Path' 185 | @ 'node/path' 186 | var x = @"NodePath" 187 | ^"node/path" 188 | 189 | --- 190 | 191 | (source 192 | (expression_statement (node_path)) 193 | (expression_statement (node_path)) 194 | (ERROR) (expression_statement (string)) 195 | (variable_statement (name) (node_path)) 196 | (expression_statement (node_path))) 197 | 198 | ===================================== 199 | Get Node 200 | ===================================== 201 | 202 | $node/path 203 | $'node/path' 204 | $"node/path" 205 | $ "node/path" 206 | var x = $"node/path" 207 | 208 | %node 209 | 210 | --- 211 | 212 | (source 213 | (expression_statement (get_node)) 214 | (expression_statement (get_node)) 215 | (expression_statement (get_node)) 216 | (ERROR (UNEXPECTED '$')) (expression_statement (string)) 217 | (variable_statement (name) (get_node)) 218 | (expression_statement (get_node))) 219 | 220 | ===================================== 221 | Await Expressions 222 | ===================================== 223 | 224 | await hello() 225 | await $node/hello.sig 226 | var x = await call() 227 | var x = await $node/hello.sig 228 | 229 | func x(): 230 | if true: 231 | await call() 232 | var x = await $"node/hello".sig 233 | 234 | --- 235 | 236 | (source 237 | (expression_statement 238 | (await_expression 239 | (call (identifier) (arguments)))) 240 | (expression_statement 241 | (await_expression 242 | (attribute (get_node) (identifier)))) 243 | (variable_statement (name) 244 | (await_expression (call (identifier) (arguments)))) 245 | (variable_statement (name) 246 | (await_expression (attribute (get_node) (identifier)))) 247 | (function_definition (name) (parameters) (body 248 | (if_statement (true) (body 249 | (expression_statement 250 | (await_expression (call (identifier) (arguments)))) 251 | (variable_statement (name) 252 | (await_expression (attribute (get_node) (identifier))))))))) 253 | 254 | ===================================== 255 | Breakpoint Statement 256 | ===================================== 257 | 258 | breakpoint 259 | breakpoint; hi() 260 | 261 | --- 262 | 263 | (source 264 | (breakpoint_statement) 265 | (breakpoint_statement) 266 | (expression_statement (call (identifier) (arguments)))) 267 | 268 | ===================================== 269 | Variables 270 | ===================================== 271 | 272 | var x 273 | var x = "hello" 274 | var x := "hello" 275 | var x: Type 276 | var x: Type = "hello" 277 | 278 | --- 279 | 280 | (source 281 | (variable_statement (name)) 282 | (variable_statement (name) (string)) 283 | (variable_statement (name) (inferred_type) (string)) 284 | (variable_statement (name) (type (identifier))) 285 | (variable_statement (name) (type (identifier)) (string))) 286 | 287 | ===================================== 288 | Variables Assign Expressions 289 | ===================================== 290 | 291 | var x = 1 + x + 1 292 | 293 | --- 294 | 295 | (source 296 | (variable_statement (name) 297 | (binary_operator (binary_operator (integer) (identifier)) (integer)))) 298 | 299 | ===================================== 300 | Augmented Assignment 301 | ===================================== 302 | 303 | x += 1 304 | x **= 2.0 305 | 306 | --- 307 | 308 | (source 309 | (expression_statement 310 | (augmented_assignment (identifier) (integer))) 311 | (expression_statement 312 | (augmented_assignment (identifier) (float)))) 313 | 314 | ===================================== 315 | Variables Remote 316 | ===================================== 317 | 318 | remote var hello 319 | master var hello 320 | puppet var hello 321 | remotesync var hello 322 | mastersync var hello 323 | puppetsync var hello 324 | 325 | --- 326 | 327 | (source 328 | (variable_statement (remote_keyword) (name)) 329 | (variable_statement (remote_keyword) (name)) 330 | (variable_statement (remote_keyword) (name)) 331 | (variable_statement (remote_keyword) (name)) 332 | (variable_statement (remote_keyword) (name)) 333 | (variable_statement (remote_keyword) (name))) 334 | 335 | ===================================== 336 | Variables Export 337 | ===================================== 338 | 339 | export var a 340 | export var a = "a" 341 | export remote var a 342 | 343 | export(Type) var a 344 | export(Type) var a = "a" 345 | export(Type) remote var a 346 | 347 | export(Type, Param) var a 348 | export(Type, "Param") var a = "a" 349 | 350 | export(Type,) var a 351 | 352 | --- 353 | 354 | (source 355 | (export_variable_statement (name)) 356 | (export_variable_statement (name) (string)) 357 | (export_variable_statement (remote_keyword) (name)) 358 | (export_variable_statement (arguments (identifier)) (name)) 359 | (export_variable_statement (arguments (identifier)) (name) (string)) 360 | (export_variable_statement (arguments (identifier)) (remote_keyword) (name)) 361 | (export_variable_statement (arguments (identifier) (identifier)) (name)) 362 | (export_variable_statement (arguments (identifier) (string)) (name) (string)) 363 | (export_variable_statement (arguments (identifier)) (name))) 364 | 365 | ===================================== 366 | Variables Annotations 367 | ===================================== 368 | 369 | @onready var a 370 | @export(FILE, "") @onready var a := 0 371 | 372 | @export(FILE, 0) @hello 373 | @onready 374 | var a := 0 375 | 376 | --- 377 | 378 | (source 379 | (variable_statement 380 | (annotations 381 | (annotation (identifier))) 382 | (name)) 383 | (variable_statement 384 | (annotations 385 | (annotation (identifier) 386 | (arguments (identifier) (string))) 387 | (annotation (identifier))) 388 | (name) 389 | (inferred_type) 390 | (integer)) 391 | (annotation (identifier) 392 | (arguments (identifier) (integer))) 393 | (annotation (identifier)) 394 | (annotation (identifier)) 395 | (variable_statement 396 | (name) 397 | (inferred_type) 398 | (integer))) 399 | 400 | ===================================== 401 | Variables SetGet 402 | ===================================== 403 | 404 | var x setget setter,getter 405 | var x setget setter 406 | var x setget ,getter 407 | 408 | --- 409 | 410 | (source 411 | (variable_statement (name) (setget (setter) (getter))) 412 | (variable_statement (name) (setget (setter))) 413 | (variable_statement (name) (setget (getter))) 414 | ) 415 | 416 | ===================================== 417 | Variables SetGet V2 418 | ===================================== 419 | 420 | var x: int: 421 | get: 422 | pass 423 | set(value): 424 | pass 425 | 426 | var x: int = 0: 427 | get: pass 428 | set(value): pass 429 | 430 | var x = 0: 431 | get: pass 432 | set(value): pass 433 | 434 | var x: 435 | get: 436 | pass 437 | 438 | var x: 439 | get: pass 440 | 441 | var x: 442 | get: pass; pass 443 | 444 | var x: int: 445 | set(val): 446 | pass 447 | 448 | var my_prop: 449 | get = get_my_prop, set = set_my_prop 450 | 451 | --- 452 | 453 | (source 454 | (variable_statement 455 | (name) 456 | (type (identifier)) 457 | (setget 458 | (get_body 459 | (pass_statement)) 460 | (set_body 461 | (parameters (identifier)) 462 | (pass_statement)))) 463 | (variable_statement 464 | (name) 465 | (type (identifier)) 466 | (integer) 467 | (setget 468 | (get_body 469 | (pass_statement)) 470 | (set_body 471 | (parameters (identifier)) 472 | (pass_statement)))) 473 | (variable_statement 474 | (name) 475 | (integer) 476 | (setget 477 | (get_body 478 | (pass_statement)) 479 | (set_body 480 | (parameters (identifier)) 481 | (pass_statement)))) 482 | (variable_statement 483 | (name) 484 | (setget 485 | (get_body 486 | (pass_statement)))) 487 | (variable_statement 488 | (name) 489 | (setget 490 | (get_body 491 | (pass_statement)))) 492 | (variable_statement 493 | (name) 494 | (setget 495 | (get_body 496 | (pass_statement) 497 | (pass_statement)))) 498 | (variable_statement 499 | (name) 500 | (type (identifier)) 501 | (setget 502 | (set_body 503 | (parameters 504 | (identifier)) 505 | (pass_statement)))) 506 | (variable_statement 507 | (name) 508 | (setget 509 | (getter) 510 | (setter)))) 511 | 512 | ===================================== 513 | Variables Onready 514 | ===================================== 515 | 516 | onready var hello 517 | onready var hello: World 518 | 519 | --- 520 | 521 | (source 522 | (onready_variable_statement (name)) 523 | (onready_variable_statement (name) (type (identifier)))) 524 | 525 | ===================================== 526 | Variables Const 527 | ===================================== 528 | 529 | const CONST = 1 530 | const CONST := 1 531 | const CONST : int = 1 532 | 533 | --- 534 | 535 | (source 536 | (const_statement (name) (integer)) 537 | (const_statement (name) (inferred_type) (integer)) 538 | (const_statement (name) (type (identifier)) (integer))) 539 | 540 | 541 | ===================================== 542 | Variables Invalid 543 | ===================================== 544 | 545 | var x = var y 546 | var x := 547 | 548 | --- 549 | 550 | (source 551 | (variable_statement (name) (ERROR (identifier)) (identifier)) 552 | (ERROR (name) (inferred_type))) 553 | 554 | ===================================== 555 | Functions 556 | ===================================== 557 | 558 | func hello(): 559 | pass 560 | 561 | func hello() -> Type: 562 | pass 563 | 564 | func hello(param): 565 | pass 566 | 567 | func hello(param) -> Type: 568 | pass 569 | 570 | func hello(default_param="string"): 571 | pass 572 | 573 | func hello(default_param="string") -> Type: 574 | pass 575 | 576 | func hello(typed_param: Type): 577 | pass 578 | 579 | func hello(typed_param: Type) -> Type: 580 | pass 581 | 582 | func hello(typed_default_param: Type="string"): 583 | pass 584 | 585 | func hello(typed_default_param: Type="string") -> Type: 586 | pass 587 | 588 | --- 589 | 590 | (source 591 | (function_definition (name) (parameters) 592 | (body (pass_statement))) 593 | (function_definition (name) (parameters) (type (identifier)) 594 | (body (pass_statement))) 595 | (function_definition (name) (parameters (identifier)) 596 | (body (pass_statement))) 597 | (function_definition (name) (parameters (identifier)) (type (identifier)) 598 | (body (pass_statement))) 599 | (function_definition (name) (parameters (default_parameter (identifier) (string))) 600 | (body (pass_statement))) 601 | (function_definition (name) (parameters (default_parameter (identifier) (string))) (type (identifier)) 602 | (body (pass_statement))) 603 | (function_definition (name) (parameters (typed_parameter (identifier) (type (identifier)))) 604 | (body (pass_statement))) 605 | (function_definition (name) (parameters (typed_parameter (identifier) (type (identifier)))) (type (identifier)) 606 | (body (pass_statement))) 607 | (function_definition (name) (parameters (typed_default_parameter (identifier) (type (identifier)) (string))) 608 | (body (pass_statement))) 609 | (function_definition (name) (parameters (typed_default_parameter (identifier) (type (identifier)) (string))) (type (identifier)) 610 | (body (pass_statement)))) 611 | 612 | ===================================== 613 | Functions Remote 614 | ===================================== 615 | 616 | remote func hello(): pass 617 | master func hello(): pass 618 | puppet func hello(): pass 619 | remotesync func hello(): pass 620 | mastersync func hello(): pass 621 | puppetsync func hello(): pass 622 | 623 | --- 624 | 625 | (source 626 | (function_definition (remote_keyword) (name) (parameters) 627 | (body (pass_statement))) 628 | (function_definition (remote_keyword) (name) (parameters) 629 | (body (pass_statement))) 630 | (function_definition (remote_keyword) (name) (parameters) 631 | (body (pass_statement))) 632 | (function_definition (remote_keyword) (name) (parameters) 633 | (body (pass_statement))) 634 | (function_definition (remote_keyword) (name) (parameters) 635 | (body (pass_statement))) 636 | (function_definition (remote_keyword) (name) (parameters) 637 | (body (pass_statement)))) 638 | 639 | ===================================== 640 | Functions Static 641 | ===================================== 642 | 643 | static func hello(): pass 644 | 645 | --- 646 | 647 | (source 648 | (function_definition (static_keyword) (name) (parameters) 649 | (body (pass_statement)))) 650 | 651 | ===================================== 652 | Functions Annotations 653 | ===================================== 654 | 655 | @rpc func hello(): pass 656 | @script_level 657 | @rpc("remote") func hello(): 658 | pass 659 | 660 | func test(): 661 | return 662 | @warning_ignore("unreachable_code") 663 | print("unreachable") 664 | 665 | --- 666 | 667 | (source 668 | (function_definition 669 | (annotations 670 | (annotation (identifier))) 671 | (name) 672 | (parameters) 673 | (body 674 | (pass_statement))) 675 | (annotation (identifier)) 676 | (function_definition 677 | (annotations 678 | (annotation 679 | (identifier) 680 | (arguments 681 | (string)))) 682 | (name) 683 | (parameters) 684 | (body 685 | (pass_statement))) 686 | (function_definition 687 | (name) 688 | (parameters) 689 | (body 690 | (return_statement) 691 | (annotation 692 | (identifier) 693 | (arguments 694 | (string))) 695 | (expression_statement 696 | (call (identifier) 697 | (arguments (string))))))) 698 | 699 | ===================================== 700 | Constructor Definitions 701 | ===================================== 702 | 703 | func _init(): pass 704 | func _init().(): pass 705 | func _init(a, b).(a,b): pass 706 | 707 | --- 708 | 709 | (source 710 | (constructor_definition 711 | (parameters) 712 | (body (pass_statement))) 713 | (constructor_definition 714 | (parameters) 715 | (arguments) 716 | (body (pass_statement))) 717 | (constructor_definition 718 | (parameters (identifier) (identifier)) 719 | (arguments (identifier) (identifier)) 720 | (body (pass_statement)))) 721 | 722 | ===================================== 723 | Base Calls 724 | ===================================== 725 | 726 | .hello() 727 | var x = .hello() 728 | var x = hello(.hello()) 729 | 730 | --- 731 | 732 | (source 733 | (expression_statement (base_call (identifier) (arguments))) 734 | (variable_statement (name) (base_call (identifier) (arguments))) 735 | (variable_statement 736 | (name) 737 | (call (identifier) (arguments (base_call (identifier) (arguments)))))) 738 | 739 | ===================================== 740 | Function Calls 741 | ===================================== 742 | 743 | hello() 744 | hello(world) 745 | hello("world") 746 | hello(1 + 1, "world") 747 | hello(hello()) 748 | hello(hello("world")) 749 | 750 | --- 751 | 752 | (source 753 | (expression_statement (call (identifier) (arguments))) 754 | (expression_statement (call (identifier) (arguments (identifier)))) 755 | (expression_statement (call (identifier) (arguments (string)))) 756 | (expression_statement (call (identifier) (arguments (binary_operator (integer) (integer)) (string)))) 757 | (expression_statement (call (identifier) (arguments (call (identifier) (arguments))))) 758 | (expression_statement (call (identifier) (arguments (call (identifier) (arguments (string)))))) 759 | ) 760 | 761 | ===================================== 762 | Attribute Accessors 763 | ===================================== 764 | 765 | x.y 766 | x.y.z 767 | x.y.z() 768 | x.y().z 769 | x().y 770 | 771 | x.y[0] 772 | x[0].z 773 | x.y.z[0] 774 | x['s'].y.z[0] 775 | 776 | --- 777 | 778 | (source 779 | (expression_statement (attribute (identifier) (identifier))) 780 | (expression_statement (attribute (identifier) (identifier) (identifier))) 781 | (expression_statement (attribute (identifier) (identifier) (attribute_call (identifier) (arguments)))) 782 | (expression_statement (attribute (identifier) (attribute_call (identifier) (arguments)) (identifier))) 783 | (expression_statement (attribute (call (identifier) (arguments)) (identifier))) 784 | 785 | (expression_statement (attribute (identifier) (attribute_subscript (identifier) (integer)))) 786 | (expression_statement (attribute (subscript (identifier) (integer)) (identifier))) 787 | (expression_statement (attribute (identifier) (identifier) (attribute_subscript (identifier) (integer)))) 788 | (expression_statement (attribute (subscript (identifier) (string)) (identifier) (attribute_subscript (identifier) (integer)))) 789 | ) 790 | 791 | ===================================== 792 | If Statements 793 | ===================================== 794 | 795 | if true: 796 | pass 797 | if true: 798 | if true: 799 | pass 800 | elif true: 801 | pass 802 | elif true: 803 | for i in array: 804 | if null: 805 | pass 806 | else: 807 | pass 808 | else: 809 | pass 810 | 811 | --- 812 | 813 | (source 814 | (if_statement 815 | (true) 816 | (body 817 | (pass_statement) 818 | (if_statement 819 | (true) 820 | (body 821 | (if_statement 822 | (true) 823 | (body 824 | (pass_statement)))) 825 | (elif_clause 826 | (true) 827 | (body 828 | (pass_statement))))) 829 | (elif_clause 830 | (true) 831 | (body 832 | (for_statement 833 | (identifier) 834 | (identifier) 835 | (body 836 | (if_statement 837 | (null) 838 | (body 839 | (pass_statement)) 840 | (else_clause 841 | (body 842 | (pass_statement)))))))) 843 | (else_clause 844 | (body 845 | (pass_statement))))) 846 | 847 | ===================================== 848 | Signal Statements 849 | ===================================== 850 | 851 | signal x 852 | signal x() 853 | signal x(a, b) 854 | 855 | signal x(a,) 856 | 857 | signal x(a: Type) 858 | signal x(a: Type,) 859 | signal x(a, b: Type) 860 | 861 | --- 862 | 863 | (source 864 | (signal_statement (name)) 865 | (signal_statement (name) (parameters)) 866 | (signal_statement (name) (parameters (identifier) (identifier))) 867 | (signal_statement (name) (parameters (identifier))) 868 | (signal_statement 869 | (name) 870 | (parameters 871 | (typed_parameter 872 | (identifier) 873 | (type (identifier))))) 874 | (signal_statement 875 | (name) 876 | (parameters 877 | (typed_parameter 878 | (identifier) 879 | (type (identifier))))) 880 | (signal_statement 881 | (name) 882 | (parameters 883 | (identifier) 884 | (typed_parameter 885 | (identifier) 886 | (type (identifier)))))) 887 | 888 | ===================================== 889 | Class Name 890 | ===================================== 891 | 892 | class_name Name 893 | class_name Name, "icon" 894 | 895 | --- 896 | 897 | (source 898 | (class_name_statement (name)) 899 | (class_name_statement (name) (string))) 900 | 901 | ===================================== 902 | Tool and Extend 903 | ===================================== 904 | 905 | tool 906 | extends "class/path" 907 | extends "class/path".Attr 908 | extends "class/path".Attr.Attr 909 | extends Ident 910 | extends Ident.Attr 911 | extends Ident.Attr.Attr 912 | 913 | --- 914 | (source 915 | (tool_statement) 916 | (extends_statement (string)) 917 | (extends_statement (type (attribute (string) (identifier)))) 918 | (extends_statement (type (attribute (string) (identifier) (identifier)))) 919 | (extends_statement (type (identifier))) 920 | (extends_statement (type (attribute (identifier) (identifier)))) 921 | (extends_statement (type (attribute (identifier) (identifier) (identifier))))) 922 | 923 | ===================================== 924 | Inner Class 925 | ===================================== 926 | 927 | class Name: 928 | func hello(): pass 929 | 930 | class Name extends Type: 931 | func hello(): pass 932 | 933 | class Name: 934 | extends Type 935 | 936 | --- 937 | 938 | (source 939 | (class_definition (name) 940 | (body 941 | (function_definition (name) (parameters) 942 | (body (pass_statement))))) 943 | (class_definition (name) (extends_statement (type (identifier))) 944 | (body 945 | (function_definition (name) (parameters) 946 | (body (pass_statement))))) 947 | (class_definition (name) 948 | (body 949 | (extends_statement (type (identifier)))))) 950 | 951 | ============================================ 952 | Enum declarations 953 | ============================================ 954 | 955 | enum Hello { 956 | val1, 957 | val2 = 5, 958 | val3 959 | } 960 | 961 | enum { 962 | val1, 963 | val2 = 5, 964 | val3 965 | } 966 | 967 | enum A { 968 | val1 = 1, 969 | } 970 | 971 | --- 972 | 973 | (source 974 | (enum_definition (name) 975 | (enumerator_list 976 | (enumerator (identifier)) 977 | (enumerator (identifier) (integer)) 978 | (enumerator (identifier)))) 979 | (enum_definition 980 | (enumerator_list 981 | (enumerator (identifier)) 982 | (enumerator (identifier) (integer)) 983 | (enumerator (identifier)))) 984 | (enum_definition (name) 985 | (enumerator_list 986 | (enumerator (identifier) (integer))))) 987 | 988 | ============================================ 989 | Enumerator Expressions 990 | ============================================ 991 | 992 | enum { 993 | A = 1 + 1, 994 | B = Hello.World, 995 | C = (Hello.World | 0xDEADBEEF), 996 | D = int(true), 997 | E = const_arr[0], 998 | F = {one = 1}, 999 | } 1000 | 1001 | --- 1002 | 1003 | (source 1004 | (enum_definition 1005 | (enumerator_list 1006 | (enumerator (identifier) (binary_operator (integer) (integer))) 1007 | (enumerator (identifier) (attribute (identifier) (identifier))) 1008 | (enumerator (identifier) (parenthesized_expression (binary_operator (attribute (identifier) (identifier)) (integer)))) 1009 | (enumerator (identifier) (call (identifier) (arguments (true)))) 1010 | (enumerator (identifier) (subscript (identifier) (integer))) 1011 | (enumerator (identifier)) (ERROR (dictionary (pair (identifier) (integer))))))) 1012 | -------------------------------------------------------------------------------- /test/corpus/source2.txt: -------------------------------------------------------------------------------- 1 | ============================================ 2 | Typed Arrays (#18) 3 | ============================================ 4 | 5 | var a1: Array[IClass.IIClass] 6 | var a3: Array[int] 7 | 8 | --- 9 | 10 | (source 11 | (variable_statement 12 | (name) 13 | (type 14 | (subscript 15 | (identifier) 16 | (attribute 17 | (identifier) 18 | (identifier))))) 19 | (variable_statement 20 | (name) 21 | (type 22 | (subscript 23 | (identifier) 24 | (identifier))))) 25 | 26 | ============================================ 27 | Static Variable Statements 28 | ============================================ 29 | 30 | static var name 31 | static var name := "Me" 32 | static var name: int 33 | 34 | # There may be valid annotations for static vars. 35 | @some_annotation static var name 36 | 37 | --- 38 | 39 | (source 40 | (variable_statement (static_keyword) (name)) 41 | (variable_statement (static_keyword) (name) (inferred_type) (string)) 42 | (variable_statement (static_keyword) (name) (type (identifier))) 43 | (comment) 44 | (variable_statement 45 | (annotations (annotation (identifier))) 46 | (static_keyword) 47 | (name))) 48 | 49 | 50 | ============================================ 51 | Static Typed For Loop 52 | ============================================ 53 | 54 | for i: int in [1, 2]: 55 | pass 56 | 57 | --- 58 | 59 | (source 60 | (for_statement 61 | (identifier) 62 | (type (identifier)) 63 | (array 64 | (integer) 65 | (integer)) 66 | (body 67 | (pass_statement)))) 68 | 69 | ============================================ 70 | SetGet Getter with Empty Parenthesis 71 | ============================================ 72 | 73 | var x: 74 | get(): pass 75 | 76 | --- 77 | 78 | (source 79 | (variable_statement 80 | (name) 81 | (setget 82 | (get_body 83 | (pass_statement))))) 84 | 85 | ============================================ 86 | Pattern Guards for Match Statement 87 | ============================================ 88 | 89 | var a = 0 90 | match a: 91 | 0 when false: print("does not run") 92 | 0 when true: print("but this does") 93 | 94 | var when = 1 95 | match when: 96 | when when when: 97 | when 98 | 99 | --- 100 | 101 | (source 102 | (variable_statement (name) (integer)) 103 | (match_statement 104 | (identifier) 105 | (match_body 106 | (pattern_section 107 | (integer) 108 | (pattern_guard 109 | (false)) 110 | (body 111 | (expression_statement 112 | (call 113 | (identifier) 114 | (arguments 115 | (string)))))) 116 | (pattern_section 117 | (integer) 118 | (pattern_guard 119 | (true)) 120 | (body 121 | (expression_statement 122 | (call 123 | (identifier) 124 | (arguments 125 | (string)))))))) 126 | (variable_statement (name) (integer)) 127 | (match_statement 128 | (identifier) 129 | (match_body 130 | (pattern_section 131 | (identifier) 132 | (pattern_guard 133 | (identifier)) 134 | (body 135 | (expression_statement (identifier))))))) 136 | 137 | ============================================ 138 | Is Not Test 139 | ============================================ 140 | 141 | x is T 142 | x is not T 143 | x is (not T) 144 | 145 | --- 146 | 147 | (source 148 | (expression_statement (binary_operator (identifier) (identifier))) 149 | (expression_statement (binary_operator (identifier) (identifier))) 150 | (expression_statement (binary_operator (identifier) (parenthesized_expression (unary_operator (identifier)))))) 151 | -------------------------------------------------------------------------------- /tree-sitter.json: -------------------------------------------------------------------------------- 1 | { 2 | "grammars": [ 3 | { 4 | "name": "gdscript", 5 | "camelcase": "GDScript", 6 | "scope": "source.gdscript", 7 | "file-types": [ 8 | ".gd" 9 | ], 10 | "injection-regex": "^gdscript$" 11 | } 12 | ], 13 | "metadata": { 14 | "version": "5.0.1", 15 | "license": "MIT", 16 | "description": "Grammar for Godot's built-in scripting language.", 17 | "authors": [ 18 | { 19 | "name": "Preston Knopp", 20 | "email": "prestonknopp@gmail.com" 21 | } 22 | ], 23 | "links": { 24 | "repository": "https://github.com/PrestonKnopp/tree-sitter-gdscript" 25 | } 26 | }, 27 | "bindings": { 28 | "c": true, 29 | "go": true, 30 | "node": true, 31 | "python": true, 32 | "rust": true, 33 | "swift": true 34 | } 35 | } 36 | --------------------------------------------------------------------------------