ligo/tools/lsp/squirrel/grammar/reasonligo/grammar.js

502 lines
10 KiB
JavaScript
Raw Normal View History

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: $ => $.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(
'...',
$._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(
'(',
$._type_expr,
',',
$.String,
',',
$._type_expr,
',',
$.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, ']'),
}
});