Я пытаюсь сделать подсветку синтаксиса в Atom для игрушечного языка, над которым я работаю. Я на этапе, когда я определяю контекстно-свободную грамматику. Я строил это шаг за шагом и писал тесты по пути. Когда я добавил грамматику для цикла for in, он сломал мой тест для анализа идентификаторов, потому что идентификатор начинался с «in». Вот грамматика в ее нынешнем виде (извините за вставку такого большого количества кода, но я не знала, что может быть уместным, поэтому я просто добавила все это):
module.exports = grammar({
name: 'MooLang',
rules: {
source_file: $ => repeat($._declaration),
_declaration: $ => choice(
$.variable_declaration,
$._statement
),
variable_declaration: $ => seq(
choice('var', 'let'),
$.identifier,
optional(seq(
':', $._type
)),
optional(seq(
'=', $._expression
)),
$.eol
),
_statement: $ => choice(
$.for_statement,
$.expression_statement
),
for_statement: $ => prec(0, seq(
'for',
'(',
choice(
$.variable_declaration,
$.expression_statement,
),
'in',
$._expression,
')',
$._statement
)),
expression_statement: $ => prec(1, seq(
$._expression,
$.eol
)),
_expression: $ => choice(
$.assignment,
$.comparison_expression,
$.addition_expression,
$.multiplication_expression,
$.unary_expression,
prec(5, $.primary),
prec(-1, $._type) // TODO:(Casey) Remove this
),
assignment: $ => prec.right(0, seq(
$.identifier,
'=',
$._expression
)),
comparison_expression: $ => prec.left(1, seq(
$._expression,
choice('<', '<=', '>', '>=', '==', '!='),
$._expression
)),
addition_expression: $ => prec.left(2, seq(
$._expression,
choice('+', '-'),
$._expression
)),
multiplication_expression: $ => prec.left(3, seq(
$._expression,
choice('*', '/', '%'),
$._expression
)),
unary_expression: $=> prec.right(4, seq(
choice('!', '-'),
$.primary
)),
_type: $ => choice(
$.primitive_type,
$.list_type,
$.map_type
),
primitive_type: $ => choice(
'bool', 'string',
'int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64',
'float32', 'float64'
),
list_type: $ => seq(
'[',
$._type,
']'
),
map_type: $ => seq(
'{',
$._type,
':',
$._type,
'}'
),
primary: $ => choice(
$.bool_literal,
$.list_literal,
$.map_literal,
$.parenthetical_expression,
$.identifier,
$.number
),
bool_literal: $ => choice('true', 'false'),
list_literal: $ => seq(
'[',
optional(seq(
$._expression,
repeat(seq(
',',
$._expression
)),
optional(','),
)),
']'
),
map_literal: $ => seq(
'{',
optional(seq(
$._expression,
':',
$._expression,
repeat(seq(
',',
$._expression,
':',
$._expression,
)),
)),
'}'
),
parenthetical_expression: $ => seq(
'(',
$._expression,
')'
),
identifier: $ => prec(99, /[a-zA-Z_][a-zA-Z0-9_]*/),
number: $ => prec(99, /\d+(_\d+)*(\.\d+)?/),
eol: $ => '\n'
}
});
Вот соответствующие тесты:
==================
Identifier Tests
==================
13india
---
(source_file
(expression_statement (primary (number)) (MISSING))
(expression_statement (primary (identifier)) (eol))
)
==================
For Tests
==================
for (var a in people) a + 1
---
(source_file
(for_statement (variable_declaration (identifier)) (primary (identifier)) (expression_statement (addition_expression (primary (identifier)) (primary (number))) (eol)))
)
Пока я не добавил грамматику для цикла for, все тесты идентификаторов проходили, но теперь я получаю следующий вывод:
Я предполагаю, что он находит неожиданное «d», потому что думает, что это ключевое слово «in». Но я не могу понять, почему он так подумал, поскольку он не соответствует ничему другому в цикле for.