package ppg.spec; import java.io.*; import java.util.*; import ppg.*; import ppg.atoms.*; import ppg.cmds.*; import ppg.code.*; import ppg.lex.*; import ppg.parse.*; import ppg.util.*; public class PPGSpec extends Spec { private String include; private Vector commands, code; private Spec parent; private Vector startSyms; /** * PPG spec * public PPGSpec (String incFile, String pkg, Vector imp, Vector codeParts, Vector syms, Vector precedence, String startSym, Vector cmds) { super(); include = incFile; packageName = pkg; imports = imp; code = codeParts; symbols = syms; prec = precedence; start = startSym; startSyms = null; commands = cmds; parent = null; } */ public PPGSpec (String incFile, String pkg, Vector imp, Vector codeParts, Vector syms, Vector precedence, Vector startList, Vector cmds) { super(); include = incFile; packageName = pkg; imports = imp; code = codeParts; symbols = syms; prec = precedence; startSyms = startList; commands = cmds; parent = null; } public boolean isMultiStartSymbol() { return (startSyms.size() > 1); } /* Token curr_sym; // for each [start_sym, method, token] create code: method () { curr_sym = token; parse(); } // ^^^^ parse code ^^^^ // in front of scanner, add code: // return the "fake" symbol only once after it's set if (curr_sym != null) Token result = curr_sym; curr_sym = null; return result; } // ^^^^ scan code ^^^^ // code added to grammar: start with new_unique_start_symbol; new_unique_start_symbol ::= token_1 start_sym_1:s {: RESULT = s; :} | token_2 start_sym_2:s {: RESULT = s; :} | ... ; // ^^^^ grammar ^^^^ */ public void patchMultiStartSymbols (CUPSpec cupSpec) { if (!isMultiStartSymbol()) { cupSpec.setStart((String) startSyms.elementAt(0)); return; } // Parse Code String parseCode = ""; // should it be dynamically generated? String currSymbolName = "ppg_curr_sym"; parseCode += "Symbol " + currSymbolName + ";\n\n"; // Generate token names Vector tokens = new Vector(); for (int i=0; i < startSyms.size(); i+=2) { tokens.addElement(new String("JLGEN_TOKEN_"+String.valueOf(i/2))); } String startSym, method, token; for (int i=0; i < startSyms.size(); i += 2) { startSym = (String) startSyms.elementAt(i); method = (String) startSyms.elementAt(i+1); token = (String) tokens.elementAt(i/2); //startSyms.elementAt(i+2); parseCode += "public Symbol "+ method + " () throws Exception {\n"+ "\t"+currSymbolName+" = "+"new Symbol("+ PPG.SYMBOL_CLASS_NAME+"."+token+")"+";\n"+"\t"+ "return parse();\n}\n\n"; } // append parseCode to the actual parse code cupSpec.parserCode.append(parseCode); /************************************/ // Scan code String scanCodeAdd = "\n// scan code generated by PPG\n"+ "if (" + currSymbolName + "!= null) {\n"+ "\tSymbol result = "+currSymbolName+";\n"+ "\t"+currSymbolName+" = null"+";\n"+ "\treturn result;\n"+ "}\n"+ "// end scan code generated by PPG\n\n"; // prepend scanCode before the actual scan code if (cupSpec.scanCode != null) cupSpec.scanCode.prepend(scanCodeAdd); else cupSpec.scanCode = new ScanCode(scanCodeAdd); /************************************/ // create a new start symbol String newStartSym = "multi_start_symbool"; // set start symbol cupSpec.setStart(newStartSym); Nonterminal startNT = new Nonterminal(newStartSym, null); Vector newSymbols = new Vector(); newSymbols.addElement(newStartSym); // add start symbol to the grammar SymbolList sl = new SymbolList(SymbolList.NONTERMINAL, null, newSymbols); Vector addedSymbols = new Vector(); addedSymbols.addElement(sl); cupSpec.addSymbols(addedSymbols); // add token declaration to the grammar SymbolList tokenList = new SymbolList(SymbolList.TERMINAL, "Symbol", tokens); Vector addedTokens = new Vector(); addedTokens.addElement(tokenList); cupSpec.addSymbols(addedTokens); Vector rhs = new Vector(); //String grammarPatch = newStartSym + " ::=\n"; Vector rhsPart; for (int i=0; i < startSyms.size(); i += 2) { rhsPart = new Vector(); startSym = (String) startSyms.elementAt(i); token = (String) tokens.elementAt(i/2); //startSyms.elementAt(i+2); //if (i > 0) grammarPatch += "|"; //grammarPatch += "\t"+token+" "+startSym+":s {: RESULT = s; :}\n"; // add new symbols into vector rhsPart.addElement(new Nonterminal(token, null)); rhsPart.addElement(new Nonterminal(startSym, "s")); rhsPart.addElement(new SemanticAction("RESULT = s;")); rhs.addElement(rhsPart); } //grammarPatch += ";\n"; // patch the grammar Production p = new Production(startNT, rhs); cupSpec.addProductions(p); } /** * Parse the chain of inheritance via include files */ public void parseChain (String basePath) { InputStream is; File file = null; String simpleName = include; try { // first look on the classpath. is = ClassLoader.getSystemResourceAsStream(include); if (is != null) { PPG.DEBUG("found " + include + " as a resource"); } else { // nothing was found on the class path. Try the basePath... String fullPath = ((basePath == "") ? "" : basePath + System.getProperty("file.separator")) + include; PPG.DEBUG("looking for " + fullPath + " as a file"); file = new File(fullPath); is = new FileInputStream(file); simpleName = file.getName(); } Lexer lex = new Lexer(is, simpleName); Parser parser = new Parser(simpleName, lex); PPG.DEBUG("parsing "+simpleName); parser.parse(); parent = (Spec)parser.getProgramNode(); is.close(); } catch (FileNotFoundException e) { System.out.println(PPG.HEADER + simpleName + " not found."); System.exit(1); } catch (Exception e) { System.out.println(PPG.HEADER+"Exception: "+e.getMessage()); System.exit(1); } parent.setChild(this); String parentDir = null; if (file != null) { parentDir = file.getParent(); } parent.parseChain(parentDir == null ? "" : parentDir); } public CUPSpec coalesce() throws PPGError { // parent cannot be null by definition CUPSpec combined = parent.coalesce(); // work with a copy so we have the unmodified original to refer to CUPSpec newSpec = (CUPSpec) combined.clone(); // override package name newSpec.setPkgName(packageName); // add imported classes newSpec.addImports(imports); /* override precedence, using these rules: * * precedence list null: delete precedence list of parent * precedence list of length 0: leave precedence of parent * precedence list of length >0: override with current list */ //TODO: test precedence inheritance/overriding/ignoring if (prec == null) { newSpec.prec.removeAllElements(); } else if (prec.size() == 0) { // do nothing to parent's precedence list } else { // override with current newSpec.prec.removeAllElements(); newSpec.prec.addAll(prec); } // override action/parser/init/scan code newSpec.replaceCode(code); // add in (non)terminals newSpec.addSymbols(symbols); // override start symbol(s), patch grammar (if multi-start-symbol) if (child == null) patchMultiStartSymbols(newSpec); // combine this spec with the rest // of the chain and return the result processTransferL(combined, newSpec); processDrop(combined, newSpec); processOverride(combined, newSpec); processTransferR(combined, newSpec); processExtend(combined, newSpec); processNew(combined, newSpec); // clean the spec, remove nonterminals with no productions newSpec.removeEmptyProductions(); return newSpec; } private void processDrop (CUPSpec combined, CUPSpec newSpec) throws PPGError { // DROP Command cmd; DropCmd drop; for (int i=0; i < commands.size(); i++) { cmd = (Command) commands.elementAt(i); if (cmd instanceof DropCmd) { drop = (DropCmd) cmd; if (drop.isProdDrop()) { // remove all productions that have NT as lhs newSpec.dropProductions(drop.getProduction()); } else { /* symbol Drop */ Vector symbols = drop.getSymbols(); String sym; for (int j=0; j < symbols.size(); j++) { sym = (String) symbols.elementAt(j); // remove nonterminals from list of symbols newSpec.dropSymbol(sym); // remove all productions that have NT as lhs, if possible newSpec.dropAllProductions(sym); } } } } } private void processOverride (CUPSpec combined, CUPSpec newSpec) { // OVERRIDE Command cmd; OverrideCmd override; for (int i=0; i < commands.size(); i++) { cmd = (Command) commands.elementAt(i); if (cmd instanceof OverrideCmd) { override = (OverrideCmd) cmd; newSpec.dropProductions(override.getLHS()); newSpec.addProductions(override.getProduction()); } } } private void processExtend (CUPSpec combined, CUPSpec newSpec) { // EXTEND Command cmd; ExtendCmd extend; for (int i=0; i < commands.size(); i++) { cmd = (Command) commands.elementAt(i); if (cmd instanceof ExtendCmd) { extend = (ExtendCmd) cmd; newSpec.addProductions(extend.getProduction()); } } } private void processTransferL (CUPSpec combined, CUPSpec newSpec) { // TRANSFER_L Command cmd; TransferCmd transfer; Production prod; Nonterminal source; Vector prodList; for (int i=0; i < commands.size(); i++) { cmd = (Command) commands.elementAt(i); if (cmd instanceof TransferCmd) { transfer = (TransferCmd) cmd; source = transfer.getSource(); prodList = transfer.getTransferList(); // there must be at least one production by the grammar definition prod = (Production) prodList.elementAt(0); prod = (Production) prod.clone(); for (int j=1; j < prodList.size(); j++) { Production prodNew = (Production) prodList.elementAt(j); prod.union( (Production) prodNew.clone() ); //prod.union( (Production) prodList.elementAt(j) ); } prod.setLHS(transfer.getSource()); newSpec.dropProductions(prod); } } } private void processTransferR (CUPSpec combined, CUPSpec newSpec) { // TRANSFER_R Command cmd; TransferCmd transfer; Production prod, prodTransfer; Vector prodList; Nonterminal target; for (int i=0; i < commands.size(); i++) { cmd = (Command) commands.elementAt(i); if (cmd instanceof TransferCmd) { transfer = (TransferCmd) cmd; prodList = transfer.getTransferList(); for (int j=0; j < prodList.size(); j++) { prod = (Production) prodList.elementAt(j); target = prod.getLHS(); // make sure we get the productions from the source! prod.setLHS(transfer.getSource()); prodTransfer = combined.findProduction(prod); // but set the LHS back to the actual target // so it is added to the right nonterminal prodTransfer.setLHS(target); newSpec.addProductions(prodTransfer); //newSpec.addProductions(prod); } } } } private void processNew (CUPSpec combined, CUPSpec newSpec) { // NEW PRODUCTIONS NewProdCmd newProd; Command cmd; for (int i=0; i < commands.size(); i++) { cmd = (Command) commands.elementAt(i); if (cmd instanceof NewProdCmd) { newProd = (NewProdCmd) cmd; newSpec.addProductions(newProd.getProduction()); } } } /** * Write out contents to a CodeWriter */ public void unparse (CodeWriter cw) { cw.begin(0); if (include != null) { cw.write(include+"\n"); } if (commands != null) { for (int i=0; i < commands.size(); i++) { ((Command)commands.elementAt(i)).unparse(cw); } } cw.end(); } }