feat: Major grammar improvements and refactoring
- Refactor statement parsing with proper precedence handling - Improve block structure parsing with indent/dedent support - Enhance control flow parsing (conditionals, loops) - Add print command support - Improve function declaration parsing - Update scanner for better string and comment handling - Add comprehensive test corpus - Better handling of newlines and statement boundaries
This commit is contained in:
203
grammar.js
203
grammar.js
@@ -4,28 +4,30 @@ module.exports = grammar({
|
||||
rules: {
|
||||
source_file: $ => repeat($._statement),
|
||||
|
||||
_statement: $ => 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'
|
||||
$.new_expression, // 'new'
|
||||
// Control flow
|
||||
$.conditional, // '?'
|
||||
$.else_if_clause, // ':?'
|
||||
$.else_clause, // ':'
|
||||
// Commands (after keywords!)
|
||||
$.command_statement,
|
||||
// Fallback
|
||||
$.expression_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('//', /.*/)),
|
||||
@@ -50,13 +52,7 @@ module.exports = grammar({
|
||||
'(',
|
||||
optional($.parameter_list),
|
||||
')',
|
||||
optional($.function_body)
|
||||
),
|
||||
|
||||
function_body: $ => seq(
|
||||
$._indent,
|
||||
repeat1($._statement),
|
||||
$._dedent
|
||||
$.block
|
||||
),
|
||||
|
||||
parameter_list: $ => seq(
|
||||
@@ -73,14 +69,14 @@ module.exports = grammar({
|
||||
$._expression,
|
||||
'..',
|
||||
$._expression,
|
||||
optional($.block)
|
||||
$.block
|
||||
),
|
||||
seq(
|
||||
'for',
|
||||
$.identifier,
|
||||
':',
|
||||
$._expression,
|
||||
optional($.block)
|
||||
$.block
|
||||
)
|
||||
),
|
||||
|
||||
@@ -90,12 +86,12 @@ module.exports = grammar({
|
||||
$.module_path
|
||||
),
|
||||
|
||||
new_expression: $ => seq(
|
||||
new_statement: $ => seq(
|
||||
'new',
|
||||
$.module_path
|
||||
),
|
||||
|
||||
module_path: $ => /[a-zA-Z_][a-zA-Z0-9_\/]*/,
|
||||
module_path: $ => /[a-zA-Z_][a-zA-Z0-9_\\/]*/,
|
||||
|
||||
// Control flow
|
||||
return_statement: $ => prec.right(seq(
|
||||
@@ -111,18 +107,12 @@ module.exports = grammar({
|
||||
conditional: $ => seq(
|
||||
'?',
|
||||
$._expression,
|
||||
optional($.block)
|
||||
$.block
|
||||
),
|
||||
|
||||
else_if_clause: $ => seq(
|
||||
':?',
|
||||
$._expression,
|
||||
optional($.block)
|
||||
),
|
||||
|
||||
else_clause: $ => seq(
|
||||
':',
|
||||
optional($.block)
|
||||
else_clause: $ => choice(
|
||||
seq(':?', $._expression, $.block),
|
||||
seq(':', $.block)
|
||||
),
|
||||
|
||||
block: $ => seq(
|
||||
@@ -131,73 +121,55 @@ module.exports = grammar({
|
||||
$._dedent
|
||||
),
|
||||
|
||||
// Commands - specific patterns
|
||||
command_statement: $ => choice(
|
||||
$.equip_command,
|
||||
$.activate_command,
|
||||
$.loadout_command,
|
||||
$.brew_command,
|
||||
$.disable_enable_command,
|
||||
$.play_command,
|
||||
$.print_command
|
||||
),
|
||||
|
||||
equip_command: $ => prec.left(seq(
|
||||
choice('equip', 'equipL', 'equipR'),
|
||||
repeat1($.item_criteria)
|
||||
)),
|
||||
|
||||
item_criteria: $ => prec.left(choice(
|
||||
// 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),
|
||||
|
||||
activate_command: $ => seq(
|
||||
'activate',
|
||||
choice(
|
||||
$.identifier,
|
||||
'P', 'L', 'R'
|
||||
)
|
||||
),
|
||||
|
||||
loadout_command: $ => seq(
|
||||
'loadout',
|
||||
$.number
|
||||
),
|
||||
|
||||
brew_command: $ => seq(
|
||||
'brew',
|
||||
$.identifier,
|
||||
repeat(seq('+', $.identifier))
|
||||
),
|
||||
|
||||
disable_enable_command: $ => prec.left(seq(
|
||||
choice('disable', 'enable'),
|
||||
choice(
|
||||
'abilities', 'hud', 'banner',
|
||||
'loadout', 'npcDialog', 'pause', 'player'
|
||||
)
|
||||
)),
|
||||
|
||||
play_command: $ => prec.left(seq(
|
||||
'play',
|
||||
$.identifier,
|
||||
optional($.number)
|
||||
)),
|
||||
|
||||
print_command: $ => prec.right(seq(
|
||||
choice('>', '>o', '>h', '>`', '>c', '>f'),
|
||||
optional($.print_args),
|
||||
repeat($.print_continuation)
|
||||
)),
|
||||
|
||||
// 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,
|
||||
'@'
|
||||
),
|
||||
|
||||
print_continuation: $ => prec.right(seq(
|
||||
'^',
|
||||
repeat(choice(
|
||||
$.identifier,
|
||||
$.string,
|
||||
$.number,
|
||||
$.color_code,
|
||||
','
|
||||
/[^@\r\n]+/,
|
||||
$.interpolation
|
||||
))
|
||||
)),
|
||||
|
||||
@@ -222,7 +194,9 @@ module.exports = grammar({
|
||||
$.update_expression,
|
||||
$.assignment_expression,
|
||||
$.parenthesized_expression,
|
||||
$.new_expression
|
||||
$.new_statement,
|
||||
// $.ascii_string,
|
||||
$.color_code
|
||||
),
|
||||
|
||||
member_expression: $ => prec.left(15, seq(
|
||||
@@ -291,11 +265,16 @@ module.exports = grammar({
|
||||
// Arrays
|
||||
array: $ => seq(
|
||||
'[',
|
||||
optional(sep1($.comma_sep, $._expression)),
|
||||
optional(','),
|
||||
optional($.array_elements),
|
||||
']'
|
||||
),
|
||||
|
||||
array_elements: $ => seq(
|
||||
$._expression,
|
||||
repeat(seq(',', $._expression)),
|
||||
optional(',')
|
||||
),
|
||||
|
||||
// Primitives
|
||||
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
|
||||
|
||||
@@ -307,11 +286,19 @@ module.exports = grammar({
|
||||
|
||||
boolean: $ => choice('true', 'false'),
|
||||
|
||||
null: $ => 'null'
|
||||
null: $ => 'null',
|
||||
// ascii_string: $ => seq(
|
||||
// 'ascii',
|
||||
// $.ascii_content,
|
||||
// 'asciiend'
|
||||
// )
|
||||
},
|
||||
|
||||
extras: $ => [
|
||||
/\s/
|
||||
/[ \t\r\f]/,
|
||||
/[\r\n]\^/,
|
||||
$.comment,
|
||||
$.block_comment
|
||||
],
|
||||
|
||||
externals: $ => [
|
||||
@@ -325,10 +312,10 @@ module.exports = grammar({
|
||||
conflicts: $ => [
|
||||
[$.identifier, $.string],
|
||||
[$._expression],
|
||||
[$.command_statement],
|
||||
[$._statement, $._expression], // new_expression can be both
|
||||
[$.equip_command], // handle repeat ambiguity
|
||||
[$.binary_expression, $.assignment_expression] // = operator ambiguity
|
||||
[$.command],
|
||||
[$._statement, $._expression], // new_statement can be both
|
||||
[$.binary_expression, $.assignment_expression], // = operator ambiguity
|
||||
[$.command, $._expression] // * operator ambiguity
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user