You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
4.3 KiB

const commaSep = (rule) => seq(rule, repeat(seq(',', rule)));
module.exports = grammar({
name: 'moshell',
extras: $ => [
/\s/,
/\\\r?\n/,
$.line_comment,
$.block_comment,
],
rules: {
source_file: $ => optional($._declarations),
_declarations: $ => seq(
repeat(seq(
$._declaration,
$._terminator
)),
$._declaration,
optional($._terminator)
),
_declaration: $ => choice(
$.function_declaration,
$.use_declaration,
$.variable_declaration,
$._statement,
$.return,
'break',
'continue',
),
function_declaration: $ => seq(
'fun',
field('name', $.identifier),
field('parameters', $.parameter_list),
optional(seq(
'->',
field('return_type', $.type_identifier)
)),
'=',
$._expression
),
parameter_list: $ => seq(
'(',
optional(commaSep($.function_parameter)),
')'
),
function_parameter: $ => seq(
$.identifier,
':',
$.type_identifier
),
use_declaration: $ => seq(
'use',
$._use_clause,
),
_use_clause: $ => seq(
$.identifier,
repeat(seq('::', $.identifier)),
optional(seq('as', $.identifier)),
),
variable_declaration: $ => seq(
choice('var', 'val'),
$.identifier,
optional(seq(':', $.type_identifier)),
optional(seq('=', $._expression))
),
command: $ => seq(
$.command_name,
repeat($._argument),
repeat($.redirection),
),
_statement: $ => choice(
$.command,
$.pipeline,
$.while,
seq('!', $.command),
$._expression,
),
while: $ => seq(
'while',
$._statement,
$.block
),
return: $ => seq(
'return',
optional($._expression)
),
_expression: $ => choice(
$.block,
$.if,
$.call_expression,
$.assignation,
$.infix_expression,
$.prefix_expression,
$.expansion,
$.primary
),
block: $ => seq(
'{',
optional($._declarations),
'}'
),
if: $ => seq(
'if',
field('condition', $._statement),
field('then', $.block),
optional(seq(
'else',
field('else', $.block)
))
),
call_expression: $ => seq(
field('name', $.identifier),
field('arguments', $.argument_list),
),
argument_list: $ => seq(
'(',
optional($._expression),
repeat(seq(',', $._expression)),
')'
),
assignation: $ => prec.right(seq(
choice($.identifier, $._expression),
choice('=', '+=', '-=', '*=', '/=', '%='),
$._expression
)),
cast_expression: $ => prec.left(seq(
field('value', $._expression),
'as',
field('type', $.type_identifier)
)),
infix_expression: $ => prec.left(seq(
$._expression,
choice('+', '-', '*', '/', '%', '==', '!=', '<', '>', '<=', '>=', '&&', '||', 'in'),
$._expression
)),
prefix_expression: $ => prec.right(seq(
choice('!', '-'),
$._expression
)),
expansion: $ => choice(
$.variable,
$.command_substitution
),
pipeline: $ => prec.left(seq(
$._statement,
'|',
$._statement
)),
redirection: $ => seq(
field('descriptor', optional($.number)),
choice('<', '>', '>>', '<<<', '&>', '&>>', '<&', '>&'),
field('destination', $._argument)
),
command_substitution: $ => seq('$(', $._declaration, ')'),
command_name: $ => $.identifier,
_argument: $ => choice(
$.raw_string,
$.template_string,
$.expansion,
$.word
),
primary: $ => choice(
$.number,
$.boolean,
$.raw_string,
$.template_string,
),
line_comment: $ => token(seq('//', /.*/)),
block_comment: $ => token(seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/')),
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
word: $ => /[^#'"\\\s$<>{}&;()]+/,
number: $ => /\d+/,
boolean: $ => choice('true', 'false'),
template_string: $ => seq(
'"',
repeat(choice($._string_content, $.expansion)),
'"'
),
raw_string: $ => /'[^']*'/,
_string_content: $ => token(prec(-1, /([^"`$\\]|\\(.|\r?\n))+/)),
_terminator: $ => choice(';', '\n'),
type_identifier: $ => $.identifier,
variable: $ => seq(
'$',
$.identifier
),
}
});