Engineering软件的结果文件包含许多页面,这些页面带有标题行,并且每页有几行数据。每个标题行包含:
这种标题行的示例是:
软件会在PAGE之后为页面编号分配后六个字符。
解析器工作正常,除了页面超过99999的文档之外,对于该文档而言,软件输出PAGE123456之类的字符串,而PAGE和页码之间没有空格(是的,某些软件生成如此大量的数据)。
我尝试的第一个语法是:
grammar F06Reader01;
readF06: dataBlock+ EOF;
dataBlock: pageLine row+;
pageLine: ONE_AT_FIRST_POS ALPNUM* PAGEATPOS ALPNUM NL;
row: ALPNUM* NL ;
PAGEATPOS: P_ATPOS A_ATPOS G_ATPOS E_ATPOS;
P_ATPOS : 'P' {getCharPositionInLine() == 119}?;
A_ATPOS : 'A' {getCharPositionInLine() == 120}?;
G_ATPOS : 'G' {getCharPositionInLine() == 121}?;
E_ATPOS : 'E' {getCharPositionInLine() == 122}?;
ONE_AT_FIRST_POS : '1' {getCharPositionInLine() == 1}?;
ALPNUM : (LETTER | DIGIT)+;
DIGIT: [0-9] ;
LETTER: ~[ \t\n\r\u0030-\u0039]; //everything but DIGITS, NL or WL
NL: '\r'? '\n';
WS : [ \t]+ ->skip;
生成的令牌将PAGE231236定义为ALPNUM,因为它发现它比PAGE大。
发现此问题后,我修改了g4文件,添加了词法模式(PAGENUM),以便在词法分析器找到PAGE时激活,但这种情况不会发生,并且词法分析器仍会生成ALPNUM令牌。
下面是词法分析器文件:
lexer grammar ModeTest01Lexer;
PAGEATPOS: P_ATPOS A_ATPOS G_ATPOS E_ATPOS -> mode(PAGENUM);
P_ATPOS : 'P' {getCharPositionInLine() == 119}?;
A_ATPOS : 'A' {getCharPositionInLine() == 120}?;
G_ATPOS : 'G' {getCharPositionInLine() == 121}?;
E_ATPOS : 'E' {getCharPositionInLine() == 122}?;
ONE_AT_FIRST_POS : '1' {getCharPositionInLine() == 1}?;
ALPNUM : (LETTER | DIGIT)+;
DIGIT: [0-9] ;
LETTER: ~[ \t\n\r\u0030-\u0039]; //everything but DIGITS, NL or WL
NL: '\r'? '\n';
WS : [ \t]+ ->skip;
mode PAGENUM;
NUM : [0-9]+;
WS2 : [ \t]+ ->skip;
NL2: '\r'? '\n' -> mode(DEFAULT_MODE);
和解析器:
parser grammar ModeTest01;
options { tokenVocab=ModeTest01Lexer; }
modeTest: dataBlock+ EOF;
dataBlock: pageLine row+;
pageLine: ONE_AT_FIRST_POS ALPNUM* PAGEATPOS NUM NL2;
row: ALPNUM* NL ;
发现下面的示例及其AST所示后,找到PAGE后,此代码仍将PAGE123456用作ALPNUM,而不是更改为PAGENUM模式:
1 MSC.NASTRAN JOB MARCH 12, 2020 MSC Nastran 11/27/13 PAGE992306
LC01 row
1 MSC.NASTRAN JOB MARCH 12, 2020 MSC Nastran 11/27/13 PAGE 2306
another row of data
您可以使用多个词法分析器模式:
1
时,按下HEADER_MODE
HEADER_MODE
中遇到PAGE
时,按下PAGE_NUMBER_MODE
(在此模式下跳过的每个(单个)其他字符)类似这样的东西:
lexer grammar NastranLexer;
ONE_AT_FIRST_POS
: {getCharPositionInLine() == 0}? '1' -> pushMode(HEADER_MODE)
;
NL
: '\r'? '\n'
;
OTHER
: .
;
mode HEADER_MODE;
HEADER_MODE_PAGE
: 'PAGE' -> pushMode(PAGE_NUMBER_MODE)
;
HEADER_MODE_ANY
: . -> skip
;
mode PAGE_NUMBER_MODE;
PAGE_NUMBER_MODE_NUMBER
: [0-9]+ -> mode(DEFAULT_MODE)
;
PAGE_NUMBER_MODE_SPACE
: [ \t] -> skip
;
解析器语法可能看起来像这样:
parser grammar NastranParser;
options {
tokenVocab=NastranLexer;
}
read
: page* EOF
;
page
: header NL row+
;
header
: ONE_AT_FIRST_POS HEADER_MODE_PAGE PAGE_NUMBER_MODE_NUMBER
;
row
: OTHER* NL
;
以及运行此命令时:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
public class Main {
public static void main(String[] args) {
String source = "1 MSC.NASTRAN JOB MARCH 12, 2020 MSC Nastran 11/27/13 PAGE 2306\n" +
"some data\n" +
"1 MSC.NASTRAN JOB MARCH 12, 2020 MSC Nastran 11/27/13 PAGE 2307\n" +
"some more data\n";
NastranLexer lexer = new NastranLexer(CharStreams.fromString(source));
NastranParser parser = new NastranParser(new CommonTokenStream(lexer));
ParseTree parseTree = parser.read();
System.out.println(parseTree.toStringTree(parser));
}
}
打印以下内容:
(read
(page
(header 1 PAGE 2306) \n
(row s o m e d a t a \n))
(page
(header 1 PAGE 2307) \n
(row s o m e m o r e d a t a \n)) <EOF>)
((我在上面的输出中添加了一些换行符)