@willbooster/tree-sitter-haskell 8.2.0 → 8.3.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/README.md +6 -2
- package/grammar/data.js +4 -4
- package/grammar/id.js +2 -0
- package/grammar/lexeme.js +7 -7
- package/grammar/literal.js +5 -2
- package/grammar.js +2 -2
- package/hsc/grammar.js +69 -0
- package/hsc/queries/highlights.scm +1 -0
- package/hsc/queries/injections.scm +2 -0
- package/hsc/src/node-types.json +6941 -0
- package/package.json +5 -2
- package/tree-sitter-haskell.wasm +0 -0
- package/tree-sitter-hsc.wasm +0 -0
- package/tree-sitter.json +20 -1
package/README.md
CHANGED
|
@@ -79,6 +79,8 @@ parser.setLanguage(await Language.load(haskell));
|
|
|
79
79
|
|
|
80
80
|
The package also ships `grammar.js` with the files in `grammar/` it loads, the queries in `queries/`, and the node types in `src/node-types.json`.
|
|
81
81
|
|
|
82
|
+
For [hsc2hs](https://github.com/haskell/hsc2hs) source in `.hsc` files, load `tree-sitter-hsc.wasm` instead. This dialect parses C directives and the doubled hashes that hsc2hs turns into literal hashes. Its node schema is `hsc/src/node-types.json`. Combine the ordinary queries with the matching suffixes in `hsc/queries/` for directive highlighting and C-body injections.
|
|
83
|
+
|
|
82
84
|
For code navigation, use `queries/tags.scm` (or `TAGS_QUERY` in Rust). It indexes module headers, top-level named functions and bindings, named types and classes, class and instance methods, and syntactic variable/operator calls. A signature and each equation have separate definition locations. Call references identify the called name without resolving its binding.
|
|
83
85
|
|
|
84
86
|
Declaration names and variable/operator callees remain indexed through nested parentheses. Class and instance equations retain their method roles; grouping a value without applying it does not create a call reference. Destructuring bindings contribute their explicit names at every nesting depth, including as-pattern binders and record puns. Local pattern bindings, names bound by patterns inside declaration quotes and constructors (including pattern synonyms) are not indexed.
|
|
@@ -96,7 +98,7 @@ fuzzed with:
|
|
|
96
98
|
```toml
|
|
97
99
|
[dependencies]
|
|
98
100
|
tree-sitter = { package = "willbooster-tree-sitter", version = "7.0.5" }
|
|
99
|
-
tree-sitter-haskell = { package = "willbooster-tree-sitter-haskell", version = "
|
|
101
|
+
tree-sitter-haskell = { package = "willbooster-tree-sitter-haskell", version = "8" }
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
```rust
|
|
@@ -104,6 +106,8 @@ let mut parser = tree_sitter::Parser::new();
|
|
|
104
106
|
parser.set_language(&tree_sitter_haskell::LANGUAGE.into())?;
|
|
105
107
|
```
|
|
106
108
|
|
|
109
|
+
For `.hsc` files in Rust, select `HSC_LANGUAGE` and `HSC_NODE_TYPES`. Use `HSC_HIGHLIGHTS_QUERY` and `HSC_INJECTIONS_QUERY` for the combined queries; `TAGS_QUERY` and `LOCALS_QUERY` are shared with ordinary Haskell.
|
|
110
|
+
|
|
107
111
|
## Development
|
|
108
112
|
|
|
109
113
|
```sh
|
|
@@ -125,7 +129,7 @@ well (e.g. `script/tree-sitter parse file.hs`).
|
|
|
125
129
|
`bun run generate` records a fresh generation profile from the applicable `test/corpus` cases and Git-tracked
|
|
126
130
|
files in `examples/`, then generates compact parser tables. The parser also embeds metadata from `tree-sitter.json`. After changing a grammar file,
|
|
127
131
|
`tree-sitter.json`, a corpus case, or a tracked example,
|
|
128
|
-
regenerate and commit `src/`. Stage added or removed examples with `git add -A examples` before generation so the profile uses the intended file list.
|
|
132
|
+
regenerate and commit `src/` and `hsc/src/`. Stage added or removed examples with `git add -A examples` before generation so the profile uses the intended file list.
|
|
129
133
|
Profiles in `.tmp/generation-profiles/` are temporary and must not be committed. `bun run build-wasm`, `bun run build/ci`,
|
|
130
134
|
and the release build regenerate the parsers before compiling them.
|
|
131
135
|
|
package/grammar/data.js
CHANGED
|
@@ -70,7 +70,7 @@ module.exports = {
|
|
|
70
70
|
*/
|
|
71
71
|
gadt_constructor: $ => seq(
|
|
72
72
|
choice(
|
|
73
|
-
field('name', $.
|
|
73
|
+
field('name', $._decl_con),
|
|
74
74
|
field('names', alias($._con_binding_list, $.binding_list)),
|
|
75
75
|
),
|
|
76
76
|
$._colon2,
|
|
@@ -97,7 +97,7 @@ module.exports = {
|
|
|
97
97
|
_field_type: $ => choice($.strict_field, $.lazy_field, $.type),
|
|
98
98
|
|
|
99
99
|
_datacon_prefix: $ => seq(
|
|
100
|
-
field('name', $.
|
|
100
|
+
field('name', $._decl_con),
|
|
101
101
|
repeat(prec('patterns', field('field', $._field_type))),
|
|
102
102
|
),
|
|
103
103
|
|
|
@@ -109,7 +109,7 @@ module.exports = {
|
|
|
109
109
|
)),
|
|
110
110
|
|
|
111
111
|
_datacon_record: $ => seq(
|
|
112
|
-
field('name', $.
|
|
112
|
+
field('name', $._decl_constructor),
|
|
113
113
|
field('fields', alias($._record_fields, $.fields)),
|
|
114
114
|
),
|
|
115
115
|
|
|
@@ -169,7 +169,7 @@ module.exports = {
|
|
|
169
169
|
_newtype_con_field: $ => $.type,
|
|
170
170
|
|
|
171
171
|
newtype_constructor: $ => seq(
|
|
172
|
-
field('name', $.
|
|
172
|
+
field('name', $._decl_con),
|
|
173
173
|
field('field', choice(
|
|
174
174
|
alias($._newtype_con_field, $.field),
|
|
175
175
|
alias($._record_fields, $.record),
|
package/grammar/id.js
CHANGED
|
@@ -37,11 +37,13 @@ module.exports = {
|
|
|
37
37
|
// ------------------------------------------------------------------------
|
|
38
38
|
|
|
39
39
|
_constructor: $ => alias($.name, $.constructor),
|
|
40
|
+
_decl_constructor: $ => $._constructor,
|
|
40
41
|
_qualified_constructor: $ => qualified($, $._constructor),
|
|
41
42
|
_qconid: $ => alias($._qualified_constructor, $.qualified),
|
|
42
43
|
_conids: $ => choice($._qconid, $._constructor),
|
|
43
44
|
|
|
44
45
|
_con: $ => choice($._constructor, $._pconsym),
|
|
46
|
+
_decl_con: $ => $._con,
|
|
45
47
|
_qcon: $ => choice($._qconid, $._pqconsym),
|
|
46
48
|
_cons: $ => choice(prec('con', $._con), $._qcon),
|
|
47
49
|
|
package/grammar/lexeme.js
CHANGED
|
@@ -6,15 +6,15 @@ const varid_start_char = /[_\p{Ll}\p{Lo}]/
|
|
|
6
6
|
|
|
7
7
|
const conid_start_char = /[\p{Lu}\p{Lt}]/
|
|
8
8
|
|
|
9
|
-
module.exports = {
|
|
9
|
+
module.exports = (hash = '#') => ({
|
|
10
10
|
|
|
11
|
-
variable: _ => token(seq(varid_start_char, id_char, /#*/)),
|
|
11
|
+
variable: _ => token(seq(varid_start_char, id_char, hash === '#' ? /#*/ : /(##)*/)),
|
|
12
12
|
|
|
13
13
|
implicit_variable: _ => token(seq('?', varid_start_char, id_char)),
|
|
14
14
|
|
|
15
|
-
name: _ => token(seq(conid_start_char, id_char, /#*/)),
|
|
15
|
+
name: _ => token(seq(conid_start_char, id_char, hash === '#' ? /#*/ : /(##)*/)),
|
|
16
16
|
|
|
17
|
-
label: _ => token(seq(
|
|
17
|
+
label: _ => token(seq(hash, choice(/[\pL\p{Mn}\pN_']+/, quoted_string(
|
|
18
18
|
/[^\p{C}\p{Zl}\p{Zp}\\"]/,
|
|
19
19
|
/\\(?:[abfnrtv\\"'&]|\^[@-_]|[0-9]+|o[0-7]+|x[0-9a-fA-F]+|NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC[1-4]|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL)/,
|
|
20
20
|
)))),
|
|
@@ -52,12 +52,12 @@ module.exports = {
|
|
|
52
52
|
_bracket_open: $ => seq('[', $._cmd_texp_start),
|
|
53
53
|
_bracket_close: $ => seq(']', $._cmd_texp_end),
|
|
54
54
|
|
|
55
|
-
_unboxed_open: $ => seq(optional($._unboxed_label_start), alias(seq($._paren_open, token.immediate(
|
|
56
|
-
_unboxed_close: $ => seq(
|
|
55
|
+
_unboxed_open: $ => seq(optional($._unboxed_label_start), alias(seq($._paren_open, token.immediate(hash)), '(#')),
|
|
56
|
+
_unboxed_close: $ => seq(`${hash})`, $._cmd_texp_end),
|
|
57
57
|
_unboxed_bar: _ => choice('|', token.immediate('|')),
|
|
58
58
|
|
|
59
59
|
_where: $ => seq(optional($._phantom_where), 'where'),
|
|
60
60
|
|
|
61
61
|
_bar: $ => seq(optional($._phantom_bar), '|'),
|
|
62
62
|
|
|
63
|
-
}
|
|
63
|
+
})
|
package/grammar/literal.js
CHANGED
|
@@ -9,9 +9,11 @@ const {
|
|
|
9
9
|
const decimal = /[0-9][0-9_]*/
|
|
10
10
|
const exponent = /[eE][+-]?[0-9_]+/
|
|
11
11
|
const hex_exponent = /[pP][+-]?[0-9a-fA-F_]+/
|
|
12
|
-
const magic_hash = rule => token(seq(rule, optional(token.immediate(/##?/))))
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
module.exports = (hashSuffix = /##?/) => {
|
|
15
|
+
const magic_hash = rule => token(seq(rule, optional(token.immediate(hashSuffix))))
|
|
16
|
+
return {
|
|
15
17
|
|
|
16
18
|
float: _ => magic_hash(
|
|
17
19
|
seq(
|
|
@@ -100,3 +102,4 @@ module.exports = {
|
|
|
100
102
|
),
|
|
101
103
|
|
|
102
104
|
}
|
|
105
|
+
}
|
package/grammar.js
CHANGED
|
@@ -9,8 +9,8 @@ const
|
|
|
9
9
|
general = require('./grammar/general.js'),
|
|
10
10
|
id = require('./grammar/id.js'),
|
|
11
11
|
inline = require('./grammar/inline.js'),
|
|
12
|
-
lexeme = require('./grammar/lexeme.js'),
|
|
13
|
-
literal = require('./grammar/literal.js'),
|
|
12
|
+
lexeme = require('./grammar/lexeme.js')(),
|
|
13
|
+
literal = require('./grammar/literal.js')(),
|
|
14
14
|
module_ = require('./grammar/module.js'),
|
|
15
15
|
operator = require('./grammar/operator.js'),
|
|
16
16
|
pat = require('./grammar/pat.js'),
|
package/hsc/grammar.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const haskell = require('../grammar.js')
|
|
2
|
+
const literal = require('../grammar/literal.js')
|
|
3
|
+
const lexeme = require('../grammar/lexeme.js')
|
|
4
|
+
|
|
5
|
+
const braced = ($, close) => seq(
|
|
6
|
+
$._cond_hsc_hash,
|
|
7
|
+
'#',
|
|
8
|
+
optional($._hsc_name_space),
|
|
9
|
+
token.immediate('{'),
|
|
10
|
+
$._cmd_hsc_brace_open,
|
|
11
|
+
optional($._hsc_braced_space),
|
|
12
|
+
field('name', $.hsc_directive_name),
|
|
13
|
+
field('body', alias($.hsc_args_nested, $.hsc_args)),
|
|
14
|
+
close,
|
|
15
|
+
$._cmd_brace_close,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
module.exports = grammar(haskell, {
|
|
19
|
+
name: 'hsc',
|
|
20
|
+
|
|
21
|
+
externals: ($, previous) => [
|
|
22
|
+
...previous.slice(0, -1),
|
|
23
|
+
$._cmd_hsc_brace_open,
|
|
24
|
+
$._cond_hsc_hash,
|
|
25
|
+
$.hsc_args_nested,
|
|
26
|
+
$.hsc_args_newline,
|
|
27
|
+
previous[previous.length - 1],
|
|
28
|
+
],
|
|
29
|
+
|
|
30
|
+
conflicts: ($, previous) => [
|
|
31
|
+
...previous,
|
|
32
|
+
[$._foreign_safety, $.entity],
|
|
33
|
+
],
|
|
34
|
+
|
|
35
|
+
rules: {
|
|
36
|
+
...literal(/(?:##){1,2}/),
|
|
37
|
+
...lexeme('##'),
|
|
38
|
+
|
|
39
|
+
_operator_hash_head: _ => seq(
|
|
40
|
+
choice('##', token.immediate('##')),
|
|
41
|
+
optional(choice(token.immediate('##'), token.immediate('|'))),
|
|
42
|
+
),
|
|
43
|
+
|
|
44
|
+
_foreign_calling_convention: ($, previous) => choice(previous, $.hsc),
|
|
45
|
+
_foreign_safety: ($, previous) => choice(previous, $.hsc),
|
|
46
|
+
_ie_entity: ($, previous) => choice(previous, $.hsc),
|
|
47
|
+
entity: ($, previous) => choice(previous, $.hsc),
|
|
48
|
+
literal: ($, previous) => choice(previous, $._hsc_literal),
|
|
49
|
+
_var: ($, previous) => choice(previous, $.hsc),
|
|
50
|
+
_tycon: ($, previous) => choice(previous, $.hsc),
|
|
51
|
+
_decl_con: ($, previous) => choice(previous, $.hsc),
|
|
52
|
+
_decl_constructor: ($, previous) => choice(previous, $.hsc),
|
|
53
|
+
|
|
54
|
+
hsc: $ => choice(
|
|
55
|
+
braced($, '}'),
|
|
56
|
+
seq(
|
|
57
|
+
$._cond_hsc_hash,
|
|
58
|
+
'#',
|
|
59
|
+
optional($._hsc_name_space),
|
|
60
|
+
field('name', $.hsc_directive_name),
|
|
61
|
+
field('body', alias($.hsc_args_newline, $.hsc_args)),
|
|
62
|
+
),
|
|
63
|
+
),
|
|
64
|
+
_hsc_literal: $ => braced($, choice('}##', '}####')),
|
|
65
|
+
_hsc_name_space: _ => token.immediate(/(?:[\t\v\f\r\p{Zs}\x85\u2028\u2029]|\\\r?\n)+/),
|
|
66
|
+
_hsc_braced_space: _ => token.immediate(/(?:\p{White_Space}|\\\r?\n)+/),
|
|
67
|
+
hsc_directive_name: _ => token.immediate(prec(-1, /[\pL_](?:[\pL\pN_]|\\\r?\n)*/)),
|
|
68
|
+
},
|
|
69
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(hsc_directive_name) @function.macro
|