Простая грамматика Ragel с дополнительным пробелом

Ragel - мощная машина, но у меня проблемы с "необязательными" элементами в грамматике. У меня есть простая строка с номером или строк. Беда с пробелами. Я не знаю, как правильно поставить необязательный пробел между ',' и переменной. Enter будет где угодно между токенами. Конечная строка ';' или введите. Мне нужно использовать функцию $err() для ошибки.

Это мой тестовый набор: хорошо

this , is , a   , test ; and, this,
is,ok

next, trouble
How,produce,good
grammar;
ok

выход:

  • линии (это, есть, а, тест)

    линия (и это, есть, нормально)

    линия (рядом, проблемы)

    линия (как, производить, хорошо)

    линия (грамматика)

    линия (ОК)

и потерпеть неудачу (это не = нет ',') (',,' без номера или переменной)

this not , working
and,
this,, too

когда я использую эту грамматику, я получаю отдельные символы или ошибку в конце строки

 whitespace = [ \t\v\f] ;
 enter      = [\r\n] ;
 string     = (alnum | '_')+ ;
 number     = ('+'|'-')?[0-9]+'.'[0-9]+( [eE] ('+'|'-')? [0-9]+ )? ;
 var        = string | number ;
 koniec     = (';' | enter)  ;
 line       = var whitespace* ( ',' whitespace* var )* whitespace* koniec ;
 main := whitespace* ( line )* ;

это весь мой код https://github.com/and09/simple_grammar

1 ответ

Трудно дать однозначные ответы, когда у вас нет полной спецификации вашей грамматики, но давайте, по крайней мере, постараемся, чтобы ваш пример работал так, как вы этого хотите, и тогда вы сможете исправить его, если это необходимо.

Итак, ваш полный пример с Github, в котором есть некоторые действия печати, на самом деле многое говорит о том, что происходит в конечном автомате (другой вещью, которую вы должны периодически проверять при работе с Ragel, является граф конечного автомата, который он может создавать для вы). В своей первоначальной спецификации (так же, как и в вопросе) он выдает следующее при запуске:

[this]< >,< >[is]

Так что есть проблема с входом в третью переменную. Это почему? Ну, это потому, что ваш line указывает только один ( ',' whitespace* var) элемент, но если вы попытаетесь исправить это, указав ( ',' whitespace* var)*, это также не сработает, потому что теперь вы требуете, чтобы ваш var должен быть сразу после запятой при повторении, без пробелов. Давайте попробуем это (действия намеренно удалены), перемещая пробелы в повторяющуюся группу:

line = var whitespace* ( ',' whitespace* var whitespace*)* koniec;

Теперь вы получите это в выводе:

[this]< >,< >[is]< >,< >[a]< >< >< >,< >[test]< >

Что является очевидным улучшением. Так почему же это не удается сейчас? Ну, это потому, что после вашего koniec машина хочет свернуть в следующую line, но для этого нужно увидеть var, Но у нас есть пробелы после ; вместо ввода. Таким образом, нам нужно изменить наше определение строки, чтобы вначале разрешить использование некоторых пробелов, но это также делает лишние пробелы в mainИтак, давайте попробуем эти определения:

line = whitespace* var whitespace* ( ',' whitespace* var whitespace*)* koniec;
main:= line*;

Теперь у нас есть такой вывод:

[this]< >,< >[is]< >,< >[a]< >< >< >,< >[test]< >
< >[and],< >[this]

Который снова лучше, но все еще недостаточно хорош. Теперь вы можете видеть, что он задыхается от перевода строки, что на самом деле для меня тоже немного неясно. Вы говорите, что

Конечная строка ';' или введите

Еще хочешь получить

линия (и это, есть, нормально)

Итак, давайте предположим, что ввод начинается новый line если у вас нет запятой в конце строки. Чтобы указать это в грамматике, давайте сделаем это:

line = whitespace* var whitespace* ( ',' (whitespace | enter)* var whitespace*)* koniec;

Теперь вы получите это в выводе:

[this]< >,< >[is]< >,< >[a]< >< >< >,< >[test]< >
< >[and],< >[this],[is],[ok]

Почему это не идет дальше? Это потому что наш line должен иметь var но вместо этого у нас есть пустая строка на входе. Это также поднимает вопрос о пустых строках, поэтому давайте сделаем наш line работать с контентом только для пробелов вот так:

line       = whitespace* (var whitespace* ( ',' (whitespace | enter)* var whitespace*)*)? koniec;

И взрыв! Внезапно у вас есть все группы слов, которые вы хотите в выводе. Но у вас также есть некоторые чрезмерные линии, которые на самом деле очень легко исправить, вам просто нужно переместить pisz_enter действие от koniec в линию, как это:

vargroup   = var whitespace* ( ',' %pisz_przecinek (whitespace | enter)* var whitespace*)* %pisz_enter;
line       = whitespace* vargroup? koniec;

Вот и все. Я могу заметить еще две вещи:

  • ты хочешь тебя number быть чем-то вроде

    number     = (('+'|'-')?[0-9]+'.'[0-9]+( [eE] ('+'|'-')? [0-9]+ )?) >Poczatek_Napisu %pisz_stala ;
    

    быть напечатанным правильно

  • на самом деле вам нужно повторить извлечение токенов, чтобы работать должным образом, причина в том, что вы читаете из файла в некоторых чанках с фиксированной суммой и в настоящее время храните некоторый указатель начала токена (poczatek_napisu) в ваших действиях. Если токен разделен между порциями (что может произойти с большой вероятностью в любом файле длиннее, чем sizeof bufor) у вас будет проблема (и это не проблема FSM, машина будет работать просто отлично, это просто то, что вы делаете в действиях), но это выходит за рамки текущего вопроса.
Другие вопросы по тегам