Most data file formats require very little in terms of symbol table management. Indeed this problem only requires a list or hash table. As you read in the list of site specs:
site main { ... }
site demo { ... }
site meditation { ... }
make a HashMap called sites or whatever that maps:
main → SiteContext object 1
demo → SiteContext object 2
meditation → SiteContext object 3
When you see the family spec's sites definition:
...
family jguru {
sites = { main, meditation };
...
}
You can test sites.get("main") and sites.get("meditation") to see if they exist. If not, flag an error.
In terms of implementation, you would see the following defs:
site
: 'site' ID '{'
{
SiteContext sx=new SiteContext($ID.text);
sites.put($ID.text, sx);
}
siteElement[sx]+
'}'
;
siteElement[SiteContext sx] : ... {sx.setXXX(...);} ...
;
After the site specs, you would parse the family spec and check the references:
family
: 'family' ID '{' element+ '}'
;
element
: sites
| ...
;
sites
: 'sites' '=' '{'
ID {sites.get($ID.text)!=null}? (',' ID {sites.get($ID.text)!=null}?)*
'}'
;
Note the use of a validating semantic predicate to test whether the ID is a valid site spec.
In summary: you need a HashMap and a bit of code to save the site specs and a bit of code to reference the HashMap.