'use strict'; const builtin_dialect = require('./dialect/builtin'); const func_dialect = require('./dialect/func'); const llvm_dialect = require('./dialect/llvm'); const arith_dialect = require('./dialect/arith'); const math_dialect = require('./dialect/math'); const cf_dialect = require('./dialect/cf'); const scf_dialect = require('./dialect/scf'); const memref_dialect = require('./dialect/memref'); const vector_dialect = require('./dialect/vector'); const tensor_dialect = require('./dialect/tensor'); const bufferization_dialect = require('./dialect/bufferization'); const affine_dialect = require('./dialect/affine'); const linalg_dialect = require('./dialect/linalg'); const common = { // Top level production: // (operation | attribute-alias-def | type-alias-def) toplevel : $ => seq($._toplevel, repeat($._toplevel)), _toplevel : $ => 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]/, integer_literal : $ => choice($._decimal_literal, $._hexadecimal_literal), _decimal_literal : $ => token(seq(optional(/[-+]/), repeat1(/[0-9]/))), _hexadecimal_literal : $ => token(seq('0x', repeat1(/[0-9a-fA-F]/))), float_literal : $ => token(seq(optional(/[-+]/), repeat1(/[0-9]/), '.', repeat(/[0-9]/), optional(seq(/[eE]/, optional(/[-+]/), repeat1(/[0-9]/))))), string_literal : $ => token(seq('"', repeat(/[^\\"\n\f\v\r]+/), '"')), bool_literal : $ => token(choice('true', 'false')), unit_literal : $ => token('unit'), complex_literal : $ => seq('(', choice($.integer_literal, $.float_literal), ',', choice($.integer_literal, $.float_literal), ')'), tensor_literal : $ => seq(token(choice('dense', 'sparse')), '<', optional(choice( seq($.nested_idx_list, repeat(seq(',', $.nested_idx_list))), $._primitive_idx_literal)), '>'), array_literal : $ => seq(token('array'), '<', $.type, ':', $._idx_list, '>'), _literal : $ => choice($.integer_literal, $.float_literal, $.string_literal, $.bool_literal, $.tensor_literal, $.array_literal, $.complex_literal, $.unit_literal), nested_idx_list : $ => seq('[', optional(choice($.nested_idx_list, $._idx_list)), repeat(seq(',', $.nested_idx_list)), ']'), _idx_list : $ => prec.right(seq($._primitive_idx_literal, repeat(seq(',', $._primitive_idx_literal)))), _primitive_idx_literal : $ => choice($.integer_literal, $.float_literal, $.bool_literal, $.complex_literal), // 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 : $ => token(seq(/[a-zA-Z_]/, repeat(/[a-zA-Z0-9_$.]/))), _alias_or_dialect_id : $ => token(seq(/[a-zA-Z_]/, repeat(/[a-zA-Z0-9_$]/))), bare_id_list : $ => seq($.bare_id, repeat(seq(',', $.bare_id))), value_use : $ => seq('%', $._suffix_id), _suffix_id : $ => token(seq( choice(repeat1(/[0-9]/), seq(/[a-zA-Z_$.-]/, repeat(/[a-zA-Z0-9_$.-]/))), optional(seq(choice(':', '#'), repeat1(/[0-9]/))))), symbol_ref_id : $ => seq('@', choice($._suffix_id, $.string_literal), optional(seq('::', $.symbol_ref_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(field('lhs', optional($._op_result_list)), field('rhs', choice($.generic_operation, $.custom_operation)), field('location', optional($.trailing_location))), generic_operation : $ => seq( $.string_literal, $._value_use_list_parens, optional($._successor_list), optional($._region_list), optional($.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_use, optional(seq(':', $.integer_literal))), _successor_list : $ => seq('[', $.successor, repeat(seq(',', $.successor)), ']'), successor : $ => seq($.caret_id, optional($._value_arg_list)), _region_list : $ => seq('(', $.region, repeat(seq(',', $.region)), ')'), dictionary_attribute : $ => seq('{', optional($.attribute_entry), repeat(seq(',', $.attribute_entry)), '}'), trailing_location : $ => seq(token('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_use_and_type : $ => seq($.value_use, optional(seq(':', $.type))), _value_use_and_type_list : $ => seq($._value_use_and_type, repeat(seq(',', $._value_use_and_type))), block_arg_list : $ => seq('(', optional($._value_use_and_type_list), ')'), _value_arg_list : $ => seq('(', optional($._value_use_type_list), ')'), _value_use_type_list : $ => seq($._value_use_list, $._type_annotation), // 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 : $ => prec.left(seq($.type, repeat(seq(',', $.type)))), _type_list_parens : $ => seq('(', optional($._type_list_no_parens), ')'), function_type : $ => seq(choice($.type, $._type_list_parens), $._function_return), _function_return : $ => seq(token('->'), choice($.type, $._type_list_parens)), _type_annotation : $ => seq(':', choice(seq($.type, choice('from', 'into', 'to'), $.type), $._type_list_no_parens)), _function_type_annotation : $ => seq(':', $.function_type), _literal_and_type : $ => seq($._literal, optional($._type_annotation)), // Type aliases // type-alias-def ::= '!' alias-name '=' type // type-alias ::= '!' alias-name type_alias_def : $ => seq('!', $._alias_or_dialect_id, '=', $.type), type_alias : $ => seq('!', $._alias_or_dialect_id), // 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 : $ => $._alias_or_dialect_id, dialect_ident : $ => $._alias_or_dialect_id, opaque_dialect_item : $ => seq($.dialect_namespace, '<', $.string_literal, '>'), pretty_dialect_item : $ => seq($.dialect_namespace, '.', $.dialect_ident, optional($.pretty_dialect_item_body)), pretty_dialect_item_body : $ => seq('<', repeat($._pretty_dialect_item_contents), '>'), _pretty_dialect_item_contents : $ => prec.left(choice($.pretty_dialect_item_body, repeat1(/[^<>]/))), // Builtin types builtin_type : $ => choice( // TODO: Add opaque_type $.integer_type, $.float_type, $.complex_type, $.index_type, $.memref_type, $.none_type, $.tensor_type, $.vector_type, $.tuple_type), // signed-integer-type ::= `si`[1-9][0-9]* // unsigned-integer-type ::= `ui`[1-9][0-9]* // signless-integer-type ::= `i`[1-9][0-9]* // integer-type ::= signed-integer-type | unsigned-integer-type | // signless-integer-type integer_type : $ => token(seq(choice('si', 'ui', 'i'), /[1-9]/, repeat(/[0-9]/))), float_type : $ => token( choice('f16', 'f32', 'f64', 'f80', 'f128', 'bf16', 'f8E4M3FN', 'f8E5M2')), index_type : $ => token('index'), none_type : $ => token('none'), complex_type : $ => seq(token('complex'), '<', $._prim_type, '>'), _prim_type : $ => choice($.integer_type, $.float_type, $.index_type, $.complex_type, $.none_type, $.memref_type, $.vector_type, $.tensor_type), // memref-type ::= `memref` `<` dimension-list-ranked type // (`,` layout-specification)? (`,` memory-space)? `>` // layout-specification ::= attribute-value // memory-space ::= attribute-value memref_type : $ => seq(token('memref'), '<', field('dimension_list', $.dim_list), optional(seq(',', $.attribute_value)), optional(seq(',', $.attribute_value)), '>'), dim_list : $ => seq($._dim_primitive, repeat(seq('x', $._dim_primitive))), _dim_primitive : $ => choice($._prim_type, repeat1($._digit), '?', '*'), // tensor-type ::= `tensor` `<` dimension-list type (`,` encoding)? `>` // dimension-list ::= (dimension `x`)* // dimension ::= `?` | decimal-literal // encoding ::= attribute-value // tensor-type ::= `tensor` `<` `*` `x` type `>` tensor_type : $ => seq(token('tensor'), '<', $.dim_list, optional(seq(',', $.tensor_encoding)), '>'), tensor_encoding : $ => $.attribute_value, // vector-type ::= `vector` `<` vector-dim-list vector-element-type `>` // vector-element-type ::= float-type | integer-type | index-type // vector-dim-list := (static-dim-list `x`)? (`[` static-dim-list `]` `x`)? // static-dim-list ::= decimal-literal (`x` decimal-literal)* vector_type : $ => seq(token('vector'), '<', optional($.vector_dim_list), $._prim_type, '>'), vector_dim_list : $ => choice(seq($._static_dim_list, 'x', optional(seq('[', $._static_dim_list, ']', 'x'))), seq('[', $._static_dim_list, ']', 'x')), _static_dim_list : $ => seq(repeat1($._digit), repeat(seq('x', repeat1($._digit)))), // tuple-type ::= `tuple` `<` (type ( `,` type)*)? `>` tuple_type : $ => seq(token('tuple'), '<', $.tuple_dim, repeat(seq(',', $.tuple_dim)), '>'), tuple_dim : $ => $._prim_type, // 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), optional(seq('=', $.attribute_value))), attribute_value : $ => choice(seq('[', optional($._attribute_value_nobracket), repeat(seq(',', $._attribute_value_nobracket)), ']'), $._attribute_value_nobracket), _attribute_value_nobracket : $ => choice($.attribute_alias, $.dialect_attribute, $.builtin_attribute, $.dictionary_attribute, $._literal_and_type, $.type), attribute : $ => choice($.attribute_alias, $.dialect_attribute, $.builtin_attribute, $.dictionary_attribute), // Attribute Value Aliases // attribute-alias-def ::= '#' alias-name '=' attribute-value // attribute-alias ::= '#' alias-name attribute_alias_def : $ => seq('#', $._alias_or_dialect_id, '=', $.attribute_value), attribute_alias : $ => seq('#', $._alias_or_dialect_id), // Dialect Attribute Values dialect_attribute : $ => seq('#', choice($.opaque_dialect_item, $.pretty_dialect_item)), // Builtin Attribute Values builtin_attribute : $ => choice( // TODO $.strided_layout, $.affine_map, $.affine_set), strided_layout : $ => seq(token('strided'), '<', '[', $._dim_list_comma, ']', optional(seq(',', token('offset'), ':', choice($.integer_literal, '?', '*'))), '>'), _dim_list_comma : $ => seq($._dim_primitive, repeat(seq(',', $._dim_primitive))), affine_map : $ => seq(token('affine_map'), '<', $._multi_dim_affine_expr_parens, optional($._multi_dim_affine_expr_sq), token('->'), $._multi_dim_affine_expr_parens, '>'), affine_set : $ => seq(token('affine_set'), '<', $._multi_dim_affine_expr_parens, optional($._multi_dim_affine_expr_sq), ':', $._multi_dim_affine_expr_parens, '>'), _multi_dim_affine_expr_parens : $ => seq('(', optional($._multi_dim_affine_expr), ')'), _multi_dim_affine_expr_sq : $ => seq('[', optional($._multi_dim_affine_expr), ']'), // affine-expr ::= `(` affine-expr `)` // | affine-expr `+` affine-expr // | affine-expr `-` affine-expr // | `-`? integer-literal `*` affine-expr // | affine-expr `ceildiv` integer-literal // | affine-expr `floordiv` integer-literal // | affine-expr `mod` integer-literal // | `-`affine-expr // | bare-id // | `-`? integer-literal // multi-dim-affine-expr ::= `(` `)` // | `(` affine-expr (`,` affine-expr)* `)` // semi-affine-expr ::= `(` semi-affine-expr `)` // | semi-affine-expr `+` semi-affine-expr // | semi-affine-expr `-` semi-affine-expr // | symbol-or-const `*` semi-affine-expr // | semi-affine-expr `ceildiv` symbol-or-const // | semi-affine-expr `floordiv` symbol-or-const // | semi-affine-expr `mod` symbol-or-const // | bare-id // | `-`? integer-literal // symbol-or-const ::= `-`? integer-literal | symbol-id // multi-dim-semi-affine-expr ::= `(` semi-affine-expr (`,` semi-affine-expr)* // `)` // affine-constraint ::= affine-expr `>=` `affine-expr` // | affine-expr `<=` `affine-expr` // | affine-expr `==` `affine-expr` // affine-constraint-conjunction ::= affine-constraint (`,` // affine-constraint)* _multi_dim_affine_expr : $ => seq($._affine_expr, repeat(seq(',', $._affine_expr))), _affine_expr : $ => prec.right(choice( seq('(', $._affine_expr, ')'), seq('-', $._affine_expr), seq($._affine_expr, $._affine_token, $._affine_expr), $._affine_prim)), _affine_prim : $ => choice($.integer_literal, $.value_use, $.bare_id, seq('symbol', '(', $.value_use, ')'), seq(choice('max', 'min'), '(', $._value_use_list, ')')), _affine_token : $ => token( choice('+', '-', '*', 'ceildiv', 'floordiv', 'mod', '==', '>=', '<=')), func_return : $ => seq(token('->'), $.type_list_attr_parens), func_arg_list : $ => seq( '(', optional(choice($.variadic, $._value_id_and_type_attr_list)), ')'), _value_id_and_type_attr_list : $ => seq( $._value_id_and_type_attr, repeat(seq(',', $._value_id_and_type_attr)), optional(seq(',', $.variadic))), _value_id_and_type_attr : $ => seq($._function_arg, optional($.attribute)), _function_arg : $ => choice(seq($.value_use, ':', $.type), $.value_use, $.type), type_list_attr_parens : $ => choice($.type, seq('(', $.type, optional($.attribute), repeat(seq(',', $.type, optional($.attribute))), ')'), seq('(', ')')), variadic : $ => token('...'), // (func.func|llvm.func) takes arguments, an optional return type, and and // optional body _op_func : $ => seq(field('visibility', optional('private')), field('name', $.symbol_ref_id), field('arguments', $.func_arg_list), field('return', optional($.func_return)), field('attributes', optional(seq(token('attributes'), $.attribute))), field('body', optional($.region))), // dim-use-list ::= `(` ssa-use-list? `)` // symbol-use-list ::= `[` ssa-use-list? `]` // dim-and-symbol-use-list ::= dim-use-list symbol-use-list? _value_use_list_parens : $ => seq('(', optional($._value_use_list), ')'), _dim_and_symbol_use_list : $ => seq($._value_use_list_parens, optional($._dense_idx_list)), // assignment-list ::= assignment | assignment `,` assignment-list // assignment ::= ssa-value `=` ssa-value _value_assignment_list : $ => seq('(', optional($._value_assignment), repeat(seq(',', $._value_assignment)), ')'), _value_assignment : $ => seq($.value_use, '=', $.value_use), _dense_idx_list : $ => seq( '[', optional(seq(choice($.integer_literal, $.value_use), repeat(seq(',', choice($.integer_literal, $.value_use))))), ']'), // lower-bound ::= `max`? affine-map-attribute dim-and-symbol-use-list | // shorthand-bound // upper-bound ::= `min`? affine-map-attribute dim-and-symbol-use-list | // shorthand-bound // shorthand-bound ::= ssa-id | `-`? integer-literal _bound : $ => choice(seq($.attribute, $._dim_and_symbol_use_list), $._shorthand_bound), _shorthand_bound : $ => choice($.value_use, $.integer_literal), // Dialect-specific attributes restrict_attr : $ => token('restrict'), writable_attr : $ => token('writable'), gather_dims_attr : $ => seq(token('gather_dims'), '(', $._dense_idx_list, ')'), scatter_dims_attr : $ => seq(token('scatter_dims'), '(', $._dense_idx_list, ')'), unique_attr : $ => token('unique'), nofold_attr : $ => token('nofold'), outer_dims_perm_attr : $ => seq(token('outer_dims_perm'), '=', $._dense_idx_list), inner_dims_pos_attr : $ => seq(token('inner_dims_pos'), '=', $._dense_idx_list), inner_tiles_attr : $ => seq(token('inner_tiles'), '=', $._dense_idx_list), isWrite_attr : $ => token(choice('read', 'write')), localityHint_attr : $ => seq(token('locality'), '<', $.integer_literal, '>'), isDataCache_attr : $ => token(choice('data', 'instr')), fastmath_attr : $ => seq(token('fastmath'), '<', seq($._fastmath_flag, repeat(seq(',', $._fastmath_flag))), '>'), _fastmath_flag : $ => token(choice('none', 'reassoc', 'nnan', 'ninf', 'nsz', 'arcp', 'contract', 'afn', 'fast')), // Comment (standard BCPL) comment : $ => token(seq('//', /.*/)), // TODO: complete custom_operation : $ => choice($.builtin_dialect, $.func_dialect, $.llvm_dialect, $.arith_dialect, $.math_dialect, $.cf_dialect, $.scf_dialect, $.memref_dialect, $.vector_dialect, $.tensor_dialect, $.bufferization_dialect, $.affine_dialect, $.linalg_dialect) } module.exports = grammar({ name : 'mlir', extras : $ => [/\s/, $.comment], conflicts : $ => [[ $._static_dim_list, $._static_dim_list ], [ $.dictionary_attribute, $.region ]], rules : Object.assign(common, builtin_dialect, func_dialect, llvm_dialect, arith_dialect, math_dialect, cf_dialect, scf_dialect, memref_dialect, vector_dialect, tensor_dialect, bufferization_dialect, affine_dialect, linalg_dialect) });