Я следую этому учебнику и успешно копировал его поведение, за исключением того, что я использую Antlr 4.7 вместо 4.5, который использовал учебник.

Я пытаюсь построить DSL для отслеживания расходов.

Интересно, может ли каждый элемент иметь атрибуты?

Например. вот как это выглядит сейчас

enter image description here

Это код для todo.g4, который можно увидеть в https://github.com/simkimsia/learn-antlr-web-js/blob/master/todo.g4

grammar todo;

elements
    : (element|emptyLine)* EOF
    ;

element
    : '*' ( ' ' | '\t' )* CONTENT NL+
    ;

emptyLine
    : NL
    ;

NL
    : '\r' | '\n' 
    ;

CONTENT
    : [a-zA-Z0-9_][a-zA-Z0-9_ \t]*
    ;    

То есть элемент также будет иметь 2 атрибута, таких как сумма и получатель. Для простоты у меня будет такая же структура предложений, чтобы сделать анализ более простым.

Формат будет pay [payee] [amount]

Пример pay Acme Corp 123,789.45

Таким образом, получателем является Acme Corp, а сумма 12378945, выраженная в целых числах для обозначения суммы в центах

Другой пример - pay Banana Inc 700

Таким образом, получателем является Banana Inc, и сумма составляет 70000, как выражено в целых числах, чтобы обозначить сумму в деноминациях центов

Я предполагаю, что мне нужно изменить todo.g4, а затем заново сгенерировать парсер.

Может ли элемент иметь другие атрибуты? Если да, то как мне начать?

< Сильный > UPDATE

Это мои последние попытки ранжированы с последними обновлениями сверху:

Я только что понял, как использовать grun и testRig. Спасибо @Raven за этот совет.

Последняя попытка: мой последний счет.g4 (единственное отличие от предыдущей попытки - регулярное выражение для оплаты)

grammar expense;

payments: (payment NL)* ;  
payment: PAY receiver amount=NUMBER ;  
receiver: surname=ID (lastname=ID)? ;  

PAY: 'pay' ;
NUMBER: ([0-9]+(','[0-9]+)*)('.'[0-9]*)?;
ID: [a-zA-Z0-9_]+ ;
NL: '\n' | '\r\n' ;  
WS: [\t ]+ -> skip ;

enter image description here

Более ранняя попытка: это мой счет.

grammar expense;

payments: (payment NL)* ;  
payment: PAY receiver amount=NUMBER ;  
receiver: surname=ID (lastname=ID)? ;  

PAY: 'pay' ;
NUMBER: [0-9]+ (',' [0-9]+)+ ('.' [0-9]+)? ;  
ID: [a-zA-Z0-9_]+ ;
NL: '\n' | '\r\n' ;  
WS: [\t ]+ -> skip ;

enter image description here

enter image description here

Более ранняя попытка:

Более ранняя попытка:

0
Kim Stacks 22 Окт 2017 в 13:18

3 ответа

Лучший ответ

Ситуация 24 октября. 2017 в 19:00 UTC + 1.

Ваша грамматика работает отлично. Я сделал полный тест на Java.

Файл Expense.g4:

grammar Expense;

payments
@init {System.out.println("Expense last update 1853");}
    : (payment NL)*
    ;

payment
    : PAY receiver amount=NUMBER
      {System.out.println("Payement found " + $amount.text + " to " + $receiver.text);}
    ;

receiver
    : surname=ID (lastname=ID)?
    ; 

PAY    : 'pay' ;
NUMBER : ([0-9]+(','[0-9]+)*)('.'[0-9]*)? ;
ID     : [a-zA-Z0-9_]+ ;
NL     : '\n' | '\r\n' ;  
WS     : [\t ]+ -> channel(HIDDEN) ; // keep the spaces (witout spaces ==> paydeltaco98)

Файл ExpenseMyListener.java:

public class ExpenseMyListener extends ExpenseBaseListener {
    ExpenseParser parser;
    public ExpenseMyListener(ExpenseParser parser) { this.parser = parser; }

