UNPKG

tree-sitter-lotus

Version:
254 lines (212 loc) 6.99 kB
// 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' )), } });