ligo/tools/lsp/pascaligo/grammar.js
2020-08-11 12:32:52 +04:00

823 lines
17 KiB
JavaScript

let sepBy1 = (sep, rule) =>
seq(
rule,
repeat(seq(sep, rule)),
optional(sep)
)
let sepBy = (sep, rule) => optional(sepBy1(sep, rule))
let op = (name, left, right, term) =>
choice(
seq(left, name, right),
term,
)
let right_op = (name, left, right) => op(name, left, right, right)
let left_op = (name, left, right) => op(name, left, right, left)
let par = x => seq('(', x, ')')
let brackets = x => seq('[', x, ']')
let ne_injection = (Kind, element) =>
choice(
seq(
Kind,
sepBy1(';', element),
'end',
),
seq(
Kind,
'[',
sepBy1(';', element),
']',
),
)
let injection = (Kind, element) =>
choice(
seq(
Kind,
sepBy(';', element),
'end',
),
seq(
Kind,
'[',
sepBy(';', element),
']',
),
)
module.exports = grammar({
name: 'PascaLigo',
word: $ => $.Keyword,
extras: $ => [$.ocaml_comment, $.comment, /\s/],
rules: {
Start: $ => sepBy(optional(';'), field("declaration", $._declaration)),
_declaration: $ =>
choice(
$.type_decl,
$.const_decl,
$.fun_decl,
$.attr_decl,
$.include,
),
attr_decl: $ =>
injection("attributes",
field("attribute", $.String)
),
type_decl: $ =>
seq(
"type",
field("typeName", $.TypeName),
"is",
field("typeValue", $._type_expr),
),
_type_expr: $ =>
choice(
$._fun_type,
$.sum_type,
$.record_type,
),
_fun_type: $ =>
choice(
$.fun_type,
$.cartesian
),
fun_type: $ =>
seq(
field("domain", $.cartesian),
'->',
field("codomain", $._fun_type),
),
cartesian: $ =>
sepBy1('*',
choice(
field("element", $._core_type),
par(field("element", $._type_expr)),
),
),
_core_type: $ =>
choice(
$.TypeName,
$.invokeBinary,
$.invokeUnary,
$.michelsonTypeOr,
$.michelsonTypeAnd,
),
michelsonTypeOr: $ =>
seq(
"michelson_or",
"(",
field("left_type", $._type_expr),
",",
field("left_type_name", $.String),
",",
field("right_type", $._type_expr),
",",
field("right_type_name", $.String),
")",
),
michelsonTypeAnd: $ =>
seq(
"michelson_pair",
"(",
field("left_type", $._type_expr),
",",
field("left_type_name", $.String),
",",
field("right_type", $._type_expr),
",",
field("right_type_name", $.String),
")",
),
invokeBinary: $ =>
seq(
field("typeConstr", choice('map', 'big_map')),
field("arguments", $.type_tuple),
),
invokeUnary: $ =>
seq(
field("typeConstr", choice('list', 'set', 'option', 'contract')),
par(field("arguments", $._type_expr)),
),
map: $ => 'map',
big_map: $ => 'big_map',
list: $ => 'list',
set: $ => 'set',
type_tuple: $ => par(sepBy1(',', field("element", $._type_expr))),
sum_type: $ =>
seq(
optional('|'),
sepBy1('|', field("variant", $.variant)),
),
variant: $ =>
choice(
field("constructor", $.constr),
seq(
field("constructor", $.constr),
'of',
field("arguments", $._fun_type)
),
),
constr: $ => $.Name_Capital,
record_type: $ =>
choice(
seq('record', sepBy(';', field("field", $.field_decl)), 'end'),
seq('record', '[', sepBy(';', field("field", $.field_decl)), ']'),
),
field_decl: $ =>
seq(
field("fieldName", $.FieldName),
':',
field("fieldType", $._type_expr),
),
fun_expr: $ =>
seq(
field("recursive", optional($.recursive)),
'function',
field("parameters", $.parameters),
':',
field("type", $._type_expr),
'is',
field("body", $._expr),
),
fun_decl: $ =>
prec.right(0,
seq(
field("recursive", optional($.recursive)),
'function',
field("name", $.Name),
field("parameters", $.parameters),
':',
field("type", $._type_expr),
'is',
field("body", $._let_expr),
),
),
_let_expr: $ =>
choice(
$.let_expr,
$._expr,
),
let_expr: $ =>
seq(
field("locals", $.block),
'with',
field("body", $._expr),
),
parameters: $ => par(sepBy(';', field("parameter", $.param_decl))),
param_decl: $ =>
seq(
field("access", $._access),
field("name", $.Name),
':',
field("type", $._param_type),
),
_access: $ => choice('var', 'const'),
_param_type: $ => $._fun_type,
_statement: $ =>
choice(
$._instruction,
$._open_data_decl,
$.attr_decl,
),
_open_data_decl: $ =>
choice(
$.const_decl,
$.var_decl,
$.fun_decl,
),
const_decl: $ =>
seq(
'const',
field("name", $.Name),
':',
field("type", $._type_expr),
'=',
field("value", $._expr),
),
var_decl: $ =>
seq(
'var',
field("name", $.Name),
':',
field("type", $._type_expr),
':=',
field("value", $._expr),
),
_instruction: $ =>
choice(
$.conditional,
$.case_instr,
$.assignment,
$._loop,
$._proc_call,
$.skip,
$.record_patch,
$.map_patch,
$.set_patch,
$.map_remove,
$.set_remove,
),
set_remove: $ =>
seq(
'remove',
field("key", $._expr),
'from',
'set',
field("container", $._path),
),
map_remove: $ =>
seq(
'remove',
field("key", $._expr),
'from',
'map',
field("container", $._path),
),
set_patch: $ =>
seq(
'patch',
field("container", $._path),
'with',
ne_injection('set', field("key", $._expr)),
),
map_patch: $ =>
seq(
'patch',
field("container", $._path),
'with',
ne_injection('map', field("binding", $.binding)),
),
binding: $ =>
seq(
field("key", $._expr),
'->',
field("value", $._expr),
),
record_patch: $ =>
seq(
'patch',
field("container", $._path),
'with',
ne_injection('record', field("binding", $.field_assignment)),
),
_proc_call: $ =>
$.fun_call,
conditional: $ =>
seq(
'if',
field("selector", $._expr),
'then',
field("then", $._if_clause),
optional(';'),
'else',
field("else", $._if_clause),
),
_if_clause: $ =>
choice(
$._instruction,
$.clause_block,
),
clause_block: $ =>
choice(
field("block", $.block),
seq('{', sepBy1(';', field("statement", $._statement)), '}')
),
block: $ =>
choice(
seq(
'begin',
sepBy(';', field("statement", $._statement)),
'end',
),
seq(
'block',
'{',
sepBy(';', field("statement", $._statement)),
'}',
),
),
case_instr: $ =>
choice(
seq(
'case',
field("subject", $._expr),
'of',
optional('|'),
sepBy1('|', field("case", $.case_clause_instr)),
'end'
),
seq(
'case',
$._expr,
'of',
'[',
optional('|'),
sepBy1('|', field("case", $.case_clause_instr)),
']'
),
),
case_clause_instr: $ =>
seq(
field("pattern", $._pattern),
'->',
field("body", $._if_clause),
),
assignment: $ =>
seq(
field("LHS", $._lhs),
':=',
field("RHS", $._rhs),
),
_rhs: $ => $._expr,
_lhs: $ => choice($._path, $.map_lookup),
_loop: $ => choice($.while_loop, $._for_loop),
while_loop: $ =>
seq(
'while',
field("breaker", $._expr),
field("body", $.block),
),
_for_loop: $ =>
choice(
$.for_cycle,
$.for_box,
),
for_cycle: $ =>
seq(
'for',
field("name", $.Name),
':=',
field("begin", $._rhs),
'to',
field("end", $._expr),
optional(seq(
"step",
field("step", $._expr),
)),
field("body", $.block),
),
for_box: $ =>
seq(
'for',
field("key", $.Name),
optional(seq('->', field("value", $.Name))),
'in',
field("kind", $._collection),
field("collection", $._expr),
field("body", $.block),
),
_collection: $ => choice('map', 'set', 'list'),
interactive_expr: $ => $._expr,
_expr: $ =>
choice(
$.case_expr,
$.cond_expr,
$._op_expr,
$.fun_expr,
),
case_expr: $ =>
choice(
seq(
'case',
field("subject", $._expr),
'of',
optional('|'),
sepBy1('|', field("case", $.case_clause_expr)),
'end'
),
seq(
'case',
field("subject", $._expr),
'of',
'[',
optional('|'),
sepBy1('|', field("case", $.case_clause_expr)),
']'
),
),
case_clause_expr: $ =>
seq(
field("pattern", $._pattern),
'->',
field("body", $._expr),
),
cond_expr: $ =>
seq(
'if',
field("selector", $._expr),
'then',
field("then", $._expr),
optional(';'),
'else',
field("else", $._expr),
),
_op_expr: $ =>
choice(
$._core_expr,
$.binop,
$.unop,
),
binop: $ =>
choice(
prec.left (0, seq(field("arg1", $._op_expr), field("op", 'or'), field("arg2", $._op_expr))),
prec.left (1, seq(field("arg1", $._op_expr), field("op", 'and'), field("arg2", $._op_expr))),
prec.right(2, seq(field("arg1", $._core_expr), field("op", 'contains'), field("arg2", $._op_expr))),
prec.left (3, seq(field("arg1", $._op_expr), field("op", $.comparison), field("arg2", $._op_expr))),
prec.right(4, seq(field("arg1", $._op_expr), field("op", '^'), field("arg2", $._op_expr))),
prec.right(5, seq(field("arg1", $._op_expr), field("op", '#'), field("arg2", $._op_expr))),
prec.left (6, seq(field("arg1", $._op_expr), field("op", $.adder), field("arg2", $._op_expr))),
prec.left (7, seq(field("arg1", $._op_expr), field("op", $.multiplier), field("arg2", $._op_expr))),
),
unop: $ => prec.right(8, seq(field("negate", $.negate), field("arg", $._core_expr))),
comparison: $ => choice('<', '<=', '>', '>=', '=', '=/='),
adder: $ => choice('-', '+'),
multiplier: $ => choice('/', '*', 'mod'),
negate: $ => choice('-', 'not'),
_core_expr: $ =>
choice(
$.Int,
$.Nat,
$.Tez,
$.Name,
$.module_field,
$.String,
$.Bytes,
$.False,
$.True,
$.Unit,
$.annot_expr,
$.tuple_expr,
$._list_expr,
$.None,
$._fun_call_or_par_or_projection,
$._map_expr,
$.set_expr,
$.record_expr,
$.update_record,
$._constr_use,
$.Some_call,
),
_constr_use: $ =>
choice(
$.constr_call,
$.constr
),
constr_call: $ =>
seq(
field("constr", $.constr),
field("arguments", $.arguments)
),
Some_call: $ =>
seq(
field("constr", 'Some'),
field("arguments", $.arguments),
),
_fun_call_or_par_or_projection: $ =>
choice(
$.par_call,
$.projection_call,
$.fun_call,
$._projection,
),
par_call: $ =>
prec.right(1, seq(
par(field("f", $._expr)),
field("arguments", $.arguments),
)),
projection_call: $ => prec(1, seq(
field("f", $._projection),
field("arguments", $.arguments),
)),
annot_expr: $ =>
par(seq(
field("subject", $._op_expr),
':',
field("type", $._type_expr)
)),
set_expr: $ => injection('set', field("element", $._expr)),
_map_expr: $ =>
choice(
$.map_lookup,
$.map_injection,
$.big_map_injection,
),
map_injection: $ => injection('map', field("binding", $.binding)),
big_map_injection: $ => injection('big_map', field("binding", $.binding)),
map_lookup: $ =>
seq(
field("container", $._path),
brackets(field("index", $._expr)),
),
_path: $ => choice($.Name, $._projection),
_fpath: $ => choice($.FieldName, $._projection),
module_field: $ =>
seq(
field("module", $.Name_Capital),
'.',
field("method", $._module_fun),
),
_module_fun: $ =>
choice(
$.Name,
$.map,
$.or,
$.and,
$.remove,
),
or: $ => 'or',
and: $ => 'and',
remove: $ => 'remove',
_projection: $ =>
choice(
$.data_projection,
$.module_projection,
),
data_projection: $ => seq(
field("struct", $.Name),
'.',
sepBy1('.', field("index", $._selection)),
),
module_projection: $ =>
seq(
field("module", $.Name_Capital),
'.',
field("index", $.Name),
'.',
sepBy1('.', field("index", $._selection)),
),
_selection: $ => choice($.FieldName, $.Int),
record_expr: $ =>
choice(
seq(
'record',
sepBy(';', field("assignment", $.field_assignment)),
'end',
),
seq(
'record',
'[',
sepBy(';', field("assignment", $.field_assignment)),
']',
),
),
update_record: $ =>
seq(
field("record", $._path),
'with',
ne_injection('record', field("assignment", $.field_path_assignment)),
),
field_assignment: $ =>
seq(
field("name", $.FieldName),
'=',
field("_rhs", $._expr),
),
field_path_assignment: $ =>
seq(
field("lhs", $._fpath),
'=',
field("_rhs", $._expr),
),
fun_call: $ =>
seq(
field("f", choice($.Name, $.module_field)),
field("arguments", $.arguments),
),
tuple_expr: $ => par(sepBy1(',', field("element", $._expr))),
arguments: $ => par(sepBy(',', field("argument", $._expr))),
_list_expr: $ => choice($.list_injection, 'nil'),
list_injection: $ => injection('list', field("element", $._expr)),
_pattern: $ =>
choice(
$.cons_pattern,
$._core_pattern,
),
_core_pattern: $ =>
choice(
$.Name,
'_',
$.Int,
$.Nat,
$.String,
$._list_pattern,
$.tuple_pattern,
$._constr_pattern,
),
_list_pattern: $ =>
choice(
$.list_pattern,
'nil',
),
list_pattern: $ => injection("list", field("element", $._pattern)),
cons_pattern: $ =>
seq(
field("head", $._core_pattern),
'#',
field("tail", $._pattern),
),
tuple_pattern: $ =>
par(sepBy1(',', field("element", $._pattern))),
_constr_pattern: $ => choice(
$.Unit,
$.False,
$.True,
$.None,
$.Some_pattern,
$.user_constr_pattern,
),
Some_pattern: $ =>
seq(
field("constr", 'Some'),
par(field("arg", $._pattern)),
),
user_constr_pattern: $ =>
seq(
field("constr", $.constr),
optional(field("arguments", $.tuple_pattern)),
),
///////////////////////////////////////////
comment: $ => /\/\/[^\n]*\n/,
ocaml_comment: $ =>
seq(
'(*',
repeat(choice(
$.ocaml_comment,
/'([^'\\]|\\[\\"'ntbr ]|\\[0-9][0-9][0-9]|\\x[0-9A-Fa-f][0-9A-Fa-f]|\\o[0-3][0-7][0-7])'/,
/"([^\\"]|\\(.|\n))*"/,
/[A-Za-z_][a-zA-Z0-9_']*/,
/[^('"{*A-Za-z_]+/,
'(', "'", '*',
)),
'*)'
),
include: $ => seq('#include', field("filename", $.String)),
String: $ => choice(/\"(\\.|[^"])*\"/, /{\|(\\.|[^\|])*\|}/),
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]+/,
FieldName: $ => /[a-z][a-zA-Z0-9_]*/,
TypeName: $ => /[a-z][a-zA-Z0-9_]*/,
Name: $ => /[a-z][a-zA-Z0-9_]*/,
Name_Capital: $ => /[A-Z][a-zA-Z0-9_]*/,
Keyword: $ => /[A-Za-z][a-z]*/,
False: $ => 'False',
True: $ => 'True',
Unit: $ => 'Unit',
None: $ => 'None',
skip: $ => 'skip',
recursive: $ => 'recursive',
}
});