ligo/tools/lsp/squirrel/grammar/camligo/grammar.js
Kirill Kuvshinov 04724a1db2
[LIGO-47] Allow let .. in in lambda body
Problem: In multisig.mligo, else clause on line 36 ends on line 55,
while it should end on line 80. This happens because of incorrect
lambda parsing on lines 42-43. In particular, `let valid, keys = vk`
in lambda body gets parsed as a
`tuple (fun_app "let" "valid") (binary_op "=" "keys" "vk")`, and `in`
is considered as a part of the outer `let`.

Solution: Allow `_program` instead of `_expr` as the lambda body.
2020-09-15 11:22:15 +03:00

422 lines
8.4 KiB
JavaScript

let sepBy1 = (sep, p) => seq(p, repeat(seq(sep, p)))
let sepBy = (sep, p) => optional(sepBy1(sep, p))
let some = x => seq(x, repeat(x))
function mkOp($, opExpr) {
return seq(
field("left", $._expr),
field("op", opExpr),
field("right", $._expr)
);
}
module.exports = grammar({
name: 'CameLigo',
word: $ => $.Keyword,
extras: $ => [$.ocaml_comment, $.comment, /\s/],
rules: {
contract: $ => repeat(field("declaration", $._declaration)),
_declaration: $ => choice(
$.let_decl,
$.fun_decl,
$.type_decl,
$.include,
),
include: $ => seq(
'#include',
field("filename", $.String)
),
_attribute: $ => /\[@@[a-z]+\]/,
fun_decl: $ => seq(
"let",
optional(field("recursive", "rec")),
field("name", $.Name),
some(field("arg", $._paren_pattern)),
optional(seq(
":",
field("type", $._type_expr)
)),
"=",
field("body",$._program),
repeat(field("attribute", $._attribute))
),
let_decl: $ => seq(
"let",
optional(field("recursive", "rec")),
field("name", $._pattern),
optional(seq(
":",
field("type", $._type_expr)
)),
"=",
field("body",$._program),
repeat(field("attribute", $._attribute))
),
//========== EXPR ============
_program: $ => choice(
$.let_expr1,
$._expr
),
let_expr1: $ => seq(
field("decl", $.let_decl),
"in",
field("body", $._program)
),
// [1;2]
list_pattern: $ => seq(
"[",
sepBy(';', field("item", $._pattern)),
"]"
),
// a :: b
list_con_pattern: $ => prec.right(9, seq(
field("x", $._pattern),
"::",
field("xs", $._pattern)
)),
// a, b, c
tup_pattern: $ => prec.right(8,seq(
field("item", $._pattern),
",",
sepBy1(",", field("item", $._pattern))
)),
_pattern: $ => choice(
$.var_pattern,
$._paren_pattern,
$.con_pattern,
$._literal,
$.list_pattern,
$.list_con_pattern,
$.tup_pattern,
"_"
),
var_pattern: $ => seq(
field("var", $.Name)
),
con_pattern: $ => prec(10,
seq(
field("ctor", $.data_con),
optional(field("args",$._pattern))
)
),
_paren_pattern: $ => choice(
$.annot_pattern,
$.paren_pattern,
),
paren_pattern: $ =>
seq(
"(",
field("pat", $._pattern),
")"
),
annot_pattern: $ =>
seq(
"(",
field("pat", $._pattern),
":",
field("type", $._type_expr),
")"
),
_call: $ => choice(
$.unary_op_app,
$.binary_op_app,
),
binary_op_app: $ => choice(
prec.left(16, mkOp($, "mod")),
prec.left(15, mkOp($, choice("/", "*"))),
prec.left(14, mkOp($, choice("-", "+"))),
prec.right(13, mkOp($, "::")),
prec.right(12, mkOp($, "^")),
prec.left(11, mkOp($, choice("&&", "||"))),
prec.left(10, mkOp($, choice("=", "<>", "==", "<", "<=", ">", ">="))),
),
// - a
unary_op_app: $ => prec(19, seq(
field("negate", "-"),
field("arg", $._expr)
)),
// f a
fun_app: $ => prec.left(20, seq(
field("f", $._sub_expr),
field("x", $._sub_expr)
)),
// a.0
index_accessor: $ => prec.right(21, seq(
field("box", $._sub_expr),
".",
field("field", $._sub_expr)
)),
// { p with a = b; c = d }
rec_literal: $ => seq(
"{",
field("field", $.rec_assignment),
repeat(seq(";", field("field", $.rec_assignment))),
optional(";"),
"}"
),
// { p with a = b; c = d }
rec_expr: $ => seq(
"{",
field("subject", $.Name),
"with",
field("field", $.rec_assignment),
repeat(seq(";", field("field", $.rec_assignment))),
optional(";"),
"}"
),
// a = b;
rec_assignment: $ => seq(
field("field", $._expr),
"=",
field("value", $._expr),
),
// if a then b else c
if_expr: $ => prec.right(seq(
"if",
field("condition", $._expr),
"then",
field("then", $._program),
optional(seq(
"else",
field("else", $._program)
))
)),
// match x with ...
match_expr: $ => prec.right(1,seq(
"match",
field("subject", $._expr),
"with",
optional('|'),
sepBy('|', field("alt", $.matching))
)),
// Dog as x -> f x
matching: $ => seq(
field("pattern", $._pattern),
"->",
field("body", $._program)
),
lambda_expr: $ => seq(
"fun",
repeat1(field("arg", $._paren_pattern)),
"->",
field("body", $._program)
),
list_expr: $ => seq(
"[",
sepBy(";", field("item", $._expr)),
"]"
),
tup_expr: $ => prec.right(9,seq(
field("x", $._expr),
some(seq(
",",
field("x", $._expr),
)),
)),
_expr: $ => choice(
$._call,
$._sub_expr,
$.tup_expr
),
_sub_expr: $ => choice(
$.fun_app,
$.paren_expr,
$.annot_expr,
$.Name,
$.Name_Capital,
$._literal,
$.rec_expr,
$.rec_literal,
$.if_expr,
$.lambda_expr,
$.match_expr,
$.list_expr,
$.index_accessor,
$.block_expr,
),
block_expr: $ => seq(
"begin",
sepBy(";", field("item", $._program)),
"end",
),
paren_expr: $ => seq(
"(",
field("expr", $._program),
")"
),
annot_expr: $ => seq(
"(",
field("expr", $._program),
":",
field("type", $._type_expr),
")",
),
//========== TYPE_EXPR ============
// t, test, string, integer
type_con: $ => $.TypeName,
// Red, Green, Blue, Cat
data_con: $ => $.Name_Capital,
// a t, (a, b) t
type_app: $ => prec(10,seq(
choice(
field("x", $._type_expr),
field("x", $.type_tuple),
),
field("f", $.type_con)
)),
type_tuple: $ => seq(
"(",
sepBy1(",", field("x", choice($._type_expr, $.String))),
")"
),
// string * integer
type_product: $ => prec.right(5, seq(
field("x", $._type_expr),
some(seq(
"*",
field("x", $._type_expr)
))
)),
// int -> string
type_fun: $ => prec.right(8, seq(
field("domain", $._type_expr),
"->",
field("codomain", $._type_expr)
)),
_type_expr: $ => choice(
$.type_fun,
$.type_product,
$.type_app,
$.type_con,
$.type_tuple,
),
// Cat of string, Person of string * string
variant: $ => seq(
field("constructor", $.data_con),
optional(seq(
"of",
field("type", $._type_expr)
))
),
// Cat of string | Personn of string * string
type_sum: $ => seq(
optional('|'),
sepBy1('|', field("variant", $.variant)),
),
// field : string * int
_label: $ => $.FieldName,
type_rec_field: $ => seq(
field("field", $._label),
":",
field("type", $._type_expr)
),
// { field1 : a; field2 : b }
type_rec: $ => seq(
"{",
sepBy(";", field("field", $.type_rec_field)),
optional(";"),
"}"
),
_type_def_body: $ => choice(
$.type_sum,
$._type_expr,
$.type_rec
),
type_decl: $ => seq(
"type",
field("name", $.type_con),
"=",
field("type", $._type_def_body)
),
_literal: $ => choice(
$.String,
$.Int,
$.Nat,
$.Tez,
$.Bytes,
$.True,
$.False,
$.Unit
),
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_]*/,
FieldName: $ => /[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: $ => '()',
comment: $ => /\/\/(\*\)[^\n]|\*[^\)\n]|[^\*\n])*\n/,
ocaml_comment: $ =>
seq(
'(*',
repeat(choice(
$.ocaml_comment,
/[^\*\(]/,
/\*[^\)]/,
/\([^\*]/,
)),
/\*+\)/
),
}
});