代码之家  ›  专栏  ›  技术社区  ›  Billy ONeal IS4

使用antlr c目标,如何在lexer中获取先前匹配的令牌?

  •  2
  • Billy ONeal IS4  · 技术社区  · 14 年前

    我有一个相对复杂的lexer问题。给出以下输入:

    -argument -argument#with hashed data# #plainhashedData#
    

    我需要这些代币:

    ARGUMENT (Text = "argument")
    ARGUMENT (Text = "argument")
    EXTRADATA (Text = "with hashed data")
    OTHER (Text = "#plainhasheddata#")
    

    我已经能够处理文本操作问题,但是我需要某种方法来指定,只有当刚才匹配的规则是参数时,才能匹配ExtraData规则。antlr支持句法谓词(甚至在lexer中),所以这不应该很难实现——但我需要能够在能够编写这样的谓词之前获得先前匹配的标记。

    是否可以使用ANTLR C代码生成目标?

    比利3

    编辑:当前的lexer规则如下:

    ARGUMENT : '-'+ (~('-'|'#'|' '))+
             ;
    EXTRADATA : '#' (~'#')* '#'
              ;
    OTHER : ~'-' (~' ')*
          ;
    
    1 回复  |  直到 14 年前
        1
  •  1
  •   Community arnoo    7 年前

    注意,我知道很少的C,并且对ANTLR的C运行时没有经验,但是我的例子中的Java代码不应该太难重写成C。


    你可以通过覆盖 emit(Token) 从基开始的方法 Lexer 上课并跟踪最后一个 Token 您的lexer进程:

    private Token last;
    
    @Override
    public void emit(Token token) {
      last = token;
      super.emit(token);
    }
    

    要将其包含在lexer中,请将其添加到语法中,并介于以下两个值之间:

    @lexer::members {
    
      // your code here
    
    }
    

    现在你必须把 Other 规则 之前 你的 ExtraData 规则并提出 gated semantic predicate 在你之前 其他 规则检查 last 令牌是 超文本 令牌:

    Other
      :  {behind(ExtraData)}?=> ~'-' (~' ')*
      ;
    

    何处 behind(int) 方法是您的 @lexer::members { ... } 章节:

    protected boolean behind(int tokenType) {
      return last != null && last.getType() == tokenType;
    }
    

    这将导致 其他 要匹配的令牌 只有 如果最后一个标记是 超文本 .

    一个小小的语法演示:

    grammar LookBehind;
    
    @lexer::members {
    
      private Token last;
    
      @Override
      public void emit(Token token) {
        last = token;
        super.emit(token);
      }
    
      protected boolean behind(int tokenType) {
        return last != null && last.getType() == tokenType;
      }
    }
    
    parse
      :  token+ EOF
      ;
    
    token
      :  Argument  {System.out.println("Argument  :: "+$Argument.text);}
      |  Other     {System.out.println("Other     :: "+$Other.text);}
      |  ExtraData {System.out.println("ExtraData :: "+$ExtraData.text);}
      ;
    
    Argument
      :  '-'+ (~('-' | '#' | ' '))+
      ;
    
    Other
      :  {behind(ExtraData)}?=> ~('-' | ' ') (~' ')*
      ;
    
    ExtraData 
      : '#' (~'#')* '#'
      ;
    
    Space
      :  (' ' | '\t' | '\r' | '\n') {skip();}
      ;
    

    以及一个测试它的主要类:

    import org.antlr.runtime.*;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            String source = "-argument -argument#with hashed data# #plainhashedData#";
            ANTLRStringStream in = new ANTLRStringStream(source);
            LookBehindLexer lexer = new LookBehindLexer(in);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            LookBehindParser parser = new LookBehindParser(tokens);
            parser.parse();
        }
    }
    

    首先从语法生成一个解析器和lexer:

    java -cp antlr-3.2.jar org.antlr.Tool LookBehind.g 
    

    然后编译全部 .java 文件夹:

    javac -cp antlr-3.2.jar *.java
    

    最后运行主类:

    java -cp .:antlr-3.2.jar Main
    

    (在Windows DO上: java -cp .;antlr-3.2.jar Main )

    然后将产生以下输出:

    Argument  :: -argument
    Argument  :: -argument
    ExtraData :: #with hashed data#
    Other     :: #plainhashedData#
    

    编辑

    正如你(比利)在评论中提到的,在C语言中,你不能重写方法。您还可以在 @after{ ... } 跟踪最后一个令牌何时为 超文本 并在谓词中使用该标志:

    grammar LookBehind;
    
    @lexer::members {
      private boolean lastExtraData = false;
    }
    
    parse
      :  token+ EOF
      ;
    
    token
      :  Argument  {System.out.println("Argument  :: "+$Argument.text);}
      |  Other     {System.out.println("Other     :: "+$Other.text);}
      |  ExtraData {System.out.println("ExtraData :: "+$ExtraData.text);}
      ;
    
    Argument
    @after{lastExtraData = false;}
      :  '-'+ (~('-' | '#' | ' '))+
      ;
    
    Other
    @after{lastExtraData = false;}
      :  {lastExtraData}?=> ~('-' | ' ') (~' ')*
      ;
    
    ExtraData
    @after{lastExtraData = true;}
      : '#' (~'#')* '#'
      ;
    
    Space
      :  (' ' | '\t' | '\r' | '\n') {skip();}
      ;
    

    虽然这有点像黑客:在每一个lexer规则中,你都必须设置标志。

    您也可以向 ANTLR mailing-list :除了许多Antlr专家之外,维护Antlr的C运行时的人也经常出现在那里。

    祝你好运!