Files
tree-sitter-stonescript/grammar.js
Bulat Kurbanov 06e6e3b098 Add full-width closing bracket support after asciiend
Allow ] (U+FF3D) after asciiend in ASCII content scanner.
This enables ASCII strings inside arrays with full-width brackets:
  var x = [ascii...asciiend]

The parser now correctly recognizes this as an array containing
an ASCII string, not as a syntax error.

Fixes 1 additional parsing error.
2025-11-27 10:40:00 +01:00

320 lines
8.2 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,
$.ascii_string,
$.array,
$.member_expression,
$.call_expression,
$.index_expression,
$.unary_expression,
$.binary_expression,
$.update_expression,
$.assignment_expression,
$.parenthesized_expression,
$.new_statement,
$.import_expression,
$.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: $ => seq('ascii', $.ascii_content, 'asciiend')
},
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)));
}