使用 setText 更改 antlr4 中的规则文本

changing text of rule in antlr4 using setText

我想将 csv 文件中的每个条目更改为 'BlahBlah' 为此,我有 antlr 语法

grammar CSV;

file : hdr row* row1;
hdr : row;

row :  field (',' value1=field)* '\r'? '\n'; // '\r' is optional at the end of a row of CSV file ..

row1 :  field (',' field)* '\r'? '\n'?;

field 
      :  TEXT
    {
        $setText("BlahBlah");
    }
      |  STRING 
      | 
      ;
TEXT : ~[,\n\r"]+ ;
STRING : '"' ('""' | ~'"')* '"' ;

但是当我在 antlr4 上 运行 这个

error(63): CSV.g4:13:3: unknown attribute reference setText in $setText
make: *** [run] Error 1

为什么在 antlr4 中不支持 setText,还有其他替代文本的方法吗?

tl;dr:

给定以下语法(源自原始 CSV.g4 示例和 OP 的语法尝试(参见问题)):

grammar CSVBlindText;

@header {
import java.util.*;
}

/** Derived from rule "file : hdr row+ ;" */
file
locals [int i=0]
     : hdr ( rows+=row[$hdr.text.split(",")] {$i++;} )+
       {
       System.out.println($i+" rows");
       for (RowContext r : $rows) {
           System.out.println("row token interval: "+r.getSourceInterval());
       }
       }
     ;

hdr : row[null] {System.out.println("header: '"+$text.trim()+"'");} ;

/** Derived from rule "row : field (',' field)* '\r'? '\n' ;" */
row[String[] columns] returns [Map<String,String> values]
locals [int col=0]
@init {
    $values = new HashMap<String,String>();
}
@after {
    if ($values!=null && $values.size()>0) {
        System.out.println("values = "+$values);
    }
}
// rule row cont'd...
    :   field
        {
        if ($columns!=null) {
            $values.put($columns[$col++].trim(), $field.text.trim());
        }
        }
        (   ',' field
            {
            if ($columns!=null) {
                $values.put($columns[$col++].trim(), $field.text.trim());
            }
            }
        )* '\r'? '\n'
    ;

field
    : TEXT
    | STRING
    |
    ;

TEXT : ~[',\n\r"]+ {setText( "BlahBlah" );} ;
STRING : '"' ('""'|~'"')* '"' ; // quote-quote is an escaped quote

一个有:

$> antlr4 -no-listener CSVBlindText.g4
$> grep setText CSVBlindText*java
CSVBlindTextLexer.java:         setText( "BlahBlah" );

编译它完美无缺:

$> javac CSVBlindText*.java

测试数据(刚刚重命名的 users.csv 文件):

$> cat blinded_by_grammar.csv 
User,  Name,    Dept
parrt, Terence, 101
tombu, Tom,     020
bke, Kevin,     008

测试产量:

$> grun CSVBlindText file blinded_by_grammar.csv 
header: 'BlahBlah,BlahBlah,BlahBlah'
values = {BlahBlah=BlahBlah}
values = {BlahBlah=BlahBlah}
values = {BlahBlah=BlahBlah}
3 rows
row token interval: 6..11
row token interval: 12..17
row token interval: 18..23

所以看起来 setText() 应该在生产分号之前注入,而不是在备选方案之间注入(这里胡乱猜测 ;-)

之前的迭代 如下:

只是猜测,因为我 1) 目前没有可用的可用 antlr4 和 2) 现在已经有一段时间没有编写 ANTLR4 语法了——也许没有美元 ($) ?

grammar CSV;

file : hdr row* row1;
hdr : row;

row :  field (',' value1=field)* '\r'? '\n'; // '\r' is optional at the end of a row of CSV file ..

row1 :  field (',' field)* '\r'? '\n'?;

field 
      :  TEXT
    {
        setText("BlahBlah");
    }
      |  STRING 
      | 
      ;
TEXT : ~[,\n\r"]+ ;
STRING : '"' ('""' | ~'"')* '"' ;

更新:现在可以使用 antlr 4.5.2(至少通过 brew)而不是 4.5.3,我深入研究并回答了一些问题OP 下​​面的评论:如果语法定义正确,setText() 将在 lexer java 模块中生成。不幸的是,为像我这样的业余爱好者调试 antlr4 语法是......但是非常好的语言构建工具包 IMO。

示例会话:

$> antlr4 -no-listener CSV.g4 
$> grep setText CSVLexer.java 
            setText( String.valueOf(getText().charAt(1)) );

使用的语法: (从通过以下方式检索的示例代码中破解:

curl -O http://media.pragprog.com/titles/tpantlr2/code/tpantlr2-code.tgz)

grammar CSV;

@header {
import java.util.*;
}

/** Derived from rule "file : hdr row+ ;" */
file
locals [int i=0]
     : hdr ( rows+=row[$hdr.text.split(",")] {$i++;} )+
       {
       System.out.println($i+" rows");
       for (RowContext r : $rows) {
           System.out.println("row token interval: "+r.getSourceInterval());
       }
       }
     ;

hdr : row[null] {System.out.println("header: '"+$text.trim()+"'");} ;

/** Derived from rule "row : field (',' field)* '\r'? '\n' ;" */
row[String[] columns] returns [Map<String,String> values]
locals [int col=0]
@init {
    $values = new HashMap<String,String>();
}
@after {
    if ($values!=null && $values.size()>0) {
        System.out.println("values = "+$values);
    }
}
// rule row cont'd...
    :   field
        {
        if ($columns!=null) {
            $values.put($columns[$col++].trim(), $field.text.trim());
        }
        }
        (   ',' field
            {
            if ($columns!=null) {
                $values.put($columns[$col++].trim(), $field.text.trim());
            }
            }
        )* '\r'? '\n'
    ;

field
    : TEXT
    | STRING
    | CHAR
    |
    ;

TEXT : ~[',\n\r"]+ ;
STRING : '"' ('""'|~'"')* '"' ; // quote-quote is an escaped quote

/** Convert 3-char 'x' input sequence to string x */
CHAR:   '\'' . '\'' {setText( String.valueOf(getText().charAt(1)) );} ;

编译作品:

$> javac CSV*.java

现在用匹配的奇怪 csv 文件进行测试:

a,b
"y",'4'

如:

$> grun CSV file foo.csv
line 1:0 no viable alternative at input 'a'
line 1:2 no viable alternative at input 'b'
header: 'a,b'
values = {a="y", b=4}
1 rows
row token interval: 4..7

所以总而言之,我建议修改语法逻辑(我认为插入 "BlahBlahBlah" 不是必需的,而只是调试 hack)。

并引用 http://www.antlr.org/support.html :

ANTLR 讨论

Please do not start discussions at Whosebug. They have asked us to 
steer discussions (i.e., non-questions/answers) away from Whosebug; we 
have a discussion forum at Google specifically for that:

https://groups.google.com/forum/#!forum/antlr-discussion

We can discuss ANTLR project features, direction, and generally argue about 
whatever we want at the google discussion forum.

希望对您有所帮助。

这里有几个问题:

首先,要识别接收者的setText方法。大概想要

field  :  TEXT    { $TEXT.setText("BlahBlah"); }
       |  STRING 
       ;

其次是setText没有在Tokenclass中定义。

通常,创建您自己的令牌 class 扩展 CommonToken 和相应的令牌工厂 class。将 TokenLableType(在选项块中)设置为您的令牌 class 名称。 CommonToken 中的 setText 方法将可见。