Как я могу по-разному анализировать специальный символ в двух терминальных правилах, используя antlr? - PullRequest
2 голосов
/ 09 ноября 2011

У меня есть грамматика, которая использует символ $ в начале многих терминальных правил, таких как $video{, $audio{, $image{, $link{ и другие подобные этому.

Однако я также хотел бы сопоставить все символы $ и { и }, которые тоже не соответствуют этим правилам. Например, моя грамматика не соответствует $100 в правиле CHUNK , но добавление $ в длинный список допустимых символов в CHUNK приводит к тому, что другие производственные правила перерыв.

Как я могу изменить свою грамматику, чтобы она была достаточно умной, чтобы отличать обычные символы $, {и} от моих специальных правил производства?

По сути, я хотел бы сказать: «если после символа $ нет символа {, видео, изображения, аудио, ссылки и т. Д., То он должен перейти к значению CHUNK”.

grammar Text;

@header {
}

@lexer::members {
    private boolean readLabel = false;
    private boolean readUrl = false;
}

@members {
    private int numberOfVideos = 0;
    private int numberOfAudios = 0;
    private StringBuilder builder = new StringBuilder();

    public String getResult() {
        return builder.toString();
    }
}

text
    :   expression*
    ;

expression
    :   fillInTheBlank 
        {
            builder.append($fillInTheBlank.value);
        }
    |   image 
        {
            builder.append($image.value);
        }
    |   video
        {
            builder.append($video.value);
        }
    |   audio
        {
            builder.append($audio.value);
        }
    |   link
        {
            builder.append($link.value);
        }
    |   everythingElse
        {
            builder.append($everythingElse.value);
        }
    ;

fillInTheBlank returns [String value]
    :   BEGIN_INPUT LABEL END_COMMAND
        {
            $value = "<input type=\"text\" id=\"" +
                $LABEL.text +
                "\" name=\"" + 
                $LABEL.text +
                "\" class=\"FillInTheBlankAnswer\" />";
        }
    ;

image returns [String value]
    :   BEGIN_IMAGE URL END_COMMAND
        {
            $value = "<img src=\"" + $URL.text + "\" />";
        }
    ;

video returns [String value]
    :   BEGIN_VIDEO URL END_COMMAND
        {
            numberOfVideos++;

            StringBuilder b = new StringBuilder();
            b.append("<div id=\"video1\">Loading the player ...</div>\r\n");
            b.append("<script type=\"text/javascript\">\r\n");
            b.append("\tjwplayer(\"video" + numberOfVideos + "\").setup({\r\n");
            b.append("\t\tflashplayer: \"/trainingdividend/js/jwplayer/player.swf\", file: \"");
            b.append($URL.text);
            b.append("\"\r\n\t});\r\n");
            b.append("</script>\r\n");

            $value = b.toString();
        }
    ;

audio returns [String value]
    :   BEGIN_AUDIO URL END_COMMAND
        {
            numberOfAudios++;

            StringBuilder b = new StringBuilder();
            b.append("<p id=\"audioplayer_");
            b.append(numberOfAudios);
            b.append("\">Alternative content</p>\r\n");
            b.append("<script type=\"text/javascript\">\r\n");
            b.append("\tAudioPlayer.embed(\"audioplayer_");
            b.append(numberOfAudios);
            b.append("\", {soundFile: \"");
            b.append($URL.text);
            b.append("\"});\r\n");
            b.append("</script>\r\n");

            $value = b.toString();
        }
    ;   

link returns [String value]
    :   BEGIN_LINK URL END_COMMAND
        {
            $value = "<a href=\"" + $URL.text + "\">" + $URL.text + "</a>";
        }
    ;   

everythingElse returns [String value]
    :   CHUNK
        {
            $value = $CHUNK.text;
        }
    ;

BEGIN_INPUT
    :   '${' 
        { 
            readLabel = true; 
        }
    ;

BEGIN_IMAGE
    :   '$image{' 
        { 
            readUrl = true; 
        }
    ;

BEGIN_VIDEO
    :   '$video{' 
        { 
            readUrl = true; 
        }
    ;

BEGIN_AUDIO
    :   '$audio{' 
        { 
            readUrl = true; 
        }
    ;

BEGIN_LINK
    :   '$link{' 
        { 
            readUrl = true; 
        }
    ;

END_COMMAND
    :   { readLabel || readUrl }?=> '}' 
        { 
            readLabel = false; 
            readUrl = false;
        }
    ;

URL
    :   { readUrl }?=> 'http://' ('a'..'z'|'A'..'Z'|'0'..'9'|'.'|'/'|'-'|'_'|'%'|'&'|'?'|':')+
    ;

LABEL
    :   { readLabel }?=> ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9')*
    ;

CHUNK
    //: (~('${'|'$video{'|'$image{'|'$audio{'))+
    :   ('a'..'z'|'A'..'Z'|'0'..'9'|' '|'\t'|'\n'|'\r'|'-'|','|'.'|'?'|'\''|':'|'\"'|'>'|'<'|'/'|'_'|'='|';'|'('|')'|'&'|'!'|'#'|'%'|'*')+
    ;

Ответы [ 2 ]

1 голос
/ 09 ноября 2011

Вы не можете отрицать больше, чем один символ. Итак, следующее недействительно:

~('${')

Но почему бы просто не добавить '$', '{' и '}' к вашему CHUNK правилу и удалить + в конце правила CHUNK (иначе это будет сожрать многое, возможно '$video{' дальше в источнике, как вы уже заметили)?

Теперь токен CHUNK будет всегда состоять из одного символа, но вы можете создать производственное правило, чтобы исправить это:

chunk
  :  CHUNK+
  ;

и используйте chunk в своих правилах производства вместо CHUNK (или используйте CHUNK+, конечно).

Ввод, подобный "{ } $foo $video{", будет размечен следующим образом:

CHUNK                {
CHUNK
CHUNK                }
CHUNK
CHUNK                $
CHUNK                f
CHUNK                o
CHUNK                o
CHUNK
BEGIN_VIDEO          $video{

EDIT

И если вы позволите вашему анализатору выводить AST, вы можете легко объединить весь текст, который соответствует одному или нескольким CHUNK, в один AST, внутренний токен которого имеет тип CHUNK, например:

grammar Text;

options {
  output=AST;
}

...

chunk
  :  CHUNK+ -> {new CommonTree(new CommonToken(CHUNK, $text))}
  ;

...
0 голосов
/ 09 ноября 2011

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

Этого можно добиться, введя правило фрагмента лексера (то есть правило, которое не определяет сам токен, но может использоваться в других регулярных выражениях токена):

fragment CHUNKBODY
    : 'a'..'z'|'A'..'Z'|'0'..'9'|' '|'\t'|'\n'|'\r'|'-'|','|'.'|'?'|'\''|':'|'\"'|'>'|'<'|'/'|'_'|'='|';'|'('|')'|'&'|'!'|'#'|'%'|'*';  

Правило CHUNK выглядит следующим образом:

CHUNK
    :   { !readLabel && !readUrl }?=> (CHUNKBODY|'$')CHUNKBODY*
    ;

Кажется, это работает для меня.

...