331 lines
7.4 KiB
JavaScript
331 lines
7.4 KiB
JavaScript
/**
|
|
* @file Parser for the Crepuscular(ray) functional programming language
|
|
* @author Alexander Nutz <alexander.nutz@vxcc.dev>
|
|
* @license MIT
|
|
*/
|
|
|
|
/// <reference types="tree-sitter-cli/dsl" />
|
|
// @ts-check
|
|
|
|
module.exports = grammar({
|
|
name: "crepuscular",
|
|
|
|
reserved: {
|
|
toplevel_kw: $ =>
|
|
['type', 'with', 'extensible', 'extend', 'union', 'def', 'await', 'if', 'then', 'else', 'in', 'match'],
|
|
},
|
|
|
|
extras: ($) => [
|
|
/\s/, // whitespace
|
|
$.comment,
|
|
],
|
|
|
|
word: $ => $._identifier_tok,
|
|
|
|
precedences: _ => [
|
|
[
|
|
"ident",
|
|
"exponent",
|
|
"multiplication",
|
|
"negate",
|
|
"addition",
|
|
"concat",
|
|
"with",
|
|
"equal",
|
|
"if",
|
|
"let",
|
|
"new_match_arm",
|
|
"match_arm",
|
|
"await",
|
|
"tag",
|
|
],
|
|
],
|
|
|
|
conflicts: $ => [
|
|
[$.match_expr, $.match_expr], // TODO
|
|
],
|
|
|
|
rules: {
|
|
source_file: $ => repeat($._definition),
|
|
|
|
_identifier_tok: $ => token(/[a-zA-Z_]+[a-zA-Z0-9_]*/),
|
|
identifier: $ => reserved('toplevel_kw', $._identifier_tok),
|
|
path: $ => prec.left(seq($.identifier, repeat(seq('.', $.identifier)))),
|
|
|
|
comment: $ =>
|
|
token(seq("#", /.*/)),
|
|
|
|
_definition: $ => choice(
|
|
$.full_partial_type_definition,
|
|
$.type_definition,
|
|
$.extensible_union,
|
|
$.extend_decl,
|
|
$.def,
|
|
),
|
|
|
|
extensible_union: $ => seq(
|
|
'extensible', 'union', $.path),
|
|
|
|
extend_decl: $ => seq(
|
|
'extend', $.path, 'with', $.tag, $._type),
|
|
|
|
full_partial_type_definition: $ => seq(
|
|
"type",
|
|
"?", $.path,
|
|
"=",
|
|
$._type
|
|
),
|
|
|
|
type_definition: $ => seq(
|
|
"type",
|
|
repeat($.identifier),
|
|
$.path,
|
|
"=",
|
|
$._type
|
|
),
|
|
|
|
_type_atom: $ => choice(
|
|
$.just_type,
|
|
$.partial_type,
|
|
seq('(', $._type, ')'),
|
|
$.record_type,
|
|
),
|
|
|
|
_type_non_fn: $ => choice(
|
|
$._type_atom,
|
|
$.tagged_type,
|
|
$.union_type,
|
|
$.partial_union_type,
|
|
$.parametrized_type,
|
|
$.with_type,
|
|
$.recursive_type,
|
|
),
|
|
|
|
_type: $ => choice(
|
|
$._type_non_fn,
|
|
$.fn_type,
|
|
),
|
|
|
|
union_type: $ => prec.left(1,
|
|
seq($._type, '|', $._type)),
|
|
|
|
partial_union_type: $ => prec.left(1,
|
|
seq($._type, '|', '...', $.partial_type)),
|
|
|
|
tag: $ => new RustRegex("'(?:[a-zA-Z_][a-zA-Z0-9_]*(?:[.][a-zA-Z_0-9]+)*)"),
|
|
tagged_type: $ => prec.right(3,
|
|
seq($.tag, optional(
|
|
choice(
|
|
$._type_atom,
|
|
$.parametrized_type)))),
|
|
|
|
multi_type_parameters: $ => seq('[', $._type, repeat(seq(',', $._type)), ']'),
|
|
parametrized_type: $ => prec.left(4, seq(
|
|
choice(
|
|
$.multi_type_parameters,
|
|
$._type_atom
|
|
),
|
|
repeat1($.path)
|
|
)),
|
|
|
|
with_type: $ => seq('with', $.identifier, repeat(seq(',', $.identifier)), ':', $._type),
|
|
|
|
recursive_type: $ => seq('&', $.identifier, $._type),
|
|
|
|
partial_type: $ => seq('?', $.identifier),
|
|
|
|
fn_type: $ => prec.left(-10,
|
|
seq($._type, '->', $._type)),
|
|
|
|
just_type: $ => prec(-1, $.path),
|
|
|
|
record_type_field: $ => seq($.identifier, ':', $._type),
|
|
record_type: $ => seq(
|
|
'{',
|
|
repeat(seq($.record_type_field, ',')),
|
|
optional(choice(
|
|
$.record_type_field,
|
|
seq('...', $.partial_type),
|
|
)),
|
|
'}'),
|
|
|
|
escape_sequence: $ =>
|
|
token.immediate(
|
|
seq('\\', /[tbrnf0"'\\]/)),
|
|
|
|
char_middle: $ => /./,
|
|
string_middle: $ => /[^\\"]+/,
|
|
|
|
char_literal: $ =>
|
|
seq('\'', choice($.escape_sequence, $.char_middle), '\''),
|
|
|
|
string_literal: $ =>
|
|
seq('"', repeat(choice($.escape_sequence, $.string_middle)), '"'),
|
|
|
|
num_literal: $ =>
|
|
seq(
|
|
choice(
|
|
/[0-9]+/,
|
|
/\-[0-9]+/
|
|
),
|
|
optional(token.immediate(/[.][0-9]+/))
|
|
),
|
|
|
|
list_expression: $ =>
|
|
seq(
|
|
'[',
|
|
repeat(seq($._expression, ',')),
|
|
optional($._expression),
|
|
']'),
|
|
|
|
field_access: $ => prec.left(
|
|
seq($._atom, ':', $.identifier)),
|
|
|
|
function_call: $ => prec.left(1,
|
|
seq($._atom, '(',
|
|
repeat(seq($._expression, ',')), optional($._expression),
|
|
')')),
|
|
|
|
ident_expr: $ => prec("ident",
|
|
$.path),
|
|
|
|
record_expr_field: $ =>
|
|
seq($.identifier, ':', $._expression),
|
|
|
|
record_expr: $ => seq(
|
|
'{',
|
|
repeat(seq($.record_expr_field, ',')),
|
|
optional($.record_expr_field),
|
|
'}'),
|
|
|
|
_atom: $ => choice(
|
|
prec(0, seq('(', $._expression, ')')),
|
|
$.ident_expr,
|
|
$.char_literal,
|
|
$.string_literal,
|
|
$.num_literal,
|
|
$.list_expression,
|
|
$.field_access,
|
|
$.function_call,
|
|
$.record_expr,
|
|
),
|
|
|
|
let_binding: $ => prec("let", seq(
|
|
'let',
|
|
field('name', $.identifier),
|
|
'=',
|
|
field('value', $._expression),
|
|
optional('in'),
|
|
field('body', $._expression),
|
|
)),
|
|
|
|
await_binding: $ => prec("let", seq(
|
|
'await',
|
|
field('name', $.identifier),
|
|
'=',
|
|
field('value', $._expression),
|
|
optional('in'),
|
|
field('body', $._expression),
|
|
)),
|
|
|
|
type_downcast: $ => seq(
|
|
field('expr', $._atom),
|
|
'::',
|
|
field('as', $._type),
|
|
),
|
|
|
|
lambda: $ => prec.right(4, seq(
|
|
field('arg', $.identifier),
|
|
optional(seq(':', field('arg_type', $._type_non_fn))),
|
|
'->',
|
|
field('body', $._expression)
|
|
)),
|
|
|
|
with_expr: $ => prec.left("with",
|
|
seq($._expression, 'with', $._atom)),
|
|
|
|
and_expr: $ => prec.left("with",
|
|
seq($._expression, 'and', $._atom)),
|
|
|
|
if_expr: $ => prec("if",
|
|
seq('if', $._expression, 'then', $._expression, 'else', $._expression)),
|
|
|
|
sub_expr: $ => prec.left("addition",
|
|
seq($._expression, '-', $._expression)),
|
|
add_expr: $ => prec.left("addition",
|
|
seq($._expression, '+', $._expression)),
|
|
|
|
divide_expr: $ => prec.left("multiplication",
|
|
seq($._expression, '/', $._expression)),
|
|
multiply_expr: $ => prec.left("multiplication",
|
|
seq($._expression, '*', $._expression)),
|
|
|
|
equal_expr: $ => prec.left("equal",
|
|
seq($._expression, '=', $._expression)),
|
|
|
|
concat_expr: $ => prec.left("concat",
|
|
seq($._expression, '++', $._expression)),
|
|
|
|
compose_expr: $ => prec.left("concat",
|
|
seq($._expression, '=>', $._expression)),
|
|
|
|
exponent_expr: $ => prec.left("exponent",
|
|
seq($._expression, '^', $._atom)),
|
|
|
|
match_arm: $ => prec("match_arm",
|
|
seq(
|
|
field('cases', seq($._atom, repeat(seq('|', $._atom)))),
|
|
'->', $._expression)),
|
|
|
|
match_expr: $ =>
|
|
seq('match', $._expression, 'with',
|
|
$.match_arm,
|
|
prec("new_match_arm", repeat(seq('|', $.match_arm)))),
|
|
|
|
negate_expr: $ => prec.right("negate",
|
|
seq('-', $._expression)),
|
|
|
|
tag_expr: $ => prec.right("tag",
|
|
seq($.tag, $._expression)),
|
|
|
|
await_expr: $ => prec.right("await",
|
|
seq('await', $._expression)),
|
|
|
|
_expression: $ => choice(
|
|
$._atom,
|
|
$.let_binding,
|
|
$.await_binding,
|
|
$.await_expr,
|
|
$.type_downcast,
|
|
$.lambda,
|
|
$.with_expr,
|
|
$.and_expr,
|
|
$.if_expr,
|
|
$.match_expr,
|
|
$.tag_expr,
|
|
|
|
$.add_expr,
|
|
$.sub_expr,
|
|
$.divide_expr,
|
|
$.multiply_expr,
|
|
$.concat_expr,
|
|
$.compose_expr,
|
|
$.equal_expr,
|
|
$.exponent_expr,
|
|
$.negate_expr,
|
|
),
|
|
|
|
def: $ => seq(
|
|
'def',
|
|
field('name', $.path),
|
|
choice(
|
|
seq(':', field('signature', $._type)),
|
|
seq(
|
|
optional(seq(':', field('signature', $._type))),
|
|
seq('=', field('value', $._expression)),
|
|
)
|
|
),
|
|
),
|
|
}
|
|
});
|