Parsing ANTLR在词法分析/解析中包含元数据的最佳方式(自定义对象、注释类型)

Parsing ANTLR在词法分析/解析中包含元数据的最佳方式(自定义对象、注释类型),parsing,metadata,antlr3,lexical-analysis,Parsing,Metadata,Antlr3,Lexical Analysis,我计划在解析过程中包含文本元数据(如粗体、字体大小等),以实现更好的识别 例如,我有一个给定的结构,在它自己的行word/r/n中有一个粗体字,大小为24px,是某篇文章的标题。为了获得更好的识别结果,我想考虑字符和元数据。就ANTR而言,我不确定如何才能做到最好。我想做一些类似的事情: 将原始文本的每个字符包装到带有元数据字段的自定义对象中,并将其传递给ANTLR 对文本进行预处理,并在特定位置插入语法考虑的元数据注释 我真的很喜欢选择1。但是我不确定我需要从ANTLR中的哪一部分子类化等。我

我计划在解析过程中包含文本元数据(如粗体、字体大小等),以实现更好的识别

例如,我有一个给定的结构,在它自己的行
word/r/n
中有一个粗体字,大小为24px,是某篇文章的标题。为了获得更好的识别结果,我想考虑字符和元数据。就ANTR而言,我不确定如何才能做到最好。我想做一些类似的事情:

  • 将原始文本的每个字符包装到带有元数据字段的自定义对象中,并将其传递给ANTLR
  • 对文本进行预处理,并在特定位置插入语法考虑的元数据注释
  • 我真的很喜欢选择1。但是我不确定我需要从ANTLR中的哪一部分子类化等。我是否必须从
    AntlInputStream对象开始,以便为子类Lexer获取合适的流,为子类解析器获取自定义标记等。是否有更优雅的方法,尤其是在使用
    {}
    块中的操作进行解析时查询令牌

    如果任何人有一些提示和/或经验,这将是伟大的

    编辑:

    这里有一个更具体的简单示例:我有一个文件,其中包含我正手解析的元数据编码。包含换行符的实际文本如下所示:

    entryOne
    Here is some content one.
    entryTwo
    Here is some content two.
    
    其中标题
    entryOne
    entryTwo
    最初的字体大小为24px,内容的字体大小为12px(作为示例性给定值)。逐字符我创建一个自定义对象的新实例,将字符封装为字符串和字体大小

    我使用字体大小的字段为每个字符初始化相应的对象,例如
    entryOne
    之类的第一个字母
    MyChar aTitelChar=新的MyChar(“e”,24)
    对于内容,如第二行
    这里有一些内容。
    我创建MyChar的实例,如:

    MyChar acontchar=新的MyChar(“H”,12)

    文本的所有字符都包装在下面的
    MyChar
    -类的实例中,并添加到
    列表中
    ,以便为ANTLR生成新的输入

    以下是字符的Java类:

    public class MyChar {
        private int fontSizePx;
        private String text;
    
        public MyChar(String text, int fontSizePx) {
            this.text = text;
            this.fontSizePx = fontSizePx;
        }
    
        public int getFontSizePx() {
            return fontSizePx;
        }
    
        public String getText() {
            return text;
        }
    }
    
    我希望我的语法匹配上述两个条目(或更多的以这种方式格式化的条目),这两个条目依次由标题和以句号结尾的内容组成。该语法可以如下所示:

    rule: entry+ NEWLINE
    ;
    entry:
    title
    content
    ;   
    title: 
    letters NEWLINE
    ;
    content:
    (letters)+ '.' NEWLINE
    ;
    letters:
    LETTERS 
    ;
    LETTERS:
    ('a'..'z' | 'A'..'Z')+
    ;
    WS:
    (' ' | '\t' | 'f' ) + {$channel = HIDDEN;};
    NEWLINE:'\r'? '\n';
    
    现在,例如,我想做的是,在
    titel
    -规则返回之前,通过检查包含title标记的所有字母的字体大小来确定它是否真的是条目的标题。如果输入符合语法,但实际上是某种错误(原始元数据编码文件以符合
    标题
    -规则的内容开头,但实际上是内容),语法作者可以在知道标题的原始字体大小为24的情况下进行分类,并检查此项。如果其中一个字母标记不等于字体大小24,则抛出异常/不返回/执行smthg。合适


    我考虑的是在哪里插入
    列表
    ,以提供此功能(在ANTLR上下文中解析时查询各种元数据)。我正在试验ANTLR的类,但由于我是ANTLR的新手,我想可能一些有经验的用户可以为我指出正确的方向,比如哪里是自定义对象的好插入点?我应该从实现
    CharStream
    开始并覆盖一些方法吗?也许ANTLR提供了一些我还没有找到的东西

    这里有一种方法可以实现我认为您要实现的目标,即使用解析器管理元数据的匹配输入。请注意,我将空格设置为重要空格,因为它是内容的一部分,不能跳过。为了简化示例,我还将句点作为内容的一部分,而不是将其用作标记

    SysEx.g

    grammar SysEx;
    
    @header {
        import java.util.List;
    }
    
    @parser::members {
            private List<MyChar> metadata;
            private int curpos;
    
            private boolean isTitleInput(String input) {
                return isFontSizeInput(input, 24);
            }
    
            private boolean isContentInput(String input){
                return isFontSizeInput(input, 12);
            }
    
            private boolean isFontSizeInput(String input, int fontSize){
                List<MyChar> sublist = metadata.subList(curpos, curpos + input.length());
    
                System.out.println(String.format("Testing metadata for input=\%s, font-size=\%d", input, fontSize));
    
                int start = curpos;            
                //move our metadata pointer forward.
                skipInput(input);
    
                for (int i = 0, count = input.length(); i < count; ++i){
                    MyChar chardata = sublist.get(i);
                    char c = input.charAt(i);
                    if (chardata.getText().charAt(0) != c){
                        //This character doesn't match the metadata (ERROR!)
                        System.out.println(String.format("Content mismatch at metadata position \%d: metadata=(\%s,\%d); input=\%c", start + i, chardata.getText(), chardata.getFontSizePx(), c));
                        return false;
                    } else if (chardata.getFontSizePx() != fontSize){
                        //The font is wrong.
                        System.out.println(String.format("Format mismatch at metadata position \%d: metadata=(\%s,\%d); input=\%c", start + i, chardata.getText(), chardata.getFontSizePx(), c));
                        return false;
                    }
                }
    
                //All characters check out.
                return true;
            }
    
            private void skipInput(String str){
                curpos += str.length();
                System.out.println("\t\tMoving metadata pointer ahead by " + str.length() + " to " + curpos);
            }
    }
    
    rule[List<MyChar> metadata]
        @init {
            this.metadata = metadata;
        }
        : entry+ EOF
        ;
    entry
        : title content
        {System.out.println("Finished reading entry.");}
        ;   
    title
        : line {isTitleInput($line.text)}? newline {System.out.println("Finished reading title " + $line.text);}
        ;
    content
        : line {isContentInput($line.text)}? newline {System.out.println("Finished reading content " + $line.text);}
        ;
    newline
        : (NEWLINE{skipInput($NEWLINE.text);})+
        ;
    line returns [String text]
        @init { 
            StringBuilder builder = new StringBuilder();
        }
        @after {
            $text = builder.toString();
        }
        : (ANY{builder.append($ANY.text);})+ 
        ;
    
    NEWLINE:'\r'? '\n';
    ANY: .; //whitespace can't be skipped because it's content.
    
    SysExTest.txt(注意这使用了Windows换行符(
    \r\n

    测试输出(经过修剪;第二个示例故意不匹配元数据):

    此解决方案要求每个
    MyChar
    对应于输入中的一个字符(包括换行符,但如果愿意,您可以删除该限制——如果我还没有编写此答案,我会删除它;)


    正如您所看到的,可以将元数据绑定到解析器,并且一切都按预期工作。我希望这能有所帮助。

    我不清楚您的要求是什么,具体是关于ANTLR还是一般的词法分析。我知道您在某个地方(输入?)有元数据,并且希望它与数据绑定(或更接近数据)。但是如果没有更多的上下文——关于语法的更多信息,语法所代表的更多输入示例——我不确定你是否会得到你想要的帮助。@tenterhook,我明白了。我已经编辑了我的问题,谢谢。巴特·基尔斯谢谢你!我忽略了删除按钮。谢谢你的更新,这很有帮助。您提到希望“在title规则返回之前,通过检查包含title标记的所有字母的字体大小来确定它是否真的是条目的标题”。这里是我感到困惑的地方:ANTLR解析的输入(“entryOne\nHere…entryTwo\nHere…”)看起来像纯文本,但您希望检查输入的字体大小,以了解某个内容是否真的是标题。输入实际上是纯文本吗?此输入的元数据是否存储在您提到的
    列表
    变量中?谢谢您的回复<代码>列表
    保存对象,其中一个字段用于纯文本,另一个字段用于字体大小。上面的输入文本示例仅用于语法。我想使用ANTLR\u lexer\uu/\u解析器,就像传递字符串Inst一样
    public class SysExGrammar {
        public static void main(String[] args) throws Exception {
            //Create some metadata that matches our input.
            List<MyChar> matchingMetadata = new ArrayList<MyChar>();
            appendMetadata(matchingMetadata, "entryOne\r\n", 24);
            appendMetadata(matchingMetadata, "Here is some content one.\r\n", 12);
            appendMetadata(matchingMetadata, "entryTwo\r\n", 24);
            appendMetadata(matchingMetadata, "Here is some content two.\r\n", 12);
    
            parseInput(matchingMetadata);
    
            System.out.println("Finished example #1");
    
    
            //Create some metadata that doesn't match our input (negative test).
            List<MyChar> mismatchingMetadata = new ArrayList<MyChar>();
            appendMetadata(mismatchingMetadata, "entryOne\r\n", 24);
            appendMetadata(mismatchingMetadata, "Here is some content one.\r\n", 12);
            appendMetadata(mismatchingMetadata, "entryTwo\r\n", 12); //content font size!
            appendMetadata(mismatchingMetadata, "Here is some content two.\r\n", 12);
    
            parseInput(mismatchingMetadata);
    
            System.out.println("Finished example #2");
        }
    
        private static void parseInput(List<MyChar> metadata) throws Exception {
            //Test setup
            InputStream resource = SysExGrammar.class.getResourceAsStream("SysExTest.txt");
    
            CharStream input = new ANTLRInputStream(resource);
    
            resource.close();
    
            SysExLexer lexer = new SysExLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
    
            SysExParser parser = new SysExParser(tokens);
            parser.rule(metadata);
    
            System.out.println("Parsing encountered " + parser.getNumberOfSyntaxErrors() + " syntax errors");
        }
    
        private static void appendMetadata(List<MyChar> metadata, String string,
                int fontSize) {
    
            for (int i = 0, count = string.length(); i < count; ++i){
                metadata.add(new MyChar(string.charAt(i) + "", fontSize));
            }
        }
    }
    
    entryOne
    Here is some content one.
    entryTwo
    Here is some content two.
    
    Parsing encountered 0 syntax errors
    Finished example #1
    Parsing encountered 2 syntax errors
    Finished example #2