АНТЛР 4: Разбор грамматики
Я хочу проанализировать некоторые данные из скрипта AppleSoft Basic. Я выбираю ANTLR и загружаю эту грамматику: jvmBasic
Я пытаюсь извлечь имя функции без параметров:
return parser.prog().line(0).amprstmt(0).statement().getText();
но он возвращает PRINT"HELLO", например, полное выражение, кроме номера строки. Вот строка, которую я хочу проанализировать:
10 печать "Привет!"
1 ответ
Я думаю, что этот вопрос действительно зависит от реализации вашей программы ANTLR, но если вы используете treewalker/listener, вы, вероятно, захотите настроить таргетинг на правило для конкретных токенов, а не на все правило "Statement", которое является циклическим и охватывает много типов операторов:
//each line can have one to many amprstmt's
line
: (linenumber ((amprstmt (COLON amprstmt?)*) | (COMMENT | REM)))
;
amprstmt
: (amperoper? statement) //encounters a statement here
| (COMMENT | REM)
;
//statements can be made of 1 to many sub statements
statement
: (CLS | LOAD | SAVE | TRACE | NOTRACE | FLASH | INVERSE | GR | NORMAL | SHLOAD | CLEAR | RUN | STOP | TEXT | HOME | HGR | HGR2)
| prstmt
| printstmt1 //the print rule
//MANY MANY OTHER RULES HERE TOO LONG TO PASTE........
;
//the example rule that occurs when the token's "print" is encountered
printstmt1
: (PRINT | QUESTION) printlist?
;
printlist
: expression (COMMA | SEMICOLON)? printlist*
;
Как вы можете видеть из грамматики типа BNF, здесь правило оператора в этой грамматике включает в себя правила для оператора печати, а также для любого другого типа оператора, так что оно будет включать 10, PRINT и hello, а затем возвращать текст с помощью getText() Метод, когда с любым из них встречаются в вашем случае, все, кроме белья, который является правилом вне правила оператора.
Если вы хотите, чтобы эти конкретные правила предназначались для обработки того, что происходит, когда они встречаются, вы, скорее всего, захотите добавить функциональность к каждому из методов, которые ANTLR генерирует для каждого правила, расширяя класс jvmBasiListener, как показано здесь
пример:
-jvmBasicListener.java
-extended to jvmBasicCustomListener.java
void enterPrintstmt1(jvmBasicParser.Printstmt1Context ctx){
System.out.println(ctx.getText());
}
Однако, если все это настроено, и вы просто хотите вернуть строковое значение и т. Д., Используя единственную строку, которую вы пытаетесь получить доступ к методам на более низком уровне, обращаясь к дочерним узлам оператора, может работать amprstmt-> Statement->printstmt1-> значение:
return parser.prog().line().amprstmt(0).statement().printstmt1().getText();
Просто чтобы немного сузить мой ответ, правила, конкретно касающиеся вашего ввода "10 PRINT "HELLO" ", будут такими:
linenumber (contains Number) , statement->printstmt1 and statement->datastmt->datum (contains STRINGLITERAL)
Таким образом, как показано выше, правило белья существует само по себе, а два других правила, которые определили ваш текст, являются потомками оператора, который объясняет вывод всего, кроме номера строки, при получении текста правил оператора.
Обращаясь к каждому из них и используя getText(), а не всеобъемлющее правило, такое как оператор, может дать вам результат, который вы ищете.
Я обновлю его, чтобы ответить на ваш вопрос, поскольку ответ может быть немного длиннее, и, на мой взгляд, самый простой способ обработки определенных правил, а не генерации слушателя или посетителя, заключается в реализации действий в правилах вашего файла грамматики, например:
printstmt1
: (PRINT | QUESTION) printlist? {System.out.println("Print"); //your java code }
;
Это просто позволит вам обратиться к каждому правилу и выполнить любое действие Java, которое вы захотите выполнить. Затем вы можете просто скомпилировать свой код с помощью чего-то вроде:
java -jar antlr-4.5.3-complete.jar jvmBasic.g4 -visitor
После этого вы можете просто запустить свой код по своему усмотрению, вот пример:
import JVM1.jvmBasicLexer;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
public class Jvm extends jvmBasicBaseVisitor<Object> {
public static void main(String[] args) {
jvmBasicLexer lexer = new jvmBasicLexer(new ANTLRInputStream("10 PRINT \"Hello!\""));
jvmBasicParser parser = new jvmBasicParser(new CommonTokenStream(lexer));
ParseTree tree = parser.prog();
}
}
Выходные данные для этого примера будут просто:
Print
Вы также можете включить в грамматику любые методы Java, которые вам нравятся, для решения каждого встречающегося правила и либо разработать собственные классы и методы для его обработки, либо напрямую распечатать результат.
Обновить
Просто чтобы ответить на последний вопрос сейчас:parser.line().linenumber().getText()
- Для строки Number, поскольку строка не является частью оператора
parser.prog().line(0).amprstmt(0).statement().printstmt1().PRINT().getText()
- Для PRINT, поскольку она изолирована в printstmt1, однако не включает CLR в правило
parser.prog().line(0).amprstmt(0).statement().printstmt1().printlist().expression().getText()
- Получить значение "привет", поскольку оно является частью выражения, содержащегося в правиле printstmt1.
:) Удачи