Как использовать файлы грамматики, созданные ANTLR? - PullRequest
5 голосов
/ 12 марта 2009

Я думаю, что это глупый вопрос, но я только начинаю с ANTLR. Я собрал грамматику «SimpleCalc» из их учебников и сгенерировал ее с C в качестве целевого языка. Я получил SimpleCalcParser.c / .h и SimpleCalcLexer.c / .h в качестве вывода, и я смог скомпилировать их и успешно построить. Но теперь, как мне на самом деле использовать сгенерированный код? У меня проблемы с поиском в документах чего-нибудь полезного.

Ниже моя функция main (). Это также из учебника.

 #include "SimpleCalcLexer.h"

 int main(int argc, char * argv[])
 {

    pANTLR3_INPUT_STREAM           input;
    pSimpleCalcLexer               lex;
    pANTLR3_COMMON_TOKEN_STREAM    tokens;
    pSimpleCalcParser              parser;

    input  = antlr3AsciiFileStreamNew          ((pANTLR3_UINT8)argv[1]);
    lex    = SimpleCalcLexerNew                (input);
    tokens = antlr3CommonTokenStreamSourceNew  (ANTLR3_SIZE_HINT, TOKENSOURCE(lex));
    parser = SimpleCalcParserNew               (tokens);

    parser  ->expr(parser);

    // Must manually clean up
    //
    parser ->free(parser);
    tokens ->free(tokens);
    lex    ->free(lex);
    input  ->close(input);

    return 0;
 }

РЕДАКТИРОВАТЬ: В первом ответе я должен сказать, что я запустил программу следующим образом: "./testantlr test.txt", где test.txt содержит "4 + 1". Выходных данных не было.

Как мне, например, получить доступ к «4» в сгенерированном синтаксическом дереве или распечатать все синтаксическое дерево? Как мне получить доступ к материалу в синтаксическом дереве, которое генерирует ANTLR?

Ответы [ 4 ]

4 голосов
/ 01 июля 2009

Я столкнулся с тем же недоумением, когда впервые попробовал это сделать. Это довольно очевидный вопрос / вопрос, который делает его более странным, так как в учебниках его не рассматривают явно и прямо.

Выход из недоумения, которое я обнаружил, - это ключевое слово «возврат»:

token returns [TreeNode value]
    :    WORD { $value = new TreeNode( "word", $WORD.Text ); }
    |    INT { $value = new TreeNode( "int", $INT.Text ); }
    ;

WORD:    ('a'..'z'|'A'..'Z')+;
INT :    ('0'..'9')+;

TreeNode - это класс, который я создал. Сложно было понять, как сделать это с помощью последовательности, скажем, нескольких токенов. Решение, которое я придумал, было рекурсией:

expr returns [Accumulator value]
    :   a=token  (WS+ b=expr)?
    {
        if( b != null )
        {
            $value = new Accumulator( "expr", a.value, b.value );
        } else
        {
            $value = new Accumulator( "expr", a.value );
        }
    }
    ;

Я создал класс, в котором есть два разных конструктора. Один конструктор инкапсулирует один токен, а другой - один токен и другой экземпляр Accumulator. Обратите внимание, что само правило определяется рекурсивно и что b.value является экземпляром Accumulator. Зачем? Поскольку b является expr, а определение expr имеет returns [Accumulator value].

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

Antlr.Runtime.ANTLRStringStream stringstream =  new Antlr.Runtime.ANTLRStringStream( script );
TokenLexer lexer = new TokenLexer( stringstream );
Antlr.Runtime.CommonTokenStream tokenstream = new Antlr.Runtime.CommonTokenStream( lexer );
TokenParser parser = new TokenParser( tokenstream );

Accumulator grandtree = parser.expr().value;

Надеюсь, что это помогает людям, которые сталкиваются с этим недоумением.


Обновление

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

sequence returns [String k]
    :   (e=atom { $k = $e.k; })
        (e=atom { $k += ", " + $e.k; })*
        { $k = "sequence (" + $k + ")"; } ;

Строка k инициализируется значением k первого атома, а последующие атомы получают значения от += до k. Фрагмент $e.k ссылается на правило atom returns [String k], определенное в другом месте. Если такого правила нет, вы можете использовать свойство text (т. Е. $e.text, которое есть у токенов. Я не уверен, есть ли у свойства без токенов это свойство. Если нет, вы можете просто сделать:

nonToken returns [String whatever] : e=TOKEN { $whatever = $e.text; } ;

Который вы бы затем использовали в более высоких правилах, например,

e=nonToken { System.out.println($e.whatever); }
0 голосов
/ 25 февраля 2010

Посмотрите видео 8-й части Скотта Стэнчфилда http://vimeo.com/groups/29150/videos/8377479. Он делает это в Java, но тот же принцип может быть применен в C (++).

0 голосов
/ 12 марта 2009

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

Очень короткий ответ: ANTLR является генератором синтаксического анализатора, что означает, что он генерирует код для анализа текста. Обычно используется для анализа текста языка программирования. Я не читал учебник, на который вы ссылаетесь, но я предполагаю, что текст, который анализирует этот анализатор, представляет собой набор инструкций калькулятора, что-то вроде

ADD 2 4
MULTIPLY 4 8

Чтобы использовать программу, показанную выше, вы должны выполнить ее как любую другую программу на Си. Первый аргумент (argc) должен быть числом аргументов, а второй (argv) должен быть текстом, который нужно проанализировать.

Чтобы изучить ANTLR с нуля, я рекомендую вам прочитать книгу , опубликованную Теренсом Парром, автором ANTLR.

0 голосов
/ 12 марта 2009

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

Шаг первый, попробуйте это (запустите его и дайте ему файл).

Шаг второй, вернитесь и отредактируйте свой вопрос, но слегка измените направление. Вместо того, чтобы спрашивать «как мне использовать код», попробуйте спросить «как мне сделать __________ с этим», где пробел заменяется некоторым описанием того, что вы пытаетесь выполнить.

Таким образом, parser->expr(parser), кажется, анализирует поток токенов, поступающий из вашего файла, который должен произвести AST. Отгадывая много деталей, я бы посоветовал посмотреть, что из этого получится, особенно. value член, если он есть. Кажется, в сети есть множество учебных пособий, похожих на то, что вы делаете, нет двух одинаковых.

Если ничего не помогло, продолжайте обучение, и либо 1) оно ответит на ваши вопросы, либо 2) нет, и вы можете попробовать другое.

...