ANTLR4,将较短的字符序列与词汇模式匹配

问题描述 投票:1回答:1

Engineering软件的结果文件包含许多页面,这些页面带有标题行,并且每页有几行数据。每个标题行包含:

  1. 行首的字符'1'
  2. 某些字母数字字符(不解析常规数据)
  3. 该行的位置122处的字符串'PAGE'
  4. 数字字符串(页码)
  5. NL

这种标题行的示例是:

HEADER LINE

软件会在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

example AST

antlr antlr4
1个回答
2
投票

您可以使用多个词法分析器模式:

  1. 在行首遇到1时,按下HEADER_MODE
  2. 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>)

((我在上面的输出中添加了一些换行符)

© www.soinside.com 2019 - 2024. All rights reserved.