ANTLR v. 3-Использование синтаксического предиката для lookahead


Все еще учимся Правильно Использовать ANTLR... Вот в чем моя проблема.

Предположим, что у вас есть (подмножество) грамматики UML и лексера/синтаксического анализатора ANTLR со следующими правилами:

    // Parser Rules

    model
    :   'MODEL' IDENTIFIER list_dec
    ;

    list_dec
    :   declaration*
    ;

    declaration
    :   class_dec ';' 
    |   association ';' 
    |   generalization ';'
    |   aggregation ';'
    ; 

    class_dec
    :   'CLASS' IDENTIFIER class_content
    ;

    ...

    association
    :   'RELATION' IDENTIFIER 'ROLES' two_roles
    ;

    two_roles
    :   role ',' role
    ;

    role
    :   'CLASS' IDENTIFIER multiplicity
    ;

    ...

Я хотел бы, чтобы правило 'role' разрешало маркер идентификатора только в том случае, если он соответствует существующему идентификатору класса. Другими словами, если вам дан входной файл и вы запускаете в нем лексер/синтаксический анализатор, то все классы, на которые ссылаются (например, идентификатор в правиле ассоциации), должны существовать. Проблема в том, что класс может не существовать (пока) во время выполнения (он может быть объявлен в любом месте файла). Каков наилучший подход к этому ?

Заранее благодарю...

1 2

1 ответ:

Это, вероятно, лучше всего сделать после разбора. Синтаксический анализатор создает для вас какое-то дерево, а затем вы проходите по дереву и собираете информацию об объявленных классах, а затем проходите по нему еще раз, чтобы проверить дерево/Правило role.

Конечно, некоторые вещи можно было бы сделать с помощью специального кода:

grammar G;

options {
  ...
}

@parser::members {
  java.util.Set<String> declaredClasses = new java.util.HashSet<String>();
}

model
:   'MODEL' IDENTIFIER list_dec
;

...

class_dec
:   'CLASS' id=IDENTIFIER class_content 
    {
      declaredClasses.add($id.text);
    }
;

...

role
:   'CLASS' id=IDENTIFIER multiplicity
    {
      if(!declaredClasses.contains($id.text)) {
        // warning or exception in here
      }
    }
;

...

EDIT

Или с помощью пользовательских методов:

@parser::members {
  java.util.Set<String> declaredClasses = new java.util.HashSet<String>();

  void addClass(String id) {
    boolean added = declaredClasses.add(id);
    if(!added) {
      // 'id' was already present, do something, perhaps?
    }
  }

  void checkClass(String id) {
    if(!declaredClasses.contains(id)) {
      // exception, error or warning?
    }
  }
}

...

class_dec
:   'CLASS' id=IDENTIFIER class_content {addClass($id.text);}
;

role
:   'CLASS' id=IDENTIFIER multiplicity {checkClass($id.text);}
;