├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── dependencies.yml │ ├── fuzz.yml │ ├── lint.yml │ └── publish.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── Package.swift ├── README.md ├── binding.gyp ├── bindings ├── c │ ├── tree-sitter-svelte.h │ └── tree-sitter-svelte.pc.in ├── go │ ├── binding.go │ └── binding_test.go ├── node │ ├── binding.cc │ ├── binding_test.js │ ├── index.d.ts │ └── index.js ├── python │ ├── tests │ │ └── test_binding.py │ └── tree_sitter_svelte │ │ ├── __init__.py │ │ ├── __init__.pyi │ │ ├── binding.c │ │ └── py.typed ├── rust │ ├── build.rs │ └── lib.rs └── swift │ ├── TreeSitterSvelte │ └── svelte.h │ └── TreeSitterSvelteTests │ └── TreeSitterSvelteTests.swift ├── compile_commands.json ├── eslint.config.mjs ├── examples └── .gitignore ├── go.mod ├── go.sum ├── grammar.js ├── package-lock.json ├── package.json ├── pyproject.toml ├── queries ├── folds.scm ├── highlights.scm ├── indents.scm ├── injections.scm └── locals.scm ├── setup.py ├── src ├── grammar.json ├── node-types.json ├── parser.c ├── scanner.c ├── tag.h └── tree_sitter │ ├── alloc.h │ ├── array.h │ └── parser.h └── test └── corpus ├── balancing.txt ├── html └── main.txt ├── main.txt └── snippets.txt /.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 | [*.{c,cc,h}] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | [*.rs] 19 | indent_style = space 20 | indent_size = 4 21 | 22 | [*.{py,pyi}] 23 | indent_style = space 24 | indent_size = 4 25 | 26 | [*.swift] 27 | indent_style = space 28 | indent_size = 4 29 | 30 | [*.go] 31 | indent_style = tab 32 | indent_size = 8 33 | 34 | [Makefile] 35 | indent_style = tab 36 | indent_size = 8 37 | 38 | [parser.c] 39 | indent_size = 2 40 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | src/*.json linguist-generated 4 | src/parser.c linguist-generated 5 | src/tree_sitter/* linguist-generated 6 | 7 | bindings/** linguist-generated 8 | binding.gyp linguist-generated 9 | setup.py linguist-generated 10 | Makefile linguist-generated 11 | Package.swift linguist-generated 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: amaanq 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - grammar.js 8 | - src/** 9 | - test/** 10 | - bindings/** 11 | - binding.gyp 12 | pull_request: 13 | paths: 14 | - grammar.js 15 | - src/** 16 | - test/** 17 | - bindings/** 18 | - binding.gyp 19 | 20 | concurrency: 21 | group: ${{github.workflow}}-${{github.ref}} 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | test: 26 | name: Test parser 27 | runs-on: ${{matrix.os}} 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | os: [ubuntu-latest, windows-latest, macos-14] 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v4 35 | - name: Set up tree-sitter 36 | uses: tree-sitter/setup-action/cli@v1 37 | - name: Clone Svelte repo 38 | uses: actions/checkout@v4 39 | with: 40 | path: examples/svelte 41 | repository: sveltejs/svelte 42 | sparse-checkout: |- 43 | packages/svelte/tests/ 44 | documentation/examples/ 45 | sites/svelte.dev/src/ 46 | - name: Run tests 47 | uses: tree-sitter/parser-test-action@v2 48 | with: 49 | test-rust: ${{runner.os == 'Linux'}} 50 | test-node: true 51 | test-python: true 52 | test-go: true 53 | test-swift: ${{runner.os == 'macOS'}} 54 | - name: Parse examples 55 | uses: tree-sitter/parse-action@v4 56 | with: 57 | files: |- 58 | examples/svelte/**/*.svelte 59 | !examples/svelte/packages/svelte/tests/parser-legacy/samples/implicitly-closed-li-block/input.svelte 60 | !examples/svelte/packages/svelte/tests/parser-legacy/samples/no-error-if-before-closing/input.svelte 61 | !examples/svelte/packages/svelte/tests/parser-legacy/samples/textarea-end-tag/input.svelte 62 | !examples/svelte/packages/svelte/tests/preprocess/samples/script-self-closing/input.svelte 63 | !examples/svelte/packages/svelte/tests/preprocess/samples/style-self-closing/input.svelte 64 | !examples/svelte/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/main.svelte 65 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/await-catch-no-expression/main.svelte 66 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-with-event-in-each/main.svelte 67 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/each-block-deconflict-name-context/main.svelte 68 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/each-block-scope-shadow-self/main.svelte 69 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/escape-template-literals/main.svelte 70 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/event-handler-removal/main.svelte 71 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/store-each-binding/main.svelte 72 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/store-each-binding-deep/main.svelte 73 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/store-each-binding-destructuring/main.svelte 74 | !examples/svelte/packages/svelte/tests/runtime-legacy/samples/unchanged-expression-escape/main.svelte 75 | invalid-files: |- 76 | examples/svelte/packages/svelte/tests/compiler-errors/**/* 77 | examples/svelte/packages/svelte/tests/validator/samples/attribute-expected-equals/input.svelte 78 | examples/svelte/packages/svelte/tests/validator/samples/attribute-invalid-name/input.svelte 79 | examples/svelte/packages/svelte/tests/validator/samples/html-block-in-attribute/input.svelte 80 | examples/svelte/packages/svelte/tests/validator/samples/logic-block-in-attribute/input.svelte 81 | examples/svelte/packages/svelte/tests/runtime-runes/samples/each-updates-6/main.svelte 82 | examples/svelte/packages/svelte/tests/runtime-runes/samples/each-updates-7/main.svelte 83 | -------------------------------------------------------------------------------- /.github/workflows/dependencies.yml: -------------------------------------------------------------------------------- 1 | name: Update dependencies 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * 0" # every week 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: write 10 | pull-requests: write 11 | 12 | jobs: 13 | update: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | - name: Set up NodeJS 19 | uses: actions/setup-node@v4 20 | with: 21 | cache: npm 22 | node-version: ${{vars.NODE_VERSION}} 23 | - name: Update dependencies 24 | uses: tree-sitter/parser-update-action@v1.1 25 | with: 26 | parent-name: html 27 | language-name: svelte 28 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Fuzz Parser 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - src/scanner.c 8 | - src/tag.h 9 | pull_request: 10 | paths: 11 | - src/scanner.c 12 | - src/tag.h 13 | 14 | jobs: 15 | fuzz: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | - name: Run fuzzer 21 | uses: tree-sitter/fuzz-action@v4 22 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - grammar.js 8 | pull_request: 9 | paths: 10 | - grammar.js 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | - name: Set up Node.js 19 | uses: actions/setup-node@v4 20 | with: 21 | cache: npm 22 | node-version: ${{vars.NODE_VERSION}} 23 | - name: Install modules 24 | run: npm ci --legacy-peer-deps 25 | - name: Run ESLint 26 | run: npm run lint 27 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Publish package 3 | 4 | on: 5 | push: 6 | tags: ["*"] 7 | 8 | jobs: 9 | npm: 10 | uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main 11 | with: 12 | package-name: "@tree-sitter-grammars/tree-sitter-yaml" 13 | secrets: 14 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 15 | crates: 16 | uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main 17 | with: 18 | package-name: tree-sitter-svelte-ng 19 | secrets: 20 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_TOKEN}} 21 | pypi: 22 | uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main 23 | secrets: 24 | PYPI_API_TOKEN: ${{secrets.PYPI_TOKEN}} 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust artifacts 2 | target/ 3 | 4 | # Node artifacts 5 | build/ 6 | prebuilds/ 7 | node_modules/ 8 | *.tgz 9 | 10 | # Swift artifacts 11 | .build/ 12 | Package.resolved 13 | 14 | # Go artifacts 15 | _obj/ 16 | 17 | # Python artifacts 18 | .venv/ 19 | dist/ 20 | *.egg-info 21 | *.whl 22 | 23 | # C artifacts 24 | *.a 25 | *.so 26 | *.so.* 27 | *.dylib 28 | *.dll 29 | *.pc 30 | 31 | # Example dirs 32 | /examples/*/ 33 | 34 | # Grammar volatiles 35 | *.wasm 36 | *.obj 37 | *.o 38 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "cc" 16 | version = "1.1.18" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" 19 | dependencies = [ 20 | "shlex", 21 | ] 22 | 23 | [[package]] 24 | name = "memchr" 25 | version = "2.7.4" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 28 | 29 | [[package]] 30 | name = "regex" 31 | version = "1.10.6" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" 34 | dependencies = [ 35 | "aho-corasick", 36 | "memchr", 37 | "regex-automata", 38 | "regex-syntax", 39 | ] 40 | 41 | [[package]] 42 | name = "regex-automata" 43 | version = "0.4.7" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 46 | dependencies = [ 47 | "aho-corasick", 48 | "memchr", 49 | "regex-syntax", 50 | ] 51 | 52 | [[package]] 53 | name = "regex-syntax" 54 | version = "0.8.4" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 57 | 58 | [[package]] 59 | name = "shlex" 60 | version = "1.3.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 63 | 64 | [[package]] 65 | name = "tree-sitter" 66 | version = "0.23.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "20f4cd3642c47a85052a887d86704f4eac272969f61b686bdd3f772122aabaff" 69 | dependencies = [ 70 | "cc", 71 | "regex", 72 | "regex-syntax", 73 | "tree-sitter-language", 74 | ] 75 | 76 | [[package]] 77 | name = "tree-sitter-language" 78 | version = "0.1.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "2545046bd1473dac6c626659cc2567c6c0ff302fc8b84a56c4243378276f7f57" 81 | 82 | [[package]] 83 | name = "tree-sitter-svelte-ng" 84 | version = "1.0.2" 85 | dependencies = [ 86 | "cc", 87 | "tree-sitter", 88 | "tree-sitter-language", 89 | ] 90 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-svelte-ng" 3 | description = "Svelte grammar for tree-sitter" 4 | version = "1.0.2" 5 | authors = ["Amaan Qureshi "] 6 | license = "MIT" 7 | keywords = ["incremental", "parsing", "svelte"] 8 | categories = ["parsing", "text-editors"] 9 | repository = "https://github.com/tree-sitter-grammars/tree-sitter-svelte" 10 | edition = "2021" 11 | autoexamples = false 12 | 13 | build = "bindings/rust/build.rs" 14 | include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"] 15 | 16 | [lib] 17 | path = "bindings/rust/lib.rs" 18 | 19 | [dependencies] 20 | tree-sitter-language = "0.1.0" 21 | 22 | [build-dependencies] 23 | cc = "1.1.17" 24 | 25 | [dev-dependencies] 26 | tree-sitter = "0.23" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Amaan Qureshi 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 | VERSION := 1.0.2 6 | 7 | LANGUAGE_NAME := tree-sitter-svelte 8 | 9 | # repository 10 | SRC_DIR := src 11 | 12 | PARSER_REPO_URL := $(shell git -C $(SRC_DIR) remote get-url origin 2>/dev/null) 13 | 14 | ifeq ($(PARSER_URL),) 15 | PARSER_URL := $(subst .git,,$(PARSER_REPO_URL)) 16 | ifeq ($(shell echo $(PARSER_URL) | grep '^[a-z][-+.0-9a-z]*://'),) 17 | PARSER_URL := $(subst :,/,$(PARSER_URL)) 18 | PARSER_URL := $(subst git@,https://,$(PARSER_URL)) 19 | endif 20 | endif 21 | 22 | TS ?= tree-sitter 23 | 24 | # install directory layout 25 | PREFIX ?= /usr/local 26 | INCLUDEDIR ?= $(PREFIX)/include 27 | LIBDIR ?= $(PREFIX)/lib 28 | PCLIBDIR ?= $(LIBDIR)/pkgconfig 29 | 30 | # source/object files 31 | PARSER := $(SRC_DIR)/parser.c 32 | EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c)) 33 | OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS)) 34 | 35 | # flags 36 | ARFLAGS ?= rcs 37 | override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC 38 | 39 | # ABI versioning 40 | SONAME_MAJOR := $(word 1,$(subst ., ,$(VERSION))) 41 | SONAME_MINOR := $(shell sed -n 's/#define LANGUAGE_VERSION //p' $(PARSER)) 42 | 43 | # OS-specific bits 44 | ifeq ($(shell uname),Darwin) 45 | SOEXT = dylib 46 | SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT) 47 | SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT) 48 | LINKSHARED := $(LINKSHARED)-dynamiclib -Wl, 49 | ifneq ($(ADDITIONAL_LIBS),) 50 | LINKSHARED := $(LINKSHARED)$(ADDITIONAL_LIBS), 51 | endif 52 | LINKSHARED := $(LINKSHARED)-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks 53 | else 54 | SOEXT = so 55 | SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) 56 | SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) 57 | LINKSHARED := $(LINKSHARED)-shared -Wl, 58 | ifneq ($(ADDITIONAL_LIBS),) 59 | LINKSHARED := $(LINKSHARED)$(ADDITIONAL_LIBS) 60 | endif 61 | LINKSHARED := $(LINKSHARED)-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) 62 | endif 63 | ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) 64 | PCLIBDIR := $(PREFIX)/libdata/pkgconfig 65 | endif 66 | 67 | all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc 68 | 69 | lib$(LANGUAGE_NAME).a: $(OBJS) 70 | $(AR) $(ARFLAGS) $@ $^ 71 | 72 | lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS) 73 | $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@ 74 | ifneq ($(STRIP),) 75 | $(STRIP) $@ 76 | endif 77 | 78 | $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in 79 | sed -e 's|@URL@|$(PARSER_URL)|' \ 80 | -e 's|@VERSION@|$(VERSION)|' \ 81 | -e 's|@LIBDIR@|$(LIBDIR)|' \ 82 | -e 's|@INCLUDEDIR@|$(INCLUDEDIR)|' \ 83 | -e 's|@REQUIRES@|$(REQUIRES)|' \ 84 | -e 's|@ADDITIONAL_LIBS@|$(ADDITIONAL_LIBS)|' \ 85 | -e 's|=$(PREFIX)|=$${prefix}|' \ 86 | -e 's|@PREFIX@|$(PREFIX)|' $< > $@ 87 | 88 | $(PARSER): $(SRC_DIR)/grammar.json 89 | $(TS) generate --no-bindings $^ 90 | 91 | install: all 92 | install -d '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)' 93 | install -m644 bindings/c/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h 94 | install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 95 | install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a 96 | install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) 97 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) 98 | ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) 99 | 100 | uninstall: 101 | $(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \ 102 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \ 103 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \ 104 | '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \ 105 | '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \ 106 | '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 107 | 108 | clean: 109 | $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) 110 | 111 | test: 112 | $(TS) test 113 | 114 | .PHONY: all install uninstall clean test 115 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "TreeSitterSvelte", 6 | products: [ 7 | .library(name: "TreeSitterSvelte", targets: ["TreeSitterSvelte"]), 8 | ], 9 | dependencies: [ 10 | .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.8.0"), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "TreeSitterSvelte", 15 | dependencies: [], 16 | path: ".", 17 | exclude: [ 18 | "Cargo.toml", 19 | "Makefile", 20 | "binding.gyp", 21 | "bindings/c", 22 | "bindings/go", 23 | "bindings/node", 24 | "bindings/python", 25 | "bindings/rust", 26 | "prebuilds", 27 | "grammar.js", 28 | "package.json", 29 | "package-lock.json", 30 | "pyproject.toml", 31 | "setup.py", 32 | "test", 33 | "examples", 34 | ".editorconfig", 35 | ".github", 36 | ".gitignore", 37 | ".gitattributes", 38 | ".gitmodules", 39 | ], 40 | sources: [ 41 | "src/parser.c", 42 | "src/scanner.c", 43 | ], 44 | resources: [ 45 | .copy("queries") 46 | ], 47 | publicHeadersPath: "bindings/swift", 48 | cSettings: [.headerSearchPath("src")] 49 | ), 50 | .testTarget( 51 | name: "TreeSitterSvelteTests", 52 | dependencies: [ 53 | "SwiftTreeSitter", 54 | "TreeSitterSvelte", 55 | ], 56 | path: "bindings/swift/TreeSitterSvelteTests" 57 | ) 58 | ], 59 | cLanguageStandard: .c11 60 | ) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-svelte 2 | 3 | [![CI][ci]](https://github.com/tree-sitter-grammars/tree-sitter-svelte/actions/workflows/ci.yml) 4 | [![discord][discord]](https://discord.gg/w7nTvsVJhm) 5 | [![matrix][matrix]](https://matrix.to/#/#tree-sitter-chat:matrix.org) 6 | [![crates][crates]](https://crates.io/crates/tree-sitter-svelte-ng) 7 | [![npm][npm]](https://www.npmjs.com/package/@tree-sitter-grammars/tree-sitter-svelte) 8 | [![pypi][pypi]](https://pypi.org/project/tree-sitter-svelte) 9 | 10 | [Svelte](https://svelte.dev/) grammar for [tree-sitter](https://tree-sitter.github.io) 11 | 12 | [ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter-grammars/tree-sitter-svelte/ci.yml?logo=github&label=CI 13 | [discord]: https://img.shields.io/discord/1063097320771698699?logo=discord&label=discord 14 | [matrix]: https://img.shields.io/matrix/tree-sitter-chat%3Amatrix.org?logo=matrix&label=matrix 15 | [npm]: https://img.shields.io/npm/v/@tree-sitter-grammars/tree-sitter-svelte?logo=npm 16 | [crates]: https://img.shields.io/crates/v/tree-sitter-svelte-ng?logo=rust 17 | [pypi]: https://img.shields.io/pypi/v/tree-sitter-svelte?logo=pypi&logoColor=ffd242 18 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_svelte_binding", 5 | "dependencies": [ 6 | " 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | extern "C" TSLanguage *tree_sitter_svelte(); 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, "svelte"); 14 | auto language = Napi::External::New(env, tree_sitter_svelte()); 15 | language.TypeTag(&LANGUAGE_TYPE_TAG); 16 | exports["language"] = language; 17 | return exports; 18 | } 19 | 20 | NODE_API_MODULE(tree_sitter_svelte_binding, Init) 21 | -------------------------------------------------------------------------------- /bindings/node/binding_test.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const assert = require("node:assert"); 4 | const { test } = require("node:test"); 5 | 6 | test("can load grammar", () => { 7 | const parser = new (require("tree-sitter"))(); 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 = require("node-gyp-build")(root); 4 | 5 | try { 6 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 7 | } catch (_) {} 8 | -------------------------------------------------------------------------------- /bindings/python/tests/test_binding.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import tree_sitter, tree_sitter_svelte 4 | 5 | 6 | class TestLanguage(TestCase): 7 | def test_can_load_grammar(self): 8 | try: 9 | tree_sitter.Language(tree_sitter_svelte.language()) 10 | except Exception: 11 | self.fail("Error loading Svelte grammar") 12 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_svelte/__init__.py: -------------------------------------------------------------------------------- 1 | """Svelte grammar for tree-sitter""" 2 | 3 | from importlib.resources import files as _files 4 | 5 | from ._binding import language 6 | 7 | 8 | def _get_query(name, file): 9 | query = _files(f"{__package__}.queries") / file 10 | globals()[name] = query.read_text() 11 | return globals()[name] 12 | 13 | 14 | def __getattr__(name): 15 | if name == "HIGHLIGHTS_QUERY": 16 | return _get_query("HIGHLIGHTS_QUERY", "highlights.scm") 17 | if name == "INJECTIONS_QUERY": 18 | return _get_query("INJECTIONS_QUERY", "injections.scm") 19 | if name == "LOCALS_QUERY": 20 | return _get_query("LOCALS_QUERY", "locals.scm") 21 | 22 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 23 | 24 | 25 | __all__ = [ 26 | "language", 27 | "HIGHLIGHTS_QUERY", 28 | "INJECTIONS_QUERY", 29 | "LOCALS_QUERY", 30 | ] 31 | 32 | 33 | def __dir__(): 34 | return sorted(__all__ + [ 35 | "__all__", "__builtins__", "__cached__", "__doc__", "__file__", 36 | "__loader__", "__name__", "__package__", "__path__", "__spec__", 37 | ]) 38 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_svelte/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Final 2 | 3 | HIGHLIGHTS_QUERY: Final[str] 4 | INJECTIONS_QUERY: Final[str] 5 | LOCALS_QUERY: Final[str] 6 | 7 | def language() -> object: ... 8 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_svelte/binding.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | TSLanguage *tree_sitter_svelte(void); 6 | 7 | static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 8 | return PyCapsule_New(tree_sitter_svelte(), "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_svelte/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tree-sitter-grammars/tree-sitter-svelte/ae5199db47757f785e43a14b332118a5474de1a2/bindings/python/tree_sitter_svelte/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-svelte"); 19 | } 20 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides Svelte language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [language][language func] function to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! use tree_sitter::Parser; 8 | //! 9 | //! let code = r#" 10 | //! 11 | //! "#; 12 | //! let mut parser = tree_sitter::Parser::new(); 13 | //! let language = tree_sitter_svelte_ng::LANGUAGE; 14 | //! parser 15 | //! .set_language(&language.into()) 16 | //! .expect("Error loading Svelte parser"); 17 | //! let tree = parser.parse(code, None).unwrap(); 18 | //! assert!(!tree.root_node().has_error()); 19 | //! ``` 20 | //! 21 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 22 | //! [language func]: fn.language.html 23 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 24 | //! [tree-sitter]: https://tree-sitter.github.io/ 25 | 26 | use tree_sitter_language::LanguageFn; 27 | 28 | extern "C" { 29 | fn tree_sitter_svelte() -> *const (); 30 | } 31 | 32 | /// The tree-sitter [`LanguageFn`] for this grammar. 33 | pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_svelte) }; 34 | 35 | /// The content of the [`node-types.json`][] file for this grammar. 36 | /// 37 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 38 | pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 39 | 40 | /// The syntax highlighting query for this language. 41 | pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); 42 | 43 | /// The injections query for this language. 44 | pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm"); 45 | 46 | /// The local-variable syntax highlighting query for this language. 47 | pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm"); 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | #[test] 52 | fn test_can_load_grammar() { 53 | let mut parser = tree_sitter::Parser::new(); 54 | parser 55 | .set_language(&super::LANGUAGE.into()) 56 | .expect("Error loading Svelte parser"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterSvelte/svelte.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_SVELTE_H_ 2 | #define TREE_SITTER_SVELTE_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | extern TSLanguage *tree_sitter_svelte(); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_SVELTE_H_ 17 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterSvelteTests/TreeSitterSvelteTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftTreeSitter 3 | import TreeSitterSvelte 4 | 5 | final class TreeSitterSvelteTests: XCTestCase { 6 | func testCanLoadGrammar() throws { 7 | let parser = Parser() 8 | let language = Language(language: tree_sitter_svelte()) 9 | XCTAssertNoThrow(try parser.setLanguage(language), 10 | "Error loading Svelte grammar") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /compile_commands.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "directory": "/home/amaanq/projects/treesitter/tree-sitter-svelte", 4 | "file": "src/parser.c", 5 | "output": "/tmp/parser-d47707.o", 6 | "arguments": [ 7 | "/usr/bin/clang-17", 8 | "-xc", 9 | "src/parser.c", 10 | "-o", 11 | "/tmp/parser-d47707.o", 12 | "-I", 13 | "src/", 14 | "-shared", 15 | "-fPIC", 16 | "-O3", 17 | "-dumpdir", 18 | "a-", 19 | "--target=x86_64-pc-linux-gnu" 20 | ] 21 | }, 22 | { 23 | "directory": "/home/amaanq/projects/treesitter/tree-sitter-svelte", 24 | "file": "src/scanner.c", 25 | "output": "/tmp/scanner-9d9e62.o", 26 | "arguments": [ 27 | "/usr/bin/clang-17", 28 | "-xc", 29 | "src/scanner.c", 30 | "-o", 31 | "/tmp/scanner-9d9e62.o", 32 | "-I", 33 | "src/", 34 | "-shared", 35 | "-fPIC", 36 | "-O3", 37 | "-dumpdir", 38 | "a-", 39 | "--target=x86_64-pc-linux-gnu" 40 | ] 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import google from "eslint-config-google"; 3 | 4 | export default { 5 | ...google, 6 | languageOptions: { 7 | ecmaVersion: "latest", 8 | sourceType: "module", 9 | globals: { 10 | ...globals.commonjs, 11 | ...globals.es2021, 12 | }, 13 | }, 14 | rules: { 15 | "arrow-parens": "off", 16 | camelcase: "off", 17 | indent: [ 18 | "error", 19 | 2, 20 | { 21 | SwitchCase: 1, 22 | }, 23 | ], 24 | "max-len": [ 25 | "error", 26 | { 27 | code: 160, 28 | ignoreComments: true, 29 | ignoreUrls: true, 30 | ignoreStrings: true, 31 | }, 32 | ], 33 | "spaced-comment": [ 34 | "warn", 35 | "always", 36 | { 37 | line: { 38 | markers: ["/"], 39 | }, 40 | }, 41 | ], 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | svelte/ 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tree-sitter-grammars/tree-sitter-svelte 2 | 3 | go 1.23 4 | 5 | require github.com/tree-sitter/go-tree-sitter v0.23.1 6 | 7 | require github.com/mattn/go-pointer v0.0.1 // indirect 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= 4 | github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 8 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 9 | github.com/tree-sitter/go-tree-sitter v0.23.1 h1:HCfaE19sKfG7q190xfM1loUZf6wEHa4TDqDEW46s9Lg= 10 | github.com/tree-sitter/go-tree-sitter v0.23.1/go.mod h1:EvIVhMvvPNvhu9x+ddSPxSnUEU5AnsSwi1LMqXIVE3A= 11 | github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb h1:A8425heRM8mylnv4H58FPUiH+aYivyitre0PzxrfmWs= 12 | github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb/go.mod h1:dOF6gtQiF9UwNh995T5OphYmtIypkjsp3ap7r9AN/iA= 13 | github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148 h1:AfFPZwtwGN01BW1jDdqBVqscTwetvMpydqYZz57RSlc= 14 | github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148/go.mod h1:Bh6U3viD57rFXRYIQ+kmiYtr+1Bx0AceypDLJJSyi9s= 15 | github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33 h1:TwqSV3qLp3tKSqirGLRHnjFk9Tc2oy57LIl+FQ4GjI4= 16 | github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33/go.mod h1:CvCKCt3v04Ufos1zZnNCelBDeCGRpPucaN8QczoUsN4= 17 | github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012 h1:Xvxck3tE5FW7F7bTS97iNM2ADMyCMJztVqn5HYKdJGo= 18 | github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012/go.mod h1:T40D0O1cPvUU/+AmiXVXy1cncYQT6wem4Z0g4SfAYvY= 19 | github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0 h1:c46K6uh5Dz00zJeU9BfjXdb8I+E4RkUdfnWJpQADXFo= 20 | github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0/go.mod h1:hcNt/kOJHcIcuMvouE7LJcYdeFUFbVpBJ6d4wmOA+tU= 21 | github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495 h1:jrt4qbJVEFs4H93/ITxygHc6u0TGqAkkate7TQ4wFSA= 22 | github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495/go.mod h1:oyaR7fLnRV0hT9z6qwE9GkaeTom/hTDwK3H2idcOJFc= 23 | github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5 h1:om4X9AVg3asL8gxNJDcz4e/Wp+VpQj1PY3uJXKr6EOg= 24 | github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5/go.mod h1:nNqgPoV/h9uYWk6kYEFdEAhNVOacpfpRW5SFmdaP4tU= 25 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5 h1:pfV3G3k7NCKqKk8THBmyuh2zA33lgYHS3GVrzRR8ry4= 26 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5/go.mod h1:GbMKRjLfk0H+PI7nLi1Sx5lHf5wCpLz9al8tQYSxpEk= 27 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1 h1:ZXZMDwE+IhUtGug4Brv6NjJWUU3rfkZBKpemf6RY8/g= 28 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1/go.mod h1:UKCLuYnJ312Mei+3cyTmGOHzn0YAnaPRECgJmHtzrqs= 29 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb h1:EXEM82lFM7JjJb6qiKZXkpIDaCcbV2obNn82ghwj9lw= 30 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb/go.mod h1:lXCF1nGG5Dr4J3BTS0ObN4xJCCICiSu/b+Xe/VqMV7g= 31 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d h1:fcYCvoXdcP1uRQYXqJHRy6Hec+uKScQdKVtMwK9JeCI= 32 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d/go.mod h1:T1nShQ4v5AJtozZ8YyAS4uzUtDAJj/iv4YfwXSbUHzg= 33 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447 h1:o9alBu1J/WjrcTKEthYtXmdkDc5OVXD+PqlvnEZ0Lzc= 34 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447/go.mod h1:1Oh95COkkTn6Ezp0vcMbvfhRP5gLeqqljR0BYnBzWvc= 35 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 36 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 37 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Svelte grammar for tree-sitter 3 | * @author Amaan Qureshi 4 | * @license MIT 5 | * @see {@link https://svelte.dev|Official website} 6 | */ 7 | 8 | /// 9 | // @ts-check 10 | 11 | const HTML = require('tree-sitter-html/grammar'); 12 | 13 | module.exports = grammar(HTML, { 14 | name: 'svelte', 15 | 16 | conflicts: $ => [ 17 | [$.else_if_block], 18 | [$.else_block], 19 | [$.then_block], 20 | [$.catch_block], 21 | ], 22 | 23 | externals: ($, _) => [ 24 | $._start_tag_name, 25 | $._script_start_tag_name, 26 | $._style_start_tag_name, 27 | $._end_tag_name, 28 | $.erroneous_end_tag_name, 29 | '/>', 30 | $._implicit_end_tag, 31 | $.raw_text, 32 | $.comment, 33 | $.svelte_raw_text, 34 | $.svelte_raw_text_each, 35 | $.svelte_raw_text_snippet_arguments, 36 | '@', 37 | '#', 38 | '/', 39 | ':', 40 | ], 41 | 42 | extras: $ => [ 43 | $.comment, 44 | /\s+/, 45 | ], 46 | 47 | supertypes: $ => [ 48 | $._node, 49 | ], 50 | 51 | rules: { 52 | _node: ($, original) => choice( 53 | original, 54 | 55 | $.if_statement, 56 | $.each_statement, 57 | $.await_statement, 58 | $.key_statement, 59 | $.snippet_statement, 60 | 61 | $.expression, 62 | 63 | $.html_tag, 64 | $.const_tag, 65 | $.debug_tag, 66 | $.render_tag, 67 | ), 68 | 69 | _single_quoted_attribute_value: $ => repeat1( 70 | choice( 71 | // Either any random non-expression piece of string… 72 | /[^{']+/, 73 | // …or an expression. 74 | $.expression, 75 | ), 76 | ), 77 | 78 | _double_quoted_attribute_value: $ => repeat1( 79 | choice( 80 | // Either any random non-expression piece of string… 81 | /[^{"]+/, 82 | // …or an expression. 83 | $.expression, 84 | ), 85 | ), 86 | 87 | // Svelte interpolations can occur anywhere inside quoted HTML attributes, 88 | // so we've got to modify `quoted_attribute_value`. 89 | quoted_attribute_value: $ => choice( 90 | seq( 91 | '\'', 92 | optional( 93 | alias($._single_quoted_attribute_value, $.attribute_value), 94 | ), 95 | '\'', 96 | ), 97 | seq( 98 | '"', 99 | optional( 100 | alias($._double_quoted_attribute_value, $.attribute_value), 101 | ), 102 | '"', 103 | ), 104 | ), 105 | 106 | // Also, an expression counts as a third type of possible attribute value 107 | // alongside quoted and unquoted. 108 | attribute: $ => seq( 109 | choice( 110 | seq( 111 | $.attribute_name, 112 | optional(seq( 113 | '=', 114 | choice( 115 | $.attribute_value, 116 | $.quoted_attribute_value, 117 | $.expression, 118 | ), 119 | )), 120 | ), 121 | alias($.expression, $.attribute_name), 122 | ), 123 | ), 124 | 125 | if_statement: $ => seq( 126 | $.if_start, 127 | repeat($._node), 128 | repeat($.else_if_block), 129 | optional($.else_block), 130 | $.if_end, 131 | ), 132 | 133 | _if_start_tag: _ => tag('#', 'if'), 134 | if_start: $ => seq( 135 | '{', 136 | alias($._if_start_tag, $.block_start_tag), 137 | field('condition', $.svelte_raw_text), 138 | '}', 139 | ), 140 | 141 | _else_if_tag: _ => tag(':', 'else if'), 142 | else_if_start: $ => seq( 143 | '{', 144 | alias($._else_if_tag, $.block_tag), 145 | field('condition', $.svelte_raw_text), 146 | '}', 147 | ), 148 | 149 | else_if_block: $ => seq( 150 | $.else_if_start, 151 | repeat($._node), 152 | ), 153 | 154 | _else_tag: _ => tag(':', 'else'), 155 | else_start: $ => seq( 156 | '{', 157 | alias($._else_tag, $.block_tag), 158 | '}', 159 | ), 160 | else_block: $ => seq( 161 | $.else_start, 162 | repeat($._node), 163 | ), 164 | 165 | _if_end_tag: _ => tag('/', 'if'), 166 | if_end: $ => seq('{', alias($._if_end_tag, $.block_end_tag), '}'), 167 | 168 | each_statement: $ => seq( 169 | $.each_start, 170 | repeat($._node), 171 | optional(seq($.else_block, repeat($._node))), 172 | $.each_end, 173 | ), 174 | 175 | _each_start_tag: _ => tag('#', 'each'), 176 | each_start: $ => seq( 177 | '{', 178 | alias($._each_start_tag, $.block_start_tag), 179 | choice( 180 | field('identifier', $.svelte_raw_text), 181 | seq( 182 | field('identifier', alias($.svelte_raw_text_each, $.svelte_raw_text)), 183 | 'as', 184 | field('parameter', $.svelte_raw_text), 185 | ), 186 | ), 187 | '}', 188 | ), 189 | 190 | _each_end_tag: _ => tag('/', 'each'), 191 | each_end: $ => seq( 192 | '{', 193 | alias($._each_end_tag, $.block_end_tag), 194 | '}', 195 | ), 196 | 197 | await_statement: $ => seq( 198 | $.await_start, 199 | repeat($._node), 200 | optional($.then_block), 201 | optional($.catch_block), 202 | $.await_end, 203 | ), 204 | 205 | _await_start_tag: _ => tag('#', 'await'), 206 | await_start: $ => seq( 207 | '{', 208 | alias($._await_start_tag, $.block_start_tag), 209 | $.svelte_raw_text, 210 | '}', 211 | ), 212 | 213 | _then_tag: _ => tag(':', 'then'), 214 | then_start: $ => seq('{', alias($._then_tag, $.block_tag), optional($.svelte_raw_text), '}'), 215 | then_block: $ => seq( 216 | $.then_start, 217 | repeat($._node), 218 | ), 219 | 220 | _catch_tag: _ => tag(':', 'catch'), 221 | catch_start: $ => seq('{', alias($._catch_tag, $.block_tag), optional($.svelte_raw_text), '}'), 222 | catch_block: $ => seq( 223 | $.catch_start, 224 | repeat($._node), 225 | ), 226 | 227 | _await_end_tag: _ => tag('/', 'await'), 228 | await_end: $ => seq('{', alias($._await_end_tag, $.block_end_tag), '}'), 229 | 230 | key_statement: $ => seq( 231 | $.key_start, 232 | repeat($._node), 233 | $.key_end, 234 | ), 235 | 236 | _key_start_tag: _ => tag('#', 'key'), 237 | key_start: $ => seq('{', alias($._key_start_tag, $.block_start_tag), $.svelte_raw_text, '}'), 238 | 239 | _key_end_tag: _ => tag('/', 'key'), 240 | key_end: $ => seq('{', alias($._key_end_tag, $.block_end_tag), '}'), 241 | 242 | snippet_statement: $ => seq( 243 | $.snippet_start, 244 | repeat($._node), 245 | $.snippet_end, 246 | ), 247 | 248 | _snippet_start_tag: _ => tag('#', 'snippet'), 249 | snippet_start: $ => seq( 250 | '{', 251 | alias($._snippet_start_tag, $.block_start_tag), 252 | alias(/[a-zA-Z$_][a-zA-Z0-9_]*/, $.snippet_name), 253 | '(', 254 | optional( 255 | alias($.svelte_raw_text_snippet_arguments, $.svelte_raw_text), 256 | ), 257 | ')', 258 | '}', 259 | ), 260 | 261 | _snippet_end_tag: _ => tag('/', 'snippet'), 262 | snippet_end: $ => seq('{', alias($._snippet_end_tag, $.block_end_tag), '}'), 263 | 264 | expression: $ => seq('{', $.svelte_raw_text, '}'), 265 | 266 | _tag_value: $ => seq(/\s+/, $.svelte_raw_text), 267 | 268 | _html_tag: _ => tag('@', 'html'), 269 | html_tag: $ => seq( 270 | '{', 271 | alias($._html_tag, $.expression_tag), 272 | $._tag_value, 273 | '}', 274 | ), 275 | 276 | _const_tag: _ => tag('@', 'const'), 277 | const_tag: $ => seq( 278 | '{', 279 | alias($._const_tag, $.expression_tag), 280 | $._tag_value, 281 | '}', 282 | ), 283 | 284 | _debug_tag: _ => tag('@', 'debug'), 285 | debug_tag: $ => seq( 286 | '{', 287 | alias($._debug_tag, $.expression_tag), 288 | $._tag_value, 289 | '}', 290 | ), 291 | 292 | _render_tag: _ => tag('@', 'render'), 293 | render_tag: $ => seq( 294 | '{', 295 | alias($._render_tag, $.expression_tag), 296 | $._tag_value, 297 | '}', 298 | ), 299 | 300 | attribute_name: _ => /[^<>{}"'/=\s]+/, 301 | 302 | attribute_value: _ => /[^<>{}"'=\s]+/, 303 | 304 | text: _ => /[^<>{}&\s]([^<>{}&]*[^<>{}&\s])?/, 305 | }, 306 | }); 307 | 308 | /** 309 | * @param {string} sym 310 | * @param {string} text 311 | * @return {SeqRule} 312 | */ 313 | function tag(sym, text) { 314 | return seq( 315 | sym, 316 | field('tag', token.immediate(text)), 317 | ); 318 | } 319 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tree-sitter-grammars/tree-sitter-svelte", 3 | "version": "1.0.2", 4 | "description": "Svelte grammar for tree-sitter", 5 | "repository": "github:tree-sitter-grammars/tree-sitter-svelte", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "license": "MIT", 10 | "author": "Amaan Qureshi ", 11 | "main": "bindings/node", 12 | "types": "bindings/node", 13 | "files": [ 14 | "grammar.js", 15 | "binding.gyp", 16 | "prebuilds/**", 17 | "bindings/node/*", 18 | "queries/*", 19 | "src/**", 20 | "*.wasm" 21 | ], 22 | "keywords": [ 23 | "incremental", 24 | "parsing", 25 | "tree-sitter", 26 | "svelte" 27 | ], 28 | "dependencies": { 29 | "node-addon-api": "^8.1.0", 30 | "node-gyp-build": "^4.8.2", 31 | "tree-sitter-html": "^0.23.0" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^9.10.0", 35 | "eslint-config-google": "^0.14.0", 36 | "prebuildify": "^6.0.1", 37 | "tree-sitter-cli": "^0.23.0" 38 | }, 39 | "peerDependencies": { 40 | "tree-sitter": "^0.21.1" 41 | }, 42 | "peerDependenciesMeta": { 43 | "tree_sitter": { 44 | "optional": true 45 | } 46 | }, 47 | "scripts": { 48 | "install": "node-gyp-build", 49 | "lint": "eslint grammar.js", 50 | "prestart": "tree-sitter build --wasm", 51 | "start": "tree-sitter playground", 52 | "test": "node --test bindings/node/*_test.js" 53 | }, 54 | "tree-sitter": [ 55 | { 56 | "scope": "source.svelte", 57 | "injection-regex": "svelte", 58 | "file-types": [ 59 | "svelte" 60 | ], 61 | "highlights": [ 62 | "queries/highlights.scm" 63 | ], 64 | "injections": [ 65 | "queries/injections.scm" 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "tree-sitter-svelte" 7 | description = "Svelte grammar for tree-sitter" 8 | version = "1.0.2" 9 | keywords = ["incremental", "parsing", "tree-sitter", "svelte"] 10 | classifiers = [ 11 | "Intended Audience :: Developers", 12 | "License :: OSI Approved :: MIT License", 13 | "Topic :: Software Development :: Compilers", 14 | "Topic :: Text Processing :: Linguistic", 15 | "Typing :: Typed", 16 | ] 17 | authors = [{ name = "Amaan Qureshi", email = "amaanq12@gmail.com" }] 18 | requires-python = ">=3.9" 19 | license.text = "MIT" 20 | readme = "README.md" 21 | 22 | [project.urls] 23 | Homepage = "https://github.com/tree-sitter-grammars/tree-sitter-svelte" 24 | 25 | [project.optional-dependencies] 26 | core = ["tree-sitter~=0.22"] 27 | 28 | [tool.cibuildwheel] 29 | build = "cp39-*" 30 | build-frontend = "build" 31 | -------------------------------------------------------------------------------- /queries/folds.scm: -------------------------------------------------------------------------------- 1 | ; inherits: html 2 | 3 | [ 4 | (if_statement) 5 | (else_if_block) 6 | (else_block) 7 | (each_statement) 8 | (await_statement) 9 | (then_block) 10 | (catch_block) 11 | (key_statement) 12 | (snippet_statement) 13 | ] @fold 14 | -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ; inherits: html 2 | 3 | (raw_text) @none 4 | 5 | [ 6 | "as" 7 | "key" 8 | "html" 9 | "snippet" 10 | "render" 11 | ] @keyword 12 | 13 | "const" @type.qualifier 14 | 15 | [ 16 | "if" 17 | "else" 18 | "then" 19 | ] @keyword.conditional 20 | 21 | "each" @keyword.repeat 22 | 23 | [ 24 | "await" 25 | "then" 26 | ] @keyword.coroutine 27 | 28 | "catch" @keyword.exception 29 | 30 | "debug" @keyword.debug 31 | 32 | [ 33 | "{" 34 | "}" 35 | ] @punctuation.bracket 36 | 37 | [ 38 | "#" 39 | ":" 40 | "/" 41 | "@" 42 | ] @tag.delimiter 43 | -------------------------------------------------------------------------------- /queries/indents.scm: -------------------------------------------------------------------------------- 1 | ; inherits: html 2 | 3 | [ 4 | (if_statement) 5 | (each_statement) 6 | (await_statement) 7 | (key_statement) 8 | (snippet_statement) 9 | ] @indent.begin 10 | 11 | (if_end 12 | "}" @indent.end) 13 | 14 | (each_end 15 | "}" @indent.end) 16 | 17 | (await_end 18 | "}" @indent.end) 19 | 20 | (key_end 21 | "}" @indent.end) 22 | 23 | (snippet_end 24 | "}" @indent.end) 25 | 26 | [ 27 | (if_end) 28 | (else_if_block) 29 | (else_block) 30 | (each_end) 31 | (await_end) 32 | (key_end) 33 | (snippet_end) 34 | ] @indent.branch 35 | -------------------------------------------------------------------------------- /queries/injections.scm: -------------------------------------------------------------------------------- 1 | ; inherits: html_tags 2 | 3 | ((style_element 4 | (start_tag 5 | (attribute 6 | (attribute_name) @_attr 7 | (quoted_attribute_value 8 | (attribute_value) @_lang))) 9 | (raw_text) @injection.content) 10 | (#eq? @_attr "lang") 11 | (#any-of? @_lang "scss" "postcss" "less" "stylus") 12 | (#set! injection.language "scss")) 13 | 14 | ((raw_text) @injection.content 15 | (#set! injection.language "javascript")) 16 | 17 | ((script_element 18 | (start_tag 19 | (attribute 20 | (attribute_name) @_attr 21 | (quoted_attribute_value 22 | (attribute_value) @_lang))) 23 | (raw_text) @injection.content) 24 | (#eq? @_attr "lang") 25 | (#any-of? @_lang "ts" "typescript") 26 | (#set! injection.language "typescript")) 27 | 28 | ((element 29 | (start_tag 30 | (attribute 31 | (attribute_name) @_attr 32 | (quoted_attribute_value 33 | (attribute_value) @injection.language))) 34 | (text) @injection.content) 35 | (#eq? @_attr "lang") 36 | (#eq? @injection.language "pug")) 37 | -------------------------------------------------------------------------------- /queries/locals.scm: -------------------------------------------------------------------------------- 1 | ; inherits: html 2 | -------------------------------------------------------------------------------- /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_svelte", "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_svelte": ["*.pyi", "py.typed"], 30 | "tree_sitter_svelte.queries": ["*.scm"], 31 | }, 32 | ext_package="tree_sitter_svelte", 33 | ext_modules=[ 34 | Extension( 35 | name="_binding", 36 | sources=[ 37 | "bindings/python/tree_sitter_svelte/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/grammar.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte", 3 | "inherits": "html", 4 | "rules": { 5 | "document": { 6 | "type": "REPEAT", 7 | "content": { 8 | "type": "SYMBOL", 9 | "name": "_node" 10 | } 11 | }, 12 | "doctype": { 13 | "type": "SEQ", 14 | "members": [ 15 | { 16 | "type": "STRING", 17 | "value": "]+" 31 | }, 32 | { 33 | "type": "STRING", 34 | "value": ">" 35 | } 36 | ] 37 | }, 38 | "_doctype": { 39 | "type": "PATTERN", 40 | "value": "[Dd][Oo][Cc][Tt][Yy][Pp][Ee]" 41 | }, 42 | "_node": { 43 | "type": "CHOICE", 44 | "members": [ 45 | { 46 | "type": "CHOICE", 47 | "members": [ 48 | { 49 | "type": "SYMBOL", 50 | "name": "doctype" 51 | }, 52 | { 53 | "type": "SYMBOL", 54 | "name": "entity" 55 | }, 56 | { 57 | "type": "SYMBOL", 58 | "name": "text" 59 | }, 60 | { 61 | "type": "SYMBOL", 62 | "name": "element" 63 | }, 64 | { 65 | "type": "SYMBOL", 66 | "name": "script_element" 67 | }, 68 | { 69 | "type": "SYMBOL", 70 | "name": "style_element" 71 | }, 72 | { 73 | "type": "SYMBOL", 74 | "name": "erroneous_end_tag" 75 | } 76 | ] 77 | }, 78 | { 79 | "type": "SYMBOL", 80 | "name": "if_statement" 81 | }, 82 | { 83 | "type": "SYMBOL", 84 | "name": "each_statement" 85 | }, 86 | { 87 | "type": "SYMBOL", 88 | "name": "await_statement" 89 | }, 90 | { 91 | "type": "SYMBOL", 92 | "name": "key_statement" 93 | }, 94 | { 95 | "type": "SYMBOL", 96 | "name": "snippet_statement" 97 | }, 98 | { 99 | "type": "SYMBOL", 100 | "name": "expression" 101 | }, 102 | { 103 | "type": "SYMBOL", 104 | "name": "html_tag" 105 | }, 106 | { 107 | "type": "SYMBOL", 108 | "name": "const_tag" 109 | }, 110 | { 111 | "type": "SYMBOL", 112 | "name": "debug_tag" 113 | }, 114 | { 115 | "type": "SYMBOL", 116 | "name": "render_tag" 117 | } 118 | ] 119 | }, 120 | "element": { 121 | "type": "CHOICE", 122 | "members": [ 123 | { 124 | "type": "SEQ", 125 | "members": [ 126 | { 127 | "type": "SYMBOL", 128 | "name": "start_tag" 129 | }, 130 | { 131 | "type": "REPEAT", 132 | "content": { 133 | "type": "SYMBOL", 134 | "name": "_node" 135 | } 136 | }, 137 | { 138 | "type": "CHOICE", 139 | "members": [ 140 | { 141 | "type": "SYMBOL", 142 | "name": "end_tag" 143 | }, 144 | { 145 | "type": "SYMBOL", 146 | "name": "_implicit_end_tag" 147 | } 148 | ] 149 | } 150 | ] 151 | }, 152 | { 153 | "type": "SYMBOL", 154 | "name": "self_closing_tag" 155 | } 156 | ] 157 | }, 158 | "script_element": { 159 | "type": "SEQ", 160 | "members": [ 161 | { 162 | "type": "ALIAS", 163 | "content": { 164 | "type": "SYMBOL", 165 | "name": "script_start_tag" 166 | }, 167 | "named": true, 168 | "value": "start_tag" 169 | }, 170 | { 171 | "type": "CHOICE", 172 | "members": [ 173 | { 174 | "type": "SYMBOL", 175 | "name": "raw_text" 176 | }, 177 | { 178 | "type": "BLANK" 179 | } 180 | ] 181 | }, 182 | { 183 | "type": "SYMBOL", 184 | "name": "end_tag" 185 | } 186 | ] 187 | }, 188 | "style_element": { 189 | "type": "SEQ", 190 | "members": [ 191 | { 192 | "type": "ALIAS", 193 | "content": { 194 | "type": "SYMBOL", 195 | "name": "style_start_tag" 196 | }, 197 | "named": true, 198 | "value": "start_tag" 199 | }, 200 | { 201 | "type": "CHOICE", 202 | "members": [ 203 | { 204 | "type": "SYMBOL", 205 | "name": "raw_text" 206 | }, 207 | { 208 | "type": "BLANK" 209 | } 210 | ] 211 | }, 212 | { 213 | "type": "SYMBOL", 214 | "name": "end_tag" 215 | } 216 | ] 217 | }, 218 | "start_tag": { 219 | "type": "SEQ", 220 | "members": [ 221 | { 222 | "type": "STRING", 223 | "value": "<" 224 | }, 225 | { 226 | "type": "ALIAS", 227 | "content": { 228 | "type": "SYMBOL", 229 | "name": "_start_tag_name" 230 | }, 231 | "named": true, 232 | "value": "tag_name" 233 | }, 234 | { 235 | "type": "REPEAT", 236 | "content": { 237 | "type": "SYMBOL", 238 | "name": "attribute" 239 | } 240 | }, 241 | { 242 | "type": "STRING", 243 | "value": ">" 244 | } 245 | ] 246 | }, 247 | "script_start_tag": { 248 | "type": "SEQ", 249 | "members": [ 250 | { 251 | "type": "STRING", 252 | "value": "<" 253 | }, 254 | { 255 | "type": "ALIAS", 256 | "content": { 257 | "type": "SYMBOL", 258 | "name": "_script_start_tag_name" 259 | }, 260 | "named": true, 261 | "value": "tag_name" 262 | }, 263 | { 264 | "type": "REPEAT", 265 | "content": { 266 | "type": "SYMBOL", 267 | "name": "attribute" 268 | } 269 | }, 270 | { 271 | "type": "STRING", 272 | "value": ">" 273 | } 274 | ] 275 | }, 276 | "style_start_tag": { 277 | "type": "SEQ", 278 | "members": [ 279 | { 280 | "type": "STRING", 281 | "value": "<" 282 | }, 283 | { 284 | "type": "ALIAS", 285 | "content": { 286 | "type": "SYMBOL", 287 | "name": "_style_start_tag_name" 288 | }, 289 | "named": true, 290 | "value": "tag_name" 291 | }, 292 | { 293 | "type": "REPEAT", 294 | "content": { 295 | "type": "SYMBOL", 296 | "name": "attribute" 297 | } 298 | }, 299 | { 300 | "type": "STRING", 301 | "value": ">" 302 | } 303 | ] 304 | }, 305 | "self_closing_tag": { 306 | "type": "SEQ", 307 | "members": [ 308 | { 309 | "type": "STRING", 310 | "value": "<" 311 | }, 312 | { 313 | "type": "ALIAS", 314 | "content": { 315 | "type": "SYMBOL", 316 | "name": "_start_tag_name" 317 | }, 318 | "named": true, 319 | "value": "tag_name" 320 | }, 321 | { 322 | "type": "REPEAT", 323 | "content": { 324 | "type": "SYMBOL", 325 | "name": "attribute" 326 | } 327 | }, 328 | { 329 | "type": "STRING", 330 | "value": "/>" 331 | } 332 | ] 333 | }, 334 | "end_tag": { 335 | "type": "SEQ", 336 | "members": [ 337 | { 338 | "type": "STRING", 339 | "value": "" 353 | } 354 | ] 355 | }, 356 | "erroneous_end_tag": { 357 | "type": "SEQ", 358 | "members": [ 359 | { 360 | "type": "STRING", 361 | "value": "" 370 | } 371 | ] 372 | }, 373 | "attribute": { 374 | "type": "SEQ", 375 | "members": [ 376 | { 377 | "type": "CHOICE", 378 | "members": [ 379 | { 380 | "type": "SEQ", 381 | "members": [ 382 | { 383 | "type": "SYMBOL", 384 | "name": "attribute_name" 385 | }, 386 | { 387 | "type": "CHOICE", 388 | "members": [ 389 | { 390 | "type": "SEQ", 391 | "members": [ 392 | { 393 | "type": "STRING", 394 | "value": "=" 395 | }, 396 | { 397 | "type": "CHOICE", 398 | "members": [ 399 | { 400 | "type": "SYMBOL", 401 | "name": "attribute_value" 402 | }, 403 | { 404 | "type": "SYMBOL", 405 | "name": "quoted_attribute_value" 406 | }, 407 | { 408 | "type": "SYMBOL", 409 | "name": "expression" 410 | } 411 | ] 412 | } 413 | ] 414 | }, 415 | { 416 | "type": "BLANK" 417 | } 418 | ] 419 | } 420 | ] 421 | }, 422 | { 423 | "type": "ALIAS", 424 | "content": { 425 | "type": "SYMBOL", 426 | "name": "expression" 427 | }, 428 | "named": true, 429 | "value": "attribute_name" 430 | } 431 | ] 432 | } 433 | ] 434 | }, 435 | "attribute_name": { 436 | "type": "PATTERN", 437 | "value": "[^<>{}\"'/=\\s]+" 438 | }, 439 | "attribute_value": { 440 | "type": "PATTERN", 441 | "value": "[^<>{}\"'=\\s]+" 442 | }, 443 | "entity": { 444 | "type": "PATTERN", 445 | "value": "&(#([xX][0-9a-fA-F]{1,6}|[0-9]{1,5})|[A-Za-z]{1,30});?" 446 | }, 447 | "quoted_attribute_value": { 448 | "type": "CHOICE", 449 | "members": [ 450 | { 451 | "type": "SEQ", 452 | "members": [ 453 | { 454 | "type": "STRING", 455 | "value": "'" 456 | }, 457 | { 458 | "type": "CHOICE", 459 | "members": [ 460 | { 461 | "type": "ALIAS", 462 | "content": { 463 | "type": "SYMBOL", 464 | "name": "_single_quoted_attribute_value" 465 | }, 466 | "named": true, 467 | "value": "attribute_value" 468 | }, 469 | { 470 | "type": "BLANK" 471 | } 472 | ] 473 | }, 474 | { 475 | "type": "STRING", 476 | "value": "'" 477 | } 478 | ] 479 | }, 480 | { 481 | "type": "SEQ", 482 | "members": [ 483 | { 484 | "type": "STRING", 485 | "value": "\"" 486 | }, 487 | { 488 | "type": "CHOICE", 489 | "members": [ 490 | { 491 | "type": "ALIAS", 492 | "content": { 493 | "type": "SYMBOL", 494 | "name": "_double_quoted_attribute_value" 495 | }, 496 | "named": true, 497 | "value": "attribute_value" 498 | }, 499 | { 500 | "type": "BLANK" 501 | } 502 | ] 503 | }, 504 | { 505 | "type": "STRING", 506 | "value": "\"" 507 | } 508 | ] 509 | } 510 | ] 511 | }, 512 | "text": { 513 | "type": "PATTERN", 514 | "value": "[^<>{}&\\s]([^<>{}&]*[^<>{}&\\s])?" 515 | }, 516 | "_single_quoted_attribute_value": { 517 | "type": "REPEAT1", 518 | "content": { 519 | "type": "CHOICE", 520 | "members": [ 521 | { 522 | "type": "PATTERN", 523 | "value": "[^{']+" 524 | }, 525 | { 526 | "type": "SYMBOL", 527 | "name": "expression" 528 | } 529 | ] 530 | } 531 | }, 532 | "_double_quoted_attribute_value": { 533 | "type": "REPEAT1", 534 | "content": { 535 | "type": "CHOICE", 536 | "members": [ 537 | { 538 | "type": "PATTERN", 539 | "value": "[^{\"]+" 540 | }, 541 | { 542 | "type": "SYMBOL", 543 | "name": "expression" 544 | } 545 | ] 546 | } 547 | }, 548 | "if_statement": { 549 | "type": "SEQ", 550 | "members": [ 551 | { 552 | "type": "SYMBOL", 553 | "name": "if_start" 554 | }, 555 | { 556 | "type": "REPEAT", 557 | "content": { 558 | "type": "SYMBOL", 559 | "name": "_node" 560 | } 561 | }, 562 | { 563 | "type": "REPEAT", 564 | "content": { 565 | "type": "SYMBOL", 566 | "name": "else_if_block" 567 | } 568 | }, 569 | { 570 | "type": "CHOICE", 571 | "members": [ 572 | { 573 | "type": "SYMBOL", 574 | "name": "else_block" 575 | }, 576 | { 577 | "type": "BLANK" 578 | } 579 | ] 580 | }, 581 | { 582 | "type": "SYMBOL", 583 | "name": "if_end" 584 | } 585 | ] 586 | }, 587 | "_if_start_tag": { 588 | "type": "SEQ", 589 | "members": [ 590 | { 591 | "type": "STRING", 592 | "value": "#" 593 | }, 594 | { 595 | "type": "FIELD", 596 | "name": "tag", 597 | "content": { 598 | "type": "IMMEDIATE_TOKEN", 599 | "content": { 600 | "type": "STRING", 601 | "value": "if" 602 | } 603 | } 604 | } 605 | ] 606 | }, 607 | "if_start": { 608 | "type": "SEQ", 609 | "members": [ 610 | { 611 | "type": "STRING", 612 | "value": "{" 613 | }, 614 | { 615 | "type": "ALIAS", 616 | "content": { 617 | "type": "SYMBOL", 618 | "name": "_if_start_tag" 619 | }, 620 | "named": true, 621 | "value": "block_start_tag" 622 | }, 623 | { 624 | "type": "FIELD", 625 | "name": "condition", 626 | "content": { 627 | "type": "SYMBOL", 628 | "name": "svelte_raw_text" 629 | } 630 | }, 631 | { 632 | "type": "STRING", 633 | "value": "}" 634 | } 635 | ] 636 | }, 637 | "_else_if_tag": { 638 | "type": "SEQ", 639 | "members": [ 640 | { 641 | "type": "STRING", 642 | "value": ":" 643 | }, 644 | { 645 | "type": "FIELD", 646 | "name": "tag", 647 | "content": { 648 | "type": "IMMEDIATE_TOKEN", 649 | "content": { 650 | "type": "STRING", 651 | "value": "else if" 652 | } 653 | } 654 | } 655 | ] 656 | }, 657 | "else_if_start": { 658 | "type": "SEQ", 659 | "members": [ 660 | { 661 | "type": "STRING", 662 | "value": "{" 663 | }, 664 | { 665 | "type": "ALIAS", 666 | "content": { 667 | "type": "SYMBOL", 668 | "name": "_else_if_tag" 669 | }, 670 | "named": true, 671 | "value": "block_tag" 672 | }, 673 | { 674 | "type": "FIELD", 675 | "name": "condition", 676 | "content": { 677 | "type": "SYMBOL", 678 | "name": "svelte_raw_text" 679 | } 680 | }, 681 | { 682 | "type": "STRING", 683 | "value": "}" 684 | } 685 | ] 686 | }, 687 | "else_if_block": { 688 | "type": "SEQ", 689 | "members": [ 690 | { 691 | "type": "SYMBOL", 692 | "name": "else_if_start" 693 | }, 694 | { 695 | "type": "REPEAT", 696 | "content": { 697 | "type": "SYMBOL", 698 | "name": "_node" 699 | } 700 | } 701 | ] 702 | }, 703 | "_else_tag": { 704 | "type": "SEQ", 705 | "members": [ 706 | { 707 | "type": "STRING", 708 | "value": ":" 709 | }, 710 | { 711 | "type": "FIELD", 712 | "name": "tag", 713 | "content": { 714 | "type": "IMMEDIATE_TOKEN", 715 | "content": { 716 | "type": "STRING", 717 | "value": "else" 718 | } 719 | } 720 | } 721 | ] 722 | }, 723 | "else_start": { 724 | "type": "SEQ", 725 | "members": [ 726 | { 727 | "type": "STRING", 728 | "value": "{" 729 | }, 730 | { 731 | "type": "ALIAS", 732 | "content": { 733 | "type": "SYMBOL", 734 | "name": "_else_tag" 735 | }, 736 | "named": true, 737 | "value": "block_tag" 738 | }, 739 | { 740 | "type": "STRING", 741 | "value": "}" 742 | } 743 | ] 744 | }, 745 | "else_block": { 746 | "type": "SEQ", 747 | "members": [ 748 | { 749 | "type": "SYMBOL", 750 | "name": "else_start" 751 | }, 752 | { 753 | "type": "REPEAT", 754 | "content": { 755 | "type": "SYMBOL", 756 | "name": "_node" 757 | } 758 | } 759 | ] 760 | }, 761 | "_if_end_tag": { 762 | "type": "SEQ", 763 | "members": [ 764 | { 765 | "type": "STRING", 766 | "value": "/" 767 | }, 768 | { 769 | "type": "FIELD", 770 | "name": "tag", 771 | "content": { 772 | "type": "IMMEDIATE_TOKEN", 773 | "content": { 774 | "type": "STRING", 775 | "value": "if" 776 | } 777 | } 778 | } 779 | ] 780 | }, 781 | "if_end": { 782 | "type": "SEQ", 783 | "members": [ 784 | { 785 | "type": "STRING", 786 | "value": "{" 787 | }, 788 | { 789 | "type": "ALIAS", 790 | "content": { 791 | "type": "SYMBOL", 792 | "name": "_if_end_tag" 793 | }, 794 | "named": true, 795 | "value": "block_end_tag" 796 | }, 797 | { 798 | "type": "STRING", 799 | "value": "}" 800 | } 801 | ] 802 | }, 803 | "each_statement": { 804 | "type": "SEQ", 805 | "members": [ 806 | { 807 | "type": "SYMBOL", 808 | "name": "each_start" 809 | }, 810 | { 811 | "type": "REPEAT", 812 | "content": { 813 | "type": "SYMBOL", 814 | "name": "_node" 815 | } 816 | }, 817 | { 818 | "type": "CHOICE", 819 | "members": [ 820 | { 821 | "type": "SEQ", 822 | "members": [ 823 | { 824 | "type": "SYMBOL", 825 | "name": "else_block" 826 | }, 827 | { 828 | "type": "REPEAT", 829 | "content": { 830 | "type": "SYMBOL", 831 | "name": "_node" 832 | } 833 | } 834 | ] 835 | }, 836 | { 837 | "type": "BLANK" 838 | } 839 | ] 840 | }, 841 | { 842 | "type": "SYMBOL", 843 | "name": "each_end" 844 | } 845 | ] 846 | }, 847 | "_each_start_tag": { 848 | "type": "SEQ", 849 | "members": [ 850 | { 851 | "type": "STRING", 852 | "value": "#" 853 | }, 854 | { 855 | "type": "FIELD", 856 | "name": "tag", 857 | "content": { 858 | "type": "IMMEDIATE_TOKEN", 859 | "content": { 860 | "type": "STRING", 861 | "value": "each" 862 | } 863 | } 864 | } 865 | ] 866 | }, 867 | "each_start": { 868 | "type": "SEQ", 869 | "members": [ 870 | { 871 | "type": "STRING", 872 | "value": "{" 873 | }, 874 | { 875 | "type": "ALIAS", 876 | "content": { 877 | "type": "SYMBOL", 878 | "name": "_each_start_tag" 879 | }, 880 | "named": true, 881 | "value": "block_start_tag" 882 | }, 883 | { 884 | "type": "CHOICE", 885 | "members": [ 886 | { 887 | "type": "FIELD", 888 | "name": "identifier", 889 | "content": { 890 | "type": "SYMBOL", 891 | "name": "svelte_raw_text" 892 | } 893 | }, 894 | { 895 | "type": "SEQ", 896 | "members": [ 897 | { 898 | "type": "FIELD", 899 | "name": "identifier", 900 | "content": { 901 | "type": "ALIAS", 902 | "content": { 903 | "type": "SYMBOL", 904 | "name": "svelte_raw_text_each" 905 | }, 906 | "named": true, 907 | "value": "svelte_raw_text" 908 | } 909 | }, 910 | { 911 | "type": "STRING", 912 | "value": "as" 913 | }, 914 | { 915 | "type": "FIELD", 916 | "name": "parameter", 917 | "content": { 918 | "type": "SYMBOL", 919 | "name": "svelte_raw_text" 920 | } 921 | } 922 | ] 923 | } 924 | ] 925 | }, 926 | { 927 | "type": "STRING", 928 | "value": "}" 929 | } 930 | ] 931 | }, 932 | "_each_end_tag": { 933 | "type": "SEQ", 934 | "members": [ 935 | { 936 | "type": "STRING", 937 | "value": "/" 938 | }, 939 | { 940 | "type": "FIELD", 941 | "name": "tag", 942 | "content": { 943 | "type": "IMMEDIATE_TOKEN", 944 | "content": { 945 | "type": "STRING", 946 | "value": "each" 947 | } 948 | } 949 | } 950 | ] 951 | }, 952 | "each_end": { 953 | "type": "SEQ", 954 | "members": [ 955 | { 956 | "type": "STRING", 957 | "value": "{" 958 | }, 959 | { 960 | "type": "ALIAS", 961 | "content": { 962 | "type": "SYMBOL", 963 | "name": "_each_end_tag" 964 | }, 965 | "named": true, 966 | "value": "block_end_tag" 967 | }, 968 | { 969 | "type": "STRING", 970 | "value": "}" 971 | } 972 | ] 973 | }, 974 | "await_statement": { 975 | "type": "SEQ", 976 | "members": [ 977 | { 978 | "type": "SYMBOL", 979 | "name": "await_start" 980 | }, 981 | { 982 | "type": "REPEAT", 983 | "content": { 984 | "type": "SYMBOL", 985 | "name": "_node" 986 | } 987 | }, 988 | { 989 | "type": "CHOICE", 990 | "members": [ 991 | { 992 | "type": "SYMBOL", 993 | "name": "then_block" 994 | }, 995 | { 996 | "type": "BLANK" 997 | } 998 | ] 999 | }, 1000 | { 1001 | "type": "CHOICE", 1002 | "members": [ 1003 | { 1004 | "type": "SYMBOL", 1005 | "name": "catch_block" 1006 | }, 1007 | { 1008 | "type": "BLANK" 1009 | } 1010 | ] 1011 | }, 1012 | { 1013 | "type": "SYMBOL", 1014 | "name": "await_end" 1015 | } 1016 | ] 1017 | }, 1018 | "_await_start_tag": { 1019 | "type": "SEQ", 1020 | "members": [ 1021 | { 1022 | "type": "STRING", 1023 | "value": "#" 1024 | }, 1025 | { 1026 | "type": "FIELD", 1027 | "name": "tag", 1028 | "content": { 1029 | "type": "IMMEDIATE_TOKEN", 1030 | "content": { 1031 | "type": "STRING", 1032 | "value": "await" 1033 | } 1034 | } 1035 | } 1036 | ] 1037 | }, 1038 | "await_start": { 1039 | "type": "SEQ", 1040 | "members": [ 1041 | { 1042 | "type": "STRING", 1043 | "value": "{" 1044 | }, 1045 | { 1046 | "type": "ALIAS", 1047 | "content": { 1048 | "type": "SYMBOL", 1049 | "name": "_await_start_tag" 1050 | }, 1051 | "named": true, 1052 | "value": "block_start_tag" 1053 | }, 1054 | { 1055 | "type": "SYMBOL", 1056 | "name": "svelte_raw_text" 1057 | }, 1058 | { 1059 | "type": "STRING", 1060 | "value": "}" 1061 | } 1062 | ] 1063 | }, 1064 | "_then_tag": { 1065 | "type": "SEQ", 1066 | "members": [ 1067 | { 1068 | "type": "STRING", 1069 | "value": ":" 1070 | }, 1071 | { 1072 | "type": "FIELD", 1073 | "name": "tag", 1074 | "content": { 1075 | "type": "IMMEDIATE_TOKEN", 1076 | "content": { 1077 | "type": "STRING", 1078 | "value": "then" 1079 | } 1080 | } 1081 | } 1082 | ] 1083 | }, 1084 | "then_start": { 1085 | "type": "SEQ", 1086 | "members": [ 1087 | { 1088 | "type": "STRING", 1089 | "value": "{" 1090 | }, 1091 | { 1092 | "type": "ALIAS", 1093 | "content": { 1094 | "type": "SYMBOL", 1095 | "name": "_then_tag" 1096 | }, 1097 | "named": true, 1098 | "value": "block_tag" 1099 | }, 1100 | { 1101 | "type": "CHOICE", 1102 | "members": [ 1103 | { 1104 | "type": "SYMBOL", 1105 | "name": "svelte_raw_text" 1106 | }, 1107 | { 1108 | "type": "BLANK" 1109 | } 1110 | ] 1111 | }, 1112 | { 1113 | "type": "STRING", 1114 | "value": "}" 1115 | } 1116 | ] 1117 | }, 1118 | "then_block": { 1119 | "type": "SEQ", 1120 | "members": [ 1121 | { 1122 | "type": "SYMBOL", 1123 | "name": "then_start" 1124 | }, 1125 | { 1126 | "type": "REPEAT", 1127 | "content": { 1128 | "type": "SYMBOL", 1129 | "name": "_node" 1130 | } 1131 | } 1132 | ] 1133 | }, 1134 | "_catch_tag": { 1135 | "type": "SEQ", 1136 | "members": [ 1137 | { 1138 | "type": "STRING", 1139 | "value": ":" 1140 | }, 1141 | { 1142 | "type": "FIELD", 1143 | "name": "tag", 1144 | "content": { 1145 | "type": "IMMEDIATE_TOKEN", 1146 | "content": { 1147 | "type": "STRING", 1148 | "value": "catch" 1149 | } 1150 | } 1151 | } 1152 | ] 1153 | }, 1154 | "catch_start": { 1155 | "type": "SEQ", 1156 | "members": [ 1157 | { 1158 | "type": "STRING", 1159 | "value": "{" 1160 | }, 1161 | { 1162 | "type": "ALIAS", 1163 | "content": { 1164 | "type": "SYMBOL", 1165 | "name": "_catch_tag" 1166 | }, 1167 | "named": true, 1168 | "value": "block_tag" 1169 | }, 1170 | { 1171 | "type": "CHOICE", 1172 | "members": [ 1173 | { 1174 | "type": "SYMBOL", 1175 | "name": "svelte_raw_text" 1176 | }, 1177 | { 1178 | "type": "BLANK" 1179 | } 1180 | ] 1181 | }, 1182 | { 1183 | "type": "STRING", 1184 | "value": "}" 1185 | } 1186 | ] 1187 | }, 1188 | "catch_block": { 1189 | "type": "SEQ", 1190 | "members": [ 1191 | { 1192 | "type": "SYMBOL", 1193 | "name": "catch_start" 1194 | }, 1195 | { 1196 | "type": "REPEAT", 1197 | "content": { 1198 | "type": "SYMBOL", 1199 | "name": "_node" 1200 | } 1201 | } 1202 | ] 1203 | }, 1204 | "_await_end_tag": { 1205 | "type": "SEQ", 1206 | "members": [ 1207 | { 1208 | "type": "STRING", 1209 | "value": "/" 1210 | }, 1211 | { 1212 | "type": "FIELD", 1213 | "name": "tag", 1214 | "content": { 1215 | "type": "IMMEDIATE_TOKEN", 1216 | "content": { 1217 | "type": "STRING", 1218 | "value": "await" 1219 | } 1220 | } 1221 | } 1222 | ] 1223 | }, 1224 | "await_end": { 1225 | "type": "SEQ", 1226 | "members": [ 1227 | { 1228 | "type": "STRING", 1229 | "value": "{" 1230 | }, 1231 | { 1232 | "type": "ALIAS", 1233 | "content": { 1234 | "type": "SYMBOL", 1235 | "name": "_await_end_tag" 1236 | }, 1237 | "named": true, 1238 | "value": "block_end_tag" 1239 | }, 1240 | { 1241 | "type": "STRING", 1242 | "value": "}" 1243 | } 1244 | ] 1245 | }, 1246 | "key_statement": { 1247 | "type": "SEQ", 1248 | "members": [ 1249 | { 1250 | "type": "SYMBOL", 1251 | "name": "key_start" 1252 | }, 1253 | { 1254 | "type": "REPEAT", 1255 | "content": { 1256 | "type": "SYMBOL", 1257 | "name": "_node" 1258 | } 1259 | }, 1260 | { 1261 | "type": "SYMBOL", 1262 | "name": "key_end" 1263 | } 1264 | ] 1265 | }, 1266 | "_key_start_tag": { 1267 | "type": "SEQ", 1268 | "members": [ 1269 | { 1270 | "type": "STRING", 1271 | "value": "#" 1272 | }, 1273 | { 1274 | "type": "FIELD", 1275 | "name": "tag", 1276 | "content": { 1277 | "type": "IMMEDIATE_TOKEN", 1278 | "content": { 1279 | "type": "STRING", 1280 | "value": "key" 1281 | } 1282 | } 1283 | } 1284 | ] 1285 | }, 1286 | "key_start": { 1287 | "type": "SEQ", 1288 | "members": [ 1289 | { 1290 | "type": "STRING", 1291 | "value": "{" 1292 | }, 1293 | { 1294 | "type": "ALIAS", 1295 | "content": { 1296 | "type": "SYMBOL", 1297 | "name": "_key_start_tag" 1298 | }, 1299 | "named": true, 1300 | "value": "block_start_tag" 1301 | }, 1302 | { 1303 | "type": "SYMBOL", 1304 | "name": "svelte_raw_text" 1305 | }, 1306 | { 1307 | "type": "STRING", 1308 | "value": "}" 1309 | } 1310 | ] 1311 | }, 1312 | "_key_end_tag": { 1313 | "type": "SEQ", 1314 | "members": [ 1315 | { 1316 | "type": "STRING", 1317 | "value": "/" 1318 | }, 1319 | { 1320 | "type": "FIELD", 1321 | "name": "tag", 1322 | "content": { 1323 | "type": "IMMEDIATE_TOKEN", 1324 | "content": { 1325 | "type": "STRING", 1326 | "value": "key" 1327 | } 1328 | } 1329 | } 1330 | ] 1331 | }, 1332 | "key_end": { 1333 | "type": "SEQ", 1334 | "members": [ 1335 | { 1336 | "type": "STRING", 1337 | "value": "{" 1338 | }, 1339 | { 1340 | "type": "ALIAS", 1341 | "content": { 1342 | "type": "SYMBOL", 1343 | "name": "_key_end_tag" 1344 | }, 1345 | "named": true, 1346 | "value": "block_end_tag" 1347 | }, 1348 | { 1349 | "type": "STRING", 1350 | "value": "}" 1351 | } 1352 | ] 1353 | }, 1354 | "snippet_statement": { 1355 | "type": "SEQ", 1356 | "members": [ 1357 | { 1358 | "type": "SYMBOL", 1359 | "name": "snippet_start" 1360 | }, 1361 | { 1362 | "type": "REPEAT", 1363 | "content": { 1364 | "type": "SYMBOL", 1365 | "name": "_node" 1366 | } 1367 | }, 1368 | { 1369 | "type": "SYMBOL", 1370 | "name": "snippet_end" 1371 | } 1372 | ] 1373 | }, 1374 | "_snippet_start_tag": { 1375 | "type": "SEQ", 1376 | "members": [ 1377 | { 1378 | "type": "STRING", 1379 | "value": "#" 1380 | }, 1381 | { 1382 | "type": "FIELD", 1383 | "name": "tag", 1384 | "content": { 1385 | "type": "IMMEDIATE_TOKEN", 1386 | "content": { 1387 | "type": "STRING", 1388 | "value": "snippet" 1389 | } 1390 | } 1391 | } 1392 | ] 1393 | }, 1394 | "snippet_start": { 1395 | "type": "SEQ", 1396 | "members": [ 1397 | { 1398 | "type": "STRING", 1399 | "value": "{" 1400 | }, 1401 | { 1402 | "type": "ALIAS", 1403 | "content": { 1404 | "type": "SYMBOL", 1405 | "name": "_snippet_start_tag" 1406 | }, 1407 | "named": true, 1408 | "value": "block_start_tag" 1409 | }, 1410 | { 1411 | "type": "ALIAS", 1412 | "content": { 1413 | "type": "PATTERN", 1414 | "value": "[a-zA-Z$_][a-zA-Z0-9_]*" 1415 | }, 1416 | "named": true, 1417 | "value": "snippet_name" 1418 | }, 1419 | { 1420 | "type": "STRING", 1421 | "value": "(" 1422 | }, 1423 | { 1424 | "type": "CHOICE", 1425 | "members": [ 1426 | { 1427 | "type": "ALIAS", 1428 | "content": { 1429 | "type": "SYMBOL", 1430 | "name": "svelte_raw_text_snippet_arguments" 1431 | }, 1432 | "named": true, 1433 | "value": "svelte_raw_text" 1434 | }, 1435 | { 1436 | "type": "BLANK" 1437 | } 1438 | ] 1439 | }, 1440 | { 1441 | "type": "STRING", 1442 | "value": ")" 1443 | }, 1444 | { 1445 | "type": "STRING", 1446 | "value": "}" 1447 | } 1448 | ] 1449 | }, 1450 | "_snippet_end_tag": { 1451 | "type": "SEQ", 1452 | "members": [ 1453 | { 1454 | "type": "STRING", 1455 | "value": "/" 1456 | }, 1457 | { 1458 | "type": "FIELD", 1459 | "name": "tag", 1460 | "content": { 1461 | "type": "IMMEDIATE_TOKEN", 1462 | "content": { 1463 | "type": "STRING", 1464 | "value": "snippet" 1465 | } 1466 | } 1467 | } 1468 | ] 1469 | }, 1470 | "snippet_end": { 1471 | "type": "SEQ", 1472 | "members": [ 1473 | { 1474 | "type": "STRING", 1475 | "value": "{" 1476 | }, 1477 | { 1478 | "type": "ALIAS", 1479 | "content": { 1480 | "type": "SYMBOL", 1481 | "name": "_snippet_end_tag" 1482 | }, 1483 | "named": true, 1484 | "value": "block_end_tag" 1485 | }, 1486 | { 1487 | "type": "STRING", 1488 | "value": "}" 1489 | } 1490 | ] 1491 | }, 1492 | "expression": { 1493 | "type": "SEQ", 1494 | "members": [ 1495 | { 1496 | "type": "STRING", 1497 | "value": "{" 1498 | }, 1499 | { 1500 | "type": "SYMBOL", 1501 | "name": "svelte_raw_text" 1502 | }, 1503 | { 1504 | "type": "STRING", 1505 | "value": "}" 1506 | } 1507 | ] 1508 | }, 1509 | "_tag_value": { 1510 | "type": "SEQ", 1511 | "members": [ 1512 | { 1513 | "type": "PATTERN", 1514 | "value": "\\s+" 1515 | }, 1516 | { 1517 | "type": "SYMBOL", 1518 | "name": "svelte_raw_text" 1519 | } 1520 | ] 1521 | }, 1522 | "_html_tag": { 1523 | "type": "SEQ", 1524 | "members": [ 1525 | { 1526 | "type": "STRING", 1527 | "value": "@" 1528 | }, 1529 | { 1530 | "type": "FIELD", 1531 | "name": "tag", 1532 | "content": { 1533 | "type": "IMMEDIATE_TOKEN", 1534 | "content": { 1535 | "type": "STRING", 1536 | "value": "html" 1537 | } 1538 | } 1539 | } 1540 | ] 1541 | }, 1542 | "html_tag": { 1543 | "type": "SEQ", 1544 | "members": [ 1545 | { 1546 | "type": "STRING", 1547 | "value": "{" 1548 | }, 1549 | { 1550 | "type": "ALIAS", 1551 | "content": { 1552 | "type": "SYMBOL", 1553 | "name": "_html_tag" 1554 | }, 1555 | "named": true, 1556 | "value": "expression_tag" 1557 | }, 1558 | { 1559 | "type": "SYMBOL", 1560 | "name": "_tag_value" 1561 | }, 1562 | { 1563 | "type": "STRING", 1564 | "value": "}" 1565 | } 1566 | ] 1567 | }, 1568 | "_const_tag": { 1569 | "type": "SEQ", 1570 | "members": [ 1571 | { 1572 | "type": "STRING", 1573 | "value": "@" 1574 | }, 1575 | { 1576 | "type": "FIELD", 1577 | "name": "tag", 1578 | "content": { 1579 | "type": "IMMEDIATE_TOKEN", 1580 | "content": { 1581 | "type": "STRING", 1582 | "value": "const" 1583 | } 1584 | } 1585 | } 1586 | ] 1587 | }, 1588 | "const_tag": { 1589 | "type": "SEQ", 1590 | "members": [ 1591 | { 1592 | "type": "STRING", 1593 | "value": "{" 1594 | }, 1595 | { 1596 | "type": "ALIAS", 1597 | "content": { 1598 | "type": "SYMBOL", 1599 | "name": "_const_tag" 1600 | }, 1601 | "named": true, 1602 | "value": "expression_tag" 1603 | }, 1604 | { 1605 | "type": "SYMBOL", 1606 | "name": "_tag_value" 1607 | }, 1608 | { 1609 | "type": "STRING", 1610 | "value": "}" 1611 | } 1612 | ] 1613 | }, 1614 | "_debug_tag": { 1615 | "type": "SEQ", 1616 | "members": [ 1617 | { 1618 | "type": "STRING", 1619 | "value": "@" 1620 | }, 1621 | { 1622 | "type": "FIELD", 1623 | "name": "tag", 1624 | "content": { 1625 | "type": "IMMEDIATE_TOKEN", 1626 | "content": { 1627 | "type": "STRING", 1628 | "value": "debug" 1629 | } 1630 | } 1631 | } 1632 | ] 1633 | }, 1634 | "debug_tag": { 1635 | "type": "SEQ", 1636 | "members": [ 1637 | { 1638 | "type": "STRING", 1639 | "value": "{" 1640 | }, 1641 | { 1642 | "type": "ALIAS", 1643 | "content": { 1644 | "type": "SYMBOL", 1645 | "name": "_debug_tag" 1646 | }, 1647 | "named": true, 1648 | "value": "expression_tag" 1649 | }, 1650 | { 1651 | "type": "SYMBOL", 1652 | "name": "_tag_value" 1653 | }, 1654 | { 1655 | "type": "STRING", 1656 | "value": "}" 1657 | } 1658 | ] 1659 | }, 1660 | "_render_tag": { 1661 | "type": "SEQ", 1662 | "members": [ 1663 | { 1664 | "type": "STRING", 1665 | "value": "@" 1666 | }, 1667 | { 1668 | "type": "FIELD", 1669 | "name": "tag", 1670 | "content": { 1671 | "type": "IMMEDIATE_TOKEN", 1672 | "content": { 1673 | "type": "STRING", 1674 | "value": "render" 1675 | } 1676 | } 1677 | } 1678 | ] 1679 | }, 1680 | "render_tag": { 1681 | "type": "SEQ", 1682 | "members": [ 1683 | { 1684 | "type": "STRING", 1685 | "value": "{" 1686 | }, 1687 | { 1688 | "type": "ALIAS", 1689 | "content": { 1690 | "type": "SYMBOL", 1691 | "name": "_render_tag" 1692 | }, 1693 | "named": true, 1694 | "value": "expression_tag" 1695 | }, 1696 | { 1697 | "type": "SYMBOL", 1698 | "name": "_tag_value" 1699 | }, 1700 | { 1701 | "type": "STRING", 1702 | "value": "}" 1703 | } 1704 | ] 1705 | } 1706 | }, 1707 | "extras": [ 1708 | { 1709 | "type": "SYMBOL", 1710 | "name": "comment" 1711 | }, 1712 | { 1713 | "type": "PATTERN", 1714 | "value": "\\s+" 1715 | } 1716 | ], 1717 | "conflicts": [ 1718 | [ 1719 | "else_if_block" 1720 | ], 1721 | [ 1722 | "else_block" 1723 | ], 1724 | [ 1725 | "then_block" 1726 | ], 1727 | [ 1728 | "catch_block" 1729 | ] 1730 | ], 1731 | "precedences": [], 1732 | "externals": [ 1733 | { 1734 | "type": "SYMBOL", 1735 | "name": "_start_tag_name" 1736 | }, 1737 | { 1738 | "type": "SYMBOL", 1739 | "name": "_script_start_tag_name" 1740 | }, 1741 | { 1742 | "type": "SYMBOL", 1743 | "name": "_style_start_tag_name" 1744 | }, 1745 | { 1746 | "type": "SYMBOL", 1747 | "name": "_end_tag_name" 1748 | }, 1749 | { 1750 | "type": "SYMBOL", 1751 | "name": "erroneous_end_tag_name" 1752 | }, 1753 | { 1754 | "type": "STRING", 1755 | "value": "/>" 1756 | }, 1757 | { 1758 | "type": "SYMBOL", 1759 | "name": "_implicit_end_tag" 1760 | }, 1761 | { 1762 | "type": "SYMBOL", 1763 | "name": "raw_text" 1764 | }, 1765 | { 1766 | "type": "SYMBOL", 1767 | "name": "comment" 1768 | }, 1769 | { 1770 | "type": "SYMBOL", 1771 | "name": "svelte_raw_text" 1772 | }, 1773 | { 1774 | "type": "SYMBOL", 1775 | "name": "svelte_raw_text_each" 1776 | }, 1777 | { 1778 | "type": "SYMBOL", 1779 | "name": "svelte_raw_text_snippet_arguments" 1780 | }, 1781 | { 1782 | "type": "STRING", 1783 | "value": "@" 1784 | }, 1785 | { 1786 | "type": "STRING", 1787 | "value": "#" 1788 | }, 1789 | { 1790 | "type": "STRING", 1791 | "value": "/" 1792 | }, 1793 | { 1794 | "type": "STRING", 1795 | "value": ":" 1796 | } 1797 | ], 1798 | "inline": [], 1799 | "supertypes": [ 1800 | "_node" 1801 | ] 1802 | } 1803 | -------------------------------------------------------------------------------- /src/node-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "_node", 4 | "named": true, 5 | "subtypes": [ 6 | { 7 | "type": "await_statement", 8 | "named": true 9 | }, 10 | { 11 | "type": "const_tag", 12 | "named": true 13 | }, 14 | { 15 | "type": "debug_tag", 16 | "named": true 17 | }, 18 | { 19 | "type": "doctype", 20 | "named": true 21 | }, 22 | { 23 | "type": "each_statement", 24 | "named": true 25 | }, 26 | { 27 | "type": "element", 28 | "named": true 29 | }, 30 | { 31 | "type": "entity", 32 | "named": true 33 | }, 34 | { 35 | "type": "erroneous_end_tag", 36 | "named": true 37 | }, 38 | { 39 | "type": "expression", 40 | "named": true 41 | }, 42 | { 43 | "type": "html_tag", 44 | "named": true 45 | }, 46 | { 47 | "type": "if_statement", 48 | "named": true 49 | }, 50 | { 51 | "type": "key_statement", 52 | "named": true 53 | }, 54 | { 55 | "type": "render_tag", 56 | "named": true 57 | }, 58 | { 59 | "type": "script_element", 60 | "named": true 61 | }, 62 | { 63 | "type": "snippet_statement", 64 | "named": true 65 | }, 66 | { 67 | "type": "style_element", 68 | "named": true 69 | }, 70 | { 71 | "type": "text", 72 | "named": true 73 | } 74 | ] 75 | }, 76 | { 77 | "type": "attribute", 78 | "named": true, 79 | "fields": {}, 80 | "children": { 81 | "multiple": true, 82 | "required": true, 83 | "types": [ 84 | { 85 | "type": "attribute_name", 86 | "named": true 87 | }, 88 | { 89 | "type": "attribute_value", 90 | "named": true 91 | }, 92 | { 93 | "type": "expression", 94 | "named": true 95 | }, 96 | { 97 | "type": "quoted_attribute_value", 98 | "named": true 99 | } 100 | ] 101 | } 102 | }, 103 | { 104 | "type": "attribute_name", 105 | "named": true, 106 | "fields": {}, 107 | "children": { 108 | "multiple": false, 109 | "required": false, 110 | "types": [ 111 | { 112 | "type": "svelte_raw_text", 113 | "named": true 114 | } 115 | ] 116 | } 117 | }, 118 | { 119 | "type": "attribute_value", 120 | "named": true, 121 | "fields": {}, 122 | "children": { 123 | "multiple": true, 124 | "required": false, 125 | "types": [ 126 | { 127 | "type": "expression", 128 | "named": true 129 | } 130 | ] 131 | } 132 | }, 133 | { 134 | "type": "await_end", 135 | "named": true, 136 | "fields": {}, 137 | "children": { 138 | "multiple": false, 139 | "required": true, 140 | "types": [ 141 | { 142 | "type": "block_end_tag", 143 | "named": true 144 | } 145 | ] 146 | } 147 | }, 148 | { 149 | "type": "await_start", 150 | "named": true, 151 | "fields": {}, 152 | "children": { 153 | "multiple": true, 154 | "required": true, 155 | "types": [ 156 | { 157 | "type": "block_start_tag", 158 | "named": true 159 | }, 160 | { 161 | "type": "svelte_raw_text", 162 | "named": true 163 | } 164 | ] 165 | } 166 | }, 167 | { 168 | "type": "await_statement", 169 | "named": true, 170 | "fields": {}, 171 | "children": { 172 | "multiple": true, 173 | "required": true, 174 | "types": [ 175 | { 176 | "type": "_node", 177 | "named": true 178 | }, 179 | { 180 | "type": "await_end", 181 | "named": true 182 | }, 183 | { 184 | "type": "await_start", 185 | "named": true 186 | }, 187 | { 188 | "type": "catch_block", 189 | "named": true 190 | }, 191 | { 192 | "type": "then_block", 193 | "named": true 194 | } 195 | ] 196 | } 197 | }, 198 | { 199 | "type": "block_end_tag", 200 | "named": true, 201 | "fields": { 202 | "tag": { 203 | "multiple": false, 204 | "required": true, 205 | "types": [ 206 | { 207 | "type": "await", 208 | "named": false 209 | }, 210 | { 211 | "type": "each", 212 | "named": false 213 | }, 214 | { 215 | "type": "if", 216 | "named": false 217 | }, 218 | { 219 | "type": "key", 220 | "named": false 221 | }, 222 | { 223 | "type": "snippet", 224 | "named": false 225 | } 226 | ] 227 | } 228 | } 229 | }, 230 | { 231 | "type": "block_start_tag", 232 | "named": true, 233 | "fields": { 234 | "tag": { 235 | "multiple": false, 236 | "required": true, 237 | "types": [ 238 | { 239 | "type": "await", 240 | "named": false 241 | }, 242 | { 243 | "type": "each", 244 | "named": false 245 | }, 246 | { 247 | "type": "if", 248 | "named": false 249 | }, 250 | { 251 | "type": "key", 252 | "named": false 253 | }, 254 | { 255 | "type": "snippet", 256 | "named": false 257 | } 258 | ] 259 | } 260 | } 261 | }, 262 | { 263 | "type": "block_tag", 264 | "named": true, 265 | "fields": { 266 | "tag": { 267 | "multiple": false, 268 | "required": true, 269 | "types": [ 270 | { 271 | "type": "catch", 272 | "named": false 273 | }, 274 | { 275 | "type": "else", 276 | "named": false 277 | }, 278 | { 279 | "type": "else if", 280 | "named": false 281 | }, 282 | { 283 | "type": "then", 284 | "named": false 285 | } 286 | ] 287 | } 288 | } 289 | }, 290 | { 291 | "type": "catch_block", 292 | "named": true, 293 | "fields": {}, 294 | "children": { 295 | "multiple": true, 296 | "required": true, 297 | "types": [ 298 | { 299 | "type": "_node", 300 | "named": true 301 | }, 302 | { 303 | "type": "catch_start", 304 | "named": true 305 | } 306 | ] 307 | } 308 | }, 309 | { 310 | "type": "catch_start", 311 | "named": true, 312 | "fields": {}, 313 | "children": { 314 | "multiple": true, 315 | "required": true, 316 | "types": [ 317 | { 318 | "type": "block_tag", 319 | "named": true 320 | }, 321 | { 322 | "type": "svelte_raw_text", 323 | "named": true 324 | } 325 | ] 326 | } 327 | }, 328 | { 329 | "type": "const_tag", 330 | "named": true, 331 | "fields": {}, 332 | "children": { 333 | "multiple": true, 334 | "required": true, 335 | "types": [ 336 | { 337 | "type": "expression_tag", 338 | "named": true 339 | }, 340 | { 341 | "type": "svelte_raw_text", 342 | "named": true 343 | } 344 | ] 345 | } 346 | }, 347 | { 348 | "type": "debug_tag", 349 | "named": true, 350 | "fields": {}, 351 | "children": { 352 | "multiple": true, 353 | "required": true, 354 | "types": [ 355 | { 356 | "type": "expression_tag", 357 | "named": true 358 | }, 359 | { 360 | "type": "svelte_raw_text", 361 | "named": true 362 | } 363 | ] 364 | } 365 | }, 366 | { 367 | "type": "doctype", 368 | "named": true, 369 | "fields": {} 370 | }, 371 | { 372 | "type": "document", 373 | "named": true, 374 | "fields": {}, 375 | "children": { 376 | "multiple": true, 377 | "required": false, 378 | "types": [ 379 | { 380 | "type": "_node", 381 | "named": true 382 | } 383 | ] 384 | } 385 | }, 386 | { 387 | "type": "each_end", 388 | "named": true, 389 | "fields": {}, 390 | "children": { 391 | "multiple": false, 392 | "required": true, 393 | "types": [ 394 | { 395 | "type": "block_end_tag", 396 | "named": true 397 | } 398 | ] 399 | } 400 | }, 401 | { 402 | "type": "each_start", 403 | "named": true, 404 | "fields": { 405 | "identifier": { 406 | "multiple": false, 407 | "required": true, 408 | "types": [ 409 | { 410 | "type": "svelte_raw_text", 411 | "named": true 412 | } 413 | ] 414 | }, 415 | "parameter": { 416 | "multiple": false, 417 | "required": false, 418 | "types": [ 419 | { 420 | "type": "svelte_raw_text", 421 | "named": true 422 | } 423 | ] 424 | } 425 | }, 426 | "children": { 427 | "multiple": false, 428 | "required": true, 429 | "types": [ 430 | { 431 | "type": "block_start_tag", 432 | "named": true 433 | } 434 | ] 435 | } 436 | }, 437 | { 438 | "type": "each_statement", 439 | "named": true, 440 | "fields": {}, 441 | "children": { 442 | "multiple": true, 443 | "required": true, 444 | "types": [ 445 | { 446 | "type": "_node", 447 | "named": true 448 | }, 449 | { 450 | "type": "each_end", 451 | "named": true 452 | }, 453 | { 454 | "type": "each_start", 455 | "named": true 456 | }, 457 | { 458 | "type": "else_block", 459 | "named": true 460 | } 461 | ] 462 | } 463 | }, 464 | { 465 | "type": "element", 466 | "named": true, 467 | "fields": {}, 468 | "children": { 469 | "multiple": true, 470 | "required": true, 471 | "types": [ 472 | { 473 | "type": "_node", 474 | "named": true 475 | }, 476 | { 477 | "type": "end_tag", 478 | "named": true 479 | }, 480 | { 481 | "type": "self_closing_tag", 482 | "named": true 483 | }, 484 | { 485 | "type": "start_tag", 486 | "named": true 487 | } 488 | ] 489 | } 490 | }, 491 | { 492 | "type": "else_block", 493 | "named": true, 494 | "fields": {}, 495 | "children": { 496 | "multiple": true, 497 | "required": true, 498 | "types": [ 499 | { 500 | "type": "_node", 501 | "named": true 502 | }, 503 | { 504 | "type": "else_start", 505 | "named": true 506 | } 507 | ] 508 | } 509 | }, 510 | { 511 | "type": "else_if_block", 512 | "named": true, 513 | "fields": {}, 514 | "children": { 515 | "multiple": true, 516 | "required": true, 517 | "types": [ 518 | { 519 | "type": "_node", 520 | "named": true 521 | }, 522 | { 523 | "type": "else_if_start", 524 | "named": true 525 | } 526 | ] 527 | } 528 | }, 529 | { 530 | "type": "else_if_start", 531 | "named": true, 532 | "fields": { 533 | "condition": { 534 | "multiple": false, 535 | "required": true, 536 | "types": [ 537 | { 538 | "type": "svelte_raw_text", 539 | "named": true 540 | } 541 | ] 542 | } 543 | }, 544 | "children": { 545 | "multiple": false, 546 | "required": true, 547 | "types": [ 548 | { 549 | "type": "block_tag", 550 | "named": true 551 | } 552 | ] 553 | } 554 | }, 555 | { 556 | "type": "else_start", 557 | "named": true, 558 | "fields": {}, 559 | "children": { 560 | "multiple": false, 561 | "required": true, 562 | "types": [ 563 | { 564 | "type": "block_tag", 565 | "named": true 566 | } 567 | ] 568 | } 569 | }, 570 | { 571 | "type": "end_tag", 572 | "named": true, 573 | "fields": {}, 574 | "children": { 575 | "multiple": false, 576 | "required": true, 577 | "types": [ 578 | { 579 | "type": "tag_name", 580 | "named": true 581 | } 582 | ] 583 | } 584 | }, 585 | { 586 | "type": "erroneous_end_tag", 587 | "named": true, 588 | "fields": {}, 589 | "children": { 590 | "multiple": false, 591 | "required": true, 592 | "types": [ 593 | { 594 | "type": "erroneous_end_tag_name", 595 | "named": true 596 | } 597 | ] 598 | } 599 | }, 600 | { 601 | "type": "expression", 602 | "named": true, 603 | "fields": {}, 604 | "children": { 605 | "multiple": false, 606 | "required": true, 607 | "types": [ 608 | { 609 | "type": "svelte_raw_text", 610 | "named": true 611 | } 612 | ] 613 | } 614 | }, 615 | { 616 | "type": "expression_tag", 617 | "named": true, 618 | "fields": { 619 | "tag": { 620 | "multiple": false, 621 | "required": true, 622 | "types": [ 623 | { 624 | "type": "const", 625 | "named": false 626 | }, 627 | { 628 | "type": "debug", 629 | "named": false 630 | }, 631 | { 632 | "type": "html", 633 | "named": false 634 | }, 635 | { 636 | "type": "render", 637 | "named": false 638 | } 639 | ] 640 | } 641 | } 642 | }, 643 | { 644 | "type": "html_tag", 645 | "named": true, 646 | "fields": {}, 647 | "children": { 648 | "multiple": true, 649 | "required": true, 650 | "types": [ 651 | { 652 | "type": "expression_tag", 653 | "named": true 654 | }, 655 | { 656 | "type": "svelte_raw_text", 657 | "named": true 658 | } 659 | ] 660 | } 661 | }, 662 | { 663 | "type": "if_end", 664 | "named": true, 665 | "fields": {}, 666 | "children": { 667 | "multiple": false, 668 | "required": true, 669 | "types": [ 670 | { 671 | "type": "block_end_tag", 672 | "named": true 673 | } 674 | ] 675 | } 676 | }, 677 | { 678 | "type": "if_start", 679 | "named": true, 680 | "fields": { 681 | "condition": { 682 | "multiple": false, 683 | "required": true, 684 | "types": [ 685 | { 686 | "type": "svelte_raw_text", 687 | "named": true 688 | } 689 | ] 690 | } 691 | }, 692 | "children": { 693 | "multiple": false, 694 | "required": true, 695 | "types": [ 696 | { 697 | "type": "block_start_tag", 698 | "named": true 699 | } 700 | ] 701 | } 702 | }, 703 | { 704 | "type": "if_statement", 705 | "named": true, 706 | "fields": {}, 707 | "children": { 708 | "multiple": true, 709 | "required": true, 710 | "types": [ 711 | { 712 | "type": "_node", 713 | "named": true 714 | }, 715 | { 716 | "type": "else_block", 717 | "named": true 718 | }, 719 | { 720 | "type": "else_if_block", 721 | "named": true 722 | }, 723 | { 724 | "type": "if_end", 725 | "named": true 726 | }, 727 | { 728 | "type": "if_start", 729 | "named": true 730 | } 731 | ] 732 | } 733 | }, 734 | { 735 | "type": "key_end", 736 | "named": true, 737 | "fields": {}, 738 | "children": { 739 | "multiple": false, 740 | "required": true, 741 | "types": [ 742 | { 743 | "type": "block_end_tag", 744 | "named": true 745 | } 746 | ] 747 | } 748 | }, 749 | { 750 | "type": "key_start", 751 | "named": true, 752 | "fields": {}, 753 | "children": { 754 | "multiple": true, 755 | "required": true, 756 | "types": [ 757 | { 758 | "type": "block_start_tag", 759 | "named": true 760 | }, 761 | { 762 | "type": "svelte_raw_text", 763 | "named": true 764 | } 765 | ] 766 | } 767 | }, 768 | { 769 | "type": "key_statement", 770 | "named": true, 771 | "fields": {}, 772 | "children": { 773 | "multiple": true, 774 | "required": true, 775 | "types": [ 776 | { 777 | "type": "_node", 778 | "named": true 779 | }, 780 | { 781 | "type": "key_end", 782 | "named": true 783 | }, 784 | { 785 | "type": "key_start", 786 | "named": true 787 | } 788 | ] 789 | } 790 | }, 791 | { 792 | "type": "quoted_attribute_value", 793 | "named": true, 794 | "fields": {}, 795 | "children": { 796 | "multiple": false, 797 | "required": false, 798 | "types": [ 799 | { 800 | "type": "attribute_value", 801 | "named": true 802 | } 803 | ] 804 | } 805 | }, 806 | { 807 | "type": "render_tag", 808 | "named": true, 809 | "fields": {}, 810 | "children": { 811 | "multiple": true, 812 | "required": true, 813 | "types": [ 814 | { 815 | "type": "expression_tag", 816 | "named": true 817 | }, 818 | { 819 | "type": "svelte_raw_text", 820 | "named": true 821 | } 822 | ] 823 | } 824 | }, 825 | { 826 | "type": "script_element", 827 | "named": true, 828 | "fields": {}, 829 | "children": { 830 | "multiple": true, 831 | "required": true, 832 | "types": [ 833 | { 834 | "type": "end_tag", 835 | "named": true 836 | }, 837 | { 838 | "type": "raw_text", 839 | "named": true 840 | }, 841 | { 842 | "type": "start_tag", 843 | "named": true 844 | } 845 | ] 846 | } 847 | }, 848 | { 849 | "type": "self_closing_tag", 850 | "named": true, 851 | "fields": {}, 852 | "children": { 853 | "multiple": true, 854 | "required": true, 855 | "types": [ 856 | { 857 | "type": "attribute", 858 | "named": true 859 | }, 860 | { 861 | "type": "tag_name", 862 | "named": true 863 | } 864 | ] 865 | } 866 | }, 867 | { 868 | "type": "snippet_end", 869 | "named": true, 870 | "fields": {}, 871 | "children": { 872 | "multiple": false, 873 | "required": true, 874 | "types": [ 875 | { 876 | "type": "block_end_tag", 877 | "named": true 878 | } 879 | ] 880 | } 881 | }, 882 | { 883 | "type": "snippet_start", 884 | "named": true, 885 | "fields": {}, 886 | "children": { 887 | "multiple": true, 888 | "required": true, 889 | "types": [ 890 | { 891 | "type": "block_start_tag", 892 | "named": true 893 | }, 894 | { 895 | "type": "snippet_name", 896 | "named": true 897 | }, 898 | { 899 | "type": "svelte_raw_text", 900 | "named": true 901 | } 902 | ] 903 | } 904 | }, 905 | { 906 | "type": "snippet_statement", 907 | "named": true, 908 | "fields": {}, 909 | "children": { 910 | "multiple": true, 911 | "required": true, 912 | "types": [ 913 | { 914 | "type": "_node", 915 | "named": true 916 | }, 917 | { 918 | "type": "snippet_end", 919 | "named": true 920 | }, 921 | { 922 | "type": "snippet_start", 923 | "named": true 924 | } 925 | ] 926 | } 927 | }, 928 | { 929 | "type": "start_tag", 930 | "named": true, 931 | "fields": {}, 932 | "children": { 933 | "multiple": true, 934 | "required": true, 935 | "types": [ 936 | { 937 | "type": "attribute", 938 | "named": true 939 | }, 940 | { 941 | "type": "tag_name", 942 | "named": true 943 | } 944 | ] 945 | } 946 | }, 947 | { 948 | "type": "style_element", 949 | "named": true, 950 | "fields": {}, 951 | "children": { 952 | "multiple": true, 953 | "required": true, 954 | "types": [ 955 | { 956 | "type": "end_tag", 957 | "named": true 958 | }, 959 | { 960 | "type": "raw_text", 961 | "named": true 962 | }, 963 | { 964 | "type": "start_tag", 965 | "named": true 966 | } 967 | ] 968 | } 969 | }, 970 | { 971 | "type": "then_block", 972 | "named": true, 973 | "fields": {}, 974 | "children": { 975 | "multiple": true, 976 | "required": true, 977 | "types": [ 978 | { 979 | "type": "_node", 980 | "named": true 981 | }, 982 | { 983 | "type": "then_start", 984 | "named": true 985 | } 986 | ] 987 | } 988 | }, 989 | { 990 | "type": "then_start", 991 | "named": true, 992 | "fields": {}, 993 | "children": { 994 | "multiple": true, 995 | "required": true, 996 | "types": [ 997 | { 998 | "type": "block_tag", 999 | "named": true 1000 | }, 1001 | { 1002 | "type": "svelte_raw_text", 1003 | "named": true 1004 | } 1005 | ] 1006 | } 1007 | }, 1008 | { 1009 | "type": "\"", 1010 | "named": false 1011 | }, 1012 | { 1013 | "type": "#", 1014 | "named": false 1015 | }, 1016 | { 1017 | "type": "'", 1018 | "named": false 1019 | }, 1020 | { 1021 | "type": "(", 1022 | "named": false 1023 | }, 1024 | { 1025 | "type": ")", 1026 | "named": false 1027 | }, 1028 | { 1029 | "type": "/", 1030 | "named": false 1031 | }, 1032 | { 1033 | "type": "/>", 1034 | "named": false 1035 | }, 1036 | { 1037 | "type": ":", 1038 | "named": false 1039 | }, 1040 | { 1041 | "type": "<", 1042 | "named": false 1043 | }, 1044 | { 1045 | "type": "", 1058 | "named": false 1059 | }, 1060 | { 1061 | "type": "@", 1062 | "named": false 1063 | }, 1064 | { 1065 | "type": "as", 1066 | "named": false 1067 | }, 1068 | { 1069 | "type": "await", 1070 | "named": false 1071 | }, 1072 | { 1073 | "type": "catch", 1074 | "named": false 1075 | }, 1076 | { 1077 | "type": "comment", 1078 | "named": true 1079 | }, 1080 | { 1081 | "type": "const", 1082 | "named": false 1083 | }, 1084 | { 1085 | "type": "debug", 1086 | "named": false 1087 | }, 1088 | { 1089 | "type": "doctype", 1090 | "named": false 1091 | }, 1092 | { 1093 | "type": "each", 1094 | "named": false 1095 | }, 1096 | { 1097 | "type": "else", 1098 | "named": false 1099 | }, 1100 | { 1101 | "type": "else if", 1102 | "named": false 1103 | }, 1104 | { 1105 | "type": "entity", 1106 | "named": true 1107 | }, 1108 | { 1109 | "type": "erroneous_end_tag_name", 1110 | "named": true 1111 | }, 1112 | { 1113 | "type": "html", 1114 | "named": false 1115 | }, 1116 | { 1117 | "type": "if", 1118 | "named": false 1119 | }, 1120 | { 1121 | "type": "key", 1122 | "named": false 1123 | }, 1124 | { 1125 | "type": "raw_text", 1126 | "named": true 1127 | }, 1128 | { 1129 | "type": "render", 1130 | "named": false 1131 | }, 1132 | { 1133 | "type": "snippet", 1134 | "named": false 1135 | }, 1136 | { 1137 | "type": "snippet_name", 1138 | "named": true 1139 | }, 1140 | { 1141 | "type": "svelte_raw_text", 1142 | "named": true 1143 | }, 1144 | { 1145 | "type": "tag_name", 1146 | "named": true 1147 | }, 1148 | { 1149 | "type": "text", 1150 | "named": true 1151 | }, 1152 | { 1153 | "type": "then", 1154 | "named": false 1155 | }, 1156 | { 1157 | "type": "{", 1158 | "named": false 1159 | }, 1160 | { 1161 | "type": "}", 1162 | "named": false 1163 | } 1164 | ] -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | #include "tag.h" 2 | #include "tree_sitter/parser.h" 3 | 4 | #include 5 | 6 | enum TokenType { 7 | START_TAG_NAME, 8 | SCRIPT_START_TAG_NAME, 9 | STYLE_START_TAG_NAME, 10 | END_TAG_NAME, 11 | ERRONEOUS_END_TAG_NAME, 12 | SELF_CLOSING_TAG_DELIMITER, 13 | IMPLICIT_END_TAG, 14 | RAW_TEXT, 15 | COMMENT, 16 | SVELTE_RAW_TEXT, 17 | SVELTE_RAW_TEXT_EACH, 18 | SVELTE_RAW_TEXT_SNIPPET_ARGUMENTS, 19 | AT, 20 | HASH, 21 | SLASH, 22 | COLON, 23 | }; 24 | 25 | typedef struct { 26 | Array(Tag) tags; 27 | } Scanner; 28 | 29 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 30 | 31 | static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); } 32 | 33 | static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); } 34 | 35 | static unsigned serialize(Scanner *scanner, char *buffer) { 36 | uint16_t tag_count = scanner->tags.size > UINT16_MAX ? UINT16_MAX : scanner->tags.size; 37 | uint16_t serialized_tag_count = 0; 38 | 39 | unsigned size = sizeof(tag_count); 40 | memcpy(&buffer[size], &tag_count, sizeof(tag_count)); 41 | size += sizeof(tag_count); 42 | 43 | for (; serialized_tag_count < tag_count; serialized_tag_count++) { 44 | Tag tag = scanner->tags.contents[serialized_tag_count]; 45 | if (tag.type == CUSTOM) { 46 | unsigned name_length = tag.custom_tag_name.size; 47 | if (name_length > UINT8_MAX) { 48 | name_length = UINT8_MAX; 49 | } 50 | if (size + 2 + name_length >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) { 51 | break; 52 | } 53 | buffer[size++] = (char)tag.type; 54 | buffer[size++] = (char)name_length; 55 | strncpy(&buffer[size], tag.custom_tag_name.contents, name_length); 56 | size += name_length; 57 | } else { 58 | if (size + 1 >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) { 59 | break; 60 | } 61 | buffer[size++] = (char)tag.type; 62 | } 63 | } 64 | 65 | memcpy(&buffer[0], &serialized_tag_count, sizeof(serialized_tag_count)); 66 | return size; 67 | } 68 | 69 | static void deserialize(Scanner *scanner, const char *buffer, unsigned length) { 70 | for (unsigned i = 0; i < scanner->tags.size; i++) { 71 | tag_free(&scanner->tags.contents[i]); 72 | } 73 | array_clear(&scanner->tags); 74 | 75 | if (length > 0) { 76 | unsigned size = 0; 77 | uint16_t tag_count = 0; 78 | uint16_t serialized_tag_count = 0; 79 | 80 | memcpy(&serialized_tag_count, &buffer[size], sizeof(serialized_tag_count)); 81 | size += sizeof(serialized_tag_count); 82 | 83 | memcpy(&tag_count, &buffer[size], sizeof(tag_count)); 84 | size += sizeof(tag_count); 85 | 86 | array_reserve(&scanner->tags, tag_count); 87 | if (tag_count > 0) { 88 | unsigned iter = 0; 89 | for (iter = 0; iter < serialized_tag_count; iter++) { 90 | Tag tag = tag_new(); 91 | tag.type = (TagType)buffer[size++]; 92 | if (tag.type == CUSTOM) { 93 | uint16_t name_length = (uint8_t)buffer[size++]; 94 | array_reserve(&tag.custom_tag_name, name_length); 95 | tag.custom_tag_name.size = name_length; 96 | memcpy(tag.custom_tag_name.contents, &buffer[size], name_length); 97 | size += name_length; 98 | } 99 | array_push(&scanner->tags, tag); 100 | } 101 | // add zero tags if we didn't read enough, this is because the 102 | // buffer had no more room but we held more tags. 103 | for (; iter < tag_count; iter++) { 104 | array_push(&scanner->tags, tag_new()); 105 | } 106 | } 107 | } 108 | } 109 | 110 | static String scan_tag_name(TSLexer *lexer) { 111 | String tag_name = array_new(); 112 | while (iswalnum(lexer->lookahead) || lexer->lookahead == '-' || lexer->lookahead == ':' || 113 | lexer->lookahead == '.') { 114 | // In `tree-sitter-html`, this is where each character is uppercased, 115 | // but we're preserving the original case. Why? 116 | // 117 | // The comparisons for HTML are case-insensitive, since browsers parse 118 | // HTML tag names in a case-insensitive manner. But Svelte enforces 119 | // that all usages of plain HTML are in lowercase! Imported Svelte 120 | // components, on the other hand, must have an initial capital letter. 121 | // 122 | // For the purposes of this parser, we'll enforce HTML's rules about 123 | // containment and void tags only on all-lowercase tag names. 124 | // 125 | // There are some hypothetical tag names that we could confidently flag 126 | // as invalid — element names like `inupt` that fail to meet the naming 127 | // requirements for both custom elements and Svelte components. We can 128 | // leave that for later, though. 129 | array_push(&tag_name, lexer->lookahead); 130 | advance(lexer); 131 | } 132 | return tag_name; 133 | } 134 | 135 | static bool scan_comment(TSLexer *lexer) { 136 | if (lexer->lookahead != '-') { 137 | return false; 138 | } 139 | advance(lexer); 140 | if (lexer->lookahead != '-') { 141 | return false; 142 | } 143 | advance(lexer); 144 | 145 | unsigned dashes = 0; 146 | while (lexer->lookahead) { 147 | switch (lexer->lookahead) { 148 | case '-': 149 | ++dashes; 150 | break; 151 | case '>': 152 | if (dashes >= 2) { 153 | lexer->result_symbol = COMMENT; 154 | advance(lexer); 155 | lexer->mark_end(lexer); 156 | return true; 157 | } 158 | dashes = 0; 159 | break; 160 | default: 161 | dashes = 0; 162 | } 163 | advance(lexer); 164 | } 165 | return false; 166 | } 167 | 168 | static bool scan_javascript_template_string(TSLexer *lexer); 169 | 170 | static bool scan_javascript_quoted_string(TSLexer *lexer, int32_t delimiter); 171 | 172 | // After consuming a forward slash and seeing an asterisk immediately after it, 173 | // call this function to advance the lexer to the end of the JavaScript block 174 | // comment. 175 | static bool scan_javascript_block_comment(TSLexer *lexer) { 176 | if (lexer->lookahead != '*') { 177 | return false; 178 | } 179 | advance(lexer); 180 | while (lexer->lookahead) { 181 | switch (lexer->lookahead) { 182 | case '*': 183 | advance(lexer); 184 | if (lexer->lookahead == '/') { 185 | advance(lexer); 186 | return true; 187 | } 188 | break; 189 | default: 190 | advance(lexer); 191 | } 192 | } 193 | return false; 194 | } 195 | 196 | // After consuming a forward slash and seeing another forward slash immediately 197 | // after it, call this function to advance the lexer to the end of the 198 | // JavaScript line comment. 199 | static bool scan_javascript_line_comment(TSLexer *lexer) { 200 | if (lexer->lookahead != '/') { 201 | return false; 202 | } 203 | advance(lexer); 204 | while (lexer->lookahead) { 205 | switch (lexer->lookahead) { 206 | case '\n': 207 | case '\r': 208 | advance(lexer); 209 | return true; 210 | default: 211 | advance(lexer); 212 | } 213 | } 214 | return false; 215 | } 216 | 217 | // When you see a `{` in front of you in a JavaScript context, call this 218 | // function to scan through until the next balanced (unescaped) brace. 219 | static bool scan_javascript_balanced_brace(TSLexer *lexer) { 220 | if (lexer->lookahead != '{') { 221 | return false; 222 | } 223 | uint8_t brace_level = 0; 224 | advance(lexer); 225 | while (lexer->lookahead) { 226 | switch (lexer->lookahead) { 227 | case '`': 228 | scan_javascript_template_string(lexer); 229 | break; 230 | case '\\': 231 | // Escape character. Advance twice. 232 | advance(lexer); 233 | advance(lexer); 234 | break; 235 | case '\'': 236 | case '"': 237 | scan_javascript_quoted_string(lexer, lexer->lookahead); 238 | break; 239 | case '{': 240 | brace_level++; 241 | advance(lexer); 242 | break; 243 | case '}': 244 | advance(lexer); 245 | if (brace_level == 0) { 246 | return true; 247 | } 248 | brace_level--; 249 | break; 250 | default: 251 | advance(lexer); 252 | } 253 | } 254 | return false; 255 | } 256 | 257 | // When you see a single or double quote that starts a string, call this 258 | // function to scan through until the end of the quoted string. 259 | static bool scan_javascript_quoted_string(TSLexer *lexer, int32_t delimiter) { 260 | if (lexer->lookahead != delimiter) { 261 | return false; 262 | } 263 | advance(lexer); 264 | while (lexer->lookahead) { 265 | switch (lexer->lookahead) { 266 | case '\\': 267 | // Escape character. Advance again. 268 | advance(lexer); 269 | advance(lexer); 270 | break; 271 | default: 272 | if (lexer->lookahead == delimiter) { 273 | advance(lexer); 274 | return true; 275 | } 276 | advance(lexer); 277 | } 278 | } 279 | return false; 280 | } 281 | 282 | // When you see a backtick in a JavaScript context, call this function to scan 283 | // through until the end of the template string. 284 | static bool scan_javascript_template_string(TSLexer *lexer) { 285 | if (lexer->lookahead != '`') { 286 | return false; 287 | } 288 | advance(lexer); 289 | while (lexer->lookahead) { 290 | switch (lexer->lookahead) { 291 | case '$': 292 | advance(lexer); 293 | if (lexer->lookahead == '{') { 294 | scan_javascript_balanced_brace(lexer); 295 | } 296 | break; 297 | case '\\': 298 | // Escape character. Advance again. 299 | advance(lexer); 300 | advance(lexer); 301 | break; 302 | case '`': 303 | advance(lexer); 304 | return true; 305 | default: 306 | advance(lexer); 307 | } 308 | } 309 | return false; 310 | } 311 | 312 | static bool scan_raw_text(Scanner *scanner, TSLexer *lexer) { 313 | if (scanner->tags.size == 0) { 314 | return false; 315 | } 316 | 317 | lexer->mark_end(lexer); 318 | 319 | const char *end_delimiter = array_back(&scanner->tags)->type == SCRIPT ? "lookahead) { 323 | if ((char)towupper(lexer->lookahead) == end_delimiter[delimiter_index]) { 324 | delimiter_index++; 325 | if (delimiter_index == strlen(end_delimiter)) { 326 | break; 327 | } 328 | advance(lexer); 329 | } else { 330 | delimiter_index = 0; 331 | advance(lexer); 332 | lexer->mark_end(lexer); 333 | } 334 | } 335 | 336 | lexer->result_symbol = RAW_TEXT; 337 | return true; 338 | } 339 | 340 | // Like `scan_svelte_raw_text`, but designed to operate inside the parentheses 341 | // of a `#snippet` definition. Consumes everything until just before the next 342 | // balanced parenthesis. 343 | static bool scan_svelte_raw_text_snippet(TSLexer *lexer) { 344 | while (iswspace(lexer->lookahead)) { 345 | skip(lexer); 346 | } 347 | lexer->result_symbol = SVELTE_RAW_TEXT_SNIPPET_ARGUMENTS; 348 | uint8_t paren_level = 0; 349 | bool advanced_once = false; 350 | while (!lexer->eof(lexer)) { 351 | switch (lexer->lookahead) { 352 | case '/': 353 | advance(lexer); 354 | if (lexer->lookahead == '*') { 355 | scan_javascript_block_comment(lexer); 356 | } else if (lexer->lookahead == '/') { 357 | scan_javascript_line_comment(lexer); 358 | } 359 | break; 360 | case '\\': 361 | // Escape mode. Advance again. 362 | advance(lexer); 363 | break; 364 | case '"': 365 | case '\'': 366 | // A quoted string is starting. Advance past the end of the 367 | // closing delimiter. 368 | scan_javascript_quoted_string(lexer, lexer->lookahead); 369 | break; 370 | case '`': 371 | // A template string is starting. Advance past the end of the 372 | // closing delimiter. 373 | scan_javascript_template_string(lexer); 374 | break; 375 | case ')': 376 | if (paren_level == 0) { 377 | lexer->mark_end(lexer); 378 | return advanced_once; 379 | } 380 | advance(lexer); 381 | paren_level--; 382 | break; 383 | case '(': 384 | advance(lexer); 385 | paren_level++; 386 | break; 387 | default: 388 | advance(lexer); 389 | break; 390 | } 391 | advanced_once = true; 392 | } 393 | return false; 394 | } 395 | 396 | static bool scan_svelte_raw_text(TSLexer *lexer, const bool *valid_symbols) { 397 | while (iswspace(lexer->lookahead)) { 398 | skip(lexer); 399 | } 400 | 401 | if ((lexer->lookahead == '@' && valid_symbols[AT]) || (lexer->lookahead == '#' && valid_symbols[HASH]) || 402 | (lexer->lookahead == ':' && valid_symbols[COLON])) { 403 | return false; 404 | } 405 | 406 | // The presence of a special Svelte sigil disqualifies this as a raw text 407 | // node. This helps us distinguish those nodes from things like `{:else}`. 408 | bool has_sigil = lexer->lookahead == '@' || lexer->lookahead == '#' || lexer->lookahead == ':'; 409 | if (has_sigil) { 410 | return false; 411 | } 412 | 413 | // Keep track of whether we've advanced even once. If we haven't, then that 414 | // implies we've encountered `{}``, which isn't a valid `svelte_raw_text` 415 | // node. 416 | bool advanced_once = false; 417 | 418 | if (lexer->lookahead == '/' && valid_symbols[SLASH]) { 419 | advance(lexer); 420 | if (lexer->lookahead == '*') { 421 | return scan_javascript_block_comment(lexer); 422 | } 423 | if (lexer->lookahead != '/') { // JavaScript comment 424 | return false; 425 | } 426 | 427 | advanced_once = true; 428 | } 429 | 430 | lexer->result_symbol = valid_symbols[SVELTE_RAW_TEXT_EACH] ? SVELTE_RAW_TEXT_EACH : SVELTE_RAW_TEXT; 431 | 432 | uint8_t brace_level = 0; 433 | 434 | // We're searching for a balanced `}`, but along the way we have to 435 | // consider characters that might put us into contexts for which braces 436 | // have a different meaning. For instance: a brace inside a comment 437 | // shouldn't count toward brace balancing, nor should a brace inside of a 438 | // string. 439 | while (!lexer->eof(lexer)) { 440 | switch (lexer->lookahead) { 441 | case '/': 442 | advance(lexer); 443 | advanced_once = true; 444 | if (lexer->lookahead == '*') { 445 | scan_javascript_block_comment(lexer); 446 | } else if (lexer->lookahead == '/') { 447 | scan_javascript_line_comment(lexer); 448 | } 449 | break; 450 | case '\\': 451 | // Escape mode. Advance again. 452 | advance(lexer); 453 | advanced_once = true; 454 | break; 455 | case '"': 456 | case '\'': 457 | // A quoted string is starting. Advance past the end of the 458 | // closing delimiter. 459 | scan_javascript_quoted_string(lexer, lexer->lookahead); 460 | advanced_once = true; 461 | break; 462 | case '`': 463 | // A template string is starting. Advance past the end of the 464 | // closing delimiter. 465 | scan_javascript_template_string(lexer); 466 | advanced_once = true; 467 | break; 468 | case '}': 469 | if (brace_level == 0) { 470 | lexer->mark_end(lexer); 471 | return advanced_once; 472 | } 473 | advance(lexer); 474 | brace_level--; 475 | advanced_once = true; 476 | break; 477 | 478 | case '{': 479 | advance(lexer); 480 | brace_level++; 481 | advanced_once = true; 482 | break; 483 | 484 | case 'a': 485 | if (lexer->result_symbol == SVELTE_RAW_TEXT_EACH) { 486 | lexer->mark_end(lexer); 487 | advance(lexer); 488 | advanced_once = true; 489 | if (lexer->lookahead == 's') { 490 | advance(lexer); 491 | if (iswspace(lexer->lookahead)) { 492 | return advanced_once; 493 | } 494 | } 495 | } else { 496 | advance(lexer); 497 | advanced_once = true; 498 | } 499 | break; 500 | 501 | default: 502 | advance(lexer); 503 | advanced_once = true; 504 | break; 505 | } 506 | } 507 | 508 | return false; 509 | } 510 | 511 | static inline void pop_tag(Scanner *scanner) { 512 | Tag popped_tag = array_pop(&scanner->tags); 513 | tag_free(&popped_tag); 514 | } 515 | 516 | static bool scan_implicit_end_tag(Scanner *scanner, TSLexer *lexer) { 517 | Tag *parent = scanner->tags.size == 0 ? NULL : array_back(&scanner->tags); 518 | 519 | bool is_closing_tag = false; 520 | if (lexer->lookahead == '/') { 521 | is_closing_tag = true; 522 | advance(lexer); 523 | } else { 524 | if (parent && tag_is_void(parent)) { 525 | pop_tag(scanner); 526 | lexer->result_symbol = IMPLICIT_END_TAG; 527 | return true; 528 | } 529 | } 530 | 531 | String tag_name = scan_tag_name(lexer); 532 | if (tag_name.size == 0 && !lexer->eof(lexer)) { 533 | array_delete(&tag_name); 534 | return false; 535 | } 536 | 537 | Tag next_tag = tag_for_name(tag_name); 538 | 539 | if (is_closing_tag) { 540 | // The tag correctly closes the topmost element on the stack 541 | if (scanner->tags.size > 0 && tag_eq(array_back(&scanner->tags), &next_tag)) { 542 | tag_free(&next_tag); 543 | return false; 544 | } 545 | 546 | // Otherwise, dig deeper and queue implicit end tags (to be nice in 547 | // the case of malformed Svelte) 548 | for (unsigned i = scanner->tags.size; i > 0; i--) { 549 | if (scanner->tags.contents[i - 1].type == next_tag.type) { 550 | pop_tag(scanner); 551 | lexer->result_symbol = IMPLICIT_END_TAG; 552 | tag_free(&next_tag); 553 | return true; 554 | } 555 | } 556 | } else if (parent && 557 | (!tag_can_contain(parent, &next_tag) || 558 | ((parent->type == HTML || parent->type == HEAD || parent->type == BODY) && lexer->eof(lexer)))) { 559 | pop_tag(scanner); 560 | lexer->result_symbol = IMPLICIT_END_TAG; 561 | tag_free(&next_tag); 562 | return true; 563 | } 564 | 565 | tag_free(&next_tag); 566 | return false; 567 | } 568 | 569 | static bool scan_start_tag_name(Scanner *scanner, TSLexer *lexer) { 570 | String tag_name = scan_tag_name(lexer); 571 | 572 | if (tag_name.size == 0) { 573 | array_delete(&tag_name); 574 | return false; 575 | } 576 | 577 | Tag tag = tag_for_name(tag_name); 578 | array_push(&scanner->tags, tag); 579 | switch (tag.type) { 580 | case SCRIPT: 581 | lexer->result_symbol = SCRIPT_START_TAG_NAME; 582 | break; 583 | case STYLE: 584 | lexer->result_symbol = STYLE_START_TAG_NAME; 585 | break; 586 | default: 587 | lexer->result_symbol = START_TAG_NAME; 588 | break; 589 | } 590 | 591 | return true; 592 | } 593 | 594 | static bool scan_end_tag_name(Scanner *scanner, TSLexer *lexer) { 595 | String tag_name = scan_tag_name(lexer); 596 | 597 | if (tag_name.size == 0) { 598 | array_delete(&tag_name); 599 | return false; 600 | } 601 | 602 | Tag tag = tag_for_name(tag_name); 603 | if (scanner->tags.size > 0 && tag_eq(array_back(&scanner->tags), &tag)) { 604 | pop_tag(scanner); 605 | lexer->result_symbol = END_TAG_NAME; 606 | } else { 607 | lexer->result_symbol = ERRONEOUS_END_TAG_NAME; 608 | } 609 | 610 | tag_free(&tag); 611 | return true; 612 | } 613 | 614 | static bool scan_self_closing_tag_delimiter(Scanner *scanner, TSLexer *lexer) { 615 | advance(lexer); 616 | if (lexer->lookahead == '>') { 617 | advance(lexer); 618 | if (scanner->tags.size > 0) { 619 | pop_tag(scanner); 620 | lexer->result_symbol = SELF_CLOSING_TAG_DELIMITER; 621 | } 622 | return true; 623 | } 624 | return false; 625 | } 626 | 627 | static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) { 628 | if (valid_symbols[RAW_TEXT] && !valid_symbols[START_TAG_NAME] && !valid_symbols[END_TAG_NAME]) { 629 | return scan_raw_text(scanner, lexer); 630 | } 631 | 632 | if (valid_symbols[SVELTE_RAW_TEXT_SNIPPET_ARGUMENTS]) { 633 | return scan_svelte_raw_text_snippet(lexer); 634 | } 635 | 636 | if (valid_symbols[SVELTE_RAW_TEXT] || valid_symbols[SVELTE_RAW_TEXT_EACH]) { 637 | return scan_svelte_raw_text(lexer, valid_symbols); 638 | } 639 | 640 | while (iswspace(lexer->lookahead)) { 641 | skip(lexer); 642 | } 643 | 644 | switch (lexer->lookahead) { 645 | case '<': 646 | lexer->mark_end(lexer); 647 | advance(lexer); 648 | 649 | if (lexer->lookahead == '!') { 650 | advance(lexer); 651 | return scan_comment(lexer); 652 | } 653 | 654 | if (valid_symbols[IMPLICIT_END_TAG]) { 655 | return scan_implicit_end_tag(scanner, lexer); 656 | } 657 | break; 658 | 659 | case '{': 660 | case '\0': 661 | if (valid_symbols[IMPLICIT_END_TAG]) { 662 | return scan_implicit_end_tag(scanner, lexer); 663 | } 664 | break; 665 | 666 | case '/': 667 | if (valid_symbols[SELF_CLOSING_TAG_DELIMITER]) { 668 | return scan_self_closing_tag_delimiter(scanner, lexer); 669 | } 670 | break; 671 | 672 | default: 673 | if ((valid_symbols[START_TAG_NAME] || valid_symbols[END_TAG_NAME]) && !valid_symbols[RAW_TEXT]) { 674 | return valid_symbols[START_TAG_NAME] ? scan_start_tag_name(scanner, lexer) 675 | : scan_end_tag_name(scanner, lexer); 676 | } 677 | } 678 | 679 | return false; 680 | } 681 | 682 | void *tree_sitter_svelte_external_scanner_create() { 683 | Scanner *scanner = (Scanner *)ts_calloc(1, sizeof(Scanner)); 684 | return scanner; 685 | } 686 | 687 | bool tree_sitter_svelte_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) { 688 | Scanner *scanner = (Scanner *)payload; 689 | return scan(scanner, lexer, valid_symbols); 690 | } 691 | 692 | unsigned tree_sitter_svelte_external_scanner_serialize(void *payload, char *buffer) { 693 | Scanner *scanner = (Scanner *)payload; 694 | return serialize(scanner, buffer); 695 | } 696 | 697 | void tree_sitter_svelte_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) { 698 | Scanner *scanner = (Scanner *)payload; 699 | deserialize(scanner, buffer, length); 700 | } 701 | 702 | void tree_sitter_svelte_external_scanner_destroy(void *payload) { 703 | Scanner *scanner = (Scanner *)payload; 704 | for (unsigned i = 0; i < scanner->tags.size; i++) { 705 | tag_free(array_get(&scanner->tags, i)); 706 | } 707 | array_delete(&scanner->tags); 708 | ts_free(scanner); 709 | } 710 | -------------------------------------------------------------------------------- /src/tag.h: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/array.h" 2 | 3 | #include 4 | 5 | typedef enum { 6 | AREA, 7 | BASE, 8 | BASEFONT, 9 | BGSOUND, 10 | BR, 11 | COL, 12 | COMMAND, 13 | EMBED, 14 | FRAME, 15 | HR, 16 | IMAGE, 17 | IMG, 18 | INPUT, 19 | ISINDEX, 20 | KEYGEN, 21 | LINK, 22 | MENUITEM, 23 | META, 24 | NEXTID, 25 | PARAM, 26 | SOURCE, 27 | TRACK, 28 | WBR, 29 | END_OF_VOID_TAGS, 30 | 31 | A, 32 | ABBR, 33 | ADDRESS, 34 | ARTICLE, 35 | ASIDE, 36 | AUDIO, 37 | B, 38 | BDI, 39 | BDO, 40 | BLOCKQUOTE, 41 | BODY, 42 | BUTTON, 43 | CANVAS, 44 | CAPTION, 45 | CITE, 46 | CODE, 47 | COLGROUP, 48 | DATA, 49 | DATALIST, 50 | DD, 51 | DEL, 52 | DETAILS, 53 | DFN, 54 | DIALOG, 55 | DIV, 56 | DL, 57 | DT, 58 | EM, 59 | FIELDSET, 60 | FIGCAPTION, 61 | FIGURE, 62 | FOOTER, 63 | FORM, 64 | H1, 65 | H2, 66 | H3, 67 | H4, 68 | H5, 69 | H6, 70 | HEAD, 71 | HEADER, 72 | HGROUP, 73 | HTML, 74 | I, 75 | IFRAME, 76 | INS, 77 | KBD, 78 | LABEL, 79 | LEGEND, 80 | LI, 81 | MAIN, 82 | MAP, 83 | MARK, 84 | MATH, 85 | MENU, 86 | METER, 87 | NAV, 88 | NOSCRIPT, 89 | OBJECT, 90 | OL, 91 | OPTGROUP, 92 | OPTION, 93 | OUTPUT, 94 | P, 95 | PICTURE, 96 | PRE, 97 | PROGRESS, 98 | Q, 99 | RB, 100 | RP, 101 | RT, 102 | RTC, 103 | RUBY, 104 | S, 105 | SAMP, 106 | SCRIPT, 107 | SECTION, 108 | SELECT, 109 | SLOT, 110 | SMALL, 111 | SPAN, 112 | STRONG, 113 | STYLE, 114 | SUB, 115 | SUMMARY, 116 | SUP, 117 | SVG, 118 | TABLE, 119 | TBODY, 120 | TD, 121 | TEMPLATE, 122 | TEXTAREA, 123 | TFOOT, 124 | TH, 125 | THEAD, 126 | TIME, 127 | TITLE, 128 | TR, 129 | U, 130 | UL, 131 | VAR, 132 | VIDEO, 133 | 134 | CUSTOM, 135 | 136 | END_, 137 | } TagType; 138 | 139 | typedef Array(char) String; 140 | 141 | typedef struct { 142 | char tag_name[16]; 143 | TagType tag_type; 144 | } TagMapEntry; 145 | 146 | typedef struct { 147 | TagType type; 148 | String custom_tag_name; 149 | } Tag; 150 | 151 | static const TagMapEntry TAG_TYPES_BY_TAG_NAME[126] = { 152 | {"area", AREA }, 153 | {"base", BASE }, 154 | {"basefont", BASEFONT }, 155 | {"bgsound", BGSOUND }, 156 | {"br", BR }, 157 | {"col", COL }, 158 | {"command", COMMAND }, 159 | {"embed", EMBED }, 160 | {"frame", FRAME }, 161 | {"hr", HR }, 162 | {"image", IMAGE }, 163 | {"img", IMG }, 164 | {"input", INPUT }, 165 | {"isindex", ISINDEX }, 166 | {"keygen", KEYGEN }, 167 | {"link", LINK }, 168 | {"menuitem", MENUITEM }, 169 | {"meta", META }, 170 | {"nextid", NEXTID }, 171 | {"param", PARAM }, 172 | {"source", SOURCE }, 173 | {"track", TRACK }, 174 | {"wbr", WBR }, 175 | {"a", A }, 176 | {"abbr", ABBR }, 177 | {"address", ADDRESS }, 178 | {"article", ARTICLE }, 179 | {"aside", ASIDE }, 180 | {"audio", AUDIO }, 181 | {"b", B }, 182 | {"bdi", BDI }, 183 | {"bdo", BDO }, 184 | {"blockquote", BLOCKQUOTE}, 185 | {"body", BODY }, 186 | {"button", BUTTON }, 187 | {"canvas", CANVAS }, 188 | {"caption", CAPTION }, 189 | {"cite", CITE }, 190 | {"code", CODE }, 191 | {"colgroup", COLGROUP }, 192 | {"data", DATA }, 193 | {"datalist", DATALIST }, 194 | {"dd", DD }, 195 | {"del", DEL }, 196 | {"details", DETAILS }, 197 | {"dfn", DFN }, 198 | {"dialog", DIALOG }, 199 | {"div", DIV }, 200 | {"dl", DL }, 201 | {"dt", DT }, 202 | {"em", EM }, 203 | {"fieldset", FIELDSET }, 204 | {"figcaption", FIGCAPTION}, 205 | {"figure", FIGURE }, 206 | {"footer", FOOTER }, 207 | {"form", FORM }, 208 | {"h1", H1 }, 209 | {"h2", H2 }, 210 | {"h3", H3 }, 211 | {"h4", H4 }, 212 | {"h5", H5 }, 213 | {"h6", H6 }, 214 | {"head", HEAD }, 215 | {"header", HEADER }, 216 | {"hgroup", HGROUP }, 217 | {"html", HTML }, 218 | {"i", I }, 219 | {"iframe", IFRAME }, 220 | {"ins", INS }, 221 | {"kbd", KBD }, 222 | {"label", LABEL }, 223 | {"legend", LEGEND }, 224 | {"li", LI }, 225 | {"main", MAIN }, 226 | {"map", MAP }, 227 | {"mark", MARK }, 228 | {"math", MATH }, 229 | {"menu", MENU }, 230 | {"meter", METER }, 231 | {"nav", NAV }, 232 | {"noscript", NOSCRIPT }, 233 | {"object", OBJECT }, 234 | {"ol", OL }, 235 | {"optgroup", OPTGROUP }, 236 | {"option", OPTION }, 237 | {"output", OUTPUT }, 238 | {"p", P }, 239 | {"picture", PICTURE }, 240 | {"pre", PRE }, 241 | {"progress", PROGRESS }, 242 | {"q", Q }, 243 | {"rb", RB }, 244 | {"rp", RP }, 245 | {"rt", RT }, 246 | {"rtc", RTC }, 247 | {"ruby", RUBY }, 248 | {"s", S }, 249 | {"samp", SAMP }, 250 | {"script", SCRIPT }, 251 | {"section", SECTION }, 252 | {"select", SELECT }, 253 | {"slot", SLOT }, 254 | {"small", SMALL }, 255 | {"span", SPAN }, 256 | {"strong", STRONG }, 257 | {"style", STYLE }, 258 | {"sub", SUB }, 259 | {"summary", SUMMARY }, 260 | {"sup", SUP }, 261 | {"svg", SVG }, 262 | {"table", TABLE }, 263 | {"tbody", TBODY }, 264 | {"td", TD }, 265 | {"template", TEMPLATE }, 266 | {"textarea", TEXTAREA }, 267 | {"tfoot", TFOOT }, 268 | {"th", TH }, 269 | {"thead", THEAD }, 270 | {"time", TIME }, 271 | {"title", TITLE }, 272 | {"tr", TR }, 273 | {"u", U }, 274 | {"ul", UL }, 275 | {"var", VAR }, 276 | {"video", VIDEO }, 277 | {"custom", CUSTOM }, 278 | }; 279 | 280 | static const TagType TAG_TYPES_NOT_ALLOWED_IN_PARAGRAPHS[] = { 281 | ADDRESS, ARTICLE, ASIDE, BLOCKQUOTE, DETAILS, DIV, DL, FIELDSET, FIGCAPTION, FIGURE, FOOTER, FORM, H1, 282 | H2, H3, H4, H5, H6, HEADER, HR, MAIN, NAV, OL, P, PRE, SECTION, 283 | }; 284 | 285 | static TagType tag_type_for_name(const String *tag_name) { 286 | for (int i = 0; i < 126; i++) { 287 | const TagMapEntry *entry = &TAG_TYPES_BY_TAG_NAME[i]; 288 | if (strlen(entry->tag_name) == tag_name->size && 289 | memcmp(tag_name->contents, entry->tag_name, tag_name->size) == 0) { 290 | return entry->tag_type; 291 | } 292 | } 293 | return CUSTOM; 294 | } 295 | 296 | static inline Tag tag_new() { 297 | Tag tag; 298 | tag.type = END_; 299 | tag.custom_tag_name = (String)array_new(); 300 | return tag; 301 | } 302 | 303 | static inline Tag tag_for_name(String name) { 304 | Tag tag = tag_new(); 305 | tag.type = tag_type_for_name(&name); 306 | if (tag.type == CUSTOM) { 307 | tag.custom_tag_name = name; 308 | } else { 309 | array_delete(&name); 310 | } 311 | return tag; 312 | } 313 | 314 | static inline void tag_free(Tag *self) { 315 | if (self->type == CUSTOM) { 316 | array_delete(&self->custom_tag_name); 317 | } 318 | } 319 | 320 | static inline bool tag_is_void(const Tag *self) { return self->type < END_OF_VOID_TAGS; } 321 | 322 | static inline bool tag_eq(const Tag *self, const Tag *other) { 323 | if (self->type != other->type) { 324 | return false; 325 | } 326 | if (self->type == CUSTOM) { 327 | if (self->custom_tag_name.size != other->custom_tag_name.size) { 328 | return false; 329 | } 330 | if (memcmp(self->custom_tag_name.contents, other->custom_tag_name.contents, self->custom_tag_name.size) != 0) { 331 | return false; 332 | } 333 | } 334 | return true; 335 | } 336 | 337 | static bool tag_can_contain(Tag *self, const Tag *other) { 338 | TagType child = other->type; 339 | 340 | switch (self->type) { 341 | case LI: 342 | return child != LI; 343 | 344 | case DT: 345 | case DD: 346 | return child != DT && child != DD; 347 | 348 | case P: 349 | for (int i = 0; i < 26; i++) { 350 | if (child == TAG_TYPES_NOT_ALLOWED_IN_PARAGRAPHS[i]) { 351 | return false; 352 | } 353 | } 354 | return true; 355 | 356 | case COLGROUP: 357 | return child == COL; 358 | 359 | case RB: 360 | case RT: 361 | case RP: 362 | return child != RB && child != RT && child != RP; 363 | 364 | case OPTGROUP: 365 | return child != OPTGROUP; 366 | 367 | case TR: 368 | return child != TR; 369 | 370 | case TD: 371 | case TH: 372 | return child != TD && child != TH && child != TR; 373 | 374 | default: 375 | return true; 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /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); 16 | extern void *(*ts_current_calloc)(size_t, size_t); 17 | extern void *(*ts_current_realloc)(void *, size_t); 18 | extern void (*ts_current_free)(void *); 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(disable : 4101) 18 | #elif defined(__GNUC__) || defined(__clang__) 19 | #pragma GCC diagnostic push 20 | #pragma GCC diagnostic ignored "-Wunused-variable" 21 | #endif 22 | 23 | #define Array(T) \ 24 | struct { \ 25 | T *contents; \ 26 | uint32_t size; \ 27 | uint32_t capacity; \ 28 | } 29 | 30 | /// Initialize an array. 31 | #define array_init(self) \ 32 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 33 | 34 | /// Create an empty array. 35 | #define array_new() \ 36 | { NULL, 0, 0 } 37 | 38 | /// Get a pointer to the element at a given `index` in the array. 39 | #define array_get(self, _index) \ 40 | (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) 41 | 42 | /// Get a pointer to the first element in the array. 43 | #define array_front(self) array_get(self, 0) 44 | 45 | /// Get a pointer to the last element in the array. 46 | #define array_back(self) array_get(self, (self)->size - 1) 47 | 48 | /// Clear the array, setting its size to zero. Note that this does not free any 49 | /// memory allocated for the array's contents. 50 | #define array_clear(self) ((self)->size = 0) 51 | 52 | /// Reserve `new_capacity` elements of space in the array. If `new_capacity` is 53 | /// less than the array's current capacity, this function has no effect. 54 | #define array_reserve(self, new_capacity) \ 55 | _array__reserve((Array *)(self), array_elem_size(self), new_capacity) 56 | 57 | /// Free any memory allocated for this array. Note that this does not free any 58 | /// memory allocated for the array's contents. 59 | #define array_delete(self) _array__delete((Array *)(self)) 60 | 61 | /// Push a new `element` onto the end of the array. 62 | #define array_push(self, element) \ 63 | (_array__grow((Array *)(self), 1, array_elem_size(self)), \ 64 | (self)->contents[(self)->size++] = (element)) 65 | 66 | /// Increase the array's size by `count` elements. 67 | /// New elements are zero-initialized. 68 | #define array_grow_by(self, count) \ 69 | do { \ 70 | if ((count) == 0) break; \ 71 | _array__grow((Array *)(self), count, array_elem_size(self)); \ 72 | memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ 73 | (self)->size += (count); \ 74 | } while (0) 75 | 76 | /// Append all elements from one array to the end of another. 77 | #define array_push_all(self, other) \ 78 | array_extend((self), (other)->size, (other)->contents) 79 | 80 | /// Append `count` elements to the end of the array, reading their values from the 81 | /// `contents` pointer. 82 | #define array_extend(self, count, contents) \ 83 | _array__splice( \ 84 | (Array *)(self), array_elem_size(self), (self)->size, \ 85 | 0, count, contents \ 86 | ) 87 | 88 | /// Remove `old_count` elements from the array starting at the given `index`. At 89 | /// the same index, insert `new_count` new elements, reading their values from the 90 | /// `new_contents` pointer. 91 | #define array_splice(self, _index, old_count, new_count, new_contents) \ 92 | _array__splice( \ 93 | (Array *)(self), array_elem_size(self), _index, \ 94 | old_count, new_count, new_contents \ 95 | ) 96 | 97 | /// Insert one `element` into the array at the given `index`. 98 | #define array_insert(self, _index, element) \ 99 | _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) 100 | 101 | /// Remove one element from the array at the given `index`. 102 | #define array_erase(self, _index) \ 103 | _array__erase((Array *)(self), array_elem_size(self), _index) 104 | 105 | /// Pop the last element off the array, returning the element by value. 106 | #define array_pop(self) ((self)->contents[--(self)->size]) 107 | 108 | /// Assign the contents of one array to another, reallocating if necessary. 109 | #define array_assign(self, other) \ 110 | _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) 111 | 112 | /// Swap one array with another 113 | #define array_swap(self, other) \ 114 | _array__swap((Array *)(self), (Array *)(other)) 115 | 116 | /// Get the size of the array contents 117 | #define array_elem_size(self) (sizeof *(self)->contents) 118 | 119 | /// Search a sorted array for a given `needle` value, using the given `compare` 120 | /// callback to determine the order. 121 | /// 122 | /// If an existing element is found to be equal to `needle`, then the `index` 123 | /// out-parameter is set to the existing value's index, and the `exists` 124 | /// out-parameter is set to true. Otherwise, `index` is set to an index where 125 | /// `needle` should be inserted in order to preserve the sorting, and `exists` 126 | /// is set to false. 127 | #define array_search_sorted_with(self, compare, needle, _index, _exists) \ 128 | _array__search_sorted(self, 0, compare, , needle, _index, _exists) 129 | 130 | /// Search a sorted array for a given `needle` value, using integer comparisons 131 | /// of a given struct field (specified with a leading dot) to determine the order. 132 | /// 133 | /// See also `array_search_sorted_with`. 134 | #define array_search_sorted_by(self, field, needle, _index, _exists) \ 135 | _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists) 136 | 137 | /// Insert a given `value` into a sorted array, using the given `compare` 138 | /// callback to determine the order. 139 | #define array_insert_sorted_with(self, compare, value) \ 140 | do { \ 141 | unsigned _index, _exists; \ 142 | array_search_sorted_with(self, compare, &(value), &_index, &_exists); \ 143 | if (!_exists) array_insert(self, _index, value); \ 144 | } while (0) 145 | 146 | /// Insert a given `value` into a sorted array, using integer comparisons of 147 | /// a given struct field (specified with a leading dot) to determine the order. 148 | /// 149 | /// See also `array_search_sorted_by`. 150 | #define array_insert_sorted_by(self, field, value) \ 151 | do { \ 152 | unsigned _index, _exists; \ 153 | array_search_sorted_by(self, field, (value) field, &_index, &_exists); \ 154 | if (!_exists) array_insert(self, _index, value); \ 155 | } while (0) 156 | 157 | // Private 158 | 159 | typedef Array(void) Array; 160 | 161 | /// This is not what you're looking for, see `array_delete`. 162 | static inline void _array__delete(Array *self) { 163 | if (self->contents) { 164 | ts_free(self->contents); 165 | self->contents = NULL; 166 | self->size = 0; 167 | self->capacity = 0; 168 | } 169 | } 170 | 171 | /// This is not what you're looking for, see `array_erase`. 172 | static inline void _array__erase(Array *self, size_t element_size, 173 | uint32_t index) { 174 | assert(index < self->size); 175 | char *contents = (char *)self->contents; 176 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 177 | (self->size - index - 1) * element_size); 178 | self->size--; 179 | } 180 | 181 | /// This is not what you're looking for, see `array_reserve`. 182 | static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { 183 | if (new_capacity > self->capacity) { 184 | if (self->contents) { 185 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 186 | } else { 187 | self->contents = ts_malloc(new_capacity * element_size); 188 | } 189 | self->capacity = new_capacity; 190 | } 191 | } 192 | 193 | /// This is not what you're looking for, see `array_assign`. 194 | static inline void _array__assign(Array *self, const Array *other, size_t element_size) { 195 | _array__reserve(self, element_size, other->size); 196 | self->size = other->size; 197 | memcpy(self->contents, other->contents, self->size * element_size); 198 | } 199 | 200 | /// This is not what you're looking for, see `array_swap`. 201 | static inline void _array__swap(Array *self, Array *other) { 202 | Array swap = *other; 203 | *other = *self; 204 | *self = swap; 205 | } 206 | 207 | /// This is not what you're looking for, see `array_push` or `array_grow_by`. 208 | static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { 209 | uint32_t new_size = self->size + count; 210 | if (new_size > self->capacity) { 211 | uint32_t new_capacity = self->capacity * 2; 212 | if (new_capacity < 8) new_capacity = 8; 213 | if (new_capacity < new_size) new_capacity = new_size; 214 | _array__reserve(self, element_size, new_capacity); 215 | } 216 | } 217 | 218 | /// This is not what you're looking for, see `array_splice`. 219 | static inline void _array__splice(Array *self, size_t element_size, 220 | uint32_t index, uint32_t old_count, 221 | uint32_t new_count, const void *elements) { 222 | uint32_t new_size = self->size + new_count - old_count; 223 | uint32_t old_end = index + old_count; 224 | uint32_t new_end = index + new_count; 225 | assert(old_end <= self->size); 226 | 227 | _array__reserve(self, element_size, new_size); 228 | 229 | char *contents = (char *)self->contents; 230 | if (self->size > old_end) { 231 | memmove( 232 | contents + new_end * element_size, 233 | contents + old_end * element_size, 234 | (self->size - old_end) * element_size 235 | ); 236 | } 237 | if (new_count > 0) { 238 | if (elements) { 239 | memcpy( 240 | (contents + index * element_size), 241 | elements, 242 | new_count * element_size 243 | ); 244 | } else { 245 | memset( 246 | (contents + index * element_size), 247 | 0, 248 | new_count * element_size 249 | ); 250 | } 251 | } 252 | self->size += new_count - old_count; 253 | } 254 | 255 | /// A binary search routine, based on Rust's `std::slice::binary_search_by`. 256 | /// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. 257 | #define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ 258 | do { \ 259 | *(_index) = start; \ 260 | *(_exists) = false; \ 261 | uint32_t size = (self)->size - *(_index); \ 262 | if (size == 0) break; \ 263 | int comparison; \ 264 | while (size > 1) { \ 265 | uint32_t half_size = size / 2; \ 266 | uint32_t mid_index = *(_index) + half_size; \ 267 | comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ 268 | if (comparison <= 0) *(_index) = mid_index; \ 269 | size -= half_size; \ 270 | } \ 271 | comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \ 272 | if (comparison == 0) *(_exists) = true; \ 273 | else if (comparison < 0) *(_index) += 1; \ 274 | } while (0) 275 | 276 | /// Helper macro for the `_sorted_by` routines below. This takes the left (existing) 277 | /// parameter by reference in order to work with the generic sorting function above. 278 | #define _compare_int(a, b) ((int)*(a) - (int)(b)) 279 | 280 | #ifdef _MSC_VER 281 | #pragma warning(default : 4101) 282 | #elif defined(__GNUC__) || defined(__clang__) 283 | #pragma GCC diagnostic pop 284 | #endif 285 | 286 | #ifdef __cplusplus 287 | } 288 | #endif 289 | 290 | #endif // TREE_SITTER_ARRAY_H_ 291 | -------------------------------------------------------------------------------- /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/balancing.txt: -------------------------------------------------------------------------------- 1 | ============================ 2 | Stray braces 3 | ============================ 4 |
  • {foo}x
  • 5 |
  • {"}" + 4}
  • 6 |
  • {`${`}`}` /* } bar */}
  • 7 | ------------------------ 8 | (document 9 | (element 10 | (start_tag 11 | (tag_name) 12 | ) 13 | (expression 14 | (svelte_raw_text) 15 | ) 16 | (text) 17 | (end_tag 18 | (tag_name) 19 | ) 20 | ) 21 | (element 22 | (start_tag 23 | (tag_name) 24 | ) 25 | (expression 26 | (svelte_raw_text) 27 | ) 28 | (end_tag 29 | (tag_name) 30 | ) 31 | ) 32 | (element 33 | (start_tag 34 | (tag_name) 35 | ) 36 | (expression 37 | (svelte_raw_text) 38 | ) 39 | (end_tag 40 | (tag_name) 41 | ) 42 | ) 43 | ) 44 | 45 | ============================ 46 | Line comments 47 | ============================ 48 | 52 | 53 | 57 | 58 |
    {width}x{height}
    59 | ------------------------ 60 | (document 61 | (script_element 62 | (start_tag (tag_name)) 63 | (raw_text) 64 | (end_tag (tag_name)) 65 | ) 66 | (element 67 | (self_closing_tag 68 | (tag_name) 69 | (attribute 70 | (attribute_name) 71 | (expression (svelte_raw_text)) 72 | ) 73 | ) 74 | ) 75 | (element 76 | (start_tag (tag_name)) 77 | (expression (svelte_raw_text)) 78 | (text) 79 | (expression (svelte_raw_text)) 80 | (end_tag (tag_name)) 81 | ) 82 | ) 83 | -------------------------------------------------------------------------------- /test/corpus/html/main.txt: -------------------------------------------------------------------------------- 1 | =================================== 2 | Tags 3 | =================================== 4 | Hello 5 | --- 6 | 7 | (document 8 | (element 9 | (start_tag 10 | (tag_name)) 11 | (text) 12 | (end_tag 13 | (tag_name)))) 14 | 15 | =================================== 16 | Tags with attributes 17 | =================================== 18 | 19 | --- 20 | 21 | (document 22 | (element 23 | (start_tag 24 | (tag_name) 25 | (attribute 26 | (attribute_name) 27 | (attribute_value)) 28 | (attribute 29 | (attribute_name) 30 | (quoted_attribute_value 31 | (attribute_value))) 32 | (attribute 33 | (attribute_name))) 34 | (end_tag 35 | (tag_name)))) 36 | 37 | =================================== 38 | Nested tags 39 | =================================== 40 |
    41 | a 42 | b 43 | c 44 | Multi-line 45 | text 46 |
    47 | --- 48 | 49 | (document 50 | (element 51 | (start_tag 52 | (tag_name)) 53 | (element 54 | (start_tag 55 | (tag_name)) 56 | (text) 57 | (end_tag 58 | (tag_name))) 59 | (text) 60 | (element 61 | (start_tag 62 | (tag_name)) 63 | (text) 64 | (end_tag 65 | (tag_name))) 66 | (text) 67 | (end_tag 68 | (tag_name)))) 69 | 70 | ================================== 71 | Void tags 72 | ================================== 73 |

    74 | --- 75 | 76 | (document 77 | (element 78 | (start_tag 79 | (tag_name)) 80 | (element 81 | (start_tag 82 | (tag_name) 83 | (attribute 84 | (attribute_name) 85 | (quoted_attribute_value 86 | (attribute_value))))) 87 | (element 88 | (start_tag 89 | (tag_name))) 90 | (element 91 | (self_closing_tag 92 | (tag_name) 93 | (attribute 94 | (attribute_name) 95 | (attribute_value)) 96 | (attribute 97 | (attribute_name) 98 | (attribute_value)))) 99 | (end_tag 100 | (tag_name)))) 101 | 102 | ================================== 103 | Void tags at EOF 104 | ================================== 105 | 106 | --- 107 | 108 | (document 109 | (element 110 | (start_tag 111 | (tag_name) 112 | (attribute 113 | (attribute_name) 114 | (quoted_attribute_value 115 | (attribute_value)))))) 116 | 117 | ================================== 118 | Custom tags 119 | ================================== 120 | 121 | 122 | 123 | Hello 124 | 125 | 126 | 127 | --- 128 | 129 | (document 130 | (element 131 | (start_tag 132 | (tag_name)) 133 | (element 134 | (start_tag 135 | (tag_name) 136 | (attribute 137 | (attribute_name))) 138 | (element 139 | (start_tag 140 | (tag_name)) 141 | (text) 142 | (end_tag 143 | (tag_name))) 144 | (end_tag 145 | (tag_name))) 146 | (end_tag 147 | (tag_name)))) 148 | 149 | ================================== 150 | Comments 151 | ================================== 152 | 153 | 154 |
    155 | 156 |
    157 | --- 158 | 159 | (document 160 | (comment) 161 | (comment) 162 | (element 163 | (start_tag 164 | (tag_name)) 165 | (comment) 166 | (end_tag 167 | (tag_name)))) 168 | 169 | ================================== 170 | Raw text elements 171 | ================================== 172 | 179 | 180 | 183 | 184 | 186 | 187 | --- 188 | 189 | (document 190 | (script_element 191 | (start_tag 192 | (tag_name)) 193 | (raw_text) 194 | (end_tag 195 | (tag_name))) 196 | (style_element 197 | (start_tag 198 | (tag_name)) 199 | (raw_text) 200 | (end_tag 201 | (tag_name))) 202 | (script_element 203 | (start_tag 204 | (tag_name)) 205 | (raw_text) 206 | (end_tag 207 | (tag_name))) 208 | (comment)) 209 | 210 | ================================== 211 | LI elements without close tags 212 | ================================== 213 |
      214 |
    • One 215 |
    • Two 216 |
    217 | --- 218 | 219 | (document 220 | (element 221 | (start_tag 222 | (tag_name)) 223 | (element 224 | (start_tag 225 | (tag_name)) 226 | (text)) 227 | (element 228 | (start_tag 229 | (tag_name)) 230 | (text)) 231 | (end_tag 232 | (tag_name)))) 233 | 234 | ====================================== 235 | DT and DL elements without close tags 236 | ====================================== 237 |
    238 |
    Coffee 239 |
    Café 240 |
    Black hot drink 241 |
    Milk 242 |
    White cold drink 243 |
    244 | --- 245 | 246 | (document 247 | (element 248 | (start_tag 249 | (tag_name)) 250 | (element 251 | (start_tag 252 | (tag_name)) 253 | (text)) 254 | (element 255 | (start_tag 256 | (tag_name)) 257 | (text)) 258 | (element 259 | (start_tag 260 | (tag_name)) 261 | (text)) 262 | (element 263 | (start_tag 264 | (tag_name)) 265 | (text)) 266 | (element 267 | (start_tag 268 | (tag_name)) 269 | (text)) 270 | (end_tag 271 | (tag_name)))) 272 | 273 | ====================================== 274 | P elements without close tags 275 | ====================================== 276 |

    One 277 |

    Two
    278 |

    Three 279 |

    Four 280 |

    Five

    281 | --- 282 | 283 | (document 284 | (element 285 | (start_tag 286 | (tag_name)) 287 | (text)) 288 | (element 289 | (start_tag 290 | (tag_name)) 291 | (text) 292 | (end_tag 293 | (tag_name))) 294 | (element 295 | (start_tag 296 | (tag_name)) 297 | (text)) 298 | (element 299 | (start_tag 300 | (tag_name)) 301 | (text)) 302 | (element 303 | (start_tag 304 | (tag_name)) 305 | (text) 306 | (end_tag 307 | (tag_name)))) 308 | 309 | ====================================== 310 | Ruby annotation elements without close tags 311 | ====================================== 312 | とうきょう 313 | --- 314 | 315 | (document 316 | (element 317 | (start_tag 318 | (tag_name)) 319 | (text) 320 | (element 321 | (start_tag 322 | (tag_name)) 323 | (text)) 324 | (element 325 | (start_tag 326 | (tag_name)) 327 | (text)) 328 | (element 329 | (start_tag 330 | (tag_name)) 331 | (text)) 332 | (end_tag 333 | (tag_name)))) 334 | 335 | ======================================= 336 | COLGROUP elements without end tags 337 | ======================================= 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 |
    LimeLemonOrange
    348 | --- 349 | 350 | (document 351 | (element 352 | (start_tag 353 | (tag_name)) 354 | (element 355 | (start_tag 356 | (tag_name)) 357 | (element 358 | (start_tag 359 | (tag_name) 360 | (attribute 361 | (attribute_name) 362 | (quoted_attribute_value 363 | (attribute_value))))) 364 | (element 365 | (start_tag 366 | (tag_name) 367 | (attribute 368 | (attribute_name) 369 | (quoted_attribute_value 370 | (attribute_value)))))) 371 | (element 372 | (start_tag 373 | (tag_name)) 374 | (element 375 | (start_tag 376 | (tag_name)) 377 | (text) 378 | (end_tag 379 | (tag_name))) 380 | (element 381 | (start_tag 382 | (tag_name)) 383 | (text) 384 | (end_tag 385 | (tag_name))) 386 | (element 387 | (start_tag 388 | (tag_name)) 389 | (text) 390 | (end_tag 391 | (tag_name))) 392 | (end_tag 393 | (tag_name))) 394 | (end_tag 395 | (tag_name)))) 396 | 397 | ========================================= 398 | TR, TD, and TH elements without end tags 399 | ========================================= 400 | 401 | 402 | 405 |
    One 403 | Two 404 |
    Three 406 | Four 407 |
    408 | --- 409 | 410 | (document 411 | (element 412 | (start_tag 413 | (tag_name)) 414 | (element 415 | (start_tag 416 | (tag_name)) 417 | (element 418 | (start_tag 419 | (tag_name)) 420 | (text)) 421 | (element 422 | (start_tag 423 | (tag_name)) 424 | (text))) 425 | (element 426 | (start_tag 427 | (tag_name)) 428 | (element 429 | (start_tag 430 | (tag_name)) 431 | (text)) 432 | (element 433 | (start_tag 434 | (tag_name)) 435 | (text))) 436 | (end_tag 437 | (tag_name)))) 438 | 439 | ============================== 440 | Named entities in tag contents 441 | ============================== 442 | 443 |

    Lorem ipsum   dolor sit © amet.

    444 | --- 445 | 446 | (document 447 | (element 448 | (start_tag 449 | (tag_name)) 450 | (text) 451 | (entity) 452 | (text) 453 | (entity) 454 | (text) 455 | (end_tag 456 | (tag_name)))) 457 | 458 | ================================ 459 | Numeric entities in tag contents 460 | ================================ 461 | 462 |

    Lorem ipsum   dolor sit — amet.

    463 | --- 464 | 465 | (document 466 | (element 467 | (start_tag 468 | (tag_name)) 469 | (text) 470 | (entity) 471 | (text) 472 | (entity) 473 | (text) 474 | (end_tag 475 | (tag_name)))) 476 | 477 | ================================= 478 | Multiple entities in tag contents 479 | ================================= 480 | 481 |

    Lorem ipsum   dolor   sit   amet.

    482 | --- 483 | 484 | (document 485 | (element 486 | (start_tag 487 | (tag_name)) 488 | (text) 489 | (entity) 490 | (text) 491 | (entity) 492 | (text) 493 | (entity) 494 | (text) 495 | (end_tag 496 | (tag_name)))) 497 | -------------------------------------------------------------------------------- /test/corpus/main.txt: -------------------------------------------------------------------------------- 1 | =================================== 2 | Simple example 3 | =================================== 4 | 11 | 12 | 16 | --- 17 | 18 | (document 19 | (script_element 20 | (start_tag 21 | (tag_name) 22 | ) 23 | (raw_text) 24 | (end_tag 25 | (tag_name) 26 | ) 27 | ) 28 | (element 29 | (start_tag 30 | (tag_name) 31 | (attribute 32 | (attribute_name) 33 | (expression (svelte_raw_text)) 34 | ) 35 | ) 36 | (text) 37 | (expression (svelte_raw_text)) 38 | (expression (svelte_raw_text)) 39 | (end_tag 40 | (tag_name) 41 | ) 42 | ) 43 | ) 44 | 45 | ================ 46 | Raw Text Expression 47 | ================ 48 | 51 |

    Hello {name'<>{}``"\''""``{}}!

    52 | ---- 53 | 54 | (document 55 | (script_element 56 | (start_tag (tag_name) 57 | (attribute (attribute_name) (quoted_attribute_value (attribute_value)))) 58 | (raw_text) 59 | (end_tag (tag_name))) 60 | (element 61 | (start_tag (tag_name)) 62 | (text) 63 | (expression (svelte_raw_text)) 64 | (text) 65 | (end_tag (tag_name))) 66 | ) 67 | 68 | ================ 69 | Expression with newlines 70 | ================ 71 |

    Hello { 72 | world 73 | }!

    74 | ---- 75 | 76 | (document 77 | (element 78 | (start_tag (tag_name)) 79 | (text) 80 | (expression (svelte_raw_text)) 81 | (text) 82 | (end_tag (tag_name))) 83 | ) 84 | 85 | ============== 86 | Dynamic Expression Attribute 87 | ============== 88 | A man dances 89 | 90 | ---- 91 | (document 92 | (element 93 | (start_tag (tag_name) 94 | (attribute (attribute_name) (expression (svelte_raw_text))) 95 | (attribute (attribute_name) (quoted_attribute_value (attribute_value))) 96 | ) 97 | 98 | ) 99 | (element 100 | (self_closing_tag 101 | (tag_name) 102 | (attribute (attribute_name (svelte_raw_text))) 103 | ) 104 | ) 105 | ) 106 | 107 | ============== 108 | Expressions inside string attribute value 109 | ============== 110 | {dancer} dances 111 | ---- 112 | (document 113 | (element 114 | (start_tag (tag_name) 115 | (attribute (attribute_name) (quoted_attribute_value (attribute_value (expression (svelte_raw_text)) (expression (svelte_raw_text))))) 116 | (attribute (attribute_name) (quoted_attribute_value (attribute_value (expression (svelte_raw_text))))) 117 | ) 118 | ) 119 | ) 120 | 121 | =============== 122 | logical_blocks 123 | =============== 124 | {#if user.loggedIn}hello 125 | 126 | {:else if hello} 127 |
  • 128 | {:else} 129 | {/if} 130 | 131 | {hello} 132 | {#each cats as cat} 133 |
  • 134 | {/each} 135 | 136 | {#each cats as cat (hello)} 137 |
    Hello
    138 | {:else} 139 |
  • Hello
  • 140 | {/each} 141 | 142 | {#each user.codes.filter(codeNotBlocked) as someItem (someItem.id)} 143 | {/each} 144 | 145 | { thenRandomExpr } 146 | { @html '

    hello world

    '} 147 | {{}} 148 | ------- 149 | (document 150 | (if_statement 151 | (if_start (block_start_tag) (svelte_raw_text)) 152 | (text) 153 | (element 154 | (start_tag 155 | (tag_name) 156 | (attribute (attribute_name) 157 | (expression (svelte_raw_text)) 158 | ) 159 | ) 160 | (text) 161 | (end_tag (tag_name)) 162 | ) 163 | (else_if_block 164 | (else_if_start (block_tag) (svelte_raw_text)) 165 | (element 166 | (start_tag (tag_name)) 167 | (end_tag (tag_name)) 168 | ) 169 | ) 170 | (else_block (else_start (block_tag))) 171 | (if_end (block_end_tag)) 172 | ) 173 | (expression (svelte_raw_text)) 174 | (each_statement 175 | (each_start 176 | (block_start_tag) 177 | (svelte_raw_text) 178 | (svelte_raw_text) 179 | ) 180 | (element (start_tag (tag_name)) (end_tag (tag_name))) 181 | (each_end (block_end_tag)) 182 | ) 183 | (each_statement 184 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 185 | (element (start_tag (tag_name)) (text) (end_tag (tag_name))) 186 | (else_block 187 | (else_start (block_tag)) 188 | ) 189 | (element (start_tag (tag_name)) (text) (end_tag (tag_name))) 190 | (each_end (block_end_tag)) 191 | ) 192 | (each_statement 193 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 194 | (each_end (block_end_tag)) 195 | ) 196 | (expression (svelte_raw_text)) 197 | (html_tag (expression_tag) (svelte_raw_text)) 198 | (expression (svelte_raw_text)) 199 | ) 200 | 201 | ========================== 202 | each (with unusual values) 203 | ========================== 204 | {#each "abcd" as letter} 205 | {letter} 206 | {/each} 207 | 208 | {#each ["a", "b"] as elem} 209 | {elem} 210 | {/each} 211 | 212 | {#each [{ foo: "bar" }, { baz: "thud" }] as pair} 213 | {elem} 214 | {/each} 215 | 216 | {#each [["a", "b"], ["c", "d"]] as pair} 217 | {#each pair as letter}{letter}{/each} 218 | {/each} 219 | 220 | {#each ["a", y, ...foo] as item} 221 | {item} 222 | {/each} 223 | 224 | {#each { foo: [1, 2, 3] }["foo"] as num} 225 | {num} 226 | {/each} 227 | 228 | ------- 229 | (document 230 | (each_statement 231 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 232 | (expression (svelte_raw_text)) 233 | (each_end (block_end_tag))) 234 | (each_statement 235 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 236 | (expression (svelte_raw_text)) 237 | (each_end (block_end_tag))) 238 | (each_statement 239 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 240 | (expression (svelte_raw_text)) 241 | (each_end (block_end_tag))) 242 | (each_statement 243 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 244 | (each_statement 245 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 246 | (expression (svelte_raw_text)) 247 | (each_end (block_end_tag))) 248 | (each_end (block_end_tag)) 249 | ) 250 | (each_statement 251 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 252 | (expression (svelte_raw_text)) 253 | (each_end (block_end_tag))) 254 | (each_statement 255 | (each_start (block_start_tag) (svelte_raw_text) (svelte_raw_text)) 256 | (expression (svelte_raw_text)) 257 | (each_end (block_end_tag))) 258 | ) 259 | 260 | ======================= 261 | await 262 | ======================== 263 | {#await promise} 264 | {:then number} 265 | {:catch error} 266 | {/await} 267 | 268 | {#await promise then value} 269 |

    270 | {/await} 271 | {#await yo thenvalue} 272 |

    273 | {:catch err} 274 |

    Error

    275 | {/await} 276 | ------ 277 | (document 278 | (await_statement 279 | (await_start (block_start_tag) (svelte_raw_text)) 280 | (then_block (then_start (block_tag) (svelte_raw_text))) 281 | (catch_block (catch_start (block_tag) (svelte_raw_text))) 282 | (await_end (block_end_tag)) 283 | ) 284 | (await_statement 285 | (await_start (block_start_tag) (svelte_raw_text)) 286 | (element (start_tag (tag_name)) (end_tag (tag_name))) 287 | (await_end (block_end_tag)) 288 | ) 289 | (await_statement 290 | (await_start (block_start_tag) (svelte_raw_text)) 291 | (element (start_tag (tag_name)) (end_tag (tag_name))) 292 | (catch_block (catch_start (block_tag) (svelte_raw_text)) 293 | (element (start_tag (tag_name)) (text) (end_tag (tag_name))) 294 | ) 295 | (await_end (block_end_tag)) 296 | ) 297 | ) 298 | 299 | ==================== 300 | Special elements 301 | ===================== 302 | 303 | 304 | 305 | 306 | ---- 307 | (document 308 | (element 309 | (start_tag (tag_name)) 310 | (element 311 | (self_closing_tag (tag_name) 312 | (attribute (attribute_name) (quoted_attribute_value (attribute_value))) 313 | (attribute (attribute_name) (quoted_attribute_value (attribute_value))) 314 | ) 315 | ) 316 | (end_tag (tag_name)) 317 | ) 318 | (element 319 | (self_closing_tag (tag_name) 320 | (attribute (attribute_name) 321 | (expression (svelte_raw_text)) 322 | ) 323 | ) 324 | ) 325 | ) 326 | 327 | ======================== 328 | if-else-nested 329 | ======================== 330 | {#if something} 331 | text 332 | {:else if someOtherThing} 333 | text2 334 | {#if something} 335 | text3 336 | {:else if someOtherThing} 337 | text4 338 | {:else} 339 | text5 340 | {/if} 341 | {:else} 342 | text6 343 | {/if} 344 | --- 345 | (document 346 | (if_statement 347 | (if_start (block_start_tag) (svelte_raw_text)) 348 | (text) 349 | (else_if_block 350 | (else_if_start (block_tag) (svelte_raw_text)) 351 | (text) 352 | (if_statement 353 | (if_start (block_start_tag) (svelte_raw_text)) 354 | (text) 355 | (else_if_block 356 | (else_if_start (block_tag) (svelte_raw_text)) 357 | (text) 358 | ) 359 | (else_block 360 | (else_start (block_tag)) 361 | (text) 362 | ) 363 | (if_end (block_end_tag)) 364 | ) 365 | ) 366 | (else_block 367 | (else_start (block_tag)) 368 | (text) 369 | ) 370 | (if_end (block_end_tag)) 371 | ) 372 | ) 373 | 374 | ======================== 375 | script-tag-inside-svelte:head 376 | ======================== 377 | 378 | 379 | 380 | --- 381 | (document 382 | (element 383 | (start_tag 384 | (tag_name)) 385 | (script_element 386 | (start_tag 387 | (tag_name)) 388 | (raw_text) 389 | (end_tag 390 | (tag_name))) 391 | (end_tag 392 | (tag_name)))) 393 | 394 | ======================== 395 | key statement 396 | ======================== 397 | {#key foo} 398 |
    bar
    399 | {/key} 400 | --- 401 | (document 402 | (key_statement 403 | (key_start 404 | (block_start_tag) 405 | (svelte_raw_text) 406 | ) 407 | (element 408 | (start_tag 409 | (tag_name) 410 | ) 411 | (text) 412 | (end_tag 413 | (tag_name) 414 | ) 415 | ) 416 | (key_end 417 | (block_end_tag) 418 | ) 419 | ) 420 | ) 421 | 422 | ===================== 423 | Dot Tags 424 | ===================== 425 | 426 | bar 427 | 428 | --------------------- 429 | (document 430 | (element 431 | (start_tag (tag_name)) 432 | (element 433 | (start_tag (tag_name)) 434 | (text) 435 | (end_tag (tag_name)) 436 | ) 437 | (end_tag (tag_name)) 438 | ) 439 | ) 440 | 441 | 442 | ======================== 443 | @const 444 | ======================== 445 | {@const a = a + 1} 446 | ------------------------ 447 | (document 448 | (const_tag (expression_tag) (svelte_raw_text)) 449 | ) 450 | 451 | ============================ 452 | @debug 453 | ============================ 454 |
    455 | {@debug foo} 456 |
    457 | ------------------------ 458 | (document 459 | (element 460 | (start_tag 461 | (tag_name)) 462 | (debug_tag 463 | (expression_tag) 464 | (svelte_raw_text)) 465 | (end_tag 466 | (tag_name))) 467 | ) 468 | -------------------------------------------------------------------------------- /test/corpus/snippets.txt: -------------------------------------------------------------------------------- 1 | ======================== 2 | Snippet 3 | ======================== 4 | {#snippet figure(image)} 5 |
    6 | {/snippet} 7 | ------------------------ 8 | (document 9 | (snippet_statement 10 | (snippet_start 11 | (block_start_tag) 12 | (snippet_name) 13 | (svelte_raw_text)) 14 | (element 15 | (start_tag 16 | (tag_name)) 17 | (end_tag 18 | (tag_name))) 19 | (snippet_end 20 | (block_end_tag))) 21 | ) 22 | 23 | ======================== 24 | Snippet (empty) 25 | ======================== 26 | {#snippet figure(image)} 27 | {/snippet} 28 | ------------------------ 29 | (document 30 | (snippet_statement 31 | (snippet_start 32 | (block_start_tag) 33 | (snippet_name) 34 | (svelte_raw_text) 35 | ) 36 | (snippet_end 37 | (block_end_tag) 38 | ) 39 | ) 40 | ) 41 | 42 | =========================== 43 | Snippet (without argument) 44 | =========================== 45 | {#snippet figure()} 46 |
    47 | {/snippet} 48 | ------------------------ 49 | (document 50 | (snippet_statement 51 | (snippet_start 52 | (block_start_tag) 53 | (snippet_name)) 54 | (element 55 | (start_tag 56 | (tag_name)) 57 | (end_tag 58 | (tag_name))) 59 | (snippet_end 60 | (block_end_tag))) 61 | ) 62 | 63 | ============================ 64 | Snippet (with destructuring) 65 | ============================ 66 | {#snippet figure({ src, caption, width, height })} 67 |
    68 | {/snippet} 69 | ------------------------ 70 | (document 71 | (snippet_statement 72 | (snippet_start 73 | (block_start_tag) 74 | (snippet_name) 75 | (svelte_raw_text)) 76 | (element 77 | (start_tag 78 | (tag_name)) 79 | (end_tag 80 | (tag_name))) 81 | (snippet_end 82 | (block_end_tag))) 83 | ) 84 | 85 | ============================ 86 | Snippet (with destructuring and default params) 87 | ============================ 88 | {#snippet figure({ src, caption = "a caption goes here :)", width = 20, height = 20, onclick = () => {} })} 89 |
    90 | {/snippet} 91 | ------------------------ 92 | (document 93 | (snippet_statement 94 | (snippet_start 95 | (block_start_tag) 96 | (snippet_name) 97 | (svelte_raw_text)) 98 | (element 99 | (start_tag 100 | (tag_name)) 101 | (end_tag 102 | (tag_name))) 103 | (snippet_end 104 | (block_end_tag))) 105 | ) 106 | 107 | ============================ 108 | Snippet (with nesting) 109 | ============================ 110 |
    111 | {#snippet x()} 112 | {#snippet y()} 113 | {/snippet} 114 | 115 | 116 | {@render y()} 117 | {/snippet} 118 | 119 | 120 | {@render y()} 121 |
    122 | ------------------------ 123 | (document 124 | (element 125 | (start_tag 126 | (tag_name)) 127 | (snippet_statement 128 | (snippet_start 129 | (block_start_tag) 130 | (snippet_name)) 131 | (snippet_statement 132 | (snippet_start 133 | (block_start_tag) 134 | (snippet_name)) 135 | (snippet_end 136 | (block_end_tag))) 137 | (comment) 138 | (render_tag 139 | (expression_tag) 140 | (svelte_raw_text)) 141 | (snippet_end 142 | (block_end_tag))) 143 | (comment) 144 | (render_tag 145 | (expression_tag) 146 | (svelte_raw_text)) 147 | (end_tag 148 | (tag_name))) 149 | ) 150 | 151 | ============================ 152 | Snippet (in component) 153 | ============================ 154 | 157 | 158 | 163 | ------------------------ 164 | (document 165 | (script_element 166 | (start_tag 167 | (tag_name)) 168 | (raw_text) 169 | (end_tag 170 | (tag_name))) 171 | (element 172 | (start_tag 173 | (tag_name) 174 | (attribute 175 | (attribute_name) 176 | (expression 177 | (svelte_raw_text)))) 178 | (snippet_statement 179 | (snippet_start 180 | (block_start_tag) 181 | (snippet_name) 182 | (svelte_raw_text)) 183 | (text) 184 | (expression 185 | (svelte_raw_text)) 186 | (snippet_end 187 | (block_end_tag))) 188 | (end_tag 189 | (tag_name)))) 190 | 191 | ============================ 192 | @render 193 | ============================ 194 |
    195 | {@render figure(image)} 196 |
    197 | ------------------------ 198 | (document 199 | (element 200 | (start_tag 201 | (tag_name)) 202 | (render_tag 203 | (expression_tag) 204 | (svelte_raw_text)) 205 | (end_tag 206 | (tag_name))) 207 | ) 208 | 209 | ============================ 210 | @render (without argument) 211 | ============================ 212 |
    213 | {@render figure()} 214 |
    215 | ------------------------ 216 | (document 217 | (element 218 | (start_tag 219 | (tag_name)) 220 | (render_tag 221 | (expression_tag) 222 | (svelte_raw_text)) 223 | (end_tag 224 | (tag_name))) 225 | ) 226 | 227 | ============================ 228 | @render (with complex argument) 229 | ============================ 230 |
    231 | {@render figure({ onclick: () => { 232 | /* heh :) */ 233 | console.log('clicked :)') 234 | }})} 235 |
    236 | ------------------------ 237 | (document 238 | (element 239 | (start_tag 240 | (tag_name)) 241 | (render_tag 242 | (expression_tag) 243 | (svelte_raw_text)) 244 | (end_tag 245 | (tag_name))) 246 | ) 247 | 248 | ============================ 249 | @render (with optional invocation) 250 | ============================ 251 |
    252 | {@render figure?.()} 253 |
    254 | ------------------------ 255 | (document 256 | (element 257 | (start_tag 258 | (tag_name)) 259 | (render_tag 260 | (expression_tag) 261 | (svelte_raw_text)) 262 | (end_tag 263 | (tag_name))) 264 | ) 265 | --------------------------------------------------------------------------------