【问题标题】:Writing custom parser using ANTLR4, C# and VS2017使用 ANTLR4、C# 和 VS2017 编写自定义解析器
【发布时间】:2018-06-26 19:37:02
【问题描述】:

我正在尝试解析格式如下的文件。我想做的是创建一些变量和一个结构数组来包含有关文件的信息。例如,可能有(伪代码)int atomNumber = 27,然后是string struct[0].element = C & float struct[0].x = 0.6877350。我需要一个解析器/脚本,它允许我从文件中获取该信息。我正在尝试使用 ANTLR/自定义解析器而不是 Regex 来执行此操作,因为其中一些文件变得很大;我在 Unity 中执行此操作,由于它对 Unity 垃圾收集的影响,Regex 很慢。但是我是使用 ANTLR 的新手,所以我很难让它发挥作用。

这是一个文件结构的例子:

27
 comment 
C 0.6877350 0.0715370 -1.2710340
C 0.0387890 -0.2132770 2.4629140
C 2.9026270 0.7676750 -0.4325690
C 1.9897370 0.2682320 -1.4699130
H 3.8932221 0.3135170 -0.4057700
H 2.3979900 0.0407470 -2.4529259
H 0.0607820 -1.2602330 2.1661179
H 0.1969520 0.2155350 -0.3100010
O -0.1311780 -0.2708320 -2.3204310
C -1.0381711 -1.2940150 -2.2029400
O -1.8197130 -1.4577920 -3.0971811
C -0.9588580 -2.1451390 -0.9617850
H 0.0764330 -2.4116330 -0.7270430
H -1.5478179 -3.0458710 -1.1361990
H -1.3813020 -1.6050299 -0.1051530
C 1.0799000 0.3532050 3.0701120
H 1.9819210 -0.2100150 3.2911880
H 1.0789630 1.4006370 3.3635521
C 2.6065600 1.7304400 0.4415480
H 3.3160350 2.0450499 1.2023780
H 1.6531780 2.2537251 0.4046360
C -1.2067200 0.5084640 2.0837281
C -1.4050720 1.9360650 2.5400000
H -0.6525800 2.5874960 2.0799990
H -2.3981910 2.2678981 2.2341671
H -1.3007090 2.0248840 3.6260951
O -2.0373070 -0.0501320 1.3878731
27
 comment 
C 0.6835910 0.0801290 -1.2651600
C 0.0385760 -0.2142480 2.4595370
C 2.9039860 0.7584860 -0.4261270
C 1.9882360 0.2606780 -1.4617670
H 3.8887999 0.2924340 -0.3916030
H 2.3965650 0.0209800 -2.4418731
H 0.0600150 -1.2637050 2.1717429
H 0.1919770 0.2365210 -0.3064940
O -0.1368310 -0.2607420 -2.3141041
C -1.0378500 -1.2895941 -2.1994519
O -1.8194150 -1.4546410 -3.0934210
C -0.9520160 -2.1443820 -0.9613080
H 0.0850850 -2.4055369 -0.7287190
H -1.5361210 -3.0478990 -1.1376450
H -1.3763330 -1.6092030 -0.1024920
C 1.0823750 0.3586390 3.0560379
H 1.9862601 -0.2015870 3.2770901
H 1.0818750 1.4086040 3.3402641
C 2.6171989 1.7337860 0.4371280
H 3.3282030 2.0474880 1.1969039
H 1.6705470 2.2685010 0.3915880
C -1.2096530 0.5029830 2.0807769
C -1.4067100 1.9345620 2.5251229
H -0.6585890 2.5824180 2.0530601
H -2.4025259 2.2620981 2.2234540
H -1.2943890 2.0340610 3.6094720
O -2.0432911 -0.0622970 1.3940520
27
 comment 
C 0.6785940 0.0895900 -1.2592160
C 0.0387820 -0.2150840 2.4559050
C 2.9046619 0.7487670 -0.4192210
C 1.9859340 0.2525420 -1.4530050
H 3.8832800 0.2704900 -0.3765630
H 2.3943379 -0.0007300 -2.4296930
H 0.0600560 -1.2667160 2.1762300
H 0.1859720 0.2598610 -0.3034400
O -0.1432220 -0.2500770 -2.3077281
C -1.0374310 -1.2852740 -2.1962149
O -1.8187211 -1.4521340 -3.0900691
C -0.9445940 -2.1436851 -0.9611460
H 0.0943740 -2.3994770 -0.7311130
H -1.5238889 -3.0499580 -1.1391890
H -1.3703721 -1.6133870 -0.1000140
C 1.0848000 0.3637940 3.0426750
H 1.9905159 -0.1934890 3.2636609
H 1.0843771 1.4159710 3.3185790
C 2.6278651 1.7370080 0.4325060
H 3.3405459 2.0497701 1.1910950
H 1.6883790 2.2834129 0.3780430
C -1.2120970 0.4977380 2.0776091
C -1.4083090 1.9328350 2.5109861
H -0.6641670 2.5774081 2.0282159
H -2.4065051 2.2563839 2.2129040
H -1.2890840 2.0420361 3.5936451
O -2.0483761 -0.0737650 1.3993220

