Для всех гуру-компилятора я хочу написать рекурсивный парсер спуска, и я хочу сделать это с помощью только кода. Нет генерации лексеров и парсеров из какой-либо другой грамматики, и не говорите мне, чтобы я читал книгу драконов, я в конце концов приду.
Я хочу получить подробные сведения о реализации lexer и parser для разумного простого языка, скажем, CSS. И я хочу сделать это правильно.
Вероятно, это будет серия вопросов, но сейчас я начинаю с lexer. Правила Tokenization для CSS можно найти здесь.
Я нахожу свой собственный код для написания подобным образом (надеюсь, вы можете сделать вывод из этого фрагмента):
public CssToken ReadNext()
{
int val;
while ((val = _reader.Read()) != -1)
{
var c = (char)val;
switch (_stack.Top)
{
case ParserState.Init:
if (c == ' ')
{
continue; // ignore
}
else if (c == '.')
{
_stack.Transition(ParserState.SubIdent, ParserState.Init);
}
break;
case ParserState.SubIdent:
if (c == '-')
{
_token.Append(c);
}
_stack.Transition(ParserState.SubNMBegin);
break;
Что это называется? и как далеко я от чего-то разумного хорошо понял? Я пытаюсь сбалансировать то, что справедливо с точки зрения эффективности и легко работать, используя стек, чтобы реализовать какой-то конечный автомат, работает довольно хорошо, но я не уверен, как это сделать.
У меня есть входной поток, из которого я могу читать 1 символ за раз. Я сейчас не вижу головы, я просто читаю персонажа, а затем в зависимости от текущего состояния пытаюсь что-то сделать с этим.
Мне бы очень хотелось познакомиться с набором повторяющихся фрагментов кода. Этот метод Transition
в настоящее время означает это сделать, он вытолкнет текущее состояние стека, а затем переместит аргументы в обратном порядке. Таким образом, когда я пишу Transition(ParserState.SubIdent, ParserState.Init)
, он "вызовет" подпрограмму SubIdent
, которая при завершении вернется в состояние Init
.
Синтаксический анализатор будет реализован почти так же, в настоящее время, имея все в одном большом подобном методе, это позволяет мне легко вернуть токен, когда я его нашел, но он также заставляет меня хранить все в одном большом методе, Есть ли хороший способ разделить эти правила токенизации на отдельные методы?