2017-09-15 78 views
0

我上ANTLR4语法工作,用于解析的Python脚本DSL(Python中的一个子集,基本上)与目标设定为Python 3中。我在处理换行时遇到了困难。处理换行与Python靶向

在我的语法,我使用的是移植到Python,使他们能够与Python 3运行时可用于ANTLR,而不是基于Java的Bart Kiers's Python3 grammar for ANTLR4lexer::membersNEWLINE嵌入式代码。我的语法与Bart提供的语法不同(它几乎与Python 3 spec中使用的语法相同),因为在我的DSL中,我只需要定位Python的某些元素。基于对语法的广泛测试,我认为语法本身的Python部分本身不是问题的根源,因此我现在不会在这里完整地发布它。

对语法的输入是一个文件,由file_input规则逮住:

file_input: (NEWLINE | statement)* EOF; 

语法执行得相当好我的DSL,并产生正确的AST。我唯一的问题是,我的词法分析器规则NEWLINE凌乱AST节点和\r\n节点,并试图扩展生成的MyGrammarListener与我自己ExtendedListener从它继承时证明麻烦。

这里是我的NEWLINE词法规则:

NEWLINE 
: ({self.at_start_of_input()}? SPACES 
    | ('\r'? '\n' | '\r' | '\f') SPACES? 
    ) 
    { 
    import re 
    from MyParser import MyParser 
    new_line = re.sub(r"[^\r\n\f]+", "", self._interp.getText(self._input)) 
    spaces = re.sub(r"[\r\n\f]+", "", self._interp.getText(self._input)) 
    next = self._input.LA(1) 

    if self.opened > 0 or next == '\r' or next == '\n' or next == '\f' or next == '#': 
     self.skip() 
    else: 
     self.emit_token(self.common_token(self.NEWLINE, new_line)) 

     indent = self.get_indentation_count(spaces) 
     if len(self.indents) == 0: 
      previous = 0 
     else: 
      previous = self.indents[-1] 

     if indent == previous: 
      self.skip() 
     elif indent > previous: 
      self.indents.append(indent) 
      self.emit_token(self.common_token(MyParser.INDENT, spaces)) 
     else: 
      while len(self.indents) > 0 and self.indents[-1] > indent: 
       self.emit_token(self.create_dedent()) 
       del self.indents[-1] 
    }; 

NEWLINE使用的SPACES词法规则片段是在这里:

fragment SPACES 
: [ \t]+ 
; 

我觉得我还要补充一点,既SPACESCOMMENTS最终是被语法忽略,但只有在声明NEWLINE词法分析规则后,据我所知,这应该表示没有不利影响米,但我想包括它以防万一。

SKIP_ 
: (SPACES | COMMENT) -> skip 
; 

当输入文件在语句之间没有任何空行的情况下运行时,所有内容都按原样运行。但是,如果有空白行中我的文件(如import语句和可变assignement之间),我收到以下错误:

line 15:4 extraneous input '\r\n ' expecting {<EOF>, 'from', 'import', NEWLINE, NAME} 
line 15:0 extraneous input '\r\n' expecting {<EOF>, 'from', 'import', NEWLINE, NAME} 

正如我以前说过,当行饲料在我输入文件中省略,则语法和我的ExtendedListener表现出他们应该的表现,所以问题肯定是\r\nNEWLINE词法分析器规则没有匹配 - 即使我得到的错误陈述说明它与替代NEWLINE不匹配。

我的语法产生的AST看起来是这样的: AST generated by ANTLR4 from my grammar and input file.

我真的很感激任何帮助,因为我不明白为什么我的NEWLINE词法规则woud不匹配\r\n,因为它应该和我想在我的DSL中允许空行。

回答

1

所以这个问题肯定是与\ r \ n不是由 NEWLINE词法规则

还有另一种解释相匹配。 LL(1)解析器会在第一次不匹配时停止,但ANTLR4是非常智能的LL(*):它会尝试匹配失配之后的输入。

由于我没有你statement规则和周围线15的输入,我将演示用下面的语法可能情况:

grammar Question; 

/* Extraneous input parsing NL and spaces. */ 

@lexer::members { 
    public boolean at_start_of_input() {return true;}; // even if it always returns true, it's not the cause of the problem 
} 

