1+ module lang ::rascal ::grammar ::analyze ::Categories
2+
3+ import Grammar ;
4+ import ParseTree ;
5+
6+ import lang ::rascal ::grammar ::Util ;
7+
8+ @synopsis {
9+ Special value to indicate that a production has no category
10+ }
11+
12+ public str NO_CATEGORY = "" ;
13+
14+ @synopsis {
15+ Gets a set of categories such that, for each category, there exists a string
16+ with that category produced by production `p`, as part of a string produced
17+ by a start production of grammar `g`
18+ }
19+
20+ set [str ] getCategories (Grammar g , Production p )
21+ = getCategoriesByProduction (g )[p ];
22+
23+ @memo
24+ private map [Production , set [str ]] getCategoriesByProduction (Grammar g ) {
25+ map [Production , set [str ]] ret = (p : {} | /p : prod (_, _, _) := g );
26+
27+ void doGet (Production p , set [str ] parentCategories ) {
28+ set [str ] categories = {c | /\tag ("category" (str c )) := p };
29+
30+ set [str ] old = ret [p ];
31+ set [str ] new = _ <- categories ? categories : old + parentCategories ;
32+ ret [p ] = new ;
33+
34+ // If the new categories of `p` are different from the old ones, then
35+ // propagate these changes to the children of `p`
36+ for (old != new , /Symbol s := p .symbols , child <- lookup (g , delabel (s ))) {
37+ doGet (child , new );
38+ }
39+ }
40+
41+ // Propagate categories from the roots of the grammar
42+ for (root : prod (\start (_), _, _) <- ret ) {
43+ doGet (root , {NO_CATEGORY });
44+ }
45+
46+ return ret ;
47+ }
0 commit comments