470 lines
8.2 KiB
JavaScript
470 lines
8.2 KiB
JavaScript
|
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 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),
|
||
|
']',
|
||
|
),
|
||
|
)
|
||
|
|
||
|
let tuple = x => seq(x, ',', x, repeat(seq(',', x)))
|
||
|
let list__ = x => brackets(sepBy(';', x))
|
||
|
|
||
|
let nseq = x => seq(x, repeat(x))
|
||
|
|
||
|
let op = (l, x, r, def) =>
|
||
|
choice(
|
||
|
seq(l, x, r),
|
||
|
def,
|
||
|
)
|
||
|
|
||
|
let rop = (l, x, r) => op(l, x, r, r)
|
||
|
let lop = (l, x, r) => op(l, x, r, l)
|
||
|
|
||
|
module.exports = grammar({
|
||
|
name: 'CAMLigo',
|
||
|
|
||
|
word: $ => $.Keyword,
|
||
|
extras: $ => [$.ocaml_comment, $.comment, /\s/],
|
||
|
|
||
|
conflicts: $ => [[$.call, $.if_then_else]],
|
||
|
|
||
|
rules: {
|
||
|
// debug: $ => $.expr,
|
||
|
|
||
|
contract: $ => repeat($.declaration),
|
||
|
|
||
|
expr: $ =>
|
||
|
choice(
|
||
|
$.let_expr,
|
||
|
$.op_call,
|
||
|
),
|
||
|
|
||
|
let_expr: $ =>
|
||
|
prec.left(0,
|
||
|
seq(
|
||
|
'let',
|
||
|
$.let_binding,
|
||
|
repeat($.attr),
|
||
|
'in',
|
||
|
$.expr,
|
||
|
),
|
||
|
),
|
||
|
|
||
|
op_call: $ =>
|
||
|
$.tuple,
|
||
|
|
||
|
tuple: $ => prec.left(2, rop($.tuple, ',', $.disj)),
|
||
|
disj: $ => prec.left(3, rop($.disj, $.or, $.conj)),
|
||
|
conj: $ => prec.left(4, rop($.conj, $.and, $.comp)),
|
||
|
comp: $ => prec.left(5, rop($.comp, $.compare, $.cat)),
|
||
|
cat: $ => prec.left(6, lop($.cons, $.catenate, $.cat)),
|
||
|
cons: $ => prec.right(7, lop($.add, $.consolidate, $.cons)),
|
||
|
add: $ => prec.left(8, rop($.add, $.plus, $.mult)),
|
||
|
mult: $ => prec.left(9, rop($.mult, $.times, $.unary)),
|
||
|
|
||
|
unary: $ =>
|
||
|
seq(
|
||
|
optional($.negate),
|
||
|
$.call,
|
||
|
),
|
||
|
|
||
|
or: $ => choice('||', 'or'),
|
||
|
and: $ => '&&',
|
||
|
compare: $ => choice('<', '<=', '>', '>=', '=', '<>'),
|
||
|
catenate: $ => '^',
|
||
|
consolidate: $ => '::',
|
||
|
plus: $ => choice('+', '-'),
|
||
|
times: $ => choice('*', '/', 'mod'),
|
||
|
negate: $ => choice('-', 'not'),
|
||
|
|
||
|
call: $ =>
|
||
|
prec.left(1,
|
||
|
choice(
|
||
|
seq($.call, $.term),
|
||
|
$.term,
|
||
|
),
|
||
|
),
|
||
|
|
||
|
term: $ =>
|
||
|
choice(
|
||
|
$.ref,
|
||
|
$.if_then_else,
|
||
|
$.literal,
|
||
|
$.match,
|
||
|
),
|
||
|
|
||
|
match: $ =>
|
||
|
prec.right(0,
|
||
|
seq(
|
||
|
'match',
|
||
|
$.expr,
|
||
|
'with',
|
||
|
optional('|'),
|
||
|
sepBy1('|', $.alt),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
alt: $ =>
|
||
|
prec.right(0,
|
||
|
seq(
|
||
|
$.pattern,
|
||
|
'->',
|
||
|
$.expr,
|
||
|
),
|
||
|
),
|
||
|
|
||
|
literal: $ =>
|
||
|
choice(
|
||
|
$.Nat,
|
||
|
$.Int,
|
||
|
$.Tez,
|
||
|
$.String,
|
||
|
$.Bytes,
|
||
|
$.group,
|
||
|
$.annotation,
|
||
|
$.list,
|
||
|
$.lambda,
|
||
|
$.unit,
|
||
|
$.record,
|
||
|
$.record_update,
|
||
|
),
|
||
|
|
||
|
record: $ =>
|
||
|
seq(
|
||
|
'{',
|
||
|
sepBy1(';', $.record_field),
|
||
|
'}',
|
||
|
),
|
||
|
|
||
|
record_update: $ =>
|
||
|
seq(
|
||
|
'{',
|
||
|
$.name,
|
||
|
'with',
|
||
|
sepBy1(';', $.record_field_update),
|
||
|
'}',
|
||
|
),
|
||
|
|
||
|
record_field_update: $ =>
|
||
|
seq(
|
||
|
sepBy1('.', $.name),
|
||
|
'=',
|
||
|
$.expr,
|
||
|
),
|
||
|
|
||
|
record_field: $ =>
|
||
|
seq(
|
||
|
$.name,
|
||
|
'=',
|
||
|
$.expr,
|
||
|
),
|
||
|
|
||
|
unit: $ => seq('(', ')'),
|
||
|
|
||
|
lambda: $ =>
|
||
|
seq(
|
||
|
'fun',
|
||
|
repeat($.pattern_term),
|
||
|
'->',
|
||
|
$.expr,
|
||
|
),
|
||
|
|
||
|
list: $ =>
|
||
|
seq(
|
||
|
'[',
|
||
|
sepBy(';', $.expr),
|
||
|
']',
|
||
|
),
|
||
|
|
||
|
annotation: $ =>
|
||
|
seq(
|
||
|
'(',
|
||
|
$.expr,
|
||
|
':',
|
||
|
$.type_expr,
|
||
|
')',
|
||
|
),
|
||
|
|
||
|
group: $ =>
|
||
|
seq(
|
||
|
'(',
|
||
|
$.expr,
|
||
|
')',
|
||
|
),
|
||
|
|
||
|
if_then_else: $ =>
|
||
|
prec.right(0,
|
||
|
seq(
|
||
|
'if',
|
||
|
$.expr,
|
||
|
'then',
|
||
|
$.expr,
|
||
|
'else',
|
||
|
$.expr,
|
||
|
),
|
||
|
),
|
||
|
|
||
|
ref: $ =>
|
||
|
choice(
|
||
|
$.module_qualified,
|
||
|
$.struct_qualified,
|
||
|
$.name,
|
||
|
$.constr,
|
||
|
),
|
||
|
|
||
|
module_qualified: $ =>
|
||
|
seq(
|
||
|
$.constr,
|
||
|
'.',
|
||
|
$.name,
|
||
|
),
|
||
|
|
||
|
struct_qualified: $ =>
|
||
|
seq(
|
||
|
$.name,
|
||
|
nseq(seq('.', $.accessor)),
|
||
|
),
|
||
|
|
||
|
accessor: $ => choice($.name, $.Int),
|
||
|
|
||
|
declaration: $ =>
|
||
|
choice(
|
||
|
$.type_decl,
|
||
|
$.let_declaration,
|
||
|
),
|
||
|
|
||
|
type_decl: $ =>
|
||
|
seq(
|
||
|
'type',
|
||
|
$.type_name,
|
||
|
'=',
|
||
|
$.type_expr,
|
||
|
),
|
||
|
|
||
|
type_expr: $ =>
|
||
|
choice(
|
||
|
$.fun_type,
|
||
|
$.sum_type,
|
||
|
$.record_type,
|
||
|
),
|
||
|
|
||
|
fun_type: $ =>
|
||
|
seq(
|
||
|
$.cartesian,
|
||
|
optional(seq('->', $.fun_type)),
|
||
|
),
|
||
|
|
||
|
cartesian: $ =>
|
||
|
prec.left(1,
|
||
|
seq(
|
||
|
$.core_type,
|
||
|
optional(seq('*', sepBy('*', $.core_type))),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
core_type: $ =>
|
||
|
choice(
|
||
|
$.type_name,
|
||
|
par($.type_expr),
|
||
|
$.module_type_name,
|
||
|
$.type_application
|
||
|
),
|
||
|
|
||
|
module_type_name: $ =>
|
||
|
seq(
|
||
|
$.module_name,
|
||
|
'.',
|
||
|
$.type_name,
|
||
|
),
|
||
|
|
||
|
type_application: $ =>
|
||
|
seq(
|
||
|
choice($.core_type, $.type_tuple),
|
||
|
$.type_name,
|
||
|
),
|
||
|
|
||
|
type_tuple: $ =>
|
||
|
par(tuple($.type_expr)),
|
||
|
|
||
|
sum_type: $ =>
|
||
|
seq(
|
||
|
optional('|'),
|
||
|
sepBy1('|', $.variant),
|
||
|
),
|
||
|
|
||
|
variant: $ =>
|
||
|
seq(
|
||
|
$.constr,
|
||
|
optional(seq('of', $.fun_type)),
|
||
|
),
|
||
|
|
||
|
record_type: $ =>
|
||
|
seq(
|
||
|
'{',
|
||
|
sepBy(';', $.field_decl),
|
||
|
'}',
|
||
|
),
|
||
|
|
||
|
field_decl: $ =>
|
||
|
seq(
|
||
|
$.field_name,
|
||
|
':',
|
||
|
$.type_expr,
|
||
|
),
|
||
|
|
||
|
let_declaration: $ =>
|
||
|
seq(
|
||
|
'let',
|
||
|
optional($.rec),
|
||
|
$.let_binding,
|
||
|
repeat($.attr),
|
||
|
),
|
||
|
|
||
|
let_binding: $ =>
|
||
|
choice(
|
||
|
seq(
|
||
|
$.name,
|
||
|
nseq($.pattern_term),
|
||
|
optional($.type_annotation),
|
||
|
'=',
|
||
|
$.expr,
|
||
|
),
|
||
|
seq(
|
||
|
$.pattern,
|
||
|
optional($.type_annotation),
|
||
|
'=',
|
||
|
$.expr,
|
||
|
),
|
||
|
),
|
||
|
|
||
|
type_annotation: $ => seq(':', $.type_expr),
|
||
|
|
||
|
pattern_term: $ =>
|
||
|
choice(
|
||
|
$.pattern_record,
|
||
|
$.constr,
|
||
|
$.pattern_list,
|
||
|
$.pattern_constant,
|
||
|
$.name,
|
||
|
$.pattern_grouped,
|
||
|
),
|
||
|
|
||
|
pattern_grouped: $ =>
|
||
|
seq(
|
||
|
'(',
|
||
|
$.pattern,
|
||
|
optional($.type_annotation),
|
||
|
')',
|
||
|
),
|
||
|
|
||
|
pattern_list: $ => list__($.pattern),
|
||
|
pattern_constant: $ =>
|
||
|
choice(
|
||
|
$.Nat,
|
||
|
$.String,
|
||
|
$.Bytes,
|
||
|
$.Int,
|
||
|
),
|
||
|
|
||
|
pattern_record: $ =>
|
||
|
seq(
|
||
|
'{',
|
||
|
sepBy1(';', $.pattern_record_field),
|
||
|
'}',
|
||
|
),
|
||
|
|
||
|
pattern_record_field: $ =>
|
||
|
seq(
|
||
|
$.name,
|
||
|
'=',
|
||
|
$.pattern,
|
||
|
),
|
||
|
|
||
|
pattern: $ => sepBy1('::', $.pattern_tuple),
|
||
|
pattern_tuple: $ => sepBy1(',', $.construct),
|
||
|
|
||
|
construct: $ =>
|
||
|
choice(
|
||
|
seq($.constr, nseq($.pattern_term)),
|
||
|
$.pattern_term,
|
||
|
),
|
||
|
|
||
|
///////////////////////////////////////////
|
||
|
|
||
|
type_name: $ => $.ident,
|
||
|
field_name: $ => $.ident,
|
||
|
fun_name: $ => $.ident,
|
||
|
struct_name: $ => $.ident,
|
||
|
var: $ => $.ident,
|
||
|
module_name: $ => $.Name_Capital,
|
||
|
constr: $ => $.Name_Capital,
|
||
|
ident: $ => $.Name,
|
||
|
name: $ => $.Name,
|
||
|
|
||
|
comment: $ => /\/\/[^\n]*\n/,
|
||
|
|
||
|
ocaml_comment: $ => seq(
|
||
|
'(*',
|
||
|
repeat(choice(
|
||
|
$.ocaml_comment,
|
||
|
/[^\(]|\(?!\*/,
|
||
|
)),
|
||
|
'*)'
|
||
|
),
|
||
|
|
||
|
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_]*/,
|
||
|
Name_Capital: $ => /[A-Z][a-zA-Z0-9_]*/,
|
||
|
Keyword: $ => /[A-Za-z][a-z]*/,
|
||
|
|
||
|
False: $ => 'False',
|
||
|
True: $ => 'True',
|
||
|
Unit: $ => 'Unit',
|
||
|
None: $ => 'None',
|
||
|
Some: $ => 'Some',
|
||
|
skip: $ => 'skip',
|
||
|
rec: $ => 'rec',
|
||
|
|
||
|
attr: $ => seq('[@@', $.name, ']'),
|
||
|
}
|
||
|
});
|