Создание собственных правил грамматики
22 февраля 2022 г.Всякий раз, когда вы начинаете задаваться вопросом, как создать свой синтаксис или правила для выражений, вероятно, пришло время проверить этот мощный инструмент, который может вас заинтересовать. [Chevrotain] (https://chevrotain.io/) позволяет вам создавать свою собственную грамматику. правила, проверьте и проанализируйте результат в конце.
Из чего структурно состоит эта библиотека?
Во-первых, это Lexer. Lexer — это класс, который в основном просматривает входной текст и сопоставляет токен с каждым найденным символом или последовательностью символов (слов). Чтобы создать экземпляр класса Lexer, мне нужно определить все токены, которые я хочу использовать. Токен должен иметь уникальное имя, шаблон — значит регулярное выражение.
Например, токен == ‘hackernoon’== будет соответствовать точному слову в тексте.
```машинопись
константа hackernoon = createToken({
имя: 'хакернун',
шаблон: /хакернун/
Также есть возможность категоризировать токены. Для этого мне нужно определить еще один токен с зарезервированным значением шаблона Lexer.NA
. В этом случае Lexer не будет использовать его для сопоставления некоторых символов. А в других токенах, которые я хочу классифицировать, я добавляю свой новый токен в свойство «категории». Как и в приведенном ниже примере, я ввожу MathOperator и добавляю этот токен в MathPlus и MathMinus.
```машинопись
экспортировать константу MathOperator = createToken({
имя: Token.MathOperator,
выкройка: Lexer.NA,
экспортировать константу MathPlus = createToken({
имя: Token.MathPlus,
шаблон: /+/,
категории: MathOperator,
экспортировать константу MathMinus = createToken({
имя: Токен.MathMinus,
шаблон: /-/,
категории: MathOperator,
экспортировать константное число = createToken({
имя: Токен.Номер,
шаблон: /[+-]?\d*.?\d+(?:[Ee][+-]?\d+)?/
экспортировать const Whitespace = createToken({
имя: Token.WhiteSpace,
шаблон: /\s+/,
группа: Lexer.SKIPPED,
Когда все токены определены, мне нужно создать новый экземпляр класса Lexer и поместить туда массив этих токенов.
```машинопись
константа allTokens = [
белый космос,
Количество,
МатПлюс,
МатематикаМинус
const mathLexer = новый лексер (все токены)
И с этого момента лексер готов токенизировать входные строки. Мне нужно вызвать функцию tokenize лексера, в результате я получу объект с токенами. С ними я могу перейти к следующему шагу.
```машинопись
const lexingResult = mathLexer.tokenize(text);
lexingResult имеет следующий интерфейс ILexingResult. Где массив ошибок может быть очень полезен, чтобы понять пользователю, что не так в выражении, потому что он содержит координаты проблемы: строку и столбец.
```машинопись
интерфейс экспорта ILexingResult {
tokens: IToken[]
группы: {
ошибки: IlexingError[]
Вторая важная вещь — это Парсер. Это инструмент для описания правил вашего самовыражения. Например, в математическом выражении вы ожидаете, что первая переменная будет «Число», вторая должна быть некоторым оператором «+/-», а третья также должна быть «Число». Поэтому правило будет описывать такое поведение. Прежде всего, я создаю свой собственный класс MathParser, унаследованный от CstParser. Это примет список токенов в конструкторе, а конструктор будет содержать все необходимые правила, а после них this.performSelfAnalysis()
для проверки всех написанных правил.
```машинопись
класс MathParser расширяет CstParser {
публичный mathCond: RuleFn;
закрытое значениеOrParen: RuleFn;
закрытые скобки: RuleFn;
закрытое значение: RuleFn;
частный mathOp: RuleFn;
конструктор (токены: TokenType []) {
супер(токены);
константа $ = это;
// Правила здесь
this.performSelfAnalysis(); // Должен быть после всех правил
Правило mathCond
является основным правилом входа, которое будет вызываться для выполнения синтаксического анализа. По сути, в моем примере я предполагаю, что выражение должно начинаться с некоторого числа или скобок. Вам интересно, как читать это правило? На самом деле он говорит, что если хотя бы одно число 123
или круглые скобки с числом (123)
или круглые скобки с некоторой операцией (123 + 456)
(операция, описанная в ключевом слове MANY
), то выражение допустимо.
Конечно, как видите, скобок и математических операций внутри одной скобки может быть больше, в этих терминах — рекурсия. А MANY
говорит, что выражение может повторяться ноль или более раз. Как и в примере с (123 + 456)
- повторяется один раз, если будет как (123 + 456 - 789)
, то 2 повторения. (123 + 456) - 789
это можно прочитать так: 1 mathCond
повторяется в скобках и один после них.
```машинопись
this.mathCond = $.RULE('mathCond', () => {
$.SUBRULE($.valueOrParen, {МЕТКА: 'lhs'});
$.МНОГИЕ(() => {
$.SUBRULE1($.mathOp)
$.SUBRULE2($.valueOrParen, {МЕТКА: 'правая'});
Правило valueOrParen
ожидает некоторое число или другие круглые скобки с математическим выражением.
```машинопись
this.valueOrParen = $.RULE('valueOrParen', () => {
$.ИЛИ([
{ ALT: () => $.SUBRULE($.value)},
{ ALT: () => $.SUBRULE($.круглые скобки)},
В скобках следующего правила я хочу охватить случай с фигурными скобками ()
, а внутри фигурных скобок происходит рекурсия. LeftParen
и RightParen
— еще два токена. mathCond
— основная функция правила.
```машинопись
this.parentheses = $.RULE('круглые скобки', () => {
$.CONSUME(LeftParen);
$.SUBRULE($.mathCond);
$.CONSUME(RightParen);
mathOp
— это правило, которое ищет +
ИЛИ -
.
```машинопись
this.mathOp = $.RULE('mathOp', () => {
$.ИЛИ([
{ ALT: () => $.CONSUME(MathPlus)},
{ ALT: () => $.CONSUME(MathMinus)},
Правило value
создано только для того, чтобы показать, что его можно расширить другими конкретными типами.
```машинопись
this.value = $.RULE('значение', () => {
$.ИЛИ([
{ ALT: () => $.CONSUME(Число) }
Когда парсер будет готов, создайте его и вызовите функцию main.
```машинопись
const mathParser = новый MathParser (все токены);
mathParser.mathCond();
Что касается Lexer, то здесь есть ошибки, которые помогают пользователям, и здесь после вызова основной функции парсер может содержать ошибки с токеном, где произошла ошибка. mathParser.errors
Таким образом, эта библиотека помогает вам проверить строку, введенную пользователем, и фактически оценить выражение. Более того, вы можете попробовать собственный Visitor, чтобы создать собственное дерево выражений.
Оригинал