我有一个相对复杂的词汇量问题给定以下输入:

-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 : ~'-' (~' ')*
      ;

最佳答案

注意,我对C知之甚少,对ANTLR的C运行时也没有经验,但是我的示例中的Java代码不应该太难重写为C。
您可以通过重写baseemit(Token)类中的Lexer方法并跟踪lexer的最后一个Token进程来实现这一点:

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置于Other规则之前,该规则检查last标记是否为ExtraData标记:
Other
  :  {behind(ExtraData)}?=> ~'-' (~' ')*
  ;

其中behind(int)方法是@lexer::members { ... }部分中的自定义方法:
protected boolean behind(int tokenType) {
  return last != null && last.getType() == tokenType;
}

这将导致Other标记仅在最后一个标记是ExtraData时匹配。
一点示范语法:
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

then compile all .java files:

javac -cp antlr-3.2.jar *.java

and finally run the main class:

java -cp .:antlr-3.2.jar Main

(on Windows do: java -cp .;antlr-3.2.jar Main)

which then will produce the following output:

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

EDIT

As you (Billy) mentioned in your comment, in C you can't override methods. You could also set a boolean flag in the @after{ ... } clause of each lexer rule to keep track of when the last token is a ExtraData and use that flag in your predicate:

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-runtime的人也经常在那里访问。
祝你好运!

10-08 13:32