tree-sitter-lotus
Version:
Tree-sitter grammar for LotusScript
254 lines (212 loc) • 6.99 kB
JavaScript
// grammar.js
// A *tiny* LotusScript grammar: Subs, Functions, Classes, Options,
// comments and generic statements. Expand as you need.
module.exports = grammar({
name: 'lotus',
conflicts: $ => [
[$.primary_expression, $.function_call],
[$.comparison_expression, $.unary_expression],
[$.arithmetic_expression, $.unary_expression],
[$.logical_expression, $.unary_expression],
[$.unary_expression, $.member_expression]
],
// ―――― Lexer ――――――――――――――――――――――――――――――――――――――――
extras: $ => [
/\s|\\\r?\n/, // whitespace or line-continuations
$.comment,
],
word: $ => $.identifier,
rules: {
source_file: $ => repeat(choice(
$.option_stmt,
$.class_decl,
$.sub_decl,
$.function_decl,
$.statement,
)),
// ----- Option statements -----
option_stmt: $ => seq(
field('keyword', alias(choice(
'Option Public', 'Option Private',
'Option Declare', 'Option Explicit'), $.keyword)),
$._eol
),
// ----- Class -----
class_decl: $ => seq(
'Class', field('name', $.identifier), $._eol,
repeat($.statement),
'End', 'Class', $._eol
),
// ----- Sub / Function -----
sub_decl: $ => seq(
'Sub', field('name', $.identifier),
optional(field('parameters', $.param_list)),
$._eol,
repeat($.statement),
'End', 'Sub', $._eol
),
function_decl: $ => seq(
'Function', field('name', $.identifier),
optional(field('parameters', $.param_list)),
optional(seq('As', $.type)),
$._eol,
repeat($.statement),
'End', 'Function', $._eol
),
// ----- Parameters -----
param_list: $ => seq(
'(', repeat(seq($.param, ',')), optional($.param), ')'
),
param: $ => seq(
$.identifier,
optional(seq('As', $.type))
),
type: $ => $.identifier, // refine later (Integer, Double, Variant…)
// ----- Generic statement fall-back -----
statement: $ => choice(
$.comment,
$.keyword_stmt,
$.identifier_stmt,
$.if_statement,
$.for_statement,
$.do_statement,
$.while_statement,
$.select_statement,
$.dim_statement,
$.set_statement,
$.let_statement,
$.call_statement,
),
// ----- Control structures -----
if_statement: $ => seq(
'If', $.expression, 'Then', $._eol,
repeat($.statement),
optional(seq(
'ElseIf', $.expression, 'Then', $._eol,
repeat($.statement)
)),
optional(seq(
'Else', $._eol,
repeat($.statement)
)),
'End', 'If', $._eol
),
for_statement: $ => seq(
'For', $.identifier, '=', $.expression, 'To', $.expression,
optional(seq('Step', $.expression)),
$._eol,
repeat($.statement),
'Next', $._eol
),
do_statement: $ => seq(
'Do', optional(seq(choice('While', 'Until'), $.expression)), $._eol,
repeat($.statement),
'Loop', optional(seq(choice('While', 'Until'), $.expression)), $._eol
),
while_statement: $ => seq(
'While', $.expression, $._eol,
repeat($.statement),
'Wend', $._eol
),
select_statement: $ => seq(
'Select', 'Case', $.expression, $._eol,
repeat($.case_clause),
optional(seq(
'Case', 'Else', $._eol,
repeat($.statement)
)),
'End', 'Select', $._eol
),
case_clause: $ => seq(
'Case', $.expression, $._eol,
repeat($.statement)
),
// ----- Variable and assignment statements -----
dim_statement: $ => seq(
'Dim', $.identifier, optional(seq('As', $.type)), $._eol
),
set_statement: $ => seq(
'Set', $.identifier, '=', $.expression, $._eol
),
let_statement: $ => seq(
'Let', $.identifier, '=', $.expression, $._eol
),
call_statement: $ => seq(
'Call', $.identifier, optional($.argument_list), $._eol
),
// ----- Expressions -----
expression: $ => choice(
$.primary_expression,
$.binary_expression,
$.unary_expression,
$.function_call,
$.member_expression
),
primary_expression: $ => choice(
$.identifier,
$.number,
$.string,
$.boolean,
$.parenthesized_expression
),
binary_expression: $ => choice(
$.arithmetic_expression,
$.comparison_expression,
$.logical_expression
),
arithmetic_expression: $ => prec.left(seq($.expression, choice('+', '-', '*', '/', '\\', '^', 'Mod'), $.expression)),
comparison_expression: $ => prec.left(seq($.expression, choice('=', '<>', '<', '>', '<=', '>='), $.expression)),
logical_expression: $ => prec.left(seq($.expression, choice('And', 'Or', 'Not'), $.expression)),
unary_expression: $ => seq(
choice('+', '-', 'Not'),
$.expression
),
function_call: $ => seq(
$.identifier,
optional($.argument_list)
),
argument_list: $ => seq(
'(', repeat(seq($.expression, ',')), optional($.expression), ')'
),
member_expression: $ => seq(
$.expression, '.', $.identifier
),
parenthesized_expression: $ => seq(
'(', $.expression, ')'
),
// ----- Literals -----
number: _ => token(choice(
/\d+\.\d+/, // float
/\d+/ // integer
)),
string: _ => token(choice(
/"[^"]*"/, // double quoted
/'[^']*'/ // single quoted
)),
boolean: _ => choice(
'True',
'False'
),
// ----- Comments -----
comment: _ => token(choice(
seq("'", /.*/), // single quote comment
seq("Rem", /\s+.*/) // Rem comment
)),
// ----- End of line -----
_eol: _ => token(choice(
/\r?\n/,
/;/
)),
// ----- Simple statements -----
keyword_stmt: $ => seq($.keyword, repeat($.identifier), $._eol),
identifier_stmt: $ => seq($.identifier, repeat(/[^'\n\r]+/), $._eol),
// ----- Tokens -----
identifier: _ => /[A-Za-z][_A-Za-z0-9]*/,
keyword: _ => token(choice(
'Dim', 'Set', 'Let', 'If', 'Else', 'ElseIf', 'End', 'For', 'Each',
'Do', 'Loop', 'While', 'Wend', 'Select', 'Case', 'Call', 'Use', 'UseLS',
'Then', 'To', 'Step', 'Next', 'True', 'False', 'Rem', 'Class', 'Sub',
'Function', 'As', 'Option', 'Public', 'Private', 'Declare', 'Explicit'
)),
}
});