    public void exitPayments(ExpenseParser.PaymentsContext ctx) {
        System.out.println(">>> in ExpenseMyListener for paymentsss");
        System.out.println(">>> there are " + ctx.payment().size() + " elements in the list of payments");
        for (int i = 0; i < ctx.payment().size(); i++) {
            System.out.println(ctx.payment(i).getText());
        }
    }

    public void exitPayment(ExpenseParser.PaymentContext ctx) {
        System.out.println(">>> in ExpenseMyListener for payment");
        System.out.println(parser.getTokenStream().getText(ctx));
    }
}

Файл test_expense.java:

import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

public class test_expense {
    public static void main(String[] args) throws IOException {
        ANTLRInputStream input = new ANTLRFileStream(args[0]);
        ExpenseLexer lexer = new ExpenseLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpenseParser parser = new ExpenseParser(tokens);
        ParseTree tree = parser.payments();
        System.out.println("---parsing ended");
        ParseTreeWalker walker = new ParseTreeWalker();
        ExpenseMyListener my_listener = new ExpenseMyListener(parser);
        System.out.println(">>>> about to walk");
        walker.walk(my_listener, tree);
    }
}

Входной файл top.text:

pay Acme Corp 123,456
pay Banana Inc 456789.00
pay charlie pte 123,456.89
pay delta co 98

Исполнение:

$ export CLASSPATH=".:/usr/local/lib/antlr-4.6-complete.jar"
$ alias
alias a4='java -jar /usr/local/lib/antlr-4.6-complete.jar'
alias grun='java org.antlr.v4.gui.TestRig'
$ a4 Expense.g4 
$ javac Ex*.java
$ javac test_expense.java 
$ grun Expense payments -tokens -diagnostics top.text
[@0,0:2='pay',<'pay'>,1:0]
[@1,3:3=' ',<WS>,channel=1,1:3]
[@2,4:7='Acme',<ID>,1:4]
[@3,8:8=' ',<WS>,channel=1,1:8]
[@4,9:12='Corp',<ID>,1:9]
...
[@32,90:89='<EOF>',<EOF>,5:0]
Expense last update 1853
Payement found 123,456 to Acme Corp
Payement found 456789.00 to Banana Inc
Payement found 123,456.89 to charlie pte
Payement found 98 to delta co

$ java test_expense top.text 
Expense last update 1853
Payement found 123,456 to Acme Corp
Payement found 456789.00 to Banana Inc
Payement found 123,456.89 to charlie pte
Payement found 98 to delta co
---parsing ended
>>>> about to walk
>>> in ExpenseMyListener for payment
pay Acme Corp 123,456
>>> in ExpenseMyListener for payment
pay Banana Inc 456789.00
>>> in ExpenseMyListener for payment
pay charlie pte 123,456.89
>>> in ExpenseMyListener for payment
pay delta co 98
>>> in ExpenseMyListener for paymentsss
>>> there are 4 elements in the list of payments
payAcmeCorp123,456
payBananaInc456789.00
paycharliepte123,456.89
paydeltaco98
2
BernardK 24 Окт 2017 в 17:19

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

payments: (payment NL)* ;  
payment: PAY receiver amount=NUMBER ;  
receiver: surname=ID (lastname=ID)? ;  

PAY: 'pay' ;
NUMBER: [0-9]+ (',' [0-9]+)+ ('.' [0-9]+)? ;  
ID: [a-zA-Z0-9_]+ ;
NL: '\n' | '\r\n' ;  
WS: [\t ]+ -> skip ;

Если это то, что вы просили, я добавлю еще несколько объяснений, если это необходимо ...

1
Raven 22 Окт 2017 в 11:37

Я предполагаю, что мне нужно изменить todo.g4, а затем заново сгенерировать парсер.

Конечно, регенерируйте после каждого изменения. Для меня это:

$ a4 Question.g4
$ javac Q*.java
$ grun Question elements -tokens -diagnostics t.text

Где

$ alias
alias a4='java -jar /usr/local/lib/antlr-4.6-complete.jar'
alias grun='java org.antlr.v4.gui.TestRig'

Чем больше вы описываете конкретное содержание, тем больше у вас могут возникнуть проблемы с неоднозначностью. Например, у вас есть два правила:

payment   : 'pay' [payee] [amount]
free_text : ... any character ...

Рассмотрим следующее содержание:

