JavaCC: Как я могу указать, какие токены ожидаются в определенном контексте? - PullRequest
1 голос
/ 29 апреля 2010

Мне нужно, чтобы JavaCC знал о контексте (текущем родительском токене) и, в зависимости от этого контекста, ожидал появления разных токенов.

Рассмотрим следующий псевдокод:

TOKEN <abc> { "abc*" } // recognizes "abc", "abcd", "abcde", ...
TOKEN <abcd> { "abcd*" } // recognizes "abcd", "abcde", "abcdef", ...

TOKEN <element1> { "element1" "[" expectOnly(<abc>) "]" }
TOKEN <element2> { "element2" "[" expectOnly(<abcd>) "]" }
...

Таким образом, когда сгенерированный синтаксический анализатор находится «внутри» токена с именем "element1" и он встречает "abcdef", он распознает его как <abc>, но когда его «внутри» токена с именем "element2" он распознает ту же строку <abcd>.

element1 [ abcdef ] // aha! it can only be <abc>
element2 [ abcdef ] // aha! it can only be <abcd>

Если я не ошибаюсь, это будет вести себя подобно более сложным определениям DTD XML-файла.

Итак, как можно указать, в каком «контексте» какие токены являются действительными / ожидаемыми?

ПРИМЕЧАНИЕ. Для моего реального случая было бы не достаточно, чтобы определить своего рода "иерархию" токенов, так что "abcdef" всегда сначала сопоставляется с <abcd> и затем <abc>. Мне действительно нужны контекстно-зависимые токены.

Ответы [ 2 ]

2 голосов
/ 05 мая 2010

ОК, похоже, вам нужна техника, которая называется lookahead. Вот очень хороший урок: Учебник Lookahead

Тогда моя первая попытка была неправильной, но поскольку она работает для отдельных токенов, определяющих контекст, я оставлю это здесь (может быть, это кому-нибудь пригодится; о)).


Допустим, мы хотим иметь какой-то язык разметки. Все, что мы хотим "разметить":

  • Выражения, состоящие из букв (abc ... zABC ... Z) и пробелов -> слова
  • Выражения, состоящие из чисел (0-9) -> чисел

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

Я создал файл WordNumber.jj, который определяет грамматику и анализатор, который будет сгенерирован:

options
{
    LOOKAHEAD= 1;

    CHOICE_AMBIGUITY_CHECK = 2;
    OTHER_AMBIGUITY_CHECK = 1;
    STATIC = true;
    DEBUG_PARSER = false;
    DEBUG_LOOKAHEAD = false;
    DEBUG_TOKEN_MANAGER = false;
    ERROR_REPORTING = true;
    JAVA_UNICODE_ESCAPE = false;
    UNICODE_INPUT = false;
    IGNORE_CASE = false;
    USER_TOKEN_MANAGER = false;
    USER_CHAR_STREAM = false;
    BUILD_PARSER = true;
    BUILD_TOKEN_MANAGER = true;
    SANITY_CHECK = true;
    FORCE_LA_CHECK = false;
}

PARSER_BEGIN(WordNumberParser)

/** Model-tree Parser */
public class WordNumberParser
{
    /** Main entry point. */
    public static void main(String args []) throws ParseException
    {
        WordNumberParser parser = new WordNumberParser(System.in);
        parser.Input();
    }
}

PARSER_END(WordNumberParser)

SKIP :
{
    " "
|   "\n"
|   "\r"
|   "\r\n"
|   "\t"
}

TOKEN :
{
    < WORD_TOKEN : (["a"-"z"] | ["A"-"Z"] | " " | "." | ",")+ > |
    < NUMBER_TOKEN : (["0"-"9"])+ >
}


/** Root production. */
void Input() :
{}
{
    ( WordContext() | NumberContext() )* < EOF >
}

/** WordContext production. */
void WordContext() :
{}
{
    "<WORDS>" (< WORD_TOKEN >)+ "</WORDS>"
}

/** NumberContext production. */
void NumberContext() :
{}
{
    "<NUMBER>" (< NUMBER_TOKEN >)+ "</NUMBER>"
}

Вы можете проверить это с помощью такого файла:

<WORDS>This is a sentence. As you can see the parser accepts it.</WORDS>
<WORDS>The answer to life, universe and everything is</WORDS><NUMBER>42</NUMBER>
<NUMBER>This sentence will make the parser sad. Do not make the parser sad.</NUMBER>

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

Exception in thread "main" ParseException: Encountered " <WORD_TOKEN> "This sentence will make the parser sad. Do not make the parser sad. "" at line 3, column 9. Was expecting: <NUMBER_TOKEN> ...

Это потому, что парсер не нашел того, что ожидал.

Надеюсь, это поможет.

Ура!

P.S .: Парсер не может быть "внутри" токена, так как токен является символом терминала (поправьте меня, если я ошибаюсь), который не может быть заменен правилами производства в дальнейшем. Таким образом, все аспекты контекста должны быть помещены в производственное правило (не терминальное), такое как «WordContext» в моем примере.

1 голос
/ 08 февраля 2011

Вам нужно использовать лексеры. Ваш пример становится примерно таким:

TOKEN: {: IN_ELEMENT1}
TOKEN: {: IN_ELEMENT2}
TOKEN: {: DEFAULT}
TOKEN: {: DEFAULT}

Обратите внимание, что (...)* не является правильным синтаксисом JavaCC, но ваш пример не так, поэтому я могу только догадываться.

...