Всякий раз, когда вы начинаете задаваться вопросом, как создать свой синтаксис или правила для выражений, вероятно, пришло время проверить этот мощный инструмент, который может вас заинтересовать. [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, чтобы создать собственное дерево выражений.