| module.exports = grammar({ |
| name : 'mlir', |
| extras : $ => [/\s/, |
| $.comment, |
| ], |
| conflicts : $ => [], |
| rules : { |
| // Top level production: |
| // (operation | attribute-alias-def | type-alias-def) |
| toplevel : $ => seq(choice( |
| $.operation, |
| $.attribute_alias_def, |
| $.type_alias_def, |
| )), |
| |
| // Common syntax (lang-ref) |
| // digit ::= [0-9] |
| // hex_digit ::= [0-9a-fA-F] |
| // letter ::= [a-zA-Z] |
| // id-punct ::= [$._-] |
| // |
| // integer-literal ::= decimal-literal | hexadecimal-literal |
| // decimal-literal ::= digit+ |
| // hexadecimal-literal ::= `0x` hex_digit+ |
| // float-literal ::= [-+]?[0-9]+[.][0-9]*([eE][-+]?[0-9]+)? |
| // string-literal ::= `"` [^"\n\f\v\r]* `"` TODO: define escaping rules |
| // |
| _digit : $ => /[0-9]/, |
| _hex_digit : $ => /[0-9a-fA-F]/, |
| integer_literal : $ => choice($._decimal_literal, $._hexadecimal_literal), |
| _decimal_literal : $ => repeat1($._digit), |
| _hexadecimal_literal : $ => seq('0x', repeat1($._hex_digit)), |
| float_literal : $ => token( |
| seq(optional(/[-+]/), repeat1(/[0_9]/), |
| optional(seq('.', repeat(/[0-9]/), |
| optional(seq(/[eE]/, optional(/[-+]/), |
| repeat1(/[0-9]/))))))), |
| string_literal : $ => seq( |
| '"', |
| repeat(token.immediate(prec(1, /[^\\"\n\f\v\r]+/))), |
| '"', |
| ), |
| |
| // Identifiers |
| // bare-id ::= (letter|[_]) (letter|digit|[_$.])* |
| // bare-id-list ::= bare-id (`,` bare-id)* |
| // value-id ::= `%` suffix-id |
| // suffix-id ::= (digit+ | ((letter|id-punct) (letter|id-punct|digit)*)) |
| // alias-name :: = bare-id |
| // |
| // symbol-ref-id ::= `@` (suffix-id | string-literal) (`::` |
| // symbol-ref-id)? |
| // value-id-list ::= value-id (`,` value-id)* |
| // |
| // // Uses of value, e.g. in an operand list to an operation. |
| // value-use ::= value-id |
| // value-use-list ::= value-use (`,` value-use)* |
| bare_id : $ => seq(token(/[a-zA-Z_]/), |
| token.immediate(repeat(/[a-zA-Z0-9_$]/))), |
| bare_id_list : $ => seq($.bare_id, repeat(seq(',', $.bare_id))), |
| value_id : $ => seq('%', $._suffix_id), |
| alias_name : $ => $.bare_id, |
| _suffix_id : $ => choice(repeat1(/[0-9]/), |
| seq(/[a-zA-Z_$.]/, repeat(/[a-zA-Z0-9_$.]/))), |
| symbol_ref_id : $ => seq('@', choice($._suffix_id, $.string_literal), |
| optional(seq('::', $.symbol_ref_id))), |
| value_use : $ => $.value_id, |
| value_use_list : $ => seq($.value_use, repeat(seq(',', $.value_use))), |
| |
| // Operations |
| // operation ::= op-result-list? (generic-operation | |
| // custom-operation) |
| // trailing-location? |
| // generic-operation ::= string-literal `(` value-use-list? `)` |
| // successor-list? |
| // region-list? dictionary-attribute? `:` |
| // function-type |
| // custom-operation ::= bare-id custom-operation-format |
| // op-result-list ::= op-result (`,` op-result)* `=` |
| // op-result ::= value-id (`:` integer-literal) |
| // successor-list ::= `[` successor (`,` successor)* `]` |
| // successor ::= caret-id (`:` bb-arg-list)? |
| // region-list ::= `(` region (`,` region)* `)` |
| // dictionary-attribute ::= `{` (attribute-entry (`,` attribute-entry)*)? |
| // `}` |
| // trailing-location ::= (`loc` `(` location `)`)? |
| operation : $ => seq(optional($.op_result_list), |
| choice($.generic_operation, $.custom_operation), |
| optional($.trailing_location)), |
| generic_operation : $ => |
| seq($.string_literal, '(', optional($.value_use_list), |
| ')', optional($.successor_list), |
| optional($.region_list), |
| optional($.dictionary_attribute), ':', |
| $.function_type), |
| // custom-operation rule is defined later in the grammar, post the generic. |
| op_result_list : $ => seq($.op_result, repeat(seq(',', $.op_result)), '='), |
| op_result : $ => seq($.value_id, optional(seq(':', $.integer_literal))), |
| successor_list : $ => seq('[', $.successor, repeat(seq(',', $.successor)), |
| ']'), |
| successor : $ => seq($.caret_id, optional(seq(':', $.block_arg_list))), |
| region_list : $ => seq('(', $.region, repeat(seq(',', $.region)), ')'), |
| dictionary_attribute : $ => seq( |
| '{', |
| optional(seq($.attribute_entry, |
| repeat(seq(',', $.attribute_entry)))), |
| '}'), |
| trailing_location : $ => seq('loc(', $.location, ')'), |
| // TODO: Complete location forms. |
| location : $ => $.string_literal, |
| |
| // Blocks |
| // block ::= block-label operation+ |
| // block-label ::= block-id block-arg-list? `:` |
| // block-id ::= caret-id |
| // caret-id ::= `^` suffix-id |
| // value-id-and-type ::= value-id `:` type |
| // |
| // // Non-empty list of names and types. |
| // value-id-and-type-list ::= value-id-and-type (`,` value-id-and-type)* |
| // |
| // block-arg-list ::= `(` value-id-and-type-list? `)` |
| block : $ => seq($.block_label, repeat1($.operation)), |
| block_label : $ => seq($._block_id, optional($.block_arg_list), ':'), |
| _block_id : $ => $.caret_id, |
| caret_id : $ => seq('^', $._suffix_id), |
| value_id_and_type : $ => seq($.value_id, ':', $.type), |
| value_id_and_type_list : $ => seq($.value_id_and_type, |
| repeat(seq(',', $.value_id_and_type))), |
| block_arg_list : $ => seq('(', optional($.value_id_and_type_list), ')'), |
| |
| // Regions |
| // region ::= `{` entry-block? block* `}` |
| // entry-block ::= operation+ |
| region : $ => seq('{', optional($.entry_block), repeat($.block), '}'), |
| entry_block : $ => repeat1($.operation), |
| |
| // Types |
| // type ::= type-alias | dialect-type | builtin-type |
| // |
| // type-list-no-parens ::= type (`,` type)* |
| // type-list-parens ::= `(` type-list-no-parens? `)` |
| // |
| // // This is a common way to refer to a value with a specified type. |
| // ssa-use-and-type ::= ssa-use `:` type |
| // ssa-use ::= value-use |
| // |
| // // Non-empty list of names and types. |
| // ssa-use-and-type-list ::= ssa-use-and-type (`,` ssa-use-and-type)* |
| // |
| // function-type ::= (type | type-list-parens) `->` (type | |
| // type-list-parens) |
| type : $ => choice($.type_alias, $.dialect_type, $.builtin_type), |
| type_list_no_parens : $ => seq($.type, repeat(seq(',', $.type))), |
| type_list_parens : $ => seq('(', optional($.type_list_no_parens), ')'), |
| ssa_use_and_type : $ => seq($.ssa_use, ':', $.type), |
| ssa_use : $ => $.value_use, |
| ssa_use_and_type_list : $ => seq($.ssa_use_and_type, |
| repeat(seq(',', $.ssa_use_and_type))), |
| function_type : $ => seq(choice($.type, $.type_list_parens), '->', |
| choice($.type, $.type_list_parens)), |
| |
| // Type aliases |
| // type-alias-def ::= '!' alias-name '=' type |
| // type-alias ::= '!' alias-name |
| type_alias_def : $ => seq('!', $.alias_name, '=', $.type), |
| type_alias : $ => seq('!', $.alias_name), |
| |
| // Dialect Types |
| // dialect-namespace ::= bare-id |
| // |
| // opaque-dialect-item ::= dialect-namespace '<' string-literal '>' |
| // |
| // pretty-dialect-item ::= dialect-namespace '.' |
| // pretty-dialect-item-lead-ident |
| // pretty-dialect-item-body? |
| // |
| // pretty-dialect-item-lead-ident ::= '[A-Za-z][A-Za-z0-9._]*' |
| // pretty-dialect-item-body ::= '<' pretty-dialect-item-contents+ '>' |
| // pretty-dialect-item-contents ::= pretty-dialect-item-body |
| // | '(' pretty-dialect-item-contents+ ')' |
| // | '[' pretty-dialect-item-contents+ ']' |
| // | '{' pretty-dialect-item-contents+ '}' |
| // | '[^[<({>\])}\0]+' |
| // |
| // dialect-type ::= '!' (opaque-dialect-item | pretty-dialect-item) |
| dialect_type : $ => seq( |
| '!', choice($.opaque_dialect_item, $.pretty_dialect_item)), |
| dialect_namespace : $ => $.bare_id, |
| opaque_dialect_item : $ => seq($.dialect_namespace, '<', $.string_literal, |
| '>'), |
| pretty_dialect_item : $ => seq($.dialect_namespace, '.', |
| $.pretty_dialect_item_lead_ident, |
| optional($.pretty_dialect_item_body)), |
| pretty_dialect_item_lead_ident : $ => $.bare_id, |
| pretty_dialect_item_body : $ => seq('<', |
| repeat1($.pretty_dialect_item_contents), |
| '>'), |
| // TODO: not sure why prec.left (setting left-associated parsing) needed |
| // here, |
| // left-associated way avoids an ambiguity flagged by generator. It may not |
| // be needed and be only papering over an issue. |
| pretty_dialect_item_contents : $ => prec.left(choice( |
| $.pretty_dialect_item_body, |
| seq('(', |
| repeat1( |
| $.pretty_dialect_item_contents), |
| ')'), |
| seq('[', |
| repeat1( |
| $.pretty_dialect_item_contents), |
| ']'), |
| seq('{', |
| repeat1( |
| $.pretty_dialect_item_contents), |
| '}'), |
| repeat1(/[^\[<({>\])}\\0]/))), |
| dialect_type : $ => seq( |
| '!', choice($.opaque_dialect_item, $.pretty_dialect_item)), |
| |
| // Builtin types |
| builtin_type : $ => choice( |
| // TODO: Add builtin types |
| seq('i', repeat1(/[0-9]/))), |
| |
| // Attributes |
| // attribute-entry ::= (bare-id | string-literal) `=` attribute-value |
| // attribute-value ::= attribute-alias | dialect-attribute | |
| // builtin-attribute |
| attribute_entry : $ => seq(choice($.bare_id, $.string_literal), '=', |
| $.attribute_value), |
| attribute_value : $ => choice($.attribute_alias, $.dialect_attribute, |
| $.builtin_attribute), |
| |
| // Attribute Value Aliases |
| // attribute-alias-def ::= '#' alias-name '=' attribute-value |
| // attribute-alias ::= '#' alias-name |
| attribute_alias_def : $ => seq('#', $.alias_name, '=', $.attribute_value), |
| attribute_alias : $ => seq('#', $.alias_name), |
| |
| // Dialect Attribute Values |
| dialect_attribute : $ => seq('#', choice($.opaque_dialect_item, |
| $.pretty_dialect_item)), |
| |
| // Builtin Attribute Values |
| builtin_attribute : $ => choice( |
| // TODO |
| $.function_type, |
| $.string_literal, |
| ), |
| |
| // Comment (standard BCPL) |
| comment : $ => token(seq('//', /.*/)), |
| |
| custom_operation : $ => choice( |
| // TODO: Just basic/incomplete instance. |
| seq('func', field('name', $.symbol_ref_id), |
| $.block_arg_list, '->', $.type, $.region), |
| ), |
| } |
| }); |