Разбор простой текстовой грамматики с Superpower
Я пытаюсь создать парсер с Superpower. Я уже посмотрел образцы, которые я нашел в репо, но их немного сложно понять, по крайней мере, для такого начинающего, как я:) Так что я пришел с этим небольшим испытанием.
Я изобрел очень простую грамматику, чтобы учиться. Я думал о лифте, который следует за списком инструкций, чтобы подняться, спуститься и ждать.
Пример:
(UP 100),
(DOWN 200),
(DOWN 100),
(DOWN @1),
(UP @3),
(WAIT),
(UP 300)
Как видите, он состоит из списка разделенных запятыми глаголов для перемещения, например, лифта.
- Глаголы UP, DOWN или WAIT.
- Каждый глагол заключен в круглые скобки: ()
- ВВЕРХ и ВНИЗ требуется либо абсолютное число, либо относительное число, которое указывает этаж, на который должен подняться лифт. Относительные номера этажа идут со знаком @ перед номером.
- ПОДОЖДИТЕ не принимает никакого числа, потому что останавливает лифт на некоторое время.
Я действительно хотел бы узнать, как создать синтаксический анализатор на основе токенов для этой грамматики, чтобы понять, как использовать SuperPower.
Кто-нибудь может помочь мне с этим?
Заранее спасибо!
2 ответа
Шаг 1 написания любого парсера Superpower состоит в том, чтобы выяснить, что такое токены. У вас есть что-то вроде:
// ECL - Elevator Control Language ;-)
enum EclToken {
LParen,
RParen,
UpKeyword,
DownKeyword,
WaitKeyword,
AtSymbol,
Number,
Comma
}
Шаг 2, напишите Tokenizer<EclToken>
, Superpower v1 оставляет это как прямую задачу программирования - помощников не так много, нужно просто написать код, как в примерах.
Токенайзер берет входную строку, удаляет пробелы и выясняет, какова последовательность токенов.
Для вашего примера ввода первая строка будет:
// (UP 100),
LParen, UpKeyword, Number, RParen, Comma
Для токенов типа Number
которые содержат контент, диапазон, связанный с Result<EclToken>
будет указывать на часть входной строки, соответствующую токену. В этой строке число будет TextSpan
покрытие 100
,
Шаг 3 - выяснить, во что вы хотите разобрать входные данные. Для языков программирования с вложенными выражениями это обычно AST. В случае с примером ECL все довольно просто, поэтому вы можете сократить его до:
struct ElevatorCommand {
public int Distance; // + or -
public bool IsRelative;
}
Шаг 4, парсер. Обычно это встроено в статический класс. Работа парсера заключается в создании более сложных результатов (ElevatorCommand[]
здесь), из более простых результатов (число, движение).
Именно здесь Superpower делает тяжелую работу, особенно в отношении ожиданий и ошибок.
static class EclParser
{
static TokenListParser<EclToken, int> Number =
Token.EqualTo(EclToken.Number).Apply(Numerics.IntegerInt32);
}
Первое, что мы делаем, это определяем парсер для чисел; этот применяет встроенный TextParser<int>
к содержанию EclToken.Number
пяди.
В этом примере вы можете увидеть больше машин для разбора.
Еще несколько подсказок, которые помогут вам найти способ (не проверенный синтаксис, не говоря уже о скомпилированном / протестированном):
static TokenListParser<EclToken, ElevatorCommand> Up =
from _ in Token.EqualTo(EclToken.UpKeyword)
from distance in Number
select new ElevatorCommand {
Distance = distance,
IsRelative = false
};
static TokenListParser<EclToken, ElevatorCommand> Command =
from lp in Token.EqualTo(EclToken.LParen)
from command in Up // .Or(Down).Or(Wait)
from rp in Token.EqualTo(EclToken.RParen)
select command;
static TokenListParser<EclToken, ElevatorCommand[]> Commands =
Command.ManyDelimitedBy(Token.EqualTo(EclToken.Comma));
}
Commands
является законченным парсером, который вы можете применить для ввода.
Лучше всего создавать синтаксический анализатор постепенно, тестируя каждый меньший анализатор на порциях ввода, которые они должны анализировать.
Хорошо, я наконец-то получил это. Это было не так сложно с руководством @Nicholas Blumhardt:)
Я создал проект в GitHub, чтобы проиллюстрировать сценарий. Так как классы большие для поста, я делаю ссылки на файлы:
- Это токенизатор
- Это класс с парсерами.