difftastic/vendored_parsers/tree-sitter-lua/grammar.js

567 lines
15 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const PREC = {
OR: 1, // or
AND: 2, // and
COMPARE: 3, // < > <= >= ~= ==
BIT_OR: 4, // |
BIT_NOT: 5, // ~
BIT_AND: 6, // &
BIT_SHIFT: 7, // << >>
CONCAT: 8, // ..
PLUS: 9, // + -
MULTI: 10, // * / // %
UNARY: 11, // not # - ~
POWER: 12, // ^
};
const list_seq = (rule, separator, trailing_separator = false) =>
trailing_separator
? seq(rule, repeat(seq(separator, rule)), optional(separator))
: seq(rule, repeat(seq(separator, rule)));
const optional_block = ($) => alias(optional($._block), $.block);
// namelist ::= Name {',' Name}
const name_list = ($) => list_seq(field('name', $.identifier), ',');
module.exports = grammar({
name: 'lua',
extras: ($) => [$.comment, /\s/],
externals: ($) => [
$._block_comment_start,
$._block_comment_content,
$._block_comment_end,
$._block_string_start,
$._block_string_content,
$._block_string_end,
],
supertypes: ($) => [$.statement, $.expression, $.declaration, $.variable],
word: ($) => $.identifier,
rules: {
// chunk ::= block
chunk: ($) =>
seq(
optional($.hash_bang_line),
repeat($.statement),
optional($.return_statement)
),
hash_bang_line: (_) => /#.*/,
// block ::= {stat} [retstat]
_block: ($) =>
choice(
seq(repeat1($.statement), optional($.return_statement)),
seq(repeat($.statement), $.return_statement)
),
/*
stat ::= ';' |
varlist '=' explist |
functioncall |
label |
break |
goto Name |
do block end |
while exp do block end |
repeat block until exp |
if exp then block {elseif exp then block} [else block] end |
for Name '=' exp ',' exp [',' exp] do block end |
for namelist in explist do block end |
function funcname funcbody |
local function Name funcbody |
local namelist ['=' explist]
*/
statement: ($) =>
choice(
$.empty_statement,
$.assignment_statement,
$.function_call,
$.label_statement,
$.break_statement,
$.goto_statement,
$.do_statement,
$.while_statement,
$.repeat_statement,
$.if_statement,
$.for_statement,
$.declaration
),
// retstat ::= return [explist] [';']
return_statement: ($) =>
seq(
'return',
optional(alias($._expression_list, $.expression_list)),
optional(';')
),
// ';'
empty_statement: (_) => ';',
// varlist '=' explist
assignment_statement: ($) =>
seq(
alias($._variable_assignment_varlist, $.variable_list),
'=',
alias($._variable_assignment_explist, $.expression_list)
),
// varlist ::= var {',' var}
_variable_assignment_varlist: ($) =>
list_seq(field('name', $.variable), ','),
// explist ::= exp {',' exp}
_variable_assignment_explist: ($) =>
list_seq(field('value', $.expression), ','),
// label ::= '::' Name '::'
label_statement: ($) => seq('::', $.identifier, '::'),
// break
break_statement: (_) => 'break',
// goto Name
goto_statement: ($) => seq('goto', $.identifier),
// do block end
do_statement: ($) => seq('do', field('body', optional_block($)), 'end'),
// while exp do block end
while_statement: ($) =>
seq(
'while',
field('condition', $.expression),
'do',
field('body', optional_block($)),
'end'
),
// repeat block until exp
repeat_statement: ($) =>
seq(
'repeat',
field('body', optional_block($)),
'until',
field('condition', $.expression)
),
// if exp then block {elseif exp then block} [else block] end
if_statement: ($) =>
seq(
'if',
field('condition', $.expression),
'then',
field('consequence', optional_block($)),
repeat(field('alternative', $.elseif_statement)),
optional(field('alternative', $.else_statement)),
'end'
),
// elseif exp then block
elseif_statement: ($) =>
seq(
'elseif',
field('condition', $.expression),
'then',
field('consequence', optional_block($))
),
// else block
else_statement: ($) => seq('else', field('body', optional_block($))),
// for Name '=' exp ',' exp [',' exp] do block end
// for namelist in explist do block end
for_statement: ($) =>
seq(
'for',
field('clause', choice($.for_generic_clause, $.for_numeric_clause)),
'do',
field('body', optional_block($)),
'end'
),
// namelist in explist
for_generic_clause: ($) =>
seq(
alias($._name_list, $.variable_list),
'in',
alias($._expression_list, $.expression_list)
),
// Name '=' exp ',' exp [',' exp]
for_numeric_clause: ($) =>
seq(
field('name', $.identifier),
'=',
field('start', $.expression),
',',
field('end', $.expression),
optional(seq(',', field('step', $.expression)))
),
// function funcname funcbody
// local function Name funcbody
// local namelist ['=' explist]
declaration: ($) =>
choice(
$.function_declaration,
field(
'local_declaration',
alias($._local_function_declaration, $.function_declaration)
),
field('local_declaration', $.variable_declaration)
),
// function funcname funcbody
function_declaration: ($) =>
seq('function', field('name', $._function_name), $._function_body),
// local function Name funcbody
_local_function_declaration: ($) =>
seq('local', 'function', field('name', $.identifier), $._function_body),
// funcname ::= Name {'.' Name} [':' Name]
_function_name: ($) =>
choice(
$._function_name_prefix_expression,
alias(
$._function_name_method_index_expression,
$.method_index_expression
)
),
_function_name_prefix_expression: ($) =>
choice(
$.identifier,
alias($._function_name_dot_index_expression, $.dot_index_expression)
),
_function_name_dot_index_expression: ($) =>
seq(
field('table', $._function_name_prefix_expression),
'.',
field('field', $.identifier)
),
_function_name_method_index_expression: ($) =>
seq(
field('table', $._function_name_prefix_expression),
':',
field('method', $.identifier)
),
// local namelist ['=' explist]
variable_declaration: ($) =>
seq(
'local',
choice(
alias($._att_name_list, $.variable_list),
alias($._local_variable_assignment, $.assignment_statement)
)
),
_local_variable_assignment: ($) =>
seq(
alias($._att_name_list, $.variable_list),
'=',
alias($._variable_assignment_explist, $.expression_list)
),
// namelist ::= Name {',' Name}
_name_list: ($) => name_list($),
// attnamelist ::= Name attrib {, Name attrib}
_att_name_list: ($) =>
list_seq(
seq(
field('name', $.identifier),
optional(field('attribute', alias($._attrib, $.attribute)))
),
','
),
// attrib ::= [< Name >]
_attrib: ($) => seq('<', $.identifier, '>'),
// explist ::= exp {',' exp}
_expression_list: ($) => list_seq($.expression, ','),
/*
exp ::= nil | false | true | Numeral | LiteralString | '...' | functiondef |
prefixexp | tableconstructor | exp binop exp | unop exp
*/
expression: ($) =>
choice(
$.nil,
$.false,
$.true,
$.number,
$.string,
$.vararg_expression,
$.function_definition,
$.variable,
$.function_call,
$.parenthesized_expression,
$.table_constructor,
$.binary_expression,
$.unary_expression
),
// nil
nil: (_) => 'nil',
// false
false: (_) => 'false',
// true
true: (_) => 'true',
// Numeral
number: (_) => {
function number_literal(digits, exponent_marker, exponent_digits) {
return choice(
seq(digits, /U?LL/i),
seq(
choice(
seq(optional(digits), optional('.'), digits),
seq(digits, optional('.'), optional(digits))
),
optional(
seq(
choice(
exponent_marker.toLowerCase(),
exponent_marker.toUpperCase()
),
seq(optional(choice('-', '+')), exponent_digits)
)
),
optional(choice('i', 'I'))
)
);
}
const decimal_digits = /[0-9]+/;
const decimal_literal = number_literal(
decimal_digits,
'e',
decimal_digits
);
const hex_digits = /[a-fA-F0-9]+/;
const hex_literal = seq(
choice('0x', '0X'),
number_literal(hex_digits, 'p', decimal_digits)
);
return token(choice(decimal_literal, hex_literal));
},
// LiteralString
string: ($) => choice($._quote_string, $._block_string),
_quote_string: ($) =>
choice(
seq(
field('start', alias('"', '"')),
field(
'content',
optional(alias($._doublequote_string_content, $.string_content))
),
field('end', alias('"', '"'))
),
seq(
field('start', alias("'", "'")),
field(
'content',
optional(alias($._singlequote_string_content, $.string_content))
),
field('end', alias("'", "'"))
)
),
_doublequote_string_content: ($) =>
repeat1(choice(token.immediate(prec(1, /[^"\\]+/)), $.escape_sequence)),
_singlequote_string_content: ($) =>
repeat1(choice(token.immediate(prec(1, /[^'\\]+/)), $.escape_sequence)),
_block_string: ($) =>
seq(
field('start', alias($._block_string_start, '[[')),
field('content', alias($._block_string_content, $.string_content)),
field('end', alias($._block_string_end, ']]'))
),
escape_sequence: () =>
token.immediate(
seq(
'\\',
choice(
/[\nabfnrtv\\'"]/,
/z\s*/,
/[0-9]{1,3}/,
/x[0-9a-fA-F]{2}/,
/u\{[0-9a-fA-F]+\}/
)
)
),
// '...'
vararg_expression: (_) => '...',
// functiondef ::= function funcbody
function_definition: ($) => seq('function', $._function_body),
// funcbody ::= '(' [parlist] ')' block end
_function_body: ($) =>
seq(
field('parameters', $.parameters),
field('body', optional_block($)),
'end'
),
// '(' [parlist] ')'
parameters: ($) => seq('(', optional($._parameter_list), ')'),
// parlist ::= namelist [',' '...'] | '...'
_parameter_list: ($) =>
choice(
seq(name_list($), optional(seq(',', $.vararg_expression))),
$.vararg_expression
),
// prefixexp ::= var | functioncall | '(' exp ')'
_prefix_expression: ($) =>
prec(1, choice($.variable, $.function_call, $.parenthesized_expression)),
// var ::= Name | prefixexp [ exp ] | prefixexp . Name
variable: ($) =>
choice($.identifier, $.bracket_index_expression, $.dot_index_expression),
// prefixexp [ exp ]
bracket_index_expression: ($) =>
seq(
field('table', $._prefix_expression),
'[',
field('field', $.expression),
']'
),
// prefixexp . Name
dot_index_expression: ($) =>
seq(
field('table', $._prefix_expression),
'.',
field('field', $.identifier)
),
// functioncall ::= prefixexp args | prefixexp ':' Name args
function_call: ($) =>
seq(
field('name', choice($._prefix_expression, $.method_index_expression)),
field('arguments', $.arguments)
),
// prefixexp ':' Name
method_index_expression: ($) =>
seq(
field('table', $._prefix_expression),
':',
field('method', $.identifier)
),
// args ::= '(' [explist] ')' | tableconstructor | LiteralString
arguments: ($) =>
choice(
seq('(', optional(list_seq($.expression, ',')), ')'),
$.table_constructor,
$.string
),
// '(' exp ')'
parenthesized_expression: ($) => seq('(', $.expression, ')'),
// tableconstructor ::= '{' [fieldlist] '}'
table_constructor: ($) => seq('{', optional($._field_list), '}'),
// fieldlist ::= field {fieldsep field} [fieldsep]
_field_list: ($) => list_seq($.field, $._field_sep, true),
// fieldsep ::= ',' | ';'
_field_sep: (_) => choice(',', ';'),
// field ::= '[' exp ']' '=' exp | Name '=' exp | exp
field: ($) =>
choice(
seq(
'[',
field('name', $.expression),
']',
'=',
field('value', $.expression)
),
seq(field('name', $.identifier), '=', field('value', $.expression)),
field('value', $.expression)
),
// exp binop exp
binary_expression: ($) =>
choice(
...[
['or', PREC.OR],
['and', PREC.AND],
['<', PREC.COMPARE],
['<=', PREC.COMPARE],
['==', PREC.COMPARE],
['~=', PREC.COMPARE],
['>=', PREC.COMPARE],
['>', PREC.COMPARE],
['|', PREC.BIT_OR],
['~', PREC.BIT_NOT],
['&', PREC.BIT_AND],
['<<', PREC.BIT_SHIFT],
['>>', PREC.BIT_SHIFT],
['+', PREC.PLUS],
['-', PREC.PLUS],
['*', PREC.MULTI],
['/', PREC.MULTI],
['//', PREC.MULTI],
['%', PREC.MULTI],
].map(([operator, precedence]) =>
prec.left(
precedence,
seq(
field('left', $.expression),
operator,
field('right', $.expression)
)
)
),
...[
['..', PREC.CONCAT],
['^', PREC.POWER],
].map(([operator, precedence]) =>
prec.right(
precedence,
seq(
field('left', $.expression),
operator,
field('right', $.expression)
)
)
)
),
// unop exp
unary_expression: ($) =>
prec.left(
PREC.UNARY,
seq(choice('not', '#', '-', '~'), field('operand', $.expression))
),
// Name
identifier: (_) => {
const identifier_start =
/[^\p{Control}\s+\-*/%^#&~|<>=(){}\[\];:,.\\'"\d]/;
const identifier_continue =
/[^\p{Control}\s+\-*/%^#&~|<>=(){}\[\];:,.\\'"]*/;
return token(seq(identifier_start, identifier_continue));
},
// comment
comment: ($) =>
choice(
seq(
field('start', '--'),
field('content', alias(/[^\r\n]*/, $.comment_content))
),
seq(
field('start', alias($._block_comment_start, '[[')),
field('content', alias($._block_comment_content, $.comment_content)),
field('end', alias($._block_comment_end, ']]'))
)
),
},
});