module.exports = grammar({ name: 'lang', word: $ => $.identifier, extras: $ => [ /\s/, ], rules: { // --- sources source_file: $ => repeat1($._statement), _statement: $ => choice( $.import, $.function_definition, $.type_definition, $.extra, $.empty_lines, ), import: $ => seq( choice('::', 'import'), field('name', choice($.simple_name_identifier, $.placeholder)), optional(seq('=', field('module', $.simple_name_identifier))), optional(seq(':', repeat1(choice( $.simple_name_identifier, seq('(',$.operator, ')'), $.simple_type_identifier, $.typeclass_identifier, )))), ';', ), constraint: $ => seq('?', $._expression), // --- definitions function_definition: $ => seq( optional($.definition_info), repeat($.annotation_info), repeat(seq($.constraint, ';')), optional('.'), // for methods choice(field('name', $.simple_name_identifier), seq('(', field('name', $.operator), ')')), optional($._optional_or_result), repeat(seq( optional($.annotation_identifier), optional($._reference), $.argument_name_identifier, optional($._optional_or_result), )), optional(seq( ':', repeat1(seq( optional($.annotation_identifier), optional($._reference), $._scoped_type, )), )), choice(seq('=', choice(prec(2, choice($.block, $.array)), seq($._super_expression, ';'))), ';'), ), // datatype or typeclass definition type_definition: $ => seq( optional($.definition_info), repeat($.annotation_info), // for datatypes only optional('^'), field('name', choice($.simple_type_identifier, $.typeclass_identifier)), optional(seq('[', repeat($.typeclass_identifier), ']')), // parametric typeclasses ?? repeat($.argument_type_identifier), // for datatypes only optional(seq('=', $._type)), // for datatypes only ';', ), // --- flow control case: $ => seq( choice(':=', '=:'), $._expression, optional(seq(choice('??', 'if'), field('condition', $._expression))), optional(field('expression', 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( $._expression, seq($._expression, ':', $._expression), )), $._do, $._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(choice(seq($._super_expression, ';'), $.extra, $.empty_lines)), '}'), array: $ => seq('[[', repeat1($._scoped_expression), ']]'), // --- modifiers return: $ => seq(choice('return', 'bring'), $._expression), name_definition: $ => seq( choice(choice('%', 'let'), choice('$', 'var')), choice($.simple_name_identifier, $.placeholder), ), array_access: $ => seq($._scoped_expression, '[', $._super_expression, ']'), tuple_access: $ => seq($._scoped_expression, '.', $.index_literal), loop_control: $ => choice('break', 'continue'), reference_expression: $ => prec(-1, seq($._reference, $._scoped_expression)), suffix_expression: $ => seq($._scoped_expression, $._optional_or_result), // --- other name_expression: $ => seq( choice( seq($._scoped_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( field('type', $._scoped_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 // same to Variant[...] variant_type: $ => seq( optional('|'), $._annotated_type, repeat1(seq('|', $._annotated_type)), ), // same to Tuple[...] tuple_type: $ => seq( optional('&'), $._annotated_type, repeat1(seq('&', $._annotated_type)), ), // add annotations to in all types ?? _annotated_type: $ => seq(optional($.annotation_identifier), $._scoped_type), // same to Array[...] array_type: $ => seq('[[', $._type, ']]'), reference_type: $ => prec(-1, seq('^', $._scoped_type)), // same to Optional[...] or Result[...] modified_type: $ => seq($._scoped_type, $._optional_or_result), simple_type: $ => seq( field('name', $._type_identifier), optional(seq('[', repeat1($._scoped_type), ']')) ), _type: $ => choice( $.simple_type, $.reference_type, $.modified_type, $.array_type, $.variant_type, $.tuple_type, ), _scoped_type: $ => choice( $.simple_type, $.reference_type, $.modified_type, $.array_type, seq('(', choice( $.variant_type, $.tuple_type, ), ')'), ), // --- comments definition_info: $ => repeat1(seq(': ', $.info)), annotation_info: $ => seq($.annotation_identifier, ' ', $.info), info: $ => /[^\n]*/, extra: $ => choice($._exec_comment, $._line_comment, $._block_comment), empty_lines: $ => prec.left(repeat1('\n')), _exec_comment: $ => token(seq('#!', /[^\n]*/)), _line_comment: $ => token(seq('//', /[^\n]*/)), _block_comment: $ => token(seq('/*', /([^*]|(\*[^/]))*/, '*/')), // --- tokens _do: $ => choice('=>', 'do'), _optional_or_result: $ => choice('?', '!'), _reference: $ => choice(choice('<-', '<>', '--', '->', '<-|<>', '<-|--', '<>|->', '--|->', '<>|--', '<-|->', '<-|<>|->', '<-|--|->', '<-|<>|--', '<>|--|->', '<-|<>|--|->'), choice('in', 'ref', 'const', 'out', 'in|ref', 'in|const', 'ref|out', 'const|out', 'ref|const', 'in|out', 'in|ref|out', 'in|const|out', 'in|ref|const', 'ref|const|out', 'in|ref|const|out')), _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_literal: $ => /\-?[0-9]+\.[0-9]+f/, double_literal: $ => /\-?[0-9]+\.[0-9]+/, int_literal: $ => /\-?[0-9]+i/, long_literal: $ => /\-?[0-9]+l/, index_literal: $ => /[0-9]+/, string_literal: $ => seq('\"', /([^\\\"]|(\\.))*/, '\"'), unicode_string_literal: $ => seq('\"', /([^\\\"]|(\\.))*/, '\"u'), char_literal: $ => seq('\'\'', /[^\\\']|(\\.)/, '\'\''), unicode_literal: $ => seq('\'\'', /[^\\\']|(\\.)/, '\'\'u'), bool_literal: $ => choice('true', 'false'), unit_literal: $ => '()', null_literal: $ => 'null', _literal: $ => choice( $.float_literal, $.double_literal, $.int_literal, $.long_literal, $.index_literal, $.string_literal, $.unicode_string_literal, $.char_literal, $.unicode_literal, $.bool_literal, $.unit_literal, $.null_literal, ), identifier: $ => /([@'#]?[a-zA-Z0-9_]+)|([+\-*/%^!?|&,<>=]+\.?\.?\.?)|\.+/, } });