Я хотел бы начать работу с ANTLR, но, потратив несколько часов на просмотр примеров на antlr.org сайте, я все еще не могу получить четкого представления о грамматике процесса Java.
Есть ли какой-нибудь простой пример, что-то вроде калькулятора с четырьмя операциями, реализованного с помощью ANTLR, проходящего через определение синтаксического анализатора и вплоть до исходного кода Java?
Переведено автоматически
Ответ 1
Примечание: этот ответ предназначен для ANTLR3! Если вы ищете ANTLR4 пример, то в этом Q & A демонстрируется, как создать простой анализатор выражений и вычислитель с использованием ANTLR4.
Сначала вы создаете грамматику. Ниже приведена небольшая грамматика, которую вы можете использовать для вычисления выражений, построенных с использованием 4 основных математических операторов: +, -, * и / . Вы также можете группировать выражения, используя круглые скобки.
Обратите внимание, что эта грамматика просто очень простая: она не обрабатывает унарные операторы (минус в: -1 + 9) или десятичные дроби, такие как .99 (без начального числа), чтобы назвать только два недостатка. Это всего лишь пример, над которым вы можете поработать самостоятельно.
Вот содержимое файла грамматики Exp.g:
grammar Exp;
/* This will be the entry point of our parser. */ eval : additionExp EOF ;
/* Addition and subtraction have the lowest precedence. */ additionExp : multiplyExp ( '+' multiplyExp | '-' multiplyExp )* ;
/* Multiplication and division have a higher precedence. */ multiplyExp : atomExp ( '*' atomExp | '/' atomExp )* ;
/* An expression atom is the smallest part of an expression: a number. Or when we encounter parenthesis, we're making a recursive call back to the rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */ atomExp : Number | '(' additionExp ')' ;
/* A number: can be an integer value, or a decimal value */ Number : ('0'..'9')+ ('.' ('0'..'9')+)? ;
/* We're going to ignore all white space characters */ WS : (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;} ;
(Правила синтаксического анализа начинаются со строчной буквы, а правила лексеринга - с заглавной)
После создания грамматики вам захочется сгенерировать из нее синтаксический анализатор и лексер. Загрузите ANTLR jar и сохраните его в том же каталоге, что и ваш файл грамматики.
Выполните следующую команду в вашей командной строке:
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
Это не должно выдавать никаких сообщений об ошибках, и теперь должны быть сгенерированы файлы ExpLexer.java, ExpParser.java и Exp.токены.
Чтобы убедиться, что все это работает правильно, создайте этот тестовый класс:
// Windows javac -cp .;antlr-3.2.jar ANTLRDemo.java
а затем запустите его:
// *nix/MacOS java -cp .:antlr-3.2.jar ANTLRDemo
// Windows java -cp .;antlr-3.2.jar ANTLRDemo
Если все идет хорошо, на консоль ничего не выводится. Это означает, что анализатор не обнаружил никакой ошибки. Когда вы изменяете "12*(5-6)" на "12*(5-6", а затем перекомпилируете и запускаете его, должно быть напечатано следующее:
line 0:-1 mismatched input '<EOF>' expecting ')'
Хорошо, теперь мы хотим добавить немного кода Java в грамматику, чтобы синтаксический анализатор действительно делал что-то полезное. Добавление кода можно выполнить, поместив { и } внутри вашей грамматики с некоторым простым Java-кодом внутри нее.
Но сначала: все правила синтаксического анализа в файле грамматики должны возвращать примитивное значение double . Вы можете сделать это, добавив returns [double value] после каждого правила:
который не требует особых пояснений: ожидается, что каждое правило вернет двойное значение. Теперь, чтобы "взаимодействовать" с возвращаемым значением double value (которое НАХОДИТСЯ НЕ внутри обычного блока кода Java {...}) изнутри блока кода, вам нужно добавить знак доллара перед value:
grammar Exp;
/* This will be the entry point of our parser. */ eval returns [double value] : additionExp { /* plain code block! */ System.out.println("value equals: "+$value); } ;
// ...
Вот грамматика, но теперь с добавлением кода Java:
и поскольку наше eval правило теперь возвращает double, измените ваше ANTLRDemo.java на это:
import org.antlr.runtime.*;
publicclassANTLRDemo { publicstaticvoidmain(String[] args)throws Exception { ANTLRStringStreamin=newANTLRStringStream("12*(5-6)"); ExpLexerlexer=newExpLexer(in); CommonTokenStreamtokens=newCommonTokenStream(lexer); ExpParserparser=newExpParser(tokens); System.out.println(parser.eval()); // print the value } }
Снова (пере) сгенерируйте новый лексер и синтаксический анализатор на основе вашей грамматики (1), скомпилируйте все классы (2) и запустите ANTLRDemo (3):
и теперь вы увидите результат выражения, 12*(5-6) напечатанный на вашей консоли!
Еще раз: это очень краткое объяснение. Я рекомендую вам просмотреть ANTLR wiki и прочитать несколько руководств и / или немного поиграть с тем, что я только что опубликовал.
Удачи!
Редактировать:
В этом посте показано, как расширить приведенный выше пример, чтобы можно было предоставить Map<String, Double> файл, который содержит переменные в предоставленном выражении.
Чтобы заставить этот код работать с текущей версией Antlr (июнь 2014 г.), мне нужно было внести несколько изменений. ANTLRStringStream необходимо было стать ANTLRInputStream, возвращаемое значение нужно было изменить с parser.eval() на parser.eval().value, и мне нужно было удалить WS предложение в конце, потому что значениям атрибутов, таким как $channel больше не разрешается появляться в действиях lexer.