question 
@init {System.out.println("Question last update 2108");} 
    : (NEWLINE 
    |  statement 
       {System.out.println("found <<" + $statement.text + ">>");} 
     )* EOF 
    ; 

statement 
    : 'line ' NUMBER NEWLINE 'something else' NEWLINE 
    ; 

NUMBER : [0-9]+ ; 
NEWLINE 
    : ({at_start_of_input()}? SPACES 
     | ('\r'? '\n' | '\r' | '\f') SPACES? 
    ) 
    ; 

SKIP_ 
    : SPACES -> skip 
    ; 

fragment SPACES 
    : [ \t]+ 
    ; 

输入文件t.text:

line 1 
    something else 

执行:

$ export CLASSPATH=".:/usr/local/lib/antlr-4.6-complete.jar" 
$ alias 
alias a4='java -jar /usr/local/lib/antlr-4.6-complete.jar' 
alias grun='java org.antlr.v4.gui.TestRig' 
$ hexdump -C t.text 
00000000 6c 69 6e 65 20 31 0a 20 20 20 73 6f 6d 65 74 68 |line 1. someth| 
00000010 69 6e 67 20 65 6c 73 65 0a      |ing else.| 
00000019 
$ a4 Question.g4 
$ javac Q*.java 
$ grun Question question -tokens -diagnostics t.text 
[@0,0:4='line ',<'line '>,1:0] 
[@1,5:5='1',<NUMBER>,1:5] 
[@2,6:9='\n ',<NEWLINE>,1:6] 
[@3,10:23='something else',<'something else'>,2:3] 
[@4,24:24='\n',<NEWLINE>,2:17] 
[@5,25:24='<EOF>',<EOF>,3:0] 
Question last update 2108 
found <<line 1 
    something else 
>> 

现在改变statement像这样:

statement 
// : 'line ' NUMBER NEWLINE 'something else' NEWLINE 
    : 'line ' NUMBER   'something else' NEWLINE // now NL will be extraneous 
    ; 

,然后再执行:

$ a4 Question.g4 
$ javac Q*.java 
$ grun Question question -tokens -diagnostics t.text 
[@0,0:4='line ',<'line '>,1:0] 
[@1,5:5='1',<NUMBER>,1:5] 
[@2,6:9='\n ',<NEWLINE>,1:6] 
[@3,10:23='something else',<'something else'>,2:3] 
[@4,24:24='\n',<NEWLINE>,2:17] 
[@5,25:24='<EOF>',<EOF>,3:0] 
Question last update 2114 
line 1:6 extraneous input '\n ' expecting 'something else' 
found <<line 1 
    something else 
>> 

注意,NL字符和空格都被NEWLINE词法规则被正确地匹配。

你可以找到The Definitive ANTLR 4 Reference在9.1节的解释:

$ GRUN简单前卫➾T类; {int i; }➾EOF❮第1行:8无关 输入';'期待“{”

错误的巡游•153

解析器在报告一个错误;但给出了稍微更多的信息答案,因为它知道下一个标记实际上是寻找 。此功能被称为单令牌删除 ,因为解析器可以简单地假装外来令牌不存在 并继续。

类似地,解析器在检测到 缺少的令牌时可以执行单令牌插入。

换句话说,ANTLR4非常强大,即使几个令牌不匹配,它也可以重新同步输入和语法。如果使用-gui选项运行

$ grun Question question -gui t.text 

你可以看到ANTLR4 解析了整个文件,尽管一个NEWLINEstatement规则缺失,并且输入不完全匹配的语法。

enter image description here

要总结:extraneous input开发语法时,是一个相当常见的错误。它可以输入之间来自不匹配解析规则预期,或者也因为一些片的输入被解释由另一个标记比我们认为,这可以通过检查令牌由产生的列表中检测-tokens选项。

+0

谢谢您的回答。尽管最终这不是我的问题的根源,但它帮助我意识到我陷入了困境,所以我会接受它并奖励赏金。为了完整起见,在我的情况下,问题是,Python的输入文件必须与它下面另一个*缩进*#评论和语法不能处理一个#评论。再次感谢您的宝贵时间和丰富的答案。 –