/** * @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'], }, extras: ($) => [ /\s/, // whitespace $.comment, ], word: $ => $._identifier_tok, precedences: _ => [ [ "exponent", "multiplication", "negate", "addition", "concat", "with", "equal", "if", "let", "new_match_arm", "match_arm", "tag", ], ], conflicts: $ => [ [$.match_expr, $.match_expr], ], 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: $ => $.identifier, 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', $.identifier, '=', $.expression, optional('in'), $.expression, )), await_binding: $ => prec("let", seq( 'await', $.identifier, '=', $.expression, optional('in'), $.expression, )), type_downcast: $ => seq( $._atom, '::', $._type, ), lambda: $ => prec.right(4, seq( $.identifier, optional(seq(':', $._type_non_fn)), '->', $.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)), expression: $ => choice( $._atom, $.let_binding, $.await_binding, $.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', $.path, choice( seq(':', $._type), seq( optional(seq(':', $._type)), seq('=', $.expression), ) ), ), } });