/** * @file Parser for the Crepuscular(ray) functional programming language * @author Alexander Nutz * @license MIT */ /// // @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)), ) ), ), } });