@cavelang/tree-sitter-cave 0.5.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.md +6 -0
- package/README.md +33 -0
- package/grammar.js +155 -0
- package/package.json +41 -0
- package/queries/highlights.scm +31 -0
- package/src/grammar.json +747 -0
- package/src/node-types.json +652 -0
- package/src/parser.c +2580 -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/tree-sitter-cave.wasm +0 -0
- package/tree-sitter.json +38 -0
package/License.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
|
2
|
+
|
|
3
|
+
To the extent possible under law, the authors listed in [Authors.md](./Authors.md) have waived all copyright and related or neighboring rights to this software and associated documentation files (the "Software").
|
|
4
|
+
|
|
5
|
+
For more information, please see:
|
|
6
|
+
https://creativecommons.org/publicdomain/zero/1.0/
|
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @cavelang/tree-sitter-cave
|
|
2
|
+
|
|
3
|
+
Tree-sitter grammar for [CAVE](https://github.com/mirek/cave) — the
|
|
4
|
+
canonical grammar artifact behind editor and terminal highlighting.
|
|
5
|
+
|
|
6
|
+
The grammar is line-oriented (one claim per physical line, spec §16), so it
|
|
7
|
+
needs no external scanner: indentation is skipped and qualifier/continuation
|
|
8
|
+
lines are recognized by their leading verb. Parent attachment (spec §8) is
|
|
9
|
+
semantic and left to consumers such as `@cavelang/parser`.
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
- `grammar.js` — the grammar (spec §16, §3–§8)
|
|
14
|
+
- `queries/highlights.scm` — highlight captures (nvim/helix vocabulary);
|
|
15
|
+
the single source used by `@cavelang/highlight` (terminal ANSI) and the
|
|
16
|
+
CAVE VSCode extension (semantic tokens)
|
|
17
|
+
- `src/` (generated parser) and `tree-sitter-cave.wasm` are **not
|
|
18
|
+
committed** — generated artifacts are unauditable, so `pnpm build`
|
|
19
|
+
produces both on demand (tree-sitter-cli fetches wasi-sdk itself; no
|
|
20
|
+
emscripten or docker). They ship inside the npm tarball via `prepack`,
|
|
21
|
+
and `pnpm test` builds them before running the corpus.
|
|
22
|
+
|
|
23
|
+
## Consuming
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { Language, Parser } from 'web-tree-sitter'
|
|
27
|
+
|
|
28
|
+
const wasm = new URL(import.meta.resolve('@cavelang/tree-sitter-cave/wasm'))
|
|
29
|
+
const language = await Language.load(wasm.pathname)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Editors that consume tree-sitter directly (Neovim, Helix, Zed) point at this
|
|
33
|
+
directory; the grammar name is `cave` and file type is `.cave`.
|
package/grammar.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter grammar for CAVE (spec §16, §3–§8).
|
|
3
|
+
*
|
|
4
|
+
* Line-oriented: newlines are structural (one claim per physical line), so
|
|
5
|
+
* no external scanner is needed. Spaces and tabs are extras — indentation is
|
|
6
|
+
* skipped, and qualifier/continuation lines are recognized by their leading
|
|
7
|
+
* verb; parent attachment (spec §8) is left to consumers.
|
|
8
|
+
*
|
|
9
|
+
* Lexical disambiguation mirrors spec §4.3: `@` + space is confidence, `@` +
|
|
10
|
+
* no space is context; `#` begins a tag; a trailing `:` turns a word into an
|
|
11
|
+
* attribute; digit-led words are values. Verb-shaped words win over entities
|
|
12
|
+
* by token precedence, so `CONTAINS REVERSE PART-OF` parses as a claim with
|
|
13
|
+
* a verb-shaped subject; ties between that shape and a continuation line
|
|
14
|
+
* (spec §8.3) go to the claim via dynamic precedence, matching the
|
|
15
|
+
* @cavelang/parser tiebreak for lines like `API NEEDS auth`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const NL = /\r?\n/
|
|
19
|
+
|
|
20
|
+
module.exports = grammar({
|
|
21
|
+
name: 'cave',
|
|
22
|
+
|
|
23
|
+
extras: () => [/[ \t]/],
|
|
24
|
+
|
|
25
|
+
rules: {
|
|
26
|
+
document: $ => seq(optional($._line), repeat(seq(NL, optional($._line)))),
|
|
27
|
+
|
|
28
|
+
_line: $ => choice(
|
|
29
|
+
$.comment_line,
|
|
30
|
+
$.qualifier_line,
|
|
31
|
+
$.claim_line,
|
|
32
|
+
$.continuation_line
|
|
33
|
+
),
|
|
34
|
+
|
|
35
|
+
comment_line: $ => $.comment,
|
|
36
|
+
|
|
37
|
+
claim_line: $ => seq(
|
|
38
|
+
field('subject', choice($._term, alias($.verb, $.entity))),
|
|
39
|
+
$._body
|
|
40
|
+
),
|
|
41
|
+
|
|
42
|
+
// Bare relational verb; the subject is inherited from the parent (§8.3).
|
|
43
|
+
continuation_line: $ => prec.dynamic(-1, $._body),
|
|
44
|
+
|
|
45
|
+
_body: $ => choice(
|
|
46
|
+
prec.right(seq(
|
|
47
|
+
field('verb', $.verb),
|
|
48
|
+
optional($.negation),
|
|
49
|
+
field('payload', $._payload),
|
|
50
|
+
repeat($._meta),
|
|
51
|
+
optional($.comment)
|
|
52
|
+
)),
|
|
53
|
+
// Bare existence — the only payload-less shape (§16).
|
|
54
|
+
prec.right(seq(
|
|
55
|
+
field('verb', alias('EXISTS', $.verb)),
|
|
56
|
+
optional($.negation),
|
|
57
|
+
repeat($._meta),
|
|
58
|
+
optional($.comment)
|
|
59
|
+
))
|
|
60
|
+
),
|
|
61
|
+
|
|
62
|
+
qualifier_line: $ => seq(
|
|
63
|
+
field('verb', $.qualifier_verb),
|
|
64
|
+
optional($.negation),
|
|
65
|
+
field('payload', $._qualifier_payload),
|
|
66
|
+
repeat($._meta),
|
|
67
|
+
optional($.comment)
|
|
68
|
+
),
|
|
69
|
+
|
|
70
|
+
qualifier_verb: () => choice('WHEN', 'UNLESS', 'VIA', 'BECAUSE'),
|
|
71
|
+
|
|
72
|
+
negation: () => 'NOT',
|
|
73
|
+
|
|
74
|
+
// Payload (§16): attribute/value, metric value, or relational object.
|
|
75
|
+
_payload: $ => choice($.attr_value, $.value, $.object),
|
|
76
|
+
|
|
77
|
+
attr_value: $ => seq(
|
|
78
|
+
field('attribute', $.attribute),
|
|
79
|
+
field('value', choice($.value, $._term))
|
|
80
|
+
),
|
|
81
|
+
|
|
82
|
+
// `20B USD/yr`, `30ms`, `~1000 req/s`, `2026-Q1` — number then unit (§7.1).
|
|
83
|
+
value: $ => seq($.number, optional(alias($.entity, $.unit))),
|
|
84
|
+
|
|
85
|
+
object: $ => choice(
|
|
86
|
+
$.string,
|
|
87
|
+
$.code,
|
|
88
|
+
seq($.entity, repeat(choice($.entity, alias($.verb, $.entity)))),
|
|
89
|
+
alias($.verb, $.entity)
|
|
90
|
+
),
|
|
91
|
+
|
|
92
|
+
// Qualifier payload (§8.2): comparison, nested claim, or bare condition.
|
|
93
|
+
_qualifier_payload: $ => choice(
|
|
94
|
+
$.comparison,
|
|
95
|
+
seq(field('subject', $._term), $._body),
|
|
96
|
+
$._term
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
comparison: $ => seq(
|
|
100
|
+
field('left', $._term),
|
|
101
|
+
field('operator', $.comparison_op),
|
|
102
|
+
field('right', choice($.value, $._term))
|
|
103
|
+
),
|
|
104
|
+
|
|
105
|
+
comparison_op: () => choice('>', '<', '>=', '<=', '=', '!='),
|
|
106
|
+
|
|
107
|
+
_term: $ => choice($.entity, $.string, $.code),
|
|
108
|
+
|
|
109
|
+
_meta: $ => choice(
|
|
110
|
+
$.context,
|
|
111
|
+
$.confidence,
|
|
112
|
+
$.tag,
|
|
113
|
+
$.uncertainty,
|
|
114
|
+
$.sigma,
|
|
115
|
+
$.importance
|
|
116
|
+
),
|
|
117
|
+
|
|
118
|
+
// `+/- 2B USD/yr` — symmetric value uncertainty (§7.2).
|
|
119
|
+
uncertainty: $ => seq('+/-', $.value),
|
|
120
|
+
|
|
121
|
+
tag: $ => seq(
|
|
122
|
+
'#',
|
|
123
|
+
field('key', alias(token.immediate(/[^ \t\r\n;:]+/), $.tag_key)),
|
|
124
|
+
optional(seq(
|
|
125
|
+
token.immediate(':'),
|
|
126
|
+
field('value', alias(token.immediate(/[^ \t\r\n;]+/), $.tag_value))
|
|
127
|
+
))
|
|
128
|
+
),
|
|
129
|
+
|
|
130
|
+
// Declared before `entity` — the earlier rule wins the all-uppercase tie;
|
|
131
|
+
// longer mixed-case words (`OpenAI`) still lex as entities by length, and
|
|
132
|
+
// keywords (`NOT`, `WHEN`, …) win as strings over this regex.
|
|
133
|
+
verb: () => /[A-Z][A-Z-]*/,
|
|
134
|
+
|
|
135
|
+
entity: () => /[A-Za-z_][A-Za-z0-9_./-]*/,
|
|
136
|
+
|
|
137
|
+
attribute: () => token(/[A-Za-z_][A-Za-z0-9_./-]*:/),
|
|
138
|
+
|
|
139
|
+
number: () => /~?[0-9][0-9A-Za-z.,%_-]*/,
|
|
140
|
+
|
|
141
|
+
string: () => token(seq('"', /[^"\r\n]*/, '"')),
|
|
142
|
+
|
|
143
|
+
code: () => token(seq('`', /[^`\r\n]*/, '`')),
|
|
144
|
+
|
|
145
|
+
context: () => token(/@[^ \t\r\n;]+/),
|
|
146
|
+
|
|
147
|
+
confidence: () => token(/@[ \t]+[0-9]+(\.[0-9]+)?%/),
|
|
148
|
+
|
|
149
|
+
sigma: () => token(/\([0-9]+(\.[0-9]+)?σ\)/),
|
|
150
|
+
|
|
151
|
+
importance: () => '!',
|
|
152
|
+
|
|
153
|
+
comment: () => token(/;[^\r\n]*/)
|
|
154
|
+
}
|
|
155
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cavelang/tree-sitter-cave",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Tree-sitter grammar for CAVE — the canonical grammar artifact behind editor and terminal highlighting.",
|
|
5
|
+
"license": "CC0-1.0",
|
|
6
|
+
"author": "Mirek Rusin (https://github.com/mirek)",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/mirek/cave.git",
|
|
10
|
+
"directory": "packages/tree-sitter-cave"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22.18"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
"./wasm": "./tree-sitter-cave.wasm",
|
|
17
|
+
"./highlights": "./queries/highlights.scm",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"grammar.js",
|
|
22
|
+
"tree-sitter.json",
|
|
23
|
+
"src",
|
|
24
|
+
"queries",
|
|
25
|
+
"tree-sitter-cave.wasm",
|
|
26
|
+
"README.md",
|
|
27
|
+
"License.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"tree-sitter-cli": "^0.26.10"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"generate": "tree-sitter generate",
|
|
37
|
+
"build": "tree-sitter generate && tree-sitter build --wasm -o tree-sitter-cave.wasm",
|
|
38
|
+
"test": "pnpm run build && tree-sitter test",
|
|
39
|
+
"typecheck": "true"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
; CAVE highlighting (spec §3–§8). Capture names follow the common
|
|
2
|
+
; nvim/helix vocabulary; one capture per node so query order does not
|
|
3
|
+
; matter across editors.
|
|
4
|
+
|
|
5
|
+
(comment) @comment
|
|
6
|
+
|
|
7
|
+
(verb) @keyword
|
|
8
|
+
(qualifier_verb) @keyword
|
|
9
|
+
(negation) @keyword.operator
|
|
10
|
+
|
|
11
|
+
(entity) @variable
|
|
12
|
+
(unit) @type
|
|
13
|
+
|
|
14
|
+
(attribute) @property
|
|
15
|
+
|
|
16
|
+
(number) @number
|
|
17
|
+
(string) @string
|
|
18
|
+
(code) @string.special
|
|
19
|
+
|
|
20
|
+
(context) @label
|
|
21
|
+
(confidence) @constant
|
|
22
|
+
(sigma) @constant
|
|
23
|
+
|
|
24
|
+
(comparison_op) @operator
|
|
25
|
+
(importance) @operator
|
|
26
|
+
(uncertainty "+/-" @operator)
|
|
27
|
+
|
|
28
|
+
(tag "#" @punctuation.special)
|
|
29
|
+
(tag ":" @punctuation.delimiter)
|
|
30
|
+
(tag_key) @tag
|
|
31
|
+
(tag_value) @constant
|