【问题标题】:ANTLR: how to parse a region within matching brackets with a lexerANTLR:如何使用词法分析器解析匹配括号内的区域
【发布时间】:2011-10-18 14:43:58
【问题描述】:

我想在我的词法分析器中解析这样的内容:

( begin expression )

where 表达式也用方括号括起来。表达式中的内容并不重要,我只想将(begin 和匹配的) 之间的所有内容作为标记。一个例子是:

(begin 
    (define x (+ 1 2)))

所以令牌的文本应该是

(define x (+ 1 2)))

类似

PROGRAM : LPAREN BEGIN .* RPAREN;

(显然)不起作用,因为一旦他看到“)”,他就认为规则已经结束,但我需要匹配的括号。

我该怎么做?

【问题讨论】:

  • 如果您想识别 (...) 之间的所有内容,包括其他嵌套 (...),那么您无法使用纯标准的基于正则表达式的词法分析器(例如 ANTLR,我认为),因为正则表达式无法计数-> 无法确定括号何时平衡。您需要实现一个可以计算括号的词位识别器。

标签: antlr matching lexer brackets


【解决方案1】:

在词法分析器规则中,您可以递归调用规则。所以,这是解决这个问题的一种方法。另一种方法是跟踪开括号和右括号的数量,只要您的计数器大于零,就让gated semantic predicate 循环。

演示:

T.g

grammar T;

parse
  :  BeginToken {System.out.println("parsed :: " + $BeginToken.text);} EOF
  ;

BeginToken 
@init{int open = 1;}
  :  '(' 'begin' ( {open > 0}?=>              // keep reapeating `( ... )*` as long as open > 0
                     ( ~('(' | ')')           // match anything other than parenthesis
                     | '('          {open++;} // match a '(' in increase the var `open`
                     | ')'          {open--;} // match a ')' in decrease the var `open`
                     )
                 )*
  ;

Main.java

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String input = "(begin (define x (+ (- 1 3) 2)))";
    TLexer lexer = new TLexer(new ANTLRStringStream(input));
    TParser parser = new TParser(new CommonTokenStream(lexer));
    parser.parse();
  }
}
java -cp antlr-3.3-complete.jar org.antlr.Tool T.g
javac -cp antlr-3.3-complete.jar *.java
java -cp .:antlr-3.3-complete.jar Main

parsed :: (begin (define x (+ (- 1 3) 2)))

请注意,您需要注意源代码中可能包含括号的字符串文字:

BeginToken
@init{int open = 1;}
  :  '(' 'begin' ( {open > 0}?=>              // ...
                     ( ~('(' | ')' | '"')     // ...
                     | '('          {open++;} // ...
                     | ')'          {open--;} // ...
                     |  '"' ...               // TODO: define a string literal here
                     )
                 )*
  ;

或可能包含括号的 cmets。

带有谓词的建议使用了一些特定于语言的代码(在本例中为 Java)。递归调用词法分析器规则的一个优点是您的词法分析器中没有自定义代码:

BeginToken 
  :  '(' Spaces? 'begin' Spaces? NestedParens Spaces? ')'
  ;

fragment NestedParens
  :  '(' ( ~('(' | ')') | NestedParens )* ')'
  ;

fragment Spaces
  :  (' ' | '\t')+
  ;

【讨论】:

  • @Ira,它一个单一的词位。以大写字母开头的规则是 ANTLR 中的标记。我将规则命名为 Root 可能是一个糟糕的选择,这让您相信我将问题推给了解析器...
  • 啊哈。是的,正如您可能猜到的那样,我并不完全是 ANTLR 专家。所以......这是我希望你提供的答案。我再也不会怀疑你了!
  • @Bart:我刚刚注意到我遇到的另一个问题:词法分析器将无法正确识别像 (bar ...) 这样的语句,而是丢弃了 '(ba' 并返回 @987654329 @ 作为标识符虽然我想要 LPAREN IDENTIFIER。我该如何解决这个问题?我应该为此打开另一个问题并再次发布语法吗?
  • 是的,这是词法分析器不喜欢的东西。如果输入“看起来”像(b...,则词法分析器无法备份到LPAREN IDENTIFIER 而不是BeginToken。它真的是你想在你的词法分析器中处理的东西吗?在解析器中匹配 beginToken 比在词法分析器中匹配 BeginToken 更容易。是的,创建一个新问题可能会更容易(如果你真的想在词法分析器中这样做)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多