forked from OSchip/llvm-project
262 lines
12 KiB
JavaScript
262 lines
12 KiB
JavaScript
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),
|
|
),
|
|
}
|
|
});
|