@willbooster/tree-sitter-haskell 8.1.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 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 = "7" }
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', $._con),
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', $._con),
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', $._constructor),
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', $._con),
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/decl.js CHANGED
@@ -5,6 +5,8 @@ const {
5
5
  layout,
6
6
  } = require('./util.js')
7
7
 
8
+ const foreignMacro = /[A-Z][A-Za-z_0-9]*/
9
+
8
10
  const binding = ($, pattern) => prec('bind', seq(
9
11
  choice(
10
12
  field('pattern', pattern),
@@ -17,10 +19,6 @@ const binding = ($, pattern) => prec('bind', seq(
17
19
 
18
20
  module.exports = {
19
21
 
20
- // ------------------------------------------------------------------------
21
- // fixity
22
- // ------------------------------------------------------------------------
23
-
24
22
  _fun_arrow_prec: _ => seq('-', '1'),
25
23
 
26
24
  // GHC.Types special decl
@@ -39,10 +37,6 @@ module.exports = {
39
37
  ),
40
38
  ),
41
39
 
42
- // ------------------------------------------------------------------------
43
- // signature
44
- // ------------------------------------------------------------------------
45
-
46
40
  _con_binding_list: $ => sep2(',', field('name', $._con)),
47
41
 
48
42
  _var_binding_list: $ => sep2(',', field('name', $._var)),
@@ -55,10 +49,6 @@ module.exports = {
55
49
  $._type_annotation,
56
50
  ),
57
51
 
58
- // ------------------------------------------------------------------------
59
- // function and pattern bind
60
- // ------------------------------------------------------------------------
61
-
62
52
  _simple_bind_match: $ => seq('=', field('expression', $._exp)),
63
53
 
64
54
  _bind_match: $ => seq(
@@ -91,14 +81,6 @@ module.exports = {
91
81
  $._function_head_parens,
92
82
  ),
93
83
 
94
- /**
95
- * The difference between a `function` with an `infix` head and a `bind` with `pat_infix` is that the former is for
96
- * _declaring_ a `varop` and the latter uses a `conop` to pattern match on the rhs expression.
97
- * The former may not have a type annotation, while the latter may.
98
- *
99
- * > a <> b = undefined
100
- * > h : t :: [Int] = undefined
101
- */
102
84
  _function_head_infix: $ => seq(
103
85
  field('left_operand', $.pattern),
104
86
  optional($._cond_no_section_op),
@@ -118,10 +100,6 @@ module.exports = {
118
100
 
119
101
  _binding_name_parens: $ => parens($, choice($._var, $._binding_name_parens)),
120
102
 
121
- /**
122
- * The `implicit_variable` here is for:
123
- * g = let ?par = Impy 5 in f
124
- */
125
103
  bind: $ => binding($, $._pat),
126
104
 
127
105
  _module_bind: $ => binding($, $._binding_pat),
@@ -147,48 +125,67 @@ module.exports = {
147
125
 
148
126
  _where_binds: $ => seq($._where, optional(field('binds', $.local_binds))),
149
127
 
150
- // ------------------------------------------------------------------------
151
- // foreign
152
- // ------------------------------------------------------------------------
153
-
154
- calling_convention: _ => token(choice(
128
+ _foreign_calling_keyword: _ => token(choice(
155
129
  'ccall',
156
130
  'stdcall',
157
131
  'capi',
158
132
  'prim',
159
133
  'javascript',
160
- /[A-Z_]+/, // It's common in GHC to use a cpp #define for this
161
134
  )),
162
135
 
163
- safety: _ => token(choice(
136
+ _foreign_safety_keyword: _ => token(choice(
164
137
  'unsafe',
165
138
  'safe',
166
139
  'interruptible',
167
140
  )),
168
141
 
142
+ _foreign_macro: _ => token(prec(-1, foreignMacro)),
143
+
144
+ _foreign_underscore_identifier: _ => token(/_[A-Za-z_0-9]+/),
145
+
146
+ _foreign_calling_convention: $ => choice(
147
+ alias('_', $.calling_convention),
148
+ alias($._foreign_calling_keyword, $.calling_convention),
149
+ alias($._foreign_macro, $.calling_convention),
150
+ alias($._foreign_underscore_identifier, $.calling_convention),
151
+ ),
152
+
153
+ _foreign_safety: $ => choice(
154
+ alias('_', $.safety),
155
+ alias($._foreign_safety_keyword, $.safety),
156
+ alias($._foreign_macro, $.safety),
157
+ alias($._foreign_underscore_identifier, $.safety),
158
+ ),
159
+
160
+ _foreign_underscore_signature: $ => seq(
161
+ field('name', alias($._foreign_underscore_identifier, $.variable)),
162
+ $._type_annotation,
163
+ ),
164
+
165
+ _foreign_signature: $ => choice(
166
+ $.signature,
167
+ alias($._foreign_underscore_signature, $.signature),
168
+ ),
169
+
169
170
  entity: $ => $.string,
170
171
 
171
172
  foreign_import: $ => seq(
172
173
  'foreign',
173
174
  'import',
174
- field('calling_convention', $.calling_convention),
175
- optional(field('safety', $.safety)),
175
+ field('calling_convention', $._foreign_calling_convention),
176
+ optional(field('safety', $._foreign_safety)),
176
177
  optional(field('entity', $.entity)),
177
- field('signature', $.signature),
178
+ field('signature', $._foreign_signature),
178
179
  ),
179
180
 
180
181
  foreign_export: $ => seq(
181
182
  'foreign',
182
183
  'export',
183
- field('calling_convention', $.calling_convention),
184
+ field('calling_convention', $._foreign_calling_convention),
184
185
  optional(field('entity', $.entity)),
185
- field('signature', $.signature),
186
+ field('signature', $._foreign_signature),
186
187
  ),
187
188
 
188
- // ------------------------------------------------------------------------
189
- // default
190
- // ------------------------------------------------------------------------
191
-
192
189
  default_types: $ => seq('default', parens($, optional(sep1(',', field('type', $._ktype))))),
193
190
 
194
191
  }
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('#', choice(/[\pL\p{Mn}\pN_']+/, quoted_string(
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('#)', $._cmd_texp_end),
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
+ })
@@ -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
- module.exports = {
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
@@ -0,0 +1,2 @@
1
+ ((hsc_args) @injection.content
2
+ (#set! injection.language "c"))