所以基本上它会有

{整数}

{评论扔掉}

{4列整数行数,CHAR FLOAT FLOAT FLOAT}

(按帧数重复,但行上的浮点数不同)

我尝试编写一个 ANTLR4 语法文件来解析这个:

grammar XYZ;
/*
 * Parser Rules
 */

file                : header comment line+ EOF;
line                : ELEMENT FLOAT FLOAT FLOAT NEWLINE;
header              : INT NEWLINE;
comment             : WORD+ NEWLINE;
/*
 * Lexer Rules
*/

fragment LOWERCASE  : [a-z] ;
fragment UPPERCASE  : [A-Z] ;
fragment NUMBER     : [0-9]+ ;
INT                 : NUMBER ;
FLOAT               : '-'? NUMBER '.' NUMBER ;
WORD                : (LOWERCASE | UPPERCASE)+ ;
ELEMENT             : 'A' .. 'Z' ;
WHITESPACE          : (' '|'\t')+ -> skip ;
NEWLINE             : ('\r'? '\n' | '\r')+ ;

我使用命令java -jar antlr-4.7.1-complete.jar -Dlanguage=CSharp XYZ.g4在 CMD 中生成脚本

最后在main我有下面的代码sn-p来运行程序(输入就是上面的文字)

    AntlrInputStream istream = new AntlrInputStream(input);
    XYZLexer lexer = new XYZLexer(istream);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    XYZParser parser = new XYZParser(tokens);
    XYZParser.LineContext lineContext = parser.line();

    Console.WriteLine(lineContext.GetText());
    Console.ReadLine();

我得到的是一个终端窗口,上面写着line 1:0 mismatched input '27' expecting ELEMENT,并在输入中返回文本。

使用

    XYZParser.FileContext fileContext = parser.file();
    Console.WriteLine(fileContext.GetText());

而是给我line 3:0 mismatched input 'C' expecting ELEMENT

我怎样才能摆脱这个错误并使用 ANTLR 运行时来获取数据?

回答:

更改语法文件以防止 WORD 和 ELEMENT 重叠

grammar XYZ;
/*
 * Parser Rules
 */

file                : frame+ EOF;
frame               : header comment line+;
line                : ELEMENT FLOAT FLOAT FLOAT NEWLINE;
header              : INT NEWLINE;
comment             : (ELEMENT | WORD+) NEWLINE;
/*
 * Lexer Rules
*/

fragment LOWERCASE  : [a-z] ;
fragment UPPERCASE  : [A-Z] ;
fragment NUMBER     : [0-9]+ ;
INT                 : NUMBER ;
FLOAT               : '-'? NUMBER '.' NUMBER ;
ELEMENT             : 'A' .. 'Z' ;
WORD                : (LOWERCASE | UPPERCASE)+ ;
WHITESPACE          : (' '|'\t')+ -> skip ;
NEWLINE             : ('\r'? '\n' | '\r')+ ;

将脚本改为

AntlrInputStream istream = new AntlrInputStream(input);
XYZLexer lexer = new XYZLexer(istream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
XYZParser parser = new XYZParser(tokens);
XYZParser.FileContext fileContext = parser.line();

Console.WriteLine(fileContext.GetText());
Console.ReadLine();

为了得到一切

只看到一个值,一个例子是

    AntlrInputStream istream = new AntlrInputStream(input);
    XYZLexer lexer = new XYZLexer(istream);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    XYZParser parser = new XYZParser(tokens);

    XYZParser.FileContext fileContext = parser.file();
    XYZParser.FrameContext frameContext = fileContext.frame()[0];
    XYZParser.LineContext lineContext = frameContext.line()[0];

    IParseTree tree = lineContext.FLOAT()[0];
    Console.WriteLine(tree.GetText());
    Console.ReadLine();

【问题讨论】:

    标签: c# antlr antlr4


    【解决方案1】:
    XYZParser.LineContext lineContext = parser.line();
    

    您正在尝试应用规则line,该规则需要以ELEMENT 开头,但您的输入以数字27 开头,这是INT,而不是ELEMENT。您应该改为应用规则file,它确实希望在开头使用INT

    【讨论】:

    • 这给了我一个不同的错误:line 3:0 mismatched input 'C' expecting ELEMENT
    • @Chylomicron 啊,那是因为您的 WORDELEMENT 规则重叠(并且 WORD 排在第一位)。
    • 哦,我明白了。我没有意识到 ANTLR 中的顺序很重要。我切换了 WORD 和 ELEMENT,还添加了一个帧解析器 (file : frame+ EOF; frame : header comment line+;),但没有产生任何错误。谢谢! :)
    • 我现在如何只获得一个值?例如,如果我想访问第一帧,第二个原子 y 坐标?
    • @Chylomicron 请注意,单个大写字符现在始终为 ELEMENTs,因此它们不再允许在 cmets 中使用,除非您修改注释规则以允许 ELEMENTs 和 @ 987654335@s.
    猜你喜欢
    • 2017-10-13
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 2016-03-18
    • 2020-10-19
    • 1970-01-01
    相关资源
    最近更新 更多