tree-sitter-glsl-spec 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/binding.gyp +21 -0
- package/bindings/node/binding.cc +20 -0
- package/bindings/node/binding_test.js +11 -0
- package/bindings/node/index.d.ts +91 -0
- package/bindings/node/index.js +39 -0
- package/grammar.js +2932 -0
- package/package.json +71 -0
- package/queries/constructor-heuristics.scm +7 -0
- package/queries/highlights.in +112 -0
- package/queries/highlights.scm +486 -0
- package/queries/injections.scm +5 -0
- package/queries/locals.scm +34 -0
- package/queries/tags.scm +50 -0
- package/queries/version-tags.scm +150 -0
- package/src/grammar.json +7839 -0
- package/src/node-types.json +5807 -0
- package/src/parser.c +237532 -0
- package/src/tree_sitter/alloc.h +54 -0
- package/src/tree_sitter/array.h +330 -0
- package/src/tree_sitter/parser.h +286 -0
- package/src/tree_sitter/runtime.h +112 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tin Švagelj
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# tree-sitter-glsl
|
|
2
|
+
|
|
3
|
+
A [specification](https://github.com/KhronosGroup/GLSL)-compliant GLSL and ESSL
|
|
4
|
+
parser for [tree-sitter](https://tree-sitter.github.io/) covering the full GLSL
|
|
5
|
+
4.60 core grammar, all current Khronos extensions, and common real-world macro
|
|
6
|
+
patterns.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Full GLSL 4.60 and ESSL 3.20 grammar
|
|
11
|
+
- All Khronos extension keywords, types, built-in functions, and variables
|
|
12
|
+
- Structured preprocessor parsing (`#define`, `#if`/`#ifdef`/`#elif`/`#else`)
|
|
13
|
+
- Macro expansion support for real-world shaders (`COMPAT_PRECISION`, `MYTYPE(args)`)
|
|
14
|
+
- Multilingual header support (`#ifdef __cplusplus` language injection)
|
|
15
|
+
- Extension-defined grammar rules (`demote`, `terminateInvocation`)
|
|
16
|
+
|
|
17
|
+
## Queries
|
|
18
|
+
|
|
19
|
+
| File | Purpose |
|
|
20
|
+
|------|---------|
|
|
21
|
+
| `highlights.scm` | Syntax highlighting with built-in function/variable/constant recognition |
|
|
22
|
+
| `injections.scm` | Language injection for `#ifdef __cplusplus` / `__STDC__` guards |
|
|
23
|
+
| `locals.scm` | Scope-aware local variable tracking |
|
|
24
|
+
| `tags.scm` | Symbol indexing for code navigation |
|
|
25
|
+
|-|-|
|
|
26
|
+
| `version-tags.scm` | `#version`-dependent construct validation |
|
|
27
|
+
| `constructor-heuristics.scm` | Opt-in heuristic for capitalized constructor calls |
|
|
28
|
+
|
|
29
|
+
`version-tags.scm` and `constructor-heuristics.scm` are custom selectors,
|
|
30
|
+
that might be useful in some cases, but you likely don't need them.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Node.js
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const Parser = require('tree-sitter');
|
|
38
|
+
const GLSL = require('tree-sitter-glsl-spec');
|
|
39
|
+
|
|
40
|
+
const parser = new Parser();
|
|
41
|
+
parser.setLanguage(GLSL);
|
|
42
|
+
const tree = parser.parse('void main() { gl_FragColor = vec4(1.0); }');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Rust
|
|
46
|
+
|
|
47
|
+
```rust
|
|
48
|
+
let mut parser = tree_sitter::Parser::new();
|
|
49
|
+
parser.set_language(&tree_sitter_glsl_spec::LANGUAGE.into()).unwrap();
|
|
50
|
+
let tree = parser.parse("void main() { gl_FragColor = vec4(1.0); }", None).unwrap();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Python
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import tree_sitter_glsl_spec as glsl
|
|
57
|
+
from tree_sitter import Parser
|
|
58
|
+
|
|
59
|
+
parser = Parser(glsl.language())
|
|
60
|
+
tree = parser.parse(b"void main() { gl_FragColor = vec4(1.0); }")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Development
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install # Install dependencies
|
|
67
|
+
npx tree-sitter generate # Regenerate parser from grammar.js
|
|
68
|
+
npx tree-sitter test # Run corpus tests
|
|
69
|
+
npx eslint # Lint
|
|
70
|
+
npx tsc --noEmit # Type check
|
|
71
|
+
node scripts/audit_node_types.js --coverage # Grammar coverage analysis
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The specification is included as a [git submodule](https://github.com/KhronosGroup/GLSL).
|
|
75
|
+
Run `git submodule update --init` to fetch it for spec-scraping scripts.
|
|
76
|
+
|
|
77
|
+
## References
|
|
78
|
+
|
|
79
|
+
- Specification Document: [Khronos - The OpenGL Shading Language, Version 4.60](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf)
|
|
80
|
+
- Specification Repository (BNF): [GitHub - KhronosGroup/GLSL](https://github.com/KhronosGroup/GLSL)
|
|
81
|
+
|
|
82
|
+
## Background
|
|
83
|
+
|
|
84
|
+
This project began as a fork of [tree-sitter-glsl](https://github.com/tree-sitter-grammars/tree-sitter-glsl)
|
|
85
|
+
by Stephan Seitz, which inherited its grammar from
|
|
86
|
+
[tree-sitter-c](https://github.com/tree-sitter/tree-sitter-c). After running into
|
|
87
|
+
too many quirks from the C grammar inheritance, I rewrote the grammar from scratch
|
|
88
|
+
against the GLSL 4.60 specification.
|
|
89
|
+
|
|
90
|
+
### Why not inherit from tree-sitter-c?
|
|
91
|
+
|
|
92
|
+
GLSL's syntax is C-like but substantially simpler. A dedicated grammar provides:
|
|
93
|
+
|
|
94
|
+
- **Fewer ambiguities** — 1 GLR conflict (without macros) vs C's 16. No pointers,
|
|
95
|
+
no K&R functions, no multi-word types, no `sizeof`/`typeof`, no casts.
|
|
96
|
+
- **Flat expression trees** — identifiers and literals appear directly in the tree
|
|
97
|
+
without the `unary_expression > postfix_expression > primary_expression` wrapper
|
|
98
|
+
chain that a C-inherited grammar produces.
|
|
99
|
+
- **GLSL-native nodes** — layout qualifiers with `name:`/`value:` fields, precision
|
|
100
|
+
qualifiers, interface blocks, `demote`/`terminateInvocation` extension statements.
|
|
101
|
+
- **Specification fidelity** — every BNF production from the spec exists as a
|
|
102
|
+
(hidden or visible) rule, documented with the original grammar notation.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
This project is licensed under the [MIT](./LICENSE) license.
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "tree_sitter_glsl_binding",
|
|
5
|
+
"dependencies": [
|
|
6
|
+
"<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except",
|
|
7
|
+
],
|
|
8
|
+
"include_dirs": [
|
|
9
|
+
"src",
|
|
10
|
+
],
|
|
11
|
+
"sources": [
|
|
12
|
+
"bindings/node/binding.cc",
|
|
13
|
+
"src/parser.c",
|
|
14
|
+
# NOTE: if your language has an external scanner, add it here.
|
|
15
|
+
],
|
|
16
|
+
"cflags_c": [
|
|
17
|
+
"-std=c11",
|
|
18
|
+
],
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
|
|
3
|
+
typedef struct TSLanguage TSLanguage;
|
|
4
|
+
|
|
5
|
+
extern "C" TSLanguage *tree_sitter_glsl();
|
|
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, "glsl");
|
|
14
|
+
auto language = Napi::External<TSLanguage>::New(env, tree_sitter_glsl());
|
|
15
|
+
language.TypeTag(&LANGUAGE_TYPE_TAG);
|
|
16
|
+
exports["language"] = language;
|
|
17
|
+
return exports;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
NODE_API_MODULE(tree_sitter_glsl_binding, Init)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import Parser from "tree-sitter";
|
|
4
|
+
|
|
5
|
+
test("can load grammar", () => {
|
|
6
|
+
const parser = new Parser();
|
|
7
|
+
assert.doesNotReject(async () => {
|
|
8
|
+
const { default: language } = await import("./index.js");
|
|
9
|
+
parser.setLanguage(language);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
/**
|
|
22
|
+
* The tree-sitter language object for this grammar.
|
|
23
|
+
*
|
|
24
|
+
* @see {@linkcode https://tree-sitter.github.io/node-tree-sitter/interfaces/Parser.Language.html Parser.Language}
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* import Parser from "tree-sitter";
|
|
28
|
+
* import Glsl from "tree-sitter-glsl-spec";
|
|
29
|
+
*
|
|
30
|
+
* const parser = new Parser();
|
|
31
|
+
* parser.setLanguage(Glsl);
|
|
32
|
+
*/
|
|
33
|
+
declare const binding: {
|
|
34
|
+
/**
|
|
35
|
+
* The inner language object.
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
language: unknown;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The content of the `node-types.json` file for this grammar.
|
|
42
|
+
*
|
|
43
|
+
* @see {@linkplain https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types Static Node Types}
|
|
44
|
+
*/
|
|
45
|
+
nodeTypeInfo: NodeInfo[];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Syntax highlighting query. Maps GLSL nodes to highlight capture names
|
|
49
|
+
* (`@keyword`, `@function`, `@type`, `@operator`, `@variable`, etc.).
|
|
50
|
+
* Includes built-in function/variable/constant recognition.
|
|
51
|
+
*/
|
|
52
|
+
HIGHLIGHTS_QUERY?: string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Language injection query. Identifies `#ifdef __cplusplus` / `#ifdef __STDC__`
|
|
56
|
+
* guard blocks and marks their foreign-language content for injection
|
|
57
|
+
* (`cpp` or `c`).
|
|
58
|
+
*
|
|
59
|
+
* Requires `OPT.MULTILINGUAL` to be enabled in grammar.js before the C
|
|
60
|
+
* sources are generated.
|
|
61
|
+
*/
|
|
62
|
+
INJECTIONS_QUERY?: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Local variable tracking query. Defines scopes (functions, blocks, loops)
|
|
66
|
+
* and tracks identifier definitions and references within them.
|
|
67
|
+
*/
|
|
68
|
+
LOCALS_QUERY?: string;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Tags query for code navigation. Captures function definitions, type
|
|
72
|
+
* definitions, variable declarations, function calls, and type references.
|
|
73
|
+
*/
|
|
74
|
+
TAGS_QUERY?: string;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Version tags query. Captures GLSL constructs whose availability depends
|
|
78
|
+
* on a minimum `#version` (e.g., `uint` requires 130, `double` requires 400).
|
|
79
|
+
* Intended for post-parse validation, not syntax highlighting.
|
|
80
|
+
*/
|
|
81
|
+
VERSION_TAGS_QUERY?: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Constructor heuristic query. Identifies function calls where the callee
|
|
85
|
+
* is a user-defined type name (capitalized identifier), which are likely
|
|
86
|
+
* struct constructors. Opt-in — not included in default highlighting.
|
|
87
|
+
*/
|
|
88
|
+
CONSTRUCTOR_HEURISTICS_QUERY?: string;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default binding;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
const root = fileURLToPath(new URL("../..", import.meta.url));
|
|
5
|
+
|
|
6
|
+
const binding = typeof process.versions.bun === "string"
|
|
7
|
+
// Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time
|
|
8
|
+
? await import(`${root}/prebuilds/${process.platform}-${process.arch}/tree-sitter-glsl.node`)
|
|
9
|
+
: (await import("node-gyp-build")).default(root);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const nodeTypes = await import(`${root}/src/node-types.json`, { with: { type: "json" } });
|
|
13
|
+
binding.nodeTypeInfo = nodeTypes.default;
|
|
14
|
+
} catch { }
|
|
15
|
+
|
|
16
|
+
const queries = [
|
|
17
|
+
["HIGHLIGHTS_QUERY", `${root}/queries/highlights.scm`],
|
|
18
|
+
["INJECTIONS_QUERY", `${root}/queries/injections.scm`],
|
|
19
|
+
["LOCALS_QUERY", `${root}/queries/locals.scm`],
|
|
20
|
+
["TAGS_QUERY", `${root}/queries/tags.scm`],
|
|
21
|
+
["VERSION_TAGS_QUERY", `${root}/queries/version-tags.scm`],
|
|
22
|
+
["CONSTRUCTOR_HEURISTICS_QUERY", `${root}/queries/constructor-heuristics.scm`],
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const [prop, path] of queries) {
|
|
26
|
+
Object.defineProperty(binding, prop, {
|
|
27
|
+
configurable: true,
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get() {
|
|
30
|
+
delete binding[prop];
|
|
31
|
+
try {
|
|
32
|
+
binding[prop] = readFileSync(path, "utf8");
|
|
33
|
+
} catch { }
|
|
34
|
+
return binding[prop];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default binding;
|