capture
[rrq/gorite.git] / com / intendico / sdp / LLBNF.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
19
20 package com.intendico.sdp;
21
22 import java.lang.reflect.Constructor;
23 import java.util.Vector;
24 import java.util.Enumeration;
25 import java.util.Hashtable;
26
27 /**
28  * Class LLBNF defines a standard recursive descent BNF, using "<" and
29  * ">" to indicate non-terminals, and "'" to indicate terminals.
30  */
31 public class LLBNF extends BNFGrammar {
32
33     /**
34      * Utility method to instantiate a Production object. The method
35      * first tries to instantiate with the name given, thereafter by
36      * assuming its an inner class of the target grammar
37      */
38     Class findClass(String clsname)
39         throws ClassNotFoundException
40     {
41         ClassNotFoundException ex = null;
42         // Try the name as given
43         try {
44             return Class.forName( clsname );
45         } catch (ClassNotFoundException e) {
46             ex = e;
47         }
48         // Trying the name as inner class of the target class, or
49         // any of the target class base classes.
50         for (Class tc = target.getClass(); tc != null;
51              tc = tc.getSuperclass() ) {
52                  try {
53                      return Class.forName( tc.getName() + "$" + clsname );
54                  } catch (ClassNotFoundException e) {
55                  }
56         }
57
58         // Trying the name as a class of the same package as the
59         // target class or any of its base classes
60         for (Class tc = target.getClass(); tc != null;
61              tc = tc.getSuperclass() ) {
62             String pkg = tc.getName();
63             int x = pkg.lastIndexOf( '.' );
64             if ( x > 0 ) {
65                 try {
66                     return Class.forName(
67                         pkg.substring( 0, x ) + "." + clsname );
68                 } catch (ClassNotFoundException e) {
69                 }
70             }
71         }
72         throw ex;
73     }
74
75     Object create(String clsname)
76         throws Exception
77     {
78         Class cls = findClass( clsname );
79         Class ctx = cls.getDeclaringClass();
80         if ( ctx == null ) 
81             return cls.newInstance();
82         return cls.getConstructor( 
83             new Class [] { ctx } ).newInstance( new Object [] { target } );
84     }
85
86     Object createNamed(String clsname,String name)
87         throws Exception
88     {
89         Class cls = findClass( clsname );
90         Class ctx = cls.getDeclaringClass();
91         if ( ctx == null ) 
92             return cls.getConstructor( 
93                 new Class [] { String.class } ).
94                 newInstance( new Object [] { name } );
95         return cls.getConstructor( 
96             new Class [] { ctx, String.class } ).
97             newInstance( new Object [] { target, name } );
98     }
99
100     Hashtable grammars = new Hashtable();
101
102     Grammar getGrammar(String grammar)
103         throws Exception
104     {
105         Class c = findClass( grammar );
106         grammar = c.getName();
107         Grammar g = (Grammar) grammars.get( grammar );
108         if ( g == null ) {
109             g = (Grammar) create( grammar );
110             grammars.put( grammar, g );
111         }
112         return g;
113     }
114
115     Link linkGrammar(String rule,String goal,String grammar)
116         throws Exception
117     {
118         return target.new Link( rule, goal, getGrammar( grammar ) );
119     }
120
121     class BackLink {
122
123         final Grammar grammar;
124         final Link link;
125
126         BackLink(String rule,String goal,String g,Grammar target)
127             throws Exception
128         {
129             grammar = getGrammar( g );
130             link = grammar.new Link( rule, goal, target );
131         }
132
133         void addRule()
134         {
135             grammar.addRule( link );
136         }
137     }
138
139     BackLink backlinkGrammar(String rule,String goal,String grammar)
140         throws Exception
141     {
142         return new BackLink( rule, goal, grammar, target );
143     }
144
145     /**
146      * Generic Helper class that tags with a string.
147      */
148     public static class Tag extends Production {
149
150         public final String tag;
151         public final Grammar grammar;
152
153         public Tag(Grammar g,String t)
154         {
155             g.super();
156             tag = t;
157             grammar = g;
158         }
159
160         public Object action(Vector v)
161             throws Exception
162         {
163             RuleAction action = (RuleAction) grammar.rule_actions.get( tag );
164             if ( action != null )
165                 return action.action( v );
166             if ( v.size() == 0 )
167                 return tag;
168             v.add( 0, tag );
169             return v;
170         }
171
172         public String toString() {
173             StringBuffer s = new StringBuffer();
174
175             for ( int i=0; i<tokens.length; i++ ) {
176                 s.append( tokens[i].toString() );
177                 s.append( " " );
178             }
179             s.append( "# '" );
180             s.append( tag );
181             return s.toString();
182         }
183     }
184
185     
186     Grammar target;
187
188     public LLBNF()
189     {
190     }
191
192     public void initialisation()
193     {
194         addRule(
195             new ProductionList(
196                 "rule",
197                 new Production [] {
198                     new Production( new Token [] {
199                         new NonTerminal( "identifier" ),
200                         new Terminal( "::=" ),
201                         new NonTerminal( "productions" )
202                     } ) {
203                         public Object action(Vector v) {
204                             Vector p = (Vector) v.get( 1 );
205                             Production [] px = new Production[ p.size() ];
206                             return target.new ProductionList(
207                                 (String) v.get( 0 ),
208                                 (Production[]) p.toArray( px ) );
209                         } },
210                     new Production( new Token [] {
211                         new NonTerminal( "identifier" ),
212                         new Terminal( "is" ),
213                         new Terminal( "lexical" ),
214                         new NonTerminal( "any" )
215                     } ) {
216                         public Object action(Vector v)
217                             throws Exception
218                         {
219                             String name = (String) v.get( 0 );
220                             String clsname = (String) v.get( 1 );
221                             return (Lexical) createNamed( clsname, name );
222                         }
223                     },
224
225                     new Production( new Token [] {
226                         new NonTerminal( "identifier" ),
227                         new Terminal( "is" ),
228                         new Terminal( "linked" ),
229                         new Terminal( "to" ),
230                         new NonTerminal( "nonterminal" ),
231                         new Terminal( "in" ),
232                         new NonTerminal( "any" )
233                     } ) {
234                         public Object action(Vector v)
235                             throws Exception
236                         {
237                             String rule = (String) v.get( 0 );
238                             String goal = (String) v.get( 1 );
239                             String grammar = (String) v.get( 2 );
240                             return linkGrammar( rule, goal, grammar );
241                         }
242                     },
243
244                     new Production( new Token [] {
245                         new NonTerminal( "identifier" ),
246                         new Terminal( "of" ),
247                         new NonTerminal( "any" ),
248                         new Terminal( "is" ),
249                         new Terminal( "linked" ),
250                         new Terminal( "to" ),
251                         new NonTerminal( "nonterminal" ),
252
253                     } ) {
254                         public Object action(Vector v)
255                             throws Exception
256                         {
257                             String rule = (String) v.get( 0 );
258                             String goal = (String) v.get( 2 );
259                             String grammar = (String) v.get( 1 );
260                             return backlinkGrammar( rule, goal, grammar );
261                         }
262                     }
263
264                 } ) );
265         addRule(
266             new ProductionList(
267                 "rules",
268                 new Production [] {
269                     new Production( new Token [] {
270                         new NonTerminal( "end" ),
271                     } ) {
272                         public Object action(Vector v) {
273                             return v;
274                         }
275                     },
276                     new Production( new Token [] {
277                         new NonTerminal( "rule" ),
278                         new NonTerminal( "rules" )
279                     } ) {
280                         public Object action(Vector v) {
281                             Vector all = (Vector) v.get( 1 );
282                             all.add( 0, v.get( 0 ) );
283                             return all;
284                         }
285                     }
286                 } ) );
287         addRule(
288             new ProductionList(
289                 "productions",
290                 new Production [] {
291                     new Production( new Token [] {
292                         new NonTerminal( "production" ),
293                         new Terminal( "|" ),
294                         new NonTerminal( "productions" )
295                     } ) {
296                         public Object action(Vector v) {
297                             Vector all = (Vector) v.get( 1 );
298                             all.add( 0, v.get( 0 ) );
299                             return all;
300                         }
301                     },
302                     new Production( new Token [] {
303                         new NonTerminal( "production" )
304                     } ) {
305                         public Object action(Vector v) {
306                             return v;
307                         }
308                     }
309                 } ) );
310         addRule(
311             new ProductionList(
312                 "production",
313                 new Production [] {
314                     new Production( new Token [] {
315                         new NonTerminal( "tokenlist" ),
316                         new Terminal( "#" ),
317                         new Terminal( "'" ),
318                         new NonTerminal( "any" ),
319                     } ) {
320                         public Object action(Vector v)
321                             throws Exception
322                         {
323                             Vector x = (Vector) v.get( 0 );
324                             Production p = new Tag(
325                                 target, (String) v.get( 1 ) );
326                             p.setTokens(
327                                 (Token[]) x.toArray( new Token[x.size()] ) );
328                             return p;
329                         }
330                     },
331                     new Production( new Token [] {
332                         new NonTerminal( "tokenlist" ),
333                         new Terminal( "#" ),
334                         new NonTerminal( "any" ),
335                     } ) {
336                         public Object action(Vector v)
337                             throws Exception
338                         {
339                             Vector x = (Vector) v.get( 0 );
340                             Production p = (Production)
341                                 create( (String) v.get( 1 ) );
342                             p.setTokens(
343                                 (Token[]) x.toArray( new Token[x.size()] ) );
344                             return p;
345                         }
346                     },
347                     new Production( new Token [] {
348                         new NonTerminal( "tokenlist" )
349                     } ) {
350                         public Object action(Vector v) {
351                             Vector x = (Vector) v.get( 0 );
352                             return target.new Production(
353                                 (Token[]) x.toArray( new Token [ x.size() ] )
354                                 );
355                         }
356                     }
357                 } ) );
358         addRule(
359             new ProductionList(
360                 "tokenlist",
361                 new Production [] {
362                     new Production( new Token [] {
363                         new NonTerminal( "token" ),
364                         new NonTerminal( "tokenlist" )
365                     } ) {
366                         public Object action(Vector v) {
367                             Vector all = (Vector) v.get( 1 );
368                             all.add( 0, v.get( 0 ) );
369                             return all;
370                         }
371                     },
372                     new Production( new Token [] {
373                         new NonTerminal( "token" )
374                     } ) {
375                         public Object action(Vector v) {
376                             return v;
377                         }
378                     }
379                 } ) );
380         addRule(
381             new ProductionList(
382                 "token",
383                 new Production [] {
384                     new Production( new Token [] {
385                         new NonTerminal( "nonterminal" ),
386                     } ) {
387                         public Object action(Vector v) {
388                             return target.new NonTerminal(
389                                 (String) v.get( 0 ) );
390                         }
391                     },
392                     new Production( new Token [] {
393                         new NonTerminal( "terminal" ),
394                     } ) {
395                         public Object action(Vector v) {
396                             v = (Vector) v.get( 0 );
397                             boolean option = "?".equals( v.get( 0 ) );
398                             boolean preserve =
399                                 option || "!".equals( v.get( 0 ) );
400                             return target.new Terminal(
401                                 (String) v.get( 1 ),
402                                 preserve, option );
403                         }
404                     }
405                 } ) );
406         addRule(
407             new ProductionList(
408                 "nonterminal",
409                 new Production [] {
410                     new Production( new Token [] {
411                         new Terminal( "<" ),
412                         new NonTerminal( "identifier" ),
413                         new Terminal( ">" ),
414                     } )
415                 } ) );
416         addRule(
417             new ProductionList(
418                 "terminal",
419                 new Production [] {
420                     new Production( new Token [] {
421                         new Terminal( "!", true ),
422                         new NonTerminal( "any" )
423                     } ),
424                     new Production( new Token [] {
425                         new Terminal( "'", true ),
426                         new NonTerminal( "any" )
427                     } ),
428                     new Production( new Token [] {
429                         new Terminal( "?", true ),
430                         new NonTerminal( "any" )
431                     } )
432                 } ) );
433         addRule( new AnySymbol( "any" ) );
434         addRule( new Identifier( "identifier" ) );
435         addRule( new End( "end" ) );
436     }
437
438     /**
439      * Utility method to define rules using LLBNF syntax.
440      */
441     public Grammar addRules(Grammar g,String rules)
442         throws Throwable
443     {
444         try {
445             //trace = TRACE_STACK;
446             target = g;
447             grammars.put( g.getClass().getName(), target );
448
449             Vector v = (Vector) parseAndProcess( "rules", rules );
450             for ( Enumeration e = v.elements(); e.hasMoreElements(); ) {
451                 Object r = e.nextElement();
452                 if ( r instanceof BackLink )
453                     ((BackLink) r).addRule();
454                 else
455                     target.addRule( (Rule) r );
456             }
457             return target;
458         } catch (Throwable t) {
459             throw hideParse( t, LLBNF.class.getName() );
460         }
461     }
462
463     /**
464      * A Test main method.
465      */
466     public static void main(String [] args)
467         throws Throwable
468     {
469         LLBNF g = new LLBNF();
470         String rules = g.toString() ;
471         Grammar x = g.addRules( new Grammar(), rules );
472         System.out.println( x );
473     }
474 }