import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.Reader; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.TreeMap; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.logging.Level; //-----------------------------------------------------------------------// // Composer-specific collections from the parse tree: // (adds composer-specific {@link Object#toString()} methods) //-----------------------------------------------------------------------// public class BaliRulesData { /** * Generates JavaCC productions for each Bali rule: * * @layer<bali2javacc> */ public String toString() { List keys = new ArrayList( keySet() ) ; StringBuffer buffer = new StringBuffer() ; buffer.append( rule2string( getStartName() ) ) ; keys.remove( getStartName() ) ; Collections.sort( keys ) ; for ( Iterator p = keys.iterator() ; p.hasNext() ; ) { buffer.append( Main.LINE_SEPARATOR ) ; buffer.append( Main.LINE_SEPARATOR ) ; buffer.append( rule2string( ( String ) p.next() ) ) ; } return buffer.toString() ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // private String assignmentCode( CodeBuffer code, Primitive prim, Variables variables ) { if ( prim instanceof BaliTokenNode ) { String var = variables.getFree( "Token" ) ; code.append( var + "=<" + prim.tok[0].tokenName() + '>' ) ; return "t2at(" + var + ')' ; } if ( prim instanceof IdentifierNode ) { String name = prim.tok[0].tokenName() ; String type = getType( name ) ; String var = variables.getFree( type ) ; code.append( var + '=' + name + "()" ) ; return var ; } if ( prim instanceof OptionalNode ) { code.append( '[' ) ; if ( prim.arg[0].arg [0] != null ) { LookaheadNode look = ( LookaheadNode ) prim.arg[0].arg [0] ; code.append( "LOOKAHEAD(" + look.arg [0] + ") " ) ; } String var = assignmentCode( code, ( Primitive ) prim.arg [1], variables ) ; code.append( ']' ) ; return "opt(" + var + ')' ; } if ( prim instanceof StringNode ) { String var = variables.getFree( "Token" ) ; code.append( var + '=' + prim.tok[0].tokenName() ) ; return "t2at(" + var + ')' ; } throw new IllegalStateException( "unexpected Primitive: " + prim ) ; } private String compact( String text ) { return text.trim().replaceAll( "\\s+", " " ) ; } private String complexList( String rule, Primitive item, ComplexListNode rest, Variables vars ) { CodeBuffer code = new CodeBuffer() ; String result = vars.declare( rule, "list", "new " + rule + " ()" ); String var = assignmentCode( code, item, vars ) ; code.endLine().append( "{" + result + ".add (new " ) ; code.append( rule + "Elem().setParms (" + var + ")) ;}" ) ; code.endLine().append( '(' ) ; // Handle LOOKAHEAD: // code.endLine().indent() ; if ( rest.arg[0].arg [0] != null ) { LookaheadNode node = ( LookaheadNode ) rest.arg[0].arg [0] ; code.append( "LOOKAHEAD(" + node.arg [0] + ") " ) ; code.endLine() ; } vars.reset() ; Primitive one = ( Primitive ) rest.arg [1] ; String varOne = assignmentCode( code, one, vars ) ; code.endLine() ; Primitive two = ( Primitive ) rest.arg [2] ; String varTwo = assignmentCode( code, two, vars ) ; code.endLine().append( "{" + result + ".add (new " ) ; code.append( rule + "Elem().setParms (" + varOne + ", " + varTwo + ")) ;}" ) ; code.endLine().outdent().append( ")*" ) ; code.endLine().append( "{return " + result + " ;}" ) ; return code.toString() ; } private String merger2string( Merger merger, Variables variables ) { ProductionNode namedNode = merger.getNamed() ; IdentifierNode nonTerminal = merger.getNonterminal() ; String rule = merger.getRule() ; ProductionNode unnamedNode = merger.getUnnamed() ; CodeBuffer code = new CodeBuffer() ; code.append( "// Merged productions from rule " + rule ) ; code.endLine() ; code.append( "// (*) " + compact( unnamedNode.toString() ) ) ; code.endLine() ; code.append( "// (*) " + compact( namedNode.toString() ) ) ; code.endLine() ; code.append( "// " ) ; code.endLine() ; List args = new ArrayList() ; args.add( assignmentCode( code, nonTerminal, variables ) ) ; code.endLine().append( '[' ).endLine().indent() ; code.append( "LOOKAHEAD(2)" ) ; // Refactor with similar code from "namedRule" method? // Pattern pat = ( Pattern ) namedNode.arg[1].arg[1].arg[0].arg [0] ; List list = ( pat != null ) ? pat.toList() : Collections.EMPTY_LIST ; for ( Iterator p = list.iterator() ; p.hasNext() ; ) { Primitive prim = ( Primitive ) p.next() ; code.endLine() ; args.add( assignmentCode( code, prim, variables ) ) ; } StringBuffer arg = new StringBuffer() ; Iterator p = args.iterator() ; if ( p.hasNext() ) arg.append( p.next().toString() ) ; while ( p.hasNext() ) { arg.append( ", " ) ; arg.append( p.next().toString() ) ; } String node = namedNode.arg[1].arg[1].arg[1].arg[0].tok[1].tokenName() ; code.endLine().append( "{return new " + node + "().setParms" ) ; code.append( "(" + arg + ") ;}" ) ; code.endLine().outdent().append( ']' ) ; String var = ( String ) args.get( 0 ) ; code.endLine().append( "{return (" + rule + ") " + var + " ;}" ) ; return code.toString() ; } private String namedRule( String rule,Primitive item,Pattern pat,String node,Variables vars ) { CodeBuffer code = new CodeBuffer() ; List args = new ArrayList() ; args.add( assignmentCode( code, item, vars ) ) ; List patList = ( pat != null ) ? pat.toList() : Collections.EMPTY_LIST ; for ( Iterator p = patList.iterator() ; p.hasNext() ; ) { Primitive prim = ( Primitive ) p.next() ; code.endLine() ; args.add( assignmentCode( code, prim, vars ) ) ; } StringBuffer arg = new StringBuffer() ; Iterator p = args.iterator() ; if ( p.hasNext() ) arg.append( p.next().toString() ) ; while ( p.hasNext() ) { arg.append( ", " ) ; arg.append( p.next().toString() ) ; } code.endLine().append( "{return new " + node + "().setParms" ) ; code.append( "(" + arg + ") ;}" ) ; return code.toString() ; } private String production2string( String rule, ProductionNode production, Variables variables ) { if ( production instanceof Merger ) return merger2string( ( Merger ) production, variables ) ; CodeBuffer code = new CodeBuffer() ; // Generate leading LOOKAHEAD, if any: // AstNode node = production.arg[0].arg [0] ; if ( node != null ) code.append( "LOOKAHEAD(" + node.arg [0] + ") " ) ; // Separate the production types and generate code for each: // Rewrite rewrite = ( Rewrite ) production.arg [1] ; // Handle a "(item)+" list: // if ( rewrite instanceof SimpleListNode ) { SimpleListNode list = ( SimpleListNode ) rewrite ; code.append( simpleList( rule, list, variables ) ) ; return code.toString() ; } // Handle a "item1 (item2)*" list: // Primitive item1 = ( Primitive ) rewrite.arg [0] ; PrimitiveRewrite rest = ( PrimitiveRewrite ) rewrite.arg [1] ; if ( rest instanceof ComplexListNode ) { ComplexListNode list = ( ComplexListNode ) rest ; code.append( complexList( rule, item1, list, variables ) ) ; return code.toString() ; } // Handle a named rule (i.e., a ":: NodeName" at production's end): // node = rest.arg[0].arg [0] ; Pattern pattern = ( node != null ) ? ( Pattern ) node : null ; if ( rest.arg[1].arg [0] != null ) { String name = rest.arg[1].arg[0].tok[1].tokenName() ; code.append( namedRule( rule,item1,pattern,name,variables ) ) ; return code.toString() ; } // Currently, unnamed rules with right-hand sequences are errors: // if ( pattern != null ) throw new IllegalStateException( "unnamed rule with right-hand sequence: " + rule ) ; // What remains are productions with singleton right-hand sides. // If the field is a rule name, it's an unnamed rule: // if ( item1 instanceof IdentifierNode ) { code.append( unnamedRule( rule, ( IdentifierNode ) item1, variables ) ) ; return code.toString() ; } throw new IllegalStateException( "singleton production without a name: " + rule ) ; } /** * Examines a {@link List} of productions for special cases where two * or more productions should be combined, such as left-recursion via * an "unnamed rule", and modifies the {@link List} to add the combined * productions while removing their source productions. * * @layer<bali2javacc> */ private List productionsMerge( String rule ) { List productions = ( List ) get( rule ) ; if ( productions.size() < 2 ) return productions ; // Extract productions to merge: // Map mergers = new HashMap() ; for ( Iterator p = productions.iterator() ; p.hasNext() ; ) { ProductionNode pn = ( ProductionNode ) p.next() ; if ( pn.arg[0].arg [0] != null ) // Disallow LOOKAHEAD. continue ; Rewrite rewrite = ( Rewrite ) pn.arg [1] ; if ( ! ( rewrite instanceof PrimitiveRewriteNode ) ) continue ; if ( ! ( rewrite.arg [0] instanceof IdentifierNode ) ) continue ; PrimitiveRewrite pr = ( PrimitiveRewrite ) rewrite.arg [1] ; if ( ! ( pr instanceof PatternNode ) ) continue ; if ( pr.arg[0].arg [0] != null && pr.arg[1].arg [0] == null ) continue ; IdentifierNode identifier = ( IdentifierNode ) rewrite.arg [0] ; String nonTerminal = identifier.tok[0].tokenName() ; Merger merger = ( Merger ) mergers.get( nonTerminal ) ; if ( merger == null ) { merger = new Merger( rule, identifier ) ; mergers.put( nonTerminal, merger ) ; } if ( pr.arg[1].arg [0] != null ) merger.setNamed( pn ) ; else if ( pr.arg[0].arg [0] == null ) merger.setUnnamed( pn ) ; } if ( mergers.size() < 1 ) return productions ; // For complete mergers, modify the {@link List} of productions: // List newProductions = new ArrayList( productions ) ; for ( Iterator p = mergers.keySet().iterator() ; p.hasNext() ; ) { String nonTerminal = ( String ) p.next() ; Merger merger = ( Merger ) mergers.get( nonTerminal ) ; if ( merger.isComplete() ) { ProductionNode named = merger.getNamed() ; ProductionNode unnamed = merger.getUnnamed() ; newProductions.remove( unnamed ) ; int namedIndex = newProductions.indexOf( named ) ; newProductions.set( namedIndex, merger ) ; } } return newProductions ; } private String rule2string( String rule ) { CodeBuffer code = new CodeBuffer() ; Variables variables = new Variables() ; Iterator p = productionsMerge( rule ).iterator() ; if ( ! p.hasNext() ) throw new IllegalStateException( "no productions: " + rule ) ; ProductionNode node = ( ProductionNode ) p.next() ; code.append( production2string( rule, node, variables ) ) ; while ( p.hasNext() ) { variables.reset() ; node = ( ProductionNode ) p.next() ; code.endLine().append( '|' ).endLine() ; code.append( production2string( rule,node,variables ).trim() ) ; } String clauses = code.toString().trim() ; code.clear().append( rule + ' ' + rule + " () : {" ) ; code.endLine().indent() ; String declarations = variables.toString() ; if ( declarations.length() > 0 ) code.appendLines( declarations ).endLine() ; code.outdent().append( "} {" ) ; code.endLine().indent().appendLines( clauses ) ; code.endLine().outdent().append( '}' ) ; return code.toString() ; } private String simpleList( String rule, SimpleListNode list, Variables vars ) { CodeBuffer code = new CodeBuffer() ; String result = vars.declare( rule, "list", "new " + rule + " ()" ); // Handle LOOKAHEAD: // code.append( '(' ).endLine().indent() ; if ( list.arg[0].arg [0] != null ) { LookaheadNode node = ( LookaheadNode ) list.arg[0].arg [0] ; code.append( "LOOKAHEAD(" + node.arg [0] + ") " ) ; code.endLine() ; } String var = assignmentCode( code, ( Primitive ) list.arg[1], vars ) ; code.endLine().append( "{" + result + ".add (new " ) ; code.append( rule + "Elem().setParms (" + var + ")) ;}" ) ; code.endLine().outdent().append( ")+" ) ; code.endLine().append( "{return " + result + " ;}" ) ; return code.toString() ; } private String unnamedRule( String rule, IdentifierNode item, Variables variables ) { CodeBuffer code = new CodeBuffer() ; String var = assignmentCode( code, item, variables ) ; code.endLine().append( "{return (" + rule + ") " + var + " ;}" ) ; return code.toString() ; } }