1module.exports = grammar({
2  name : 'mlir',
3  extras : $ => [/\s/,
4                 $.comment,
5],
6  conflicts : $ => [],
7  rules : {
8    // Top level production:
9    //   (operation | attribute-alias-def | type-alias-def)
10    toplevel : $ => seq(choice(
11                 $.operation,
12                 $.attribute_alias_def,
13                 $.type_alias_def,
14                 )),
15
16    // Common syntax (lang-ref)
17    //  digit     ::= [0-9]
18    //  hex_digit ::= [0-9a-fA-F]
19    //  letter    ::= [a-zA-Z]
20    //  id-punct  ::= [$._-]
21    //
22    //  integer-literal ::= decimal-literal | hexadecimal-literal
23    //  decimal-literal ::= digit+
24    //  hexadecimal-literal ::= `0x` hex_digit+
25    //  float-literal ::= [-+]?[0-9]+[.][0-9]*([eE][-+]?[0-9]+)?
26    //  string-literal  ::= `"` [^"\n\f\v\r]* `"`   TODO: define escaping rules
27    //
28    _digit : $ => /[0-9]/,
29    _hex_digit : $ => /[0-9a-fA-F]/,
30    integer_literal : $ => choice($._decimal_literal, $._hexadecimal_literal),
31    _decimal_literal : $ => repeat1($._digit),
32    _hexadecimal_literal : $ => seq('0x', repeat1($._hex_digit)),
33    float_literal : $ => token(
34                      seq(optional(/[-+]/), repeat1(/[0_9]/),
35                          optional(seq('.', repeat(/[0-9]/),
36                                       optional(seq(/[eE]/, optional(/[-+]/),
37                                                    repeat1(/[0-9]/))))))),
38    string_literal : $ => seq(
39                       '"',
40                       repeat(token.immediate(prec(1, /[^\\"\n\f\v\r]+/))),
41                       '"',
42                       ),
43
44    // Identifiers
45    //   bare-id ::= (letter|[_]) (letter|digit|[_$.])*
46    //   bare-id-list ::= bare-id (`,` bare-id)*
47    //   value-id ::= `%` suffix-id
48    //   suffix-id ::= (digit+ | ((letter|id-punct) (letter|id-punct|digit)*))
49    //   alias-name :: = bare-id
50    //
51    //   symbol-ref-id ::= `@` (suffix-id | string-literal) (`::`
52    //   symbol-ref-id)?
53    //   value-id-list ::= value-id (`,` value-id)*
54    //
55    //   // Uses of value, e.g. in an operand list to an operation.
56    //   value-use ::= value-id
57    //   value-use-list ::= value-use (`,` value-use)*
58    bare_id : $ => seq(token(/[a-zA-Z_]/),
59                       token.immediate(repeat(/[a-zA-Z0-9_$]/))),
60    bare_id_list : $ => seq($.bare_id, repeat(seq(',', $.bare_id))),
61    value_id : $ => seq('%', $._suffix_id),
62    alias_name : $ => $.bare_id,
63    _suffix_id : $ => choice(repeat1(/[0-9]/),
64                             seq(/[a-zA-Z_$.]/, repeat(/[a-zA-Z0-9_$.]/))),
65    symbol_ref_id : $ => seq('@', choice($._suffix_id, $.string_literal),
66                             optional(seq('::', $.symbol_ref_id))),
67    value_use : $ => $.value_id,
68    value_use_list : $ => seq($.value_use, repeat(seq(',', $.value_use))),
69
70    // Operations
71    //   operation            ::= op-result-list? (generic-operation |
72    //                            custom-operation)
73    //                            trailing-location?
74    //   generic-operation    ::= string-literal `(` value-use-list? `)`
75    //   successor-list?
76    //                            region-list? dictionary-attribute? `:`
77    //                            function-type
78    //   custom-operation     ::= bare-id custom-operation-format
79    //   op-result-list       ::= op-result (`,` op-result)* `=`
80    //   op-result            ::= value-id (`:` integer-literal)
81    //   successor-list       ::= `[` successor (`,` successor)* `]`
82    //   successor            ::= caret-id (`:` bb-arg-list)?
83    //   region-list          ::= `(` region (`,` region)* `)`
84    //   dictionary-attribute ::= `{` (attribute-entry (`,` attribute-entry)*)?
85    //                            `}`
86    //   trailing-location    ::= (`loc` `(` location `)`)?
87    operation : $ => seq(optional($.op_result_list),
88                         choice($.generic_operation, $.custom_operation),
89                         optional($.trailing_location)),
90    generic_operation : $ =>
91                          seq($.string_literal, '(', optional($.value_use_list),
92                              ')', optional($.successor_list),
93                              optional($.region_list),
94                              optional($.dictionary_attribute), ':',
95                              $.function_type),
96    // custom-operation rule is defined later in the grammar, post the generic.
97    op_result_list : $ => seq($.op_result, repeat(seq(',', $.op_result)), '='),
98    op_result : $ => seq($.value_id, optional(seq(':', $.integer_literal))),
99    successor_list : $ => seq('[', $.successor, repeat(seq(',', $.successor)),
100                              ']'),
101    successor : $ => seq($.caret_id, optional(seq(':', $.block_arg_list))),
102    region_list : $ => seq('(', $.region, repeat(seq(',', $.region)), ')'),
103    dictionary_attribute : $ => seq(
104                             '{',
105                             optional(seq($.attribute_entry,
106                                          repeat(seq(',', $.attribute_entry)))),
107                             '}'),
108    trailing_location : $ => seq('loc(', $.location, ')'),
109    // TODO: Complete location forms.
110    location : $ => $.string_literal,
111
112    // Blocks
113    //   block           ::= block-label operation+
114    //   block-label     ::= block-id block-arg-list? `:`
115    //   block-id        ::= caret-id
116    //   caret-id        ::= `^` suffix-id
117    //   value-id-and-type ::= value-id `:` type
118    //
119    //   // Non-empty list of names and types.
120    //   value-id-and-type-list ::= value-id-and-type (`,` value-id-and-type)*
121    //
122    //   block-arg-list ::= `(` value-id-and-type-list? `)`
123    block : $ => seq($.block_label, repeat1($.operation)),
124    block_label : $ => seq($._block_id, optional($.block_arg_list), ':'),
125    _block_id : $ => $.caret_id,
126    caret_id : $ => seq('^', $._suffix_id),
127    value_id_and_type : $ => seq($.value_id, ':', $.type),
128    value_id_and_type_list : $ => seq($.value_id_and_type,
129                                      repeat(seq(',', $.value_id_and_type))),
130    block_arg_list : $ => seq('(', optional($.value_id_and_type_list), ')'),
131
132    // Regions
133    //   region      ::= `{` entry-block? block* `}`
134    //   entry-block ::= operation+
135    region : $ => seq('{', optional($.entry_block), repeat($.block), '}'),
136    entry_block : $ => repeat1($.operation),
137
138    // Types
139    //   type ::= type-alias | dialect-type | builtin-type
140    //
141    //   type-list-no-parens ::=  type (`,` type)*
142    //   type-list-parens ::= `(` type-list-no-parens? `)`
143    //
144    //   // This is a common way to refer to a value with a specified type.
145    //   ssa-use-and-type ::= ssa-use `:` type
146    //   ssa-use ::= value-use
147    //
148    //   // Non-empty list of names and types.
149    //   ssa-use-and-type-list ::= ssa-use-and-type (`,` ssa-use-and-type)*
150    //
151    //   function-type ::= (type | type-list-parens) `->` (type |
152    //   type-list-parens)
153    type : $ => choice($.type_alias, $.dialect_type, $.builtin_type),
154    type_list_no_parens : $ => seq($.type, repeat(seq(',', $.type))),
155    type_list_parens : $ => seq('(', optional($.type_list_no_parens), ')'),
156    ssa_use_and_type : $ => seq($.ssa_use, ':', $.type),
157    ssa_use : $ => $.value_use,
158    ssa_use_and_type_list : $ => seq($.ssa_use_and_type,
159                                     repeat(seq(',', $.ssa_use_and_type))),
160    function_type : $ => seq(choice($.type, $.type_list_parens), '->',
161                             choice($.type, $.type_list_parens)),
162
163    // Type aliases
164    //   type-alias-def ::= '!' alias-name '=' type
165    //   type-alias ::= '!' alias-name
166    type_alias_def : $ => seq('!', $.alias_name, '=', $.type),
167    type_alias : $ => seq('!', $.alias_name),
168
169    // Dialect Types
170    //   dialect-namespace ::= bare-id
171    //
172    //   opaque-dialect-item ::= dialect-namespace '<' string-literal '>'
173    //
174    //   pretty-dialect-item ::= dialect-namespace '.'
175    //   pretty-dialect-item-lead-ident
176    //                                                 pretty-dialect-item-body?
177    //
178    //   pretty-dialect-item-lead-ident ::= '[A-Za-z][A-Za-z0-9._]*'
179    //   pretty-dialect-item-body ::= '<' pretty-dialect-item-contents+ '>'
180    //   pretty-dialect-item-contents ::= pretty-dialect-item-body
181    //                                 | '(' pretty-dialect-item-contents+ ')'
182    //                                 | '[' pretty-dialect-item-contents+ ']'
183    //                                 | '{' pretty-dialect-item-contents+ '}'
184    //                                 | '[^[<({>\])}\0]+'
185    //
186    //   dialect-type ::= '!' (opaque-dialect-item | pretty-dialect-item)
187    dialect_type : $ => seq(
188                     '!', choice($.opaque_dialect_item, $.pretty_dialect_item)),
189    dialect_namespace : $ => $.bare_id,
190    opaque_dialect_item : $ => seq($.dialect_namespace, '<', $.string_literal,
191                                   '>'),
192    pretty_dialect_item : $ => seq($.dialect_namespace, '.',
193                                   $.pretty_dialect_item_lead_ident,
194                                   optional($.pretty_dialect_item_body)),
195    pretty_dialect_item_lead_ident : $ => $.bare_id,
196    pretty_dialect_item_body : $ => seq('<',
197                                        repeat1($.pretty_dialect_item_contents),
198                                        '>'),
199    // TODO: not sure why prec.left (setting left-associated parsing) needed
200    // here,
201    // left-associated way avoids an ambiguity flagged by generator. It may not
202    // be needed and be only papering over an issue.
203    pretty_dialect_item_contents : $ => prec.left(choice(
204                                     $.pretty_dialect_item_body,
205                                     seq('(',
206                                         repeat1(
207                                             $.pretty_dialect_item_contents),
208                                         ')'),
209                                     seq('[',
210                                         repeat1(
211                                             $.pretty_dialect_item_contents),
212                                         ']'),
213                                     seq('{',
214                                         repeat1(
215                                             $.pretty_dialect_item_contents),
216                                         '}'),
217                                     repeat1(/[^\[<({>\])}\\0]/))),
218    dialect_type : $ => seq(
219                     '!', choice($.opaque_dialect_item, $.pretty_dialect_item)),
220
221    // Builtin types
222    builtin_type : $ => choice(
223                     // TODO: Add builtin types
224                     seq('i', repeat1(/[0-9]/))),
225
226    // Attributes
227    //   attribute-entry ::= (bare-id | string-literal) `=` attribute-value
228    //   attribute-value ::= attribute-alias | dialect-attribute |
229    //   builtin-attribute
230    attribute_entry : $ => seq(choice($.bare_id, $.string_literal), '=',
231                               $.attribute_value),
232    attribute_value : $ => choice($.attribute_alias, $.dialect_attribute,
233                                  $.builtin_attribute),
234
235    // Attribute Value Aliases
236    //   attribute-alias-def ::= '#' alias-name '=' attribute-value
237    //   attribute-alias ::= '#' alias-name
238    attribute_alias_def : $ => seq('#', $.alias_name, '=', $.attribute_value),
239    attribute_alias : $ => seq('#', $.alias_name),
240
241    // Dialect Attribute Values
242    dialect_attribute : $ => seq('#', choice($.opaque_dialect_item,
243                                             $.pretty_dialect_item)),
244
245    // Builtin Attribute Values
246    builtin_attribute : $ => choice(
247                          // TODO
248                          $.function_type,
249                          $.string_literal,
250                          ),
251
252    // Comment (standard BCPL)
253    comment : $ => token(seq('//', /.*/)),
254
255    custom_operation : $ => choice(
256                         // TODO: Just basic/incomplete instance.
257                         seq('func', field('name', $.symbol_ref_id),
258                             $.block_arg_list, '->', $.type, $.region),
259                         ),
260  }
261});
262