001 /* 002 * mesopotamia @mesopotamia.version@ 003 * Multilingual parser and repository. 004 * Copyright (C) 2005 Hammurapi Group 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 019 * 020 * URL: http://http://www.hammurapi.biz 021 * e-Mail: support@hammurapi.biz 022 */ 023 package org.mesopotamia.util; 024 025 import java.io.File; 026 import java.io.FileInputStream; 027 import java.io.FileOutputStream; 028 import java.io.FileReader; 029 import java.io.FileWriter; 030 import java.io.IOException; 031 import java.io.InputStream; 032 import java.io.PrintStream; 033 import java.io.Reader; 034 import java.util.ArrayList; 035 import java.util.Collection; 036 import java.util.HashMap; 037 import java.util.HashSet; 038 import java.util.Iterator; 039 import java.util.Map; 040 import java.util.Set; 041 import java.util.logging.Level; 042 import java.util.logging.Logger; 043 044 import org.mesopotamia.MesopotamiaException; 045 import org.mesopotamia.MesopotamiaRuntimeException; 046 import org.onemind.jxp.CachedJxpPage; 047 import org.onemind.jxp.CachingPageSource; 048 import org.onemind.jxp.JxpContext; 049 import org.onemind.jxp.JxpPageSource; 050 import org.onemind.jxp.JxpProcessor; 051 052 import antlr.ASTFactory; 053 import antlr.RecognitionException; 054 import antlr.TokenStreamException; 055 import antlr.collections.AST; 056 import biz.hammurapi.util.CollectionVisitable; 057 import biz.hammurapi.util.VisitableBase; 058 import biz.hammurapi.util.Visitor; 059 060 public class BnfModel extends VisitableBase { 061 062 private static final Logger logger = Logger.getLogger(BnfModel.class.getName()); 063 064 private Collection<Object> ruleDefinitions=new ArrayList<Object>(); 065 /** 066 * Keeps mapping subclass (String) -> superclass (RuleDefinition) 067 */ 068 Map<String, RuleDefinition> superClasses=new HashMap<String, RuleDefinition>(); 069 Map<String, Collection<RuleDefinition>> interfaces=new HashMap<String, Collection<RuleDefinition>>(); 070 Map<MatchPath, RuleDefinition> matchPath=new HashMap<MatchPath, RuleDefinition>(); 071 Map<String, RuleDefinition> rulesMap=new HashMap<String, RuleDefinition>(); // name -> rule 072 private String packageName; 073 private Class tokenTypesClass; 074 075 void addInterface(String implementor, RuleDefinition theInterface) { 076 Collection<RuleDefinition> interfaceCollection=interfaces.get(implementor); 077 if (interfaceCollection==null) { 078 interfaceCollection=new ArrayList<RuleDefinition>(); 079 interfaces.put(implementor, interfaceCollection); 080 } 081 interfaceCollection.add(theInterface); 082 083 // Validation that no interface extends class. 084 Iterator<Object> it=ruleDefinitions.iterator(); 085 while (it.hasNext()) { 086 RuleDefinition rule=(RuleDefinition) it.next(); 087 if (rule.isInterface() && !(rule.getSuperRule()==null || rule.getSuperRule().isInterface())) { 088 System.err.println("Interface rule "+rule.getName()+" extends class rule "+rule.getSuperClassName()); 089 } 090 } 091 } 092 093 public BnfModel(Reader r, Class tokenTypesClass, String rootLanguageElementClassName) throws MesopotamiaException { 094 this.tokenTypesClass=tokenTypesClass; 095 try { 096 BnfLexer lexer=new BnfLexer(r); 097 lexer.setTokenObjectClass(biz.hammurapi.antlr.Token.class.getName()); 098 BnfRecognizer parser=new BnfRecognizer(lexer); 099 ASTFactory astFactory = new ASTFactory(); 100 astFactory.setASTNodeClass(biz.hammurapi.antlr.AST.class); 101 parser.setASTFactory(astFactory); 102 parser.grammar(); 103 AST ast=parser.getAST(); 104 105 PrintStream printStream = new PrintStream(new FileOutputStream("ast.txt")); 106 ((biz.hammurapi.antlr.AST) ast).print(parser.getTokenNames(), printStream, 0, true); 107 printStream.close(); 108 109 Set<String> names=new HashSet<String>(); 110 for (AST node=ast; node!=null; node=node.getNextSibling()) { 111 if (node.getType()==BnfTokenTypes.LITERAL_package) { 112 StringBuffer sb=new StringBuffer(); 113 for (AST pnode=node.getFirstChild(); pnode!=null; pnode=pnode.getNextSibling()) { 114 sb.append(pnode.getText()); 115 } 116 packageName=sb.toString(); 117 } else if (names.add(node.getText())) { 118 RuleDefinition ruleDefinition = new RuleDefinition(this, node, rootLanguageElementClassName); 119 ruleDefinitions.add(ruleDefinition); 120 rulesMap.put(ruleDefinition.getName(), ruleDefinition); 121 } else { 122 System.err.println("Rule already defined: "+node.getText()); 123 } 124 } 125 } catch (TokenStreamException e) { 126 throw new MesopotamiaException(e); 127 } catch (IOException e) { 128 throw new MesopotamiaException(e); 129 } catch (RecognitionException e) { 130 throw new MesopotamiaException(e); 131 } 132 133 } 134 135 protected void acceptChildren(Visitor visitor) { 136 new CollectionVisitable(ruleDefinitions, false).accept(visitor); 137 } 138 139 public void checkTokenName(String tokenName) throws MesopotamiaRuntimeException { 140 if (tokenTypesClass!=null && !"*".equals(tokenName)) { 141 try { 142 tokenTypesClass.getField(tokenName); 143 } catch (SecurityException e) { 144 throw new MesopotamiaRuntimeException("Field "+tokenName+" cannot be accessed", e); 145 } catch (NoSuchFieldException e) { 146 throw new MesopotamiaRuntimeException("Invalid token name: "+tokenName); 147 } 148 } 149 } 150 151 152 /** 153 * @param args 154 */ 155 public static void main(final String[] args) throws Exception { 156 System.out.println("Arguments: <bnf file> <token types class> <root language element class> <template directory> <output directory>"); 157 if (args.length!=5) { 158 System.err.println("Invalid number of arguments"); 159 return; 160 } 161 162 BnfModel model = new BnfModel(new FileReader(args[0]), Class.forName(args[1]), args[2]); 163 164 final File templateDir = new File(args[3]); 165 166 final JxpPageSource pageSource = new CachingPageSource() { 167 168 protected boolean isExpired(CachedJxpPage page) { 169 return false; 170 } 171 172 protected boolean hasStream(String pageName) { 173 return new File(templateDir, pageName+".jxp").isFile(); 174 } 175 176 protected InputStream loadStream(CachedJxpPage page) throws IOException { 177 File pageFile = new File(templateDir, page.getName()+".jxp"); 178 page.setPageTimepstamp(pageFile.lastModified()); 179 return new FileInputStream(pageFile); 180 } 181 }; 182 183 final File outputDir = new File(args[4]); 184 185 model.accept(new Visitor() { 186 187 public boolean visit(Object target) { 188 logger.info("Processing: "+target); 189 Map<String, Object> context = new HashMap<String, Object>(); 190 JxpContext jxpContext = new JxpContext(pageSource, context); 191 JxpProcessor processor = new JxpProcessor(jxpContext); 192 try { 193 if (target instanceof RuleDefinition) { 194 context.put("rule", target); 195 RuleDefinition rd = (RuleDefinition) target; 196 String templateName; 197 if (rd.isFactory()) { 198 templateName = "factory"; 199 } else if (rd.isInterface()) { 200 templateName = "interface"; 201 } else { 202 templateName = "class"; 203 } 204 FileWriter writer = new FileWriter(new File(outputDir, rd.getName()+".java")); 205 try { 206 processor.process(templateName, writer); 207 } finally { 208 writer.close(); 209 } 210 return false; 211 } else if (target instanceof BnfModel) { 212 context.put("model", target); 213 FileWriter writer = new FileWriter(new File(outputDir, "LanguageModel.java")); 214 try { 215 processor.process("model", writer); 216 } finally { 217 writer.close(); 218 } 219 } 220 } catch (Exception e) { 221 logger.log(Level.WARNING, "Processing of "+target+" failed: "+e, e); 222 } 223 return true; 224 } 225 226 }); 227 } 228 229 public String getPackage() { 230 return packageName; 231 } 232 }