502 lines
10 KiB
JavaScript
502 lines
10 KiB
JavaScript
const PREC = {
|
|
OR: 0,
|
|
AND: 1,
|
|
COMPARE: 3,
|
|
CONCAT: 5,
|
|
PLUS: 6,
|
|
MINUS: 6,
|
|
MUL: 7,
|
|
DIV: 7,
|
|
ATTR: 1,
|
|
TYPE: 101,
|
|
LET: 100,
|
|
};
|
|
|
|
const OPS = [
|
|
['+', PREC.PLUS],
|
|
['-', PREC.MINUS],
|
|
['mod', PREC.DIV],
|
|
['/', PREC.DIV],
|
|
['*', PREC.MUL],
|
|
['++', PREC.CONCAT],
|
|
['<', PREC.COMPARE],
|
|
['>', PREC.COMPARE],
|
|
['<=', PREC.COMPARE],
|
|
['>=', PREC.COMPARE],
|
|
['==', PREC.COMPARE],
|
|
['!=', PREC.COMPARE],
|
|
['&&', PREC.COMPARE],
|
|
['||', PREC.OR],
|
|
]
|
|
|
|
let sepBy1 = (sep, rule) =>
|
|
seq(
|
|
rule,
|
|
repeat(seq(sep, rule)),
|
|
optional(sep)
|
|
)
|
|
|
|
let sepBy = (sep, rule) => optional(sepBy1(sep, rule))
|
|
|
|
let par = x => seq('(', x, ')')
|
|
let opar = x => seq(optional('('), x, optional(')'))
|
|
let brackets = x => seq('[', x, ']')
|
|
let block = x => seq('{', x, '}')
|
|
|
|
let tuple = x => seq(x, ',', x, repeat(seq(',', x)))
|
|
let list__ = x => brackets(sepBy(';', x))
|
|
|
|
let nseq = x => seq(x, repeat(x))
|
|
|
|
module.exports = grammar({
|
|
name: 'ReasonLigo',
|
|
|
|
word: $ => $.Keyword,
|
|
extras: $ => [$.oneline_comment, $.block_comment, /\s/, $.attr],
|
|
|
|
conflicts: $ =>
|
|
[[$._expr_term, $._pattern]
|
|
, [$._expr_term, $.var_pattern]
|
|
, [$.Name, $.TypeName]
|
|
, [$._expr_term, $.module_name]
|
|
, [$.annot_pattern, $.let_declaration]
|
|
, [$.lambda, $.tuple_pattern]
|
|
, [$._expr_term, $.nullary_constr_pattern]
|
|
, [$.list, $.list_pattern]
|
|
, [$._expr_term, $.lhs]
|
|
, [$._field_name, $.lhs]
|
|
, [$._expr_term, $.capture]
|
|
, [$.type_string, $._literal]
|
|
],
|
|
|
|
rules: {
|
|
contract: $ => sepBy1(optional(';'), field("declaration", $._declaration)),
|
|
|
|
_declaration: $ =>
|
|
field("declaration",
|
|
choice(
|
|
$.type_decl,
|
|
$.let_declaration,
|
|
$.include,
|
|
)
|
|
),
|
|
|
|
//// EXPRESSIONS ///////////////////////////////////////////////////////////
|
|
|
|
let_declaration: $ => prec.left(PREC.LET, seq(
|
|
'let',
|
|
optional(field("rec", $.rec)),
|
|
sepBy1(',', field("binding", $._pattern)),
|
|
optional(seq(
|
|
':',
|
|
field("type", $._type_expr)
|
|
)),
|
|
'=',
|
|
field("value", $._expr),
|
|
)),
|
|
|
|
var_pattern: $ => field("var", $.Name),
|
|
|
|
fun_type: $ =>
|
|
prec.right(8,
|
|
seq(
|
|
field("domain", $._type_expr),
|
|
'=>',
|
|
field("codomain", $._type_expr),
|
|
),
|
|
),
|
|
|
|
module_TypeName: $ =>
|
|
seq(
|
|
$.module_name,
|
|
'.',
|
|
$.TypeName,
|
|
),
|
|
|
|
_expr: $ => choice(
|
|
$.lambda,
|
|
$.indexing,
|
|
$.binary_call,
|
|
$.unary_call,
|
|
$._expr_term,
|
|
$.apply,
|
|
$.Some_call,
|
|
),
|
|
|
|
Some_call: $ => prec.right(10, seq(
|
|
field("some", $.Some),
|
|
field("argument", $._expr),
|
|
)),
|
|
|
|
apply: $ => prec.left(20, seq(
|
|
field("function", $._expr),
|
|
par(sepBy(',', field("argument", $._expr))),
|
|
)),
|
|
|
|
_expr_term: $ => choice(
|
|
$.block,
|
|
$.tuple,
|
|
$.list,
|
|
$._literal,
|
|
$.Name,
|
|
$.Name_Capital,
|
|
$.qualified_name,
|
|
$.if,
|
|
$.switch,
|
|
$.record,
|
|
),
|
|
|
|
record: $ => block(
|
|
seq(
|
|
// TODO: possible multiple spreads
|
|
optional(seq(field("spread", $.spread), ',')),
|
|
sepBy(',', field("assignment", $._record_field)),
|
|
),
|
|
),
|
|
|
|
_record_field: $ => choice(
|
|
$.record_field,
|
|
$.capture,
|
|
),
|
|
|
|
capture: $ => field("name", $.Name),
|
|
|
|
record_field: $ => seq(
|
|
field("name", $.lhs),
|
|
':',
|
|
field("value", $._expr),
|
|
),
|
|
|
|
lhs: $ => seq(
|
|
optional(seq(field("name", $.Name), '.')),
|
|
field("callee", $.Name),
|
|
),
|
|
|
|
list: $ => brackets(
|
|
sepBy(',', field("element", $._spread_expr)),
|
|
),
|
|
|
|
_spread_expr: $ => choice(
|
|
$._expr,
|
|
$.spread,
|
|
),
|
|
|
|
spread: $ => seq(
|
|
'...',
|
|
field("name", $._expr),
|
|
),
|
|
|
|
if: $ => seq(
|
|
'if',
|
|
field("selector", $._expr),
|
|
field("then", $.block),
|
|
optional(seq(
|
|
'else',
|
|
field('else', $.block),
|
|
))
|
|
),
|
|
|
|
switch: $ => seq(
|
|
'switch',
|
|
field("subject", $._expr_term),
|
|
block(seq(
|
|
optional('|'),
|
|
sepBy('|', field("alt", $.alt)),
|
|
))
|
|
),
|
|
|
|
alt: $ => seq(
|
|
field("pattern", $._pattern),
|
|
'=>',
|
|
field("expr", $._expr),
|
|
optional(';'),
|
|
),
|
|
|
|
qualified_name: $ => seq(
|
|
field("expr", $._expr),
|
|
'.',
|
|
field("name", $.Name),
|
|
),
|
|
|
|
binary_call: $ => choice(
|
|
...OPS
|
|
.map(([op, precendence]) =>
|
|
prec.right(precendence, seq(
|
|
field("left", $._expr),
|
|
field("op", $[op]),
|
|
field("right", $._expr),
|
|
))
|
|
)
|
|
),
|
|
|
|
// Workaround to make operators to be statements as well
|
|
// so that they won't be stripped when naming them as fields
|
|
...OPS.reduce(
|
|
(acc, [e, _]) => ({ ...acc, [e]: $ => seq(e) }), {}
|
|
),
|
|
|
|
unary_call: $ => prec.right(8, seq(field("negate", $.negate), field("arg", $._expr_term))),
|
|
|
|
negate: $ => choice('-', '!'),
|
|
|
|
indexing: $ => prec.right(12, seq(
|
|
field("box", $._expr),
|
|
brackets(
|
|
field("index", $._expr),
|
|
)
|
|
)),
|
|
|
|
block: $ => prec(1, block(
|
|
seq(
|
|
sepBy(';', field("statement", $._statement)),
|
|
optional(';'),
|
|
)
|
|
)),
|
|
|
|
_statement: $ => prec(1, choice(
|
|
$.let_declaration,
|
|
$._expr,
|
|
)),
|
|
|
|
tuple: $ =>
|
|
par(sepBy1(',', field("item", $._annot_expr))),
|
|
|
|
_annot_expr: $ => choice(
|
|
$.annot_expr,
|
|
$._expr,
|
|
),
|
|
|
|
annot_expr: $ => seq(
|
|
field("subject", $._expr),
|
|
':',
|
|
field("type", $._type_expr),
|
|
),
|
|
|
|
lambda: $ => prec.right(12, seq(
|
|
par(sepBy(',', field("argument", $._pattern))),
|
|
optional(seq(
|
|
':',
|
|
field("type", $._type_expr),
|
|
)),
|
|
'=>',
|
|
field("body", $._expr),
|
|
)),
|
|
|
|
//// TYPES /////////////////////////////////////////////////////////////////
|
|
|
|
type_decl: $ =>
|
|
seq(
|
|
'type',
|
|
field("type_name", $.TypeName),
|
|
'=',
|
|
field("type_value", $._type_expr),
|
|
),
|
|
|
|
_type_expr: $ =>
|
|
choice(
|
|
$.fun_type,
|
|
$._core_type,
|
|
$.type_tuple,
|
|
$.sum_type,
|
|
$.record_type,
|
|
$.type_string,
|
|
),
|
|
|
|
michelson_tuple: $ => seq(
|
|
'(',
|
|
field("arg1", $._type_expr),
|
|
',',
|
|
field("label1", $.String),
|
|
',',
|
|
field("arg2", $._type_expr),
|
|
',',
|
|
field("label2", $.String),
|
|
')',
|
|
),
|
|
|
|
fun_type: $ =>
|
|
prec.right(8,
|
|
seq(
|
|
field("domain", $._type_expr),
|
|
'=>',
|
|
field("codomain", $._type_expr),
|
|
),
|
|
),
|
|
|
|
_core_type: $ =>
|
|
choice(
|
|
$.type_application,
|
|
$.TypeName,
|
|
// $.module_TypeName,
|
|
),
|
|
|
|
module_TypeName: $ =>
|
|
seq(
|
|
field("module", $.module_name),
|
|
'.',
|
|
field("type", $.TypeName),
|
|
),
|
|
|
|
type_application: $ =>
|
|
seq(
|
|
field("functor", $._core_type),
|
|
field("arguments", $._type_arguments),
|
|
),
|
|
|
|
_type_arguments: $ => choice(
|
|
// $.michelson_tuple,
|
|
par(sepBy(',', field("argument", $._type_expr))),
|
|
),
|
|
|
|
type_string: $ => $.String,
|
|
|
|
type_tuple: $ =>
|
|
par(sepBy1(',', field("element", $._type_expr))),
|
|
|
|
sum_type: $ =>
|
|
prec.left(8,
|
|
seq(
|
|
optional('|'),
|
|
sepBy1('|', field("variant", $.variant)),
|
|
),
|
|
),
|
|
|
|
variant: $ =>
|
|
prec.left(8,
|
|
seq(
|
|
field("constructor", $.Name_Capital),
|
|
optional(par(field("arguments", $._type_expr))),
|
|
),
|
|
),
|
|
|
|
record_type: $ =>
|
|
block(sepBy(',', field("field", $.field_decl))),
|
|
|
|
field_decl: $ =>
|
|
seq(
|
|
field("field_name", $._field_name),
|
|
':',
|
|
field("field_type", $._type_expr),
|
|
),
|
|
|
|
//// PATTERNS //////////////////////////////////////////////////////////////
|
|
|
|
_pattern: $ =>
|
|
choice(
|
|
$.tuple_pattern,
|
|
$._literal,
|
|
$.var_pattern,
|
|
$.annot_pattern,
|
|
$.wildcard,
|
|
$.unary_constr_pattern,
|
|
$.nullary_constr_pattern,
|
|
$.list_pattern,
|
|
),
|
|
|
|
nullary_constr_pattern: $ => seq(
|
|
field("constructor", $.Name_Capital),
|
|
),
|
|
|
|
unary_constr_pattern: $ => prec(1, seq(
|
|
field("constructor", $.Name_Capital),
|
|
field("arg", $._pattern),
|
|
)),
|
|
|
|
constr_pattern: $ => seq(
|
|
field("ctor", $.Name_Capital),
|
|
optional(seq(
|
|
'(',
|
|
sepBy(',', field("arg", $._pattern)),
|
|
')',
|
|
)),
|
|
),
|
|
|
|
annot_pattern: $ => seq(
|
|
field("subject", $._pattern),
|
|
':',
|
|
field("type", $._type_expr),
|
|
),
|
|
|
|
tuple_pattern: $ => prec(13, par(
|
|
sepBy1(',', field("pattern", $._pattern)),
|
|
)),
|
|
|
|
list_pattern: $ => brackets(
|
|
sepBy(',', field("pattern", $._spread_pattern)),
|
|
),
|
|
|
|
_spread_pattern: $ => choice(
|
|
$.spread_pattern,
|
|
$._pattern,
|
|
),
|
|
|
|
spread_pattern: $ => seq(
|
|
'...',
|
|
field("expr", $._pattern),
|
|
),
|
|
|
|
_literal: $ =>
|
|
choice(
|
|
$.Nat,
|
|
$.Int,
|
|
$.Bool,
|
|
$.Tez,
|
|
$.String,
|
|
$.Bytes,
|
|
$.Unit,
|
|
$.Nil,
|
|
$.None,
|
|
),
|
|
|
|
|
|
///////////////////////////////////////////
|
|
|
|
_field_name: $ => $.Name,
|
|
fun_name: $ => $.Name,
|
|
struct_name: $ => $.Name,
|
|
var: $ => $.Name,
|
|
module_name: $ => $.Name_Capital,
|
|
_ident: $ => $.Name,
|
|
|
|
comment: $ => choice(
|
|
$.oneline_comment,
|
|
$.block_comment
|
|
),
|
|
|
|
oneline_comment: $ => token(seq('//', /.*/)),
|
|
|
|
block_comment: $ => seq(
|
|
'/*',
|
|
repeat(/./),
|
|
'*/'
|
|
),
|
|
|
|
include: $ => seq('#include', $.String),
|
|
|
|
String: $ => /\"(\\.|[^"])*\"/,
|
|
Int: $ => /-?([1-9][0-9_]*|0)/,
|
|
Nat: $ => /([1-9][0-9_]*|0)n/,
|
|
Tez: $ => /([1-9][0-9_]*|0)(\.[0-9_]+)?(tz|tez|mutez)/,
|
|
Bytes: $ => /0x[0-9a-fA-F]+/,
|
|
Name: $ => /[a-z][a-zA-Z0-9_]*/,
|
|
TypeName: $ => /[a-z][a-zA-Z0-9_]*/,
|
|
Name_Capital: $ => /[A-Z][a-zA-Z0-9_]*/,
|
|
Keyword: $ => /[A-Za-z][a-z]*/,
|
|
Bool: $ => choice($.False, $.True),
|
|
|
|
include: $ => seq('#include', field("filename", $.String)),
|
|
|
|
False: $ => 'false',
|
|
True: $ => 'true',
|
|
Unit: $ => '()',
|
|
Nil: $ => '[]',
|
|
None: $ => 'None',
|
|
Some: $ => 'Some',
|
|
skip: $ => 'skip',
|
|
rec: $ => 'rec',
|
|
wildcard: $ => '_',
|
|
rec: $ => 'rec',
|
|
|
|
attr: $ => seq('[@', $.Name, ']'),
|
|
}
|
|
}); |