* pay Federico Tomassetti 10 € for the tutorial

* pay Federico Tomassetti 10 является неоднозначным и может соответствовать двум правилам, но в конечном итоге он будет проанализирован как свободный текст из-за € for the tutorial, который не удовлетворяет payment.

Если позже вы измените правило payment, чтобы получать больше информации после суммы:

payment   : 'pay' [payee] [amount] payment_info

Вышеуказанный контент будет соответствовать payment (в случае неоднозначности ANTLR выбирает первое правило). Хорошей новостью является то, что ANTLR 4 очень силен для устранения неоднозначности, при необходимости он читает весь файл.

О неоднозначных токенах и правилах приоритета читайте в постах за последние три недели, много сказано.

Смешивая грамматику ворона с вашей, это одно из возможных решений:

Файл Question.g4

grammar Question;

elements
@init {System.out.println("Question last update 1432");}
    : ( element | emptyLine )* EOF
    ;

element
    : '*' content NL
    ;

content
    : payment   //{System.out.println("Payement found " + $payment.text);}
    | free_text {System.out.println("Free text found " + $free_text.text);}
    ;

payment
    : PAY receiver amount=NUMBER
      {System.out.println("Payement found " + $amount.text + " to " + $receiver.text);}
    ;

receiver
    : surname=WORD ( lastname=WORD )?
    ;  

free_text
    : ( WORD | PAY | NUMBER )+
    ;

emptyLine
    : NL
    ;

PAY    : 'pay' ;
WORD   : LETTER ( LETTER | DIGIT | '_' )* ;
NUMBER : DIGIT+ ( ',' DIGIT+ )? ( '.' DIGIT+ )? ;  

NL  : [\r\n]
    | '\r\n' 
    ;
//WS  : [ \t]+ -> skip ; // $payment.text => payAcmeCorp123,789.45
WS  : [ \t]+ -> channel(HIDDEN) ; // spaces are needed to nicely display $payment.text

fragment DIGIT  : [0-9] ;
fragment LETTER : [a-zA-Z] ;

Файл t.text

* play with ANTLR 4
* write a tutorial
* pay Acme Corp 123,789.45
* pay Banana Inc 700
* pay Federico Tomassetti 10 € for the tutorial

Исполнение:

$ grun Question elements -tokens -diagnostics t.text
line 5:29 token recognition error at: '€'
[@0,0:0='*',<'*'>,1:0]
[@1,1:1=' ',<WS>,channel=1,1:1]
[@2,2:5='play',<WORD>,1:2]
[@3,6:6=' ',<WS>,channel=1,1:6]
[@4,7:10='with',<WORD>,1:7]
[@5,11:11=' ',<WS>,channel=1,1:11]
[@6,12:16='ANTLR',<WORD>,1:12]
[@7,17:17=' ',<WS>,channel=1,1:17]
[@8,18:18='4',<NUMBER>,1:18]
[@9,19:19='\n',<NL>,1:19]
[@10,20:20='*',<'*'>,2:0]
[@11,21:21=' ',<WS>,channel=1,2:1]
[@12,22:26='write',<WORD>,2:2]
[@13,27:27=' ',<WS>,channel=1,2:7]
[@14,28:28='a',<WORD>,2:8]
[@15,29:29=' ',<WS>,channel=1,2:9]
[@16,30:37='tutorial',<WORD>,2:10]
[@17,38:38='\n',<NL>,2:18]
...
[@56,136:135='<EOF>',<EOF>,7:0]
Question last update 1432
Free text found play with ANTLR 4
Free text found write a tutorial
line 3:26 reportAttemptingFullContext d=2 (content), input='pay Acme Corp 123,789.45
'
...
Payement found 700 to Banana Inc
Free text found pay Federico Tomassetti 10  for the tutorial

Как видите, символ € не распознается. Вам может понадобиться CONTENT правило, аналогичное FIELDTEXT вот, и тогда у тебя неприятности ...

Мега-учебник Федерико - хорошее начало. Подробные сведения см. В полномасштабном справочнике ANTLR 4 или онлайн-документ с веб-сайта www.antlr.org.

1
BernardK 22 Окт 2017 в 15:58