module.exports = grammar({ name: 'langexp', word: $ => $.identifier, extras: $ => [ $._exec_comment, $._line_comment, $._block_comment, /\s/ ], rules: { // --- sources source_file: $ => repeat1($._statement), _statement: $ => choice( $.import, $.type_definition, $.function_definition, $.typeclass_definition, ), import: $ => seq( choice('::', 'import'), field('name', choice($.simple_name_identifier, '_')), optional(seq('=', field('module', $.simple_name_identifier))), optional(seq(':', repeat(choice( $.simple_type_identifier, $.simple_name_identifier, $.typeclass_identifier, seq('(',$.operator, ')'), )))), ';', ), constraint: $ => seq('?', $._expression), // --- definitions type_definition: $ => seq( optional($.definition_info), repeat($.annotation_info), optional('^'), field('name', $.simple_type_identifier), optional(seq(repeat($.argument_type_identifier), '=', choice($.variant_type, $.tuple_type))), choice(seq('{', repeat($.function_definition), '}'), ';') ), function_definition: $ => seq( optional($.definition_info), repeat($.annotation_info), repeat(seq($.constraint, ';')), /*optional(seq(*/optional($._var_let)/*, '.'))*/, choice(field('name', $.simple_name_identifier), seq('(', field('name', $.operator), ')')), repeat(seq( optional($.annotation_identifier), optional($._reference), $.argument_name_identifier, optional('?'), )), optional(seq(':', repeat1(seq( optional($.annotation_identifier), optional($._reference), $.type, )))), choice(seq('=', choice(prec(2, $.block), seq($._super_expression, ';'))), ';'), ), typeclass_definition: $ => seq( optional($.definition_info), repeat($.annotation_info), $.typeclass_identifier, optional(seq(':', repeat($.typeclass_identifier))), choice(seq('{', repeat($.function_definition), '}'), ';'), ), // --- flow control case: $ => seq( choice(':=', '=:'), $._expression, optional(seq(choice('??', 'if'), $._expression)), optional(seq($._do, $._expression)), ), match: $ => seq( $._expression, repeat1($.case), ), condition: $ => seq( choice('??', 'if'), $._expression, $._do, $._expression, repeat(seq( choice('!!', 'elif'), $._expression, $._do, $._expression, )), optional(seq(choice('!!=>', 'else'), $._expression)), ), loop: $ => seq( choice('@', 'for'), optional(choice( field('condition', $._expression), seq(field('variable', $._expression), ':', field('interval', $._expression)), )), $._do, field('expression', $._expression), ), // --- operators comma_expression: $ => prec.left(seq($._super_expression, ',', $._super_expression)), operator_expression: $ => prec.left(choice( prec(4, seq($._expression, field('name', $.operator), $._expression)), prec(3, seq($._expression, field('name', $.operator_tail1), $._expression)), prec(2, seq($._expression, field('name', $.operator_tail2), $._expression)), prec(1, seq($._expression, field('name', $.operator_tail3), $._expression)), )), // --- continers block: $ => seq('{', repeat(seq($._super_expression, ';')), '}'), array: $ => seq('[[', repeat1($._scoped_expression), ']]'), // --- modifiers return: $ => seq(choice('return', 'bring'), field('value', $._expression)), name_definition: $ => seq( $._var_let, choice($.simple_name_identifier, $.placeholder), ), array_access: $ => seq($._scoped_expression, '[', $._super_expression, ']'), tuple_access: $ => seq($._scoped_expression, '.', $.number_literal), loop_control: $ => choice('break', 'continue'), reference_expression: $ => prec(-1, seq($._reference, $._scoped_expression)), suffix_expression: $ => seq($._scoped_expression, choice('?', '!')), // --- other name_expression: $ => seq( choice( seq($.type, '.', field('name', $.simple_name_identifier)), seq($._scoped_expression, '.', field('name', $.simple_name_identifier)), field('name', $._name_identifier), seq('(', field('name', $.operator), ')'), ), repeat(seq(optional($.annotation_identifier), $._scoped_expression)), ), constructor: $ => seq( $.type, repeat1(seq(optional($.annotation_identifier), $._scoped_expression)), ), lambda: $ => seq( choice('\\', 'lambda'), repeat($.argument_name_identifier), $._do, $._expression, ), // --- expression _super_expression: $ => choice( $.match, $.condition, $.loop, $.comma_expression, $._expression, ), _expression: $ => choice( $.operator_expression, $.return, $.name_expression, $.constructor, $.lambda, $._not_name_scoped_expression, ), _scoped_expression: $ => choice( $._name_identifier, $._not_name_scoped_expression, ), _not_name_scoped_expression: $ => choice( $.block, $.array, $.placeholder, $.name_definition, $.array_access, $.tuple_access, $.loop_control, $.reference_expression, $.suffix_expression, $._literal, seq('(', $._super_expression, ')'), ), // --- type variant_type: $ => seq( optional('|'), $.tuple_type, repeat1(seq('|', $.tuple_type)), ), tuple_type: $ => seq( optional('&'), $._annotated_type, repeat(seq('&', $._annotated_type)), ), _annotated_type: $ => seq(optional($.annotation_identifier), $.type), type: $ => seq( optional('^'), field('name', $._type_identifier), optional('?'), optional(seq('[', repeat1($.type), ']')) ), // --- comments definition_info: $ => repeat1(seq(': ', /[^\n]*/)), annotation_info: $ => seq($.annotation_identifier, /[^\n]*/), _exec_comment: $ => token(seq('#!', /[^\n]*/)), _line_comment: $ => token(seq('//', /[^\n]*/)), _block_comment: $ => token(seq('/*', /([^*]|(\*[^/]))*/, '*/')), // --- tokens _do: $ => choice('=>', 'do'), _var_let: $ => choice(choice('%', 'let'), choice('$', 'var')), _reference: $ => choice(choice('->', 'out'), choice('<-', 'in'), choice('<>', 'ref')), _name_identifier: $ => choice($.argument_name_identifier, $.simple_name_identifier), _type_identifier: $ => choice($.argument_type_identifier, $.simple_type_identifier), placeholder: $ => '_', simple_name_identifier: $ => token(seq(repeat(seq(/[a-z_][a-z0-9_]*/, '.')), /[a-z_][a-z0-9_]*/)), simple_type_identifier: $ => /([a-z_][a-z0-9_]*\.)*[A-Z][a-zA-Z0-9]*/, typeclass_identifier: $ => /([a-z_][a-z0-9_]*\.)*#[A-Z][a-zA-Z0-9]*/, argument_name_identifier: $ => /'[a-z_][a-z0-9_]*/, argument_type_identifier: $ => /'[A-Z][a-zA-Z0-9]*/, annotation_identifier: $ => /@[a-z_][a-z0-9_]*/, operator: $ => /([+\-*/%^!?|&,<>=]+)|\.+/, operator_tail1: $ => /[+\-*/%^!?|&,<>=]+\./, operator_tail2: $ => /[+\-*/%^!?|&,<>=]+\.\./, operator_tail3: $ => /[+\-*/%^!?|&,<>=]+\.\.\./, float_number_literal: $ => /[0-9]+\.[0-9]+/, number_literal: $ => /[0-9]+/, string_literal: $ => seq('\"', /([^\\\"]|(\\.))*/, '\"'), char_literal: $ => seq('\'\'', /[^\\\']|(\\.)/, '\'\''), bool_literal: $ => choice('true', 'false'), unit_literal: $ => '()', null_literal: $ => 'null', _literal: $ => choice( $.float_number_literal, $.number_literal, $.string_literal, $.char_literal, $.bool_literal, $.unit_literal, $.null_literal, ), identifier: $ => /([@'#]?[a-zA-Z0-9_]+)|([+\-*/%^!?|&,<>=]+\.?\.?\.?)|\.+/, } });