3 const builtin_dialect = require('./dialect/builtin');
4 const func_dialect = require('./dialect/func');
5 const llvm_dialect = require('./dialect/llvm');
6 const arith_dialect = require('./dialect/arith');
7 const math_dialect = require('./dialect/math');
8 const cf_dialect = require('./dialect/cf');
9 const scf_dialect = require('./dialect/scf');
10 const memref_dialect = require('./dialect/memref');
11 const vector_dialect = require('./dialect/vector');
12 const tensor_dialect = require('./dialect/tensor');
13 const bufferization_dialect = require('./dialect/bufferization');
14 const affine_dialect = require('./dialect/affine');
15 const linalg_dialect = require('./dialect/linalg');
18 // Top level production:
19 // (operation | attribute-alias-def | type-alias-def)
20 toplevel : $ => seq($._toplevel, repeat($._toplevel)),
21 _toplevel : $ => choice($.operation, $.attribute_alias_def, $.type_alias_def),
23 // Common syntax (lang-ref)
25 // hex_digit ::= [0-9a-fA-F]
26 // letter ::= [a-zA-Z]
27 // id-punct ::= [$._-]
29 // integer-literal ::= decimal-literal | hexadecimal-literal
30 // decimal-literal ::= digit+
31 // hexadecimal-literal ::= `0x` hex_digit+
32 // float-literal ::= [-+]?[0-9]+[.][0-9]*([eE][-+]?[0-9]+)?
33 // string-literal ::= `"` [^"\n\f\v\r]* `"` TODO: define escaping rules
35 _digit : $ => /[0-9]/,
36 integer_literal : $ => choice($._decimal_literal, $._hexadecimal_literal),
37 _decimal_literal : $ => token(seq(optional(/[-+]/), repeat1(/[0-9]/))),
38 _hexadecimal_literal : $ => token(seq('0x', repeat1(/[0-9a-fA-F]/))),
40 token(seq(optional(/[-+]/), repeat1(/[0-9]/), '.', repeat(/[0-9]/),
41 optional(seq(/[eE]/, optional(/[-+]/), repeat1(/[0-9]/))))),
42 string_literal : $ => token(seq('"', repeat(/[^\\"\n\f\v\r]+/), '"')),
43 bool_literal : $ => token(choice('true', 'false')),
44 unit_literal : $ => token('unit'),
45 complex_literal : $ =>
46 seq('(', choice($.integer_literal, $.float_literal), ',',
47 choice($.integer_literal, $.float_literal), ')'),
49 seq(token(choice('dense', 'sparse')), '<',
51 seq($.nested_idx_list, repeat(seq(',', $.nested_idx_list))),
52 $._primitive_idx_literal)),
54 array_literal : $ => seq(token('array'), '<', $.type, ':', $._idx_list, '>'),
55 _literal : $ => choice($.integer_literal, $.float_literal, $.string_literal,
56 $.bool_literal, $.tensor_literal, $.array_literal,
57 $.complex_literal, $.unit_literal),
59 nested_idx_list : $ =>
60 seq('[', optional(choice($.nested_idx_list, $._idx_list)),
61 repeat(seq(',', $.nested_idx_list)), ']'),
62 _idx_list : $ => prec.right(seq($._primitive_idx_literal,
63 repeat(seq(',', $._primitive_idx_literal)))),
64 _primitive_idx_literal : $ => choice($.integer_literal, $.float_literal,
65 $.bool_literal, $.complex_literal),
68 // bare-id ::= (letter|[_]) (letter|digit|[_$.])*
69 // bare-id-list ::= bare-id (`,` bare-id)*
70 // value-id ::= `%` suffix-id
71 // suffix-id ::= (digit+ | ((letter|id-punct) (letter|id-punct|digit)*))
72 // alias-name :: = bare-id
74 // symbol-ref-id ::= `@` (suffix-id | string-literal) (`::`
76 // value-id-list ::= value-id (`,` value-id)*
78 // // Uses of value, e.g. in an operand list to an operation.
79 // value-use ::= value-id
80 // value-use-list ::= value-use (`,` value-use)*
81 bare_id : $ => token(seq(/[a-zA-Z_]/, repeat(/[a-zA-Z0-9_$.]/))),
82 _alias_or_dialect_id : $ => token(seq(/[a-zA-Z_]/, repeat(/[a-zA-Z0-9_$]/))),
83 bare_id_list : $ => seq($.bare_id, repeat(seq(',', $.bare_id))),
84 value_use : $ => seq('%', $._suffix_id),
85 _suffix_id : $ => token(seq(
86 choice(repeat1(/[0-9]/), seq(/[a-zA-Z_$.-]/, repeat(/[a-zA-Z0-9_$.-]/))),
87 optional(seq(choice(':', '#'), repeat1(/[0-9]/))))),
88 symbol_ref_id : $ => seq('@', choice($._suffix_id, $.string_literal),
89 optional(seq('::', $.symbol_ref_id))),
90 _value_use_list : $ => seq($.value_use, repeat(seq(',', $.value_use))),
93 // operation ::= op-result-list? (generic-operation |
96 // generic-operation ::= string-literal `(` value-use-list? `)`
97 // successor-list? region-list?
98 // dictionary-attribute? `:` function-type
99 // custom-operation ::= bare-id custom-operation-format
100 // op-result-list ::= op-result (`,` op-result)* `=`
101 // op-result ::= value-id (`:` integer-literal)
102 // successor-list ::= `[` successor (`,` successor)* `]`
103 // successor ::= caret-id (`:` bb-arg-list)?
104 // region-list ::= `(` region (`,` region)* `)`
105 // dictionary-attribute ::= `{` (attribute-entry (`,` attribute-entry)*)?
107 // trailing-location ::= (`loc` `(` location `)`)?
109 seq(field('lhs', optional($._op_result_list)),
110 field('rhs', choice($.generic_operation, $.custom_operation)),
111 field('location', optional($.trailing_location))),
112 generic_operation : $ => seq(
113 $.string_literal, $._value_use_list_parens, optional($._successor_list),
114 optional($._region_list), optional($.attribute), ':', $.function_type),
115 // custom-operation rule is defined later in the grammar, post the generic.
116 _op_result_list : $ => seq($.op_result, repeat(seq(',', $.op_result)), '='),
117 op_result : $ => seq($.value_use, optional(seq(':', $.integer_literal))),
118 _successor_list : $ =>
119 seq('[', $.successor, repeat(seq(',', $.successor)), ']'),
120 successor : $ => seq($.caret_id, optional($._value_arg_list)),
121 _region_list : $ => seq('(', $.region, repeat(seq(',', $.region)), ')'),
122 dictionary_attribute : $ => seq('{', optional($.attribute_entry),
123 repeat(seq(',', $.attribute_entry)), '}'),
124 trailing_location : $ => seq(token('loc'), '(', $.location, ')'),
125 // TODO: Complete location forms.
126 location : $ => $.string_literal,
129 // block ::= block-label operation+
130 // block-label ::= block-id block-arg-list? `:`
131 // block-id ::= caret-id
132 // caret-id ::= `^` suffix-id
133 // value-id-and-type ::= value-id `:` type
135 // // Non-empty list of names and types.
136 // value-id-and-type-list ::= value-id-and-type (`,` value-id-and-type)*
138 // block-arg-list ::= `(` value-id-and-type-list? `)`
139 block : $ => seq($.block_label, repeat1($.operation)),
140 block_label : $ => seq($._block_id, optional($.block_arg_list), ':'),
141 _block_id : $ => $.caret_id,
142 caret_id : $ => seq('^', $._suffix_id),
143 _value_use_and_type : $ => seq($.value_use, optional(seq(':', $.type))),
144 _value_use_and_type_list : $ =>
145 seq($._value_use_and_type, repeat(seq(',', $._value_use_and_type))),
146 block_arg_list : $ => seq('(', optional($._value_use_and_type_list), ')'),
147 _value_arg_list : $ => seq('(', optional($._value_use_type_list), ')'),
148 _value_use_type_list : $ => seq($._value_use_list, $._type_annotation),
151 // region ::= `{` entry-block? block* `}`
152 // entry-block ::= operation+
153 region : $ => seq('{', optional($.entry_block), repeat($.block), '}'),
154 entry_block : $ => repeat1($.operation),
157 // type ::= type-alias | dialect-type | builtin-type
159 // type-list-no-parens ::= type (`,` type)*
160 // type-list-parens ::= `(` type-list-no-parens? `)`
162 // // This is a common way to refer to a value with a specified type.
163 // ssa-use-and-type ::= ssa-use `:` type
164 // ssa-use ::= value-use
166 // // Non-empty list of names and types.
167 // ssa-use-and-type-list ::= ssa-use-and-type (`,` ssa-use-and-type)*
169 // function-type ::= (type | type-list-parens) `->` (type |
171 type : $ => choice($.type_alias, $.dialect_type, $.builtin_type),
172 _type_list_no_parens : $ => prec.left(seq($.type, repeat(seq(',', $.type)))),
173 _type_list_parens : $ => seq('(', optional($._type_list_no_parens), ')'),
175 seq(choice($.type, $._type_list_parens), $._function_return),
176 _function_return : $ => seq(token('->'), choice($.type, $._type_list_parens)),
177 _type_annotation : $ =>
178 seq(':', choice(seq($.type, choice('from', 'into', 'to'), $.type),
179 $._type_list_no_parens)),
180 _function_type_annotation : $ => seq(':', $.function_type),
181 _literal_and_type : $ => seq($._literal, optional($._type_annotation)),
184 // type-alias-def ::= '!' alias-name '=' type
185 // type-alias ::= '!' alias-name
186 type_alias_def : $ => seq('!', $._alias_or_dialect_id, '=', $.type),
187 type_alias : $ => seq('!', $._alias_or_dialect_id),
190 // dialect-namespace ::= bare-id
192 // opaque-dialect-item ::= dialect-namespace '<' string-literal '>'
194 // pretty-dialect-item ::= dialect-namespace '.'
195 // pretty-dialect-item-lead-ident pretty-dialect-item-body?
197 // pretty-dialect-item-lead-ident ::= '[A-Za-z][A-Za-z0-9._]*'
198 // pretty-dialect-item-body ::= '<' pretty-dialect-item-contents+ '>'
199 // pretty-dialect-item-contents ::= pretty-dialect-item-body
200 // | '(' pretty-dialect-item-contents+ ')'
201 // | '[' pretty-dialect-item-contents+ ']'
202 // | '{' pretty-dialect-item-contents+ '}'
203 // | '[^[<({>\])}\0]+'
205 // dialect-type ::= '!' (opaque-dialect-item | pretty-dialect-item)
207 seq('!', choice($.opaque_dialect_item, $.pretty_dialect_item)),
208 dialect_namespace : $ => $._alias_or_dialect_id,
209 dialect_ident : $ => $._alias_or_dialect_id,
210 opaque_dialect_item : $ =>
211 seq($.dialect_namespace, '<', $.string_literal, '>'),
212 pretty_dialect_item : $ => seq($.dialect_namespace, '.', $.dialect_ident,
213 optional($.pretty_dialect_item_body)),
214 pretty_dialect_item_body : $ =>
215 seq('<', repeat($._pretty_dialect_item_contents), '>'),
216 _pretty_dialect_item_contents : $ =>
217 prec.left(choice($.pretty_dialect_item_body, repeat1(/[^<>]/))),
220 builtin_type : $ => choice(
221 // TODO: Add opaque_type
222 $.integer_type, $.float_type, $.complex_type, $.index_type, $.memref_type,
223 $.none_type, $.tensor_type, $.vector_type, $.tuple_type),
225 // signed-integer-type ::= `si`[1-9][0-9]*
226 // unsigned-integer-type ::= `ui`[1-9][0-9]*
227 // signless-integer-type ::= `i`[1-9][0-9]*
228 // integer-type ::= signed-integer-type | unsigned-integer-type |
229 // signless-integer-type
231 token(seq(choice('si', 'ui', 'i'), /[1-9]/, repeat(/[0-9]/))),
232 float_type : $ => token(
233 choice('f16', 'f32', 'f64', 'f80', 'f128', 'bf16', 'f8E4M3FN', 'f8E4M3',
235 index_type : $ => token('index'),
236 none_type : $ => token('none'),
237 complex_type : $ => seq(token('complex'), '<', $._prim_type, '>'),
239 choice($.integer_type, $.float_type, $.index_type, $.complex_type,
240 $.none_type, $.memref_type, $.vector_type, $.tensor_type),
242 // memref-type ::= `memref` `<` dimension-list-ranked type
243 // (`,` layout-specification)? (`,` memory-space)? `>`
244 // layout-specification ::= attribute-value
245 // memory-space ::= attribute-value
247 seq(token('memref'), '<', field('dimension_list', $.dim_list),
248 optional(seq(',', $.attribute_value)),
249 optional(seq(',', $.attribute_value)), '>'),
250 dim_list : $ => seq($._dim_primitive, repeat(seq('x', $._dim_primitive))),
251 _dim_primitive : $ => choice($._prim_type, repeat1($._digit), '?', '*'),
253 // tensor-type ::= `tensor` `<` dimension-list type (`,` encoding)? `>`
254 // dimension-list ::= (dimension `x`)*
255 // dimension ::= `?` | decimal-literal
256 // encoding ::= attribute-value
257 // tensor-type ::= `tensor` `<` `*` `x` type `>`
258 tensor_type : $ => seq(token('tensor'), '<', $.dim_list,
259 optional(seq(',', $.tensor_encoding)), '>'),
260 tensor_encoding : $ => $.attribute_value,
262 // vector-type ::= `vector` `<` vector-dim-list vector-element-type `>`
263 // vector-element-type ::= float-type | integer-type | index-type
264 // vector-dim-list := (static-dim-list `x`)? (`[` static-dim-list `]` `x`)?
265 // static-dim-list ::= decimal-literal (`x` decimal-literal)*
267 seq(token('vector'), '<', optional($.vector_dim_list), $._prim_type, '>'),
268 vector_dim_list : $ =>
269 choice(seq($._static_dim_list, 'x',
270 optional(seq('[', $._static_dim_list, ']', 'x'))),
271 seq('[', $._static_dim_list, ']', 'x')),
272 _static_dim_list : $ =>
273 seq(repeat1($._digit), repeat(seq('x', repeat1($._digit)))),
275 // tuple-type ::= `tuple` `<` (type ( `,` type)*)? `>`
277 seq(token('tuple'), '<', $.tuple_dim, repeat(seq(',', $.tuple_dim)), '>'),
278 tuple_dim : $ => $._prim_type,
281 // attribute-entry ::= (bare-id | string-literal) `=` attribute-value
282 // attribute-value ::= attribute-alias | dialect-attribute |
284 attribute_entry : $ => seq(choice($.bare_id, $.string_literal),
285 optional(seq('=', $.attribute_value))),
286 attribute_value : $ =>
287 choice(seq('[', optional($._attribute_value_nobracket),
288 repeat(seq(',', $._attribute_value_nobracket)), ']'),
289 $._attribute_value_nobracket),
290 _attribute_value_nobracket : $ =>
291 choice($.attribute_alias, $.dialect_attribute, $.builtin_attribute,
292 $.dictionary_attribute, $._literal_and_type, $.type),
293 attribute : $ => choice($.attribute_alias, $.dialect_attribute,
294 $.builtin_attribute, $.dictionary_attribute),
296 // Attribute Value Aliases
297 // attribute-alias-def ::= '#' alias-name '=' attribute-value
298 // attribute-alias ::= '#' alias-name
299 attribute_alias_def : $ =>
300 seq('#', $._alias_or_dialect_id, '=', $.attribute_value),
301 attribute_alias : $ => seq('#', $._alias_or_dialect_id),
303 // Dialect Attribute Values
304 dialect_attribute : $ =>
305 seq('#', choice($.opaque_dialect_item, $.pretty_dialect_item)),
307 // Builtin Attribute Values
308 builtin_attribute : $ => choice(
310 $.strided_layout, $.affine_map, $.affine_set),
311 strided_layout : $ => seq(token('strided'), '<', '[', $._dim_list_comma, ']',
312 optional(seq(',', token('offset'), ':',
313 choice($.integer_literal, '?', '*'))),
315 _dim_list_comma : $ =>
316 seq($._dim_primitive, repeat(seq(',', $._dim_primitive))),
319 seq(token('affine_map'), '<', $._multi_dim_affine_expr_parens,
320 optional($._multi_dim_affine_expr_sq), token('->'),
321 $._multi_dim_affine_expr_parens, '>'),
323 seq(token('affine_set'), '<', $._multi_dim_affine_expr_parens,
324 optional($._multi_dim_affine_expr_sq), ':',
325 $._multi_dim_affine_expr_parens, '>'),
326 _multi_dim_affine_expr_parens : $ =>
327 seq('(', optional($._multi_dim_affine_expr), ')'),
328 _multi_dim_affine_expr_sq : $ =>
329 seq('[', optional($._multi_dim_affine_expr), ']'),
331 // affine-expr ::= `(` affine-expr `)`
332 // | affine-expr `+` affine-expr
333 // | affine-expr `-` affine-expr
334 // | `-`? integer-literal `*` affine-expr
335 // | affine-expr `ceildiv` integer-literal
336 // | affine-expr `floordiv` integer-literal
337 // | affine-expr `mod` integer-literal
340 // | `-`? integer-literal
341 // multi-dim-affine-expr ::= `(` `)`
342 // | `(` affine-expr (`,` affine-expr)* `)`
344 // semi-affine-expr ::= `(` semi-affine-expr `)`
345 // | semi-affine-expr `+` semi-affine-expr
346 // | semi-affine-expr `-` semi-affine-expr
347 // | symbol-or-const `*` semi-affine-expr
348 // | semi-affine-expr `ceildiv` symbol-or-const
349 // | semi-affine-expr `floordiv` symbol-or-const
350 // | semi-affine-expr `mod` symbol-or-const
352 // | `-`? integer-literal
353 // symbol-or-const ::= `-`? integer-literal | symbol-id
354 // multi-dim-semi-affine-expr ::= `(` semi-affine-expr (`,` semi-affine-expr)*
357 // affine-constraint ::= affine-expr `>=` `affine-expr`
358 // | affine-expr `<=` `affine-expr`
359 // | affine-expr `==` `affine-expr`
360 // affine-constraint-conjunction ::= affine-constraint (`,`
361 // affine-constraint)*
363 _multi_dim_affine_expr : $ =>
364 seq($._affine_expr, repeat(seq(',', $._affine_expr))),
365 _affine_expr : $ => prec.right(choice(
366 seq('(', $._affine_expr, ')'), seq('-', $._affine_expr),
367 seq($._affine_expr, $._affine_token, $._affine_expr), $._affine_prim)),
369 choice($.integer_literal, $.value_use, $.bare_id,
370 seq('symbol', '(', $.value_use, ')'),
371 seq(choice('max', 'min'), '(', $._value_use_list, ')')),
372 _affine_token : $ => token(
373 choice('+', '-', '*', 'ceildiv', 'floordiv', 'mod', '==', '>=', '<=')),
375 func_return : $ => seq(token('->'), $.type_list_attr_parens),
376 func_arg_list : $ => seq(
377 '(', optional(choice($.variadic, $._value_id_and_type_attr_list)), ')'),
378 _value_id_and_type_attr_list : $ => seq(
379 $._value_id_and_type_attr, repeat(seq(',', $._value_id_and_type_attr)),
380 optional(seq(',', $.variadic))),
381 _value_id_and_type_attr : $ => seq($._function_arg, optional($.attribute)),
383 choice(seq($.value_use, ':', $.type), $.value_use, $.type),
384 type_list_attr_parens : $ =>
386 seq('(', $.type, optional($.attribute),
387 repeat(seq(',', $.type, optional($.attribute))), ')'),
389 variadic : $ => token('...'),
391 // (func.func|llvm.func) takes arguments, an optional return type, and and
394 seq(field('visibility', optional('private')),
395 field('name', $.symbol_ref_id), field('arguments', $.func_arg_list),
396 field('return', optional($.func_return)),
397 field('attributes', optional(seq(token('attributes'), $.attribute))),
398 field('body', optional($.region))),
400 // dim-use-list ::= `(` ssa-use-list? `)`
401 // symbol-use-list ::= `[` ssa-use-list? `]`
402 // dim-and-symbol-use-list ::= dim-use-list symbol-use-list?
403 _value_use_list_parens : $ => seq('(', optional($._value_use_list), ')'),
404 _dim_and_symbol_use_list : $ =>
405 seq($._value_use_list_parens, optional($._dense_idx_list)),
407 // assignment-list ::= assignment | assignment `,` assignment-list
408 // assignment ::= ssa-value `=` ssa-value
409 _value_assignment_list : $ => seq('(', optional($._value_assignment),
410 repeat(seq(',', $._value_assignment)), ')'),
411 _value_assignment : $ => seq($.value_use, '=', $.value_use),
413 _dense_idx_list : $ => seq(
415 optional(seq(choice($.integer_literal, $.value_use),
416 repeat(seq(',', choice($.integer_literal, $.value_use))))),
419 // lower-bound ::= `max`? affine-map-attribute dim-and-symbol-use-list |
421 // upper-bound ::= `min`? affine-map-attribute dim-and-symbol-use-list |
423 // shorthand-bound ::= ssa-id | `-`? integer-literal
425 choice(seq($.attribute, $._dim_and_symbol_use_list), $._shorthand_bound),
426 _shorthand_bound : $ => choice($.value_use, $.integer_literal),
428 // Dialect-specific attributes
429 restrict_attr : $ => token('restrict'),
430 writable_attr : $ => token('writable'),
431 gather_dims_attr : $ =>
432 seq(token('gather_dims'), '(', $._dense_idx_list, ')'),
433 scatter_dims_attr : $ =>
434 seq(token('scatter_dims'), '(', $._dense_idx_list, ')'),
435 unique_attr : $ => token('unique'),
436 nofold_attr : $ => token('nofold'),
437 outer_dims_perm_attr : $ =>
438 seq(token('outer_dims_perm'), '=', $._dense_idx_list),
439 inner_dims_pos_attr : $ =>
440 seq(token('inner_dims_pos'), '=', $._dense_idx_list),
441 inner_tiles_attr : $ => seq(token('inner_tiles'), '=', $._dense_idx_list),
442 isWrite_attr : $ => token(choice('read', 'write')),
443 localityHint_attr : $ => seq(token('locality'), '<', $.integer_literal, '>'),
444 isDataCache_attr : $ => token(choice('data', 'instr')),
446 seq(token('fastmath'), '<',
447 seq($._fastmath_flag, repeat(seq(',', $._fastmath_flag))), '>'),
448 _fastmath_flag : $ => token(choice('none', 'reassoc', 'nnan', 'ninf', 'nsz',
449 'arcp', 'contract', 'afn', 'fast')),
451 // Comment (standard BCPL)
452 comment : $ => token(seq('//', /.*/)),
455 custom_operation : $ =>
456 choice($.builtin_dialect, $.func_dialect, $.llvm_dialect, $.arith_dialect,
457 $.math_dialect, $.cf_dialect, $.scf_dialect, $.memref_dialect,
458 $.vector_dialect, $.tensor_dialect, $.bufferization_dialect,
459 $.affine_dialect, $.linalg_dialect)
462 module.exports = grammar({
464 extras : $ => [/\s/, $.comment],
465 conflicts : $ => [[ $._static_dim_list, $._static_dim_list ],
466 [ $.dictionary_attribute, $.region ]],
467 rules : Object.assign(common, builtin_dialect, func_dialect, llvm_dialect,
468 arith_dialect, math_dialect, cf_dialect, scf_dialect,
469 memref_dialect, vector_dialect, tensor_dialect,
470 bufferization_dialect, affine_dialect, linalg_dialect)