Files
tree-sitter-stonescript/grammar.js
Bulat Kurbanov 0b78c43138 feat: major grammar improvements
- Enable ASCII blocks in print commands
- Add import as expression (not just statement)
- Fix operator precedence (& and | now lower than comparisons)
- Allow comments and newlines as top-level statements
- Fix source_file to handle leading comments and empty lines

Progress: 253 → 98 errors (155 files fixed, 55% success)
2025-11-26 23:32:44 +01:00

323 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module.exports = grammar({
name: 'stonescript',
rules: {
source_file: $ => repeat($._statement),
_statement: $ => prec.right(seq(
choice(
$._newline,
$.comment,
$.block_comment,
// Keyword-based statements (must come before generic command)
$.variable_declaration, // 'var'
$.function_declaration, // 'func'
$.for_loop, // 'for'
$.return_statement, // 'return'
$.break_statement, // 'break'
$.continue_statement, // 'continue'
// Control flow
$.conditional, // '?'
$.else_clause, // ':'
// Commands (higher precedence!)
prec.dynamic(1, $.command),
$.print_command,
// Fallback
$.expression_statement
),
optional($._newline)
)),
// Comments
comment: $ => token(seq('//', /.*/)),
block_comment: $ => token(seq(
'/*',
/[^*]*\*+(?:[^/*][^*]*\*+)*/,
'/'
)),
// Variable declaration
variable_declaration: $ => seq(
'var',
field('name', $.identifier),
optional(seq('=', field('value', $._expression)))
),
// Function declaration
function_declaration: $ => seq(
'func',
field('name', $.identifier),
'(',
optional($.parameter_list),
')',
$.block
),
parameter_list: $ => seq(
$.identifier,
repeat(seq(',', $.identifier))
),
// Loops
for_loop: $ => choice(
seq(
'for',
$.identifier,
'=',
$._expression,
'..',
$._expression,
$.block
),
seq(
'for',
$.identifier,
':',
$._expression,
$.block
)
),
// Import
import_expression: $ => seq(
'import',
$.module_path
),
new_statement: $ => seq(
'new',
$.module_path
),
module_path: $ => /[a-zA-Z_][a-zA-Z0-9_\\/]*/,
// Control flow
return_statement: $ => prec.right(seq(
'return',
optional($._expression)
)),
break_statement: $ => 'break',
continue_statement: $ => 'continue',
// Conditionals
conditional: $ => seq(
'?',
$._expression,
$.block
),
else_clause: $ => choice(
seq(':?', $._expression, $.block),
seq(':', $.block)
),
block: $ => seq(
$._indent,
repeat1($._statement),
$._dedent
),
// Commands - Generic structure to match tests
// Must have at least one argument to distinguish from simple identifier expressions
command: $ => prec.dynamic(1, prec.right(seq(
$.identifier,
repeat1($._command_arg)
))),
_command_arg: $ => choice(
$.identifier,
$.number,
$.string,
$.star_level,
$.enchantment_level
),
star_level: $ => seq('*', $.number),
enchantment_level: $ => seq('+', $.number),
print_command: $ => prec.right(seq(
choice('>', '>o', '>h', '>`', '>c', '>f'),
optional($.print_args)
)),
// Print specific helpers
print_args: $ => sep1(',', $.print_argument),
print_argument: $ => prec.left(repeat1(choice(
$.interpolation,
$.string,
$.ascii_string,
$.color_code,
$.print_text
))),
print_text: $ => /[^,@\r\n"]+/,
interpolation: $ => seq(
'@',
$._expression,
'@'
),
color_code: $ => /#[a-zA-Z0-9]+/,
// Expressions
expression_statement: $ => $._expression,
_expression: $ => choice(
$.identifier,
$.number,
$.float,
$.string,
$.boolean,
$.null,
$.array,
$.member_expression,
$.call_expression,
$.index_expression,
$.unary_expression,
$.binary_expression,
$.update_expression,
$.assignment_expression,
$.parenthesized_expression,
$.new_statement,
$.import_expression,
$.ascii_string,
$.color_code
),
member_expression: $ => prec.left(15, seq(
field('object', $._expression),
'.',
field('property', $.identifier)
)),
call_expression: $ => prec.left(14, seq(
field('function', $._expression),
'(',
optional($.argument_list),
')'
)),
argument_list: $ => sep1($.comma_sep, $._expression),
comma_sep: $ => ',',
index_expression: $ => prec.left(13, seq(
$._expression,
choice('[', ''),
$._expression,
choice(']', '')
)),
unary_expression: $ => prec.right(12, seq(
choice('!', '-'),
$._expression
)),
// Binary operators with proper precedence
binary_expression: $ => choice(
prec.left(6, seq($._expression, choice('*', '/', '%'), $._expression)),
prec.left(5, seq($._expression, choice('+', '-'), $._expression)),
prec.left(4, seq($._expression, choice('=', '!=', '!', '<', '>', '<=', '>='), $._expression)),
prec.left(3, seq($._expression, '&', $._expression)),
prec.left(2, seq($._expression, '|', $._expression))
),
update_expression: $ => choice(
prec.left(12, seq($._expression, choice('++', '--'))),
prec.right(12, seq(choice('++', '--'), $._expression))
),
assignment_expression: $ => prec.right(2, seq(
$._expression,
choice('=', '+=', '-=', '*=', '/='),
$._expression
)),
parenthesized_expression: $ => seq(
'(',
$._expression,
')'
),
// Arrays
array: $ => seq(
choice('[', ''),
repeat($._newline),
optional(seq(
$.array_elements,
repeat($._newline)
)),
choice(']', '')
),
array_elements: $ => seq(
$._expression,
repeat(seq(
',',
repeat($._newline),
$._expression
)),
optional(',')
),
// Primitives
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
number: $ => /\d+/,
float: $ => /\d+\.\d+/,
string: $ => choice(
seq('"', repeat(choice(/[^"\\]/, /\\./)), '"'),
seq('', repeat(choice(/[^\\]/, /\\./)), '')
),
boolean: $ => choice('true', 'false'),
null: $ => 'null',
ascii_string: $ => choice(
seq('ascii', $.ascii_content, 'asciiend'),
seq(choice('[', ''), 'ascii', $.ascii_content, 'asciiend', choice(']', ''))
)
},
extras: $ => [
/[ \t\r\f]/,
/\r?\n[ \t]*\^/,
$.comment,
$.block_comment
],
externals: $ => [
$._newline,
$._indent,
$._dedent,
$.ascii_content
],
word: $ => $.identifier,
conflicts: $ => [
[$.identifier, $.string],
[$._expression],
[$.command],
[$._statement, $._expression], // new_statement can be both
[$.binary_expression, $.assignment_expression], // = operator ambiguity
[$.command, $._expression], // * operator ambiguity
[$.array_elements],
[$.ascii_string]
]
});
// Helper to create comma-separated lists
function sep1(separator, rule) {
return seq(rule, repeat(seq(separator, rule)));
}