2015-11-27 12 views
0

У меня есть простой формат файла, который я хочу проанализировать с генератором синтаксиса jison. Этот файл может состоять из нескольких выражений в произвольном порядке и количестве. Вот файл jison для синтаксического анализа:Jison parser останавливается после первого правила

/* lexical grammar */ 
%lex 

%% 

\s+     /* skip whitespace */ 
\"(\\.|[^"])*\"   return 'STRING' 

File\s*Version\s*\:  return 'FILEVERSION' 
[0-9]+("."[0-9]+)?\b  return 'NUMBER' 
<<EOF>>     return 'EOF' 
.      return 'INVALID' 

/lex 

%start expressions 

%% /* language grammar */ 

expressions 
    : EOF 
    | e expressions EOF 
    ; 

e 
    : STRING 
    | FILEID 
    ; 

FILEID 
    : FILEVERSION NUMBER { return $1 + $2; } 
    ; 

Для простоты я сократил файл, чтобы только строки и выражения идентификатор файла.

Моя проблема заключается в том, что сгенерированный синтаксический анализатор распознает только одно полное выражение или два, если второе выражение состоит только из одного токена, такого как строки. Например:

Версия файла: 1,0

Будет разобран, или версия

файла: 1.0 "Моя строка"

также будет анализироваться, но для

Версия

файла: 1,0 «Моя строка» «Не разобраны строка»

последняя строка не будет анализироваться.

Я пробовал этот код с jison debugger и сам по себе jison page, но обе страницы показывают одинаковые результаты.

Мои предложения о проблеме являются:

  1. Некоторые ошибки LeXeR (регулярное выражение)
  2. Некоторые грамматики ошибки (слева направо рекурсии)
  3. Некоторые действия отсутствуют в синтаксический анализатор (вид {$$ = $ 1;})
  4. Некоторые другие бизоны/jison магии я пропускаю

Я не то, что EBNF-анализатор-гуру так, пожалуйста, держите ваши ответы как Simpl e, насколько это возможно.

ответ

2

Непосредственная проблема заключается в том, что вы return от FILEID продукции. return возвращает, поэтому синтаксический разбор завершается возвращенным значением. Обычно семантические правила должны обеспечивать их результат, присваивая переменной $$. (Это не обязательно для «правил единицы», где справа есть только один символ: перед выполнением действия парсер делает $$ = $1, поэтому, если это то, что вы хотите, вы можете просто оставить действие, как вы это делаете в ваших двух FILEID правил.)

Кроме того, ваш expressions производство ничего не делать с $2, так что даже если вы исправили первую проблему, вы бы еще видеть только один e в результате.

Неверный товар expressions, так как для каждого требуется дополнительный токен EOF, в дополнение к EOF от базового футляра.Рассмотрим, как работают производства:

expressions -> e expressions EOF 
      -> e e expressions EOF EOF 
      -> e e e expressions EOF EOF EOF 
      -> e e e EOF EOF EOF EOF 

Лично я предложил бы использовать левую рекурсию вместо правой рекурсию. Партизаны снизу вверх, такие как jison, предпочитают левую рекурсию, и это обычно приводит к более естественным семантическим правилам, как в этом случае.

Наконец, вам нужно вернуть окончательное значение, когда вы действительно достигнете конца ввода. В jison обычно требуется явное правило запуска, семантическое действие которого равно return.

Итак, при всем этом помните, что давайте попробуем это: (Я изменил имена некоторых не-терминалов и опустил FILEID, потому что традиционно использовать нижний регистр для нетерминалов и верхних частот, чехол для терминалов)

%start prog 
%% 
prog : exprs EOF   { return $1; } 
     ; 
exprs :     { $$ = []; } 
     | exprs expr   { $$.push($2); } 
     ; 
expr : file_id 
     | STRING 
     ; 
file_id: FILEVERSION NUMBER { $$ = $1 + $2; } 
     ; 

Одно замечание о вашем регулярном выражении для соответствующих строк:

\"(\\.|[^"])*\"   return 'STRING' 

Хотя это, очевидно, работает в JavaScript (в основном, смотри ниже), я t будет отображаться ошибка в flex (или совместимая с Posix библиотека регулярных выражений). Он в основном работает в javascript, потому что jouncript регулярного выражения чередует оператор |приказал; если первая альтернатива совпадает, вторая альтернатива никогда не используется, если остальная часть шаблона не соответствует (и в этом случае ошибка будет вызвана).

Но в (f) lex оператор чередования замечает все подходящие альтернативы и в конечном итоге выбирает максимально возможное совпадение. Результатом является то, что при совпадении "\\"...", флекс будет соответствовать маркер до тех пор, третьей цитаты, используя [^"] в соответствии с первым \, а затем \\., чтобы соответствовать \". Это позволяет ему продолжать искать для закрытие цитаты.

это легко написать регулярное выражение, так что он будет работать с любым семантическим, и я настоятельно рекомендую делать это в случае, если вы когда-либо хотите перейти на другой генератор синтаксических анализаторов, просто гарантируя, что \ не соответствует [^"]:

\"(\\.|[^\\"])*\"  return 'STRING' 

Это изменение также исправить тонкую ошибку, даже в JavaScript, что "\" считается действительной строкой маркера, если это последняя строка в поле ввода. В этом случае, JavaScript будет первым использовать \\., чтобы соответствовать \", но как только он делает, что он не найдет никакого закрывающую кавычку. Он будет возвращаться назад и попытаться соответствие с [^"], который будет соответствовать \, что позволяет цитата, чтобы затем была признана закрывающей цитатой.

+0

Вау, большое вам спасибо за этот ясный и всеобъемлющий ответ! – tomvodi