2
votes

Still learning how to properly use ANTLR... Here's my problem.

Say you have a (subset) of an UML grammar and an ANTLR Lexer/Parser with the following rules :

    // 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
    ;

    ...

I would like the 'role' rule to only allow the IDENTIFIER token if it matches an existing class IDENTIFIER. In other words, if you are given an input file and you run the lexer/parser on it, then all the classes that are referenced (e.g. the IDENTIFIER in the association rule) should exist. The problem is that a class might not exist (yet) at runtime (it can be declared anywhere in the file). What is the best approach to this ?

Thanks in advance...

1

1 Answers

1
votes

This is probably best done after parsing. The parser creates some sort of tree for you, and afterwards you walk the tree and collect information about declared classes, and walk it a second time to validate the role tree/rule.

Of course, some things could be done with a bit of custom code:

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

Or with custom methods:

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