Files
tree-sitter-stonescript/grammar.js
Bulat Kurbanov 99dadd9ca7 feat: add support for fullwidth Unicode, multiline arrays, and ASCII blocks
- Add fullwidth brackets [] (U+FF3B, U+FF3D) support
- Add fullwidth quotes " (U+FF02) support
- Fix multiline arrays with newlines between elements
- Fix line continuation with CRLF (^)
- Enable ASCII block syntax (ascii...asciiend and [ascii...asciiend])
- Update conflicts to resolve ambiguities

Fixed 51 parsing errors (253 -> 202 errors)
2025-11-26 23:04:03 +01:00

331 lines
8.8 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(
// Comments first
$.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'
$.import_statement, // 'import'
// 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_statement: $ => 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,
$.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(4, seq($._expression, '|', $._expression)),
prec.left(5, seq($._expression, '&', $._expression)),
prec.left(7, seq($._expression, '!', $._expression)),
prec.left(7, seq($._expression, '=', $._expression)),
prec.left(8, seq($._expression, '<', $._expression)),
prec.left(8, seq($._expression, '>', $._expression)),
prec.left(8, seq($._expression, '<=', $._expression)),
prec.left(8, seq($._expression, '>=', $._expression)),
prec.left(9, seq($._expression, '+', $._expression)),
prec.left(9, seq($._expression, '-', $._expression)),
prec.left(10, seq($._expression, '*', $._expression)),
prec.left(10, seq($._expression, '/', $._expression)),
prec.left(11, 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)));
}