Используя цель ANTLR C, как я могу получить ранее подобранный токен в Lexer? - PullRequest
2 голосов
/ 19 июля 2010

У меня довольно сложная проблема с лексером.Учитывая следующее:

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

Мне нужны эти токены:

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

Мне удалось позаботиться о проблемах с обработкой текста, но мне нужен какой-то способ указать, чтоПравило EXTRADATA может быть сопоставлено только в том случае, если ранее только что найденное правило было ARGUMENT.ANTLR поддерживает синтаксические предикаты (даже в лексерах), так что это не должно быть трудным для достижения - но мне нужно иметь возможность получить то, чем является ранее подобранный токен, прежде чем я смогу написать такой предикат.

Возможно ли это с помощью цели генерации кода ANTLR C?

Billy3

РЕДАКТИРОВАТЬ: текущие правила лексера выглядят примерно так:

ARGUMENT : '-'+ (~('-'|'#'|' '))+
         ;
EXTRADATA : '#' (~'#')* '#'
          ;
OTHER : ~'-' (~' ')*
      ;

1 Ответ

1 голос
/ 19 июля 2010

Обратите внимание, я немного знаю C и не имею опыта работы с C для ANTLR, но код Java из моих примеров не должен быть слишком сложным для перезаписи на C.


Вы можете сделать это, переопределив метод emit(Token) из базового класса Lexer и отслеживая последние Token ваших процессов лексера:

private Token last;

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

Чтобы включить это в свой лексер,добавьте его в свою грамматику между следующими словами:

@lexer::members {

  // your code here

}

Теперь вы должны поставить Other rule перед вашим ExtraData правилом и поставить стробированный семантический предикат перед правилом Other, которое проверяет, был ли токен last токеном ExtraData:

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

, где метод behind(int) - это пользовательский метод в разделе @lexer::members { ... }:

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

, что приведет к совпадению токена Other only , если последний токен был 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();
    }
}

Сначала сгенерируйте парсер и лексер из грамматики:

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: java -cp .;antlr-3.2.jar Main)

, который затем выдаст следующий вывод:

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

РЕДАКТИРОВАТЬ

Как вы (Билли) упомянули в своем комментарии, вC вы не можете переопределить методы.Вы также можете установить логический флаг в предложении @after{ ... } каждого правила лексера, чтобы отслеживать, когда последний токен является ExtraData, и использовать этот флаг в своем предикате:

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();}
  ;

Хотя этонемного хак: в каждом правиле лексера вы должны будете установить флаг.

Вы также можете отправить вопрос в список рассылки ANTLR : помимо многих экспертов ANTLR, человек, поддерживающий там частоту выполнения C-ANTLR.

Удачи!

...