Правило ANTLR для использования фиксированного количества символов - PullRequest
6 голосов
/ 25 октября 2010

Я пытаюсь написать грамматику ANTLR для формата PHP serialize (), и кажется, что все работает нормально, кроме строк.Проблема заключается в том, что формат сериализованных строк:

s:6:"length";

В терминах регулярных выражений правило, подобное s:(\d+):".{\1}";, будет описывать этот формат, если в числе «количество совпадений» разрешены только обратные ссылки (ноони не).

Но я не могу найти способ выразить это ни для грамматики лексера, ни для синтаксического анализатора: вся идея состоит в том, чтобы сделать количество прочитанных символов зависимым от обратной ссылки, описывающей количество символов для чтения, как в Фортран Холлеритконстанты (то есть 6HLength), а не разделитель строк.

Этот пример из грамматики ANTLR для Fortran , кажется, указывает путь, но я не вижу, как.Обратите внимание, что моим целевым языком является Python, в то время как большинство документов и примеров для Java:

// numeral literal
ICON {int counter=0;} :
    /* other alternatives */
    // hollerith
    'h' ({counter>0}? NOTNL {counter--;})* {counter==0}?
      {
      $setType(HOLLERITH);
      String str = $getText;
      str = str.replaceFirst("([0-9])+h", "");
      $setText(str);
      }
    /* more alternatives */
    ;

1 Ответ

4 голосов
/ 25 октября 2010

Поскольку ввод, такой как s:3:"a"b";, действителен, вы не можете определить токен String в своем лексере, если только первая и последняя двойные кавычки не равны всегда началу и концу вашей строки. Но я думаю, что это не так.

Итак, вам понадобится правило лексера, например:

SString
  :  's:' Int ':"' ( . )* '";'
  ;

Другими словами: соответствует s:, затем значение integer, затем :", затем один или несколько символов, которые могут быть любыми, заканчивающимися ";. Но вы должны указать лексеру прекратить потребление, когда значение Int не достигнуто. Вы можете сделать это, смешав некоторый простой код в вашей грамматике, чтобы сделать это. Вы можете встроить простой код, обернув его внутри { и }. Поэтому сначала преобразуйте значение, которое токен Int содержит в целочисленную переменную с именем chars:

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( . )* '";'
  ;

Теперь вставьте некоторый код в цикл ( . )*, чтобы остановить его потребление, как только chars будет отсчитан до нуля:

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
  ;

и все.

Небольшая демонстрационная грамматика:

grammar Test;

options {
  language=Python;
}

parse
  :  (SString {print 'parsed: [\%s]' \% $SString.text})+ EOF
  ;

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
  ;

Int
  :  '0'..'9'+
  ;

(обратите внимание, что вам нужно экранировать % внутри вашей грамматики!)

И тестовый скрипт:

import antlr3
from TestLexer import TestLexer
from TestParser import TestParser

input = 's:6:"length";s:1:""";s:0:"";s:3:"end";'
char_stream = antlr3.ANTLRStringStream(input)
lexer = TestLexer(char_stream)
tokens = antlr3.CommonTokenStream(lexer)
parser = TestParser(tokens)
parser.parse()

, который выдает следующий вывод:

parsed: [s:6:"length";]
parsed: [s:1:""";]
parsed: [s:0:"";]
parsed: [s:3:"end";]
...