improved code generation and fixed emitter symbol table problem
[charm.git] / src / langs / charj / src / charj / translator / Translator.java
1
2 package charj.translator;
3
4 import java.io.*;
5 import java.util.*;
6 import org.antlr.runtime.*;
7 import org.antlr.runtime.tree.*;
8 import org.antlr.stringtemplate.*;
9
10
11 /**
12  * Driver class for lexing, parsing, and output.
13  * Takes in file names, parses them and generates code
14  * for .ci and .cc files in a .charj directory. Invokes
15  * charmc on these outputs and moves any resulting .o file 
16  * to the appropriate directory.
17  */
18 public class Translator {
19
20     public static final String templateFile = "charj/translator/Charj.stg";
21
22     // variables controlled by command-line arguments
23     private String m_charmc;
24     private boolean m_debug;
25     private boolean m_verbose;
26     private boolean m_errorCondition;
27     private boolean m_printAST;
28
29     // library locations to search for classes
30     private String m_stdlib;
31     private List<String> m_usrlibs;
32
33     private SymbolTable m_symtab;
34     private CommonTree m_ast;
35     private CommonTreeNodeStream m_nodes;
36
37     public Translator(
38             String _charmc,
39             boolean _debug,
40             boolean _verbose,
41             boolean _printAST,
42             String _stdlib,
43             List<String> _usrlibs)
44     {
45         m_charmc    = _charmc;
46         m_debug     = _debug;
47         m_verbose   = _verbose;
48         m_printAST  = _printAST;
49         m_stdlib    = _stdlib;
50         m_usrlibs   = _usrlibs;
51         m_symtab    = new SymbolTable(this);
52         m_errorCondition = false;
53     }
54
55     public boolean debug()      { return m_debug; }
56     public boolean verbose()    { return m_verbose; }
57
58     public static TreeAdaptor m_adaptor = new CommonTreeAdaptor() {
59         public Object create(Token token) {
60             return new CharjAST(token);
61         }
62         
63         public Object dupNode(Object t) {
64             if (t == null) {
65                 return null;
66             }
67             return create(((CharjAST)t).token);
68         }
69     };
70
71     public String translate(String filename) throws Exception 
72     {
73         ANTLRFileStream input = new ANTLRFileStream(filename);
74             
75         CharjLexer lexer = new CharjLexer(input);
76         CommonTokenStream tokens = new CommonTokenStream(lexer);
77
78         // Use lexer tokens to feed tree parser
79         CharjParser parser = new CharjParser(tokens);
80         parser.setTreeAdaptor(m_adaptor);
81         CharjParser.charjSource_return r = parser.charjSource();
82
83         // Create node stream for AST traversals
84         m_ast = (CommonTree)r.getTree();
85         m_nodes = new CommonTreeNodeStream(m_ast);
86         m_nodes.setTokenStream(tokens);
87         m_nodes.setTreeAdaptor(m_adaptor);
88
89         printAST("Before Semantic Pass");
90         semanticPass();
91         printAST("After Semantic Pass");
92
93         m_nodes.reset();
94         String ciOutput = translationPass(OutputMode.ci);
95         writeTempFile(filename, ciOutput, OutputMode.ci);
96
97         m_nodes.reset();
98         String hOutput = translationPass(OutputMode.h);
99         writeTempFile(filename, hOutput, OutputMode.h);
100         
101         m_nodes.reset();
102         String ccOutput = translationPass(OutputMode.cc);
103         writeTempFile(filename, ccOutput, OutputMode.cc);
104         compileTempFiles(filename, m_charmc);
105
106         // Build a string representing all emitted code. This will be printed
107         // by the main driver if requested via command-line argument. 
108         String ciHeader = "-----CI----------------------------\n";
109         String hHeader  = "-----H-----------------------------\n";
110         String ccHeader = "-----CC----------------------------\n";
111         String footer   = "-----------------------------------\n";
112         return ciHeader + ciOutput + hHeader + hOutput + 
113             ccHeader + ccOutput + footer;
114     }
115
116     private ClassSymbol semanticPass() throws
117         RecognitionException, IOException, InterruptedException
118     {
119         CharjSemantics sem = new CharjSemantics(m_nodes);
120         return sem.charjSource(m_symtab);
121     }
122
123     private String translationPass(OutputMode m) throws
124         RecognitionException, IOException, InterruptedException
125     {
126         CharjEmitter emitter = new CharjEmitter(m_nodes);
127         StringTemplateGroup templates = getTemplates(templateFile);
128         emitter.setTemplateLib(templates);
129         StringTemplate st = 
130             (StringTemplate)emitter.charjSource(m_symtab, m).getTemplate();
131         return st.toString();
132     }
133
134     private StringTemplateGroup getTemplates(String templateFile)
135     {
136         StringTemplateGroup templates = null;
137         try {
138             ClassLoader loader = Thread.currentThread().getContextClassLoader();
139             InputStream istream = loader.getResourceAsStream(templateFile);
140             BufferedReader reader = 
141                 new BufferedReader(new InputStreamReader(istream));
142             templates = new StringTemplateGroup(reader);
143             reader.close();
144         } catch(IOException ex) {
145             error("Failed to load template file", ex); 
146         }
147         return templates;
148     }
149
150     public File findPackage(String packageName)
151     {
152         String packageDir = packageName.replace(".", "/");
153         File p = new File(packageDir);
154         if ( debug() ) System.out.println(
155                 " [charj] findPackage " + packageName + 
156                 " trying " + p.getAbsoluteFile());
157        
158         // check current directory
159         if ( p.exists() ) {
160             return p;
161         }
162
163         // look in user libs if any
164         if ( m_usrlibs != null ) {
165             for (String lib : m_usrlibs) {
166                 p = new File(lib, packageDir);
167                 if (debug() ) System.out.println(
168                         " \tnot found, now trying " + p.getAbsoluteFile());
169                 if ( p.exists() ) {
170                     return p;
171                 }
172             }
173         }
174
175         // look in standard lib
176         p = new File(m_stdlib, packageDir);
177         if ( debug() ) System.out.println(
178                 " \tnot found, now trying " + p.getAbsoluteFile());
179         if ( p.exists() ) {
180             return p;
181         }
182
183         return null;
184     }
185
186     /** Load a class from disk looking in lib/package
187      *  Side-effect: add class to symtab. This is used by ClassSymbol to
188      *  load unknown types from disk. packageName comes from the output
189      *  of PackageScope.getFullyQualifiedName
190      */
191     public ClassSymbol loadType(String packageName, String typeName)
192     {
193         if (debug()) System.out.println(
194                 " [charj] loadType(" + typeName + ") from " + packageName);
195         
196         ClassSymbol cs = null;
197         try {
198             String packageDir = ".";
199             if ( packageName!=null ) {
200                 packageDir = packageName.replace(".", "/");
201             }
202             String fullName = packageDir + "/" + typeName + ".cj";
203                 
204             ClassLoader cl = Thread.currentThread().getContextClassLoader();
205             boolean fileExists = (cl.getResource(fullName) == null);
206             if (!fileExists) {
207                 if (debug()) System.out.println(
208                         " \tloadType(" + typeName + "): not found");
209                 return null;
210             }
211
212             if (debug()) System.out.println(
213                     " \tloadType(" + typeName + "): parsing " + 
214                     packageName + "." + typeName);
215             
216             ANTLRInputStream fs = new ANTLRInputStream(
217                     cl.getResourceAsStream(fullName));
218             fs.name = packageDir + "/" + typeName + ".cj";
219             CharjLexer lexer = new CharjLexer(fs);
220             
221             cs = semanticPass();
222         } catch (Exception e) {
223             e.printStackTrace();
224         }
225         return cs;
226     }
227
228     /**
229      * Utility function to write a generated .ci or .cc
230      * file to disk. Takes a .cj filename and writes a .cc
231      * or .ci file to the .charj directory depending on
232      * the OutputMode.
233      */
234     private void writeTempFile(
235             String filename, 
236             String output,
237             OutputMode m) throws
238         IOException
239     {
240         int lastDot = filename.lastIndexOf(".");
241         int lastSlash = filename.lastIndexOf("/");
242         String tempFile = filename.substring(0, lastSlash + 1) + ".charj/";
243         new File(tempFile).mkdir();
244         tempFile += filename.substring(lastSlash + 1, lastDot) + m.extension();
245         if (m_verbose) System.out.println(" [charjc] create: " + tempFile);
246         FileWriter fw = new FileWriter(tempFile);
247         fw.write(output);
248         fw.close();
249         return;
250     }
251
252     /**
253      * Enters the .charj directory and compiles the .cc and .ci files 
254      * generated from the given filename. The given charmc string 
255      * includes all options to be passed to charmc. Any generated .o 
256      * file is moved back to the initial directory.
257      */
258     private void compileTempFiles(
259             String filename,
260             String charmc) throws
261         IOException, InterruptedException
262     {
263         int lastDot = filename.lastIndexOf(".");
264         int lastSlash = filename.lastIndexOf("/");
265         String baseDirectory = filename.substring(0, lastSlash + 1);
266         if (baseDirectory.equals("")) {
267             baseDirectory = "./";
268         }
269         String tempDirectory = baseDirectory + ".charj/";
270         String moduleName = filename.substring(lastSlash + 1, lastDot);
271         String baseTempFilename = tempDirectory + moduleName;
272
273         // Compile interface file
274         String cmd = charmc + " " + baseTempFilename + ".ci";
275         File currentDir = new File(".");
276         int retVal = exec(cmd, currentDir);
277         if (retVal != 0) {
278             error("Could not compile generated interface file");
279             return;
280         }
281
282         // Move decl.h and def.h into temp directory.
283         // charmxi/charmc doesn't offer control over where to generate these
284         cmd = "mv " + moduleName + ".decl.h " + moduleName + ".def.h " +
285             tempDirectory;
286         retVal = exec(cmd, currentDir);
287          if (retVal != 0) {
288             error("Could not move .decl.h and .def.h files " +
289                     "into temp directory");
290             return;
291         }       
292
293         // Compile c++ output
294         cmd = charmc + " -c " + baseTempFilename + ".cc" + 
295             " -o " + baseTempFilename + ".o";
296         retVal = exec(cmd, currentDir);
297         if (retVal != 0) {
298             error("Could not compile generated C++ file");
299             return;
300         }
301
302         // move generated .o and .h file into .cj directory
303         cmd = "mv -f " + baseTempFilename + ".o " + baseDirectory;
304         exec(cmd, currentDir);
305         cmd = "cp -f " + baseTempFilename + ".h " + baseDirectory;
306         exec(cmd, currentDir);
307     }
308
309     /**
310      * Utility function to execute a given command line.
311      */
312     private int exec(String cmd, File outputDir) throws
313         IOException, InterruptedException
314     {
315         if (m_verbose) System.out.println(" [charjc] exec: " + cmd);
316         Process p = Runtime.getRuntime().exec(cmd, null, outputDir);
317         StreamEmitter stdout = new StreamEmitter(
318                 p.getInputStream(), System.out);
319         StreamEmitter stderr = new StreamEmitter(
320                 p.getErrorStream(), System.err);
321         stdout.start();
322         stderr.start();
323         p.waitFor();
324         stdout.join();
325         stderr.join();
326         int retVal = p.exitValue();
327         return retVal;
328     }
329
330     public void printAST(String message)
331     {
332         String header = "----------\n" + "AST: " + message + "\n----------\n";
333         String footer = "\n----------\n";
334         String body = null;
335         if (m_printAST && m_ast != null) {
336             body = m_ast.toStringTree();
337         } else if (m_printAST) {
338             body = "Null tree, no AST available";
339         }
340         System.out.println(header + body + footer);
341     }
342     
343     public void error(
344             BaseRecognizer recog, 
345             String msg, 
346             CharjAST node) 
347     {
348         String sourceName = "";
349         if (recog == null) {
350             sourceName = "<anonymous>";
351         } else {
352             sourceName = recog.getSourceName();
353         }
354         error(sourceName, msg, node);
355     } 
356
357     private void error(
358             String sourceName, 
359             String msg, 
360             CharjAST node) 
361     {
362         m_errorCondition = true;
363         String linecol = ":";
364         if ( node!=null ) {
365             CommonToken t = (CommonToken)node.getToken();
366             linecol = ": line " + t.getLine() + ":" + 
367                 t.getCharPositionInLine();
368         }
369         System.err.println(sourceName + linecol + " " + msg);
370         System.err.flush();
371     }
372
373     private void error(
374             String sourceName, 
375             String msg) {
376         error(sourceName, msg, (CharjAST)null);
377     }
378
379
380     public void error(String msg) {
381         error(" [charjc] error", msg, (CharjAST)null);
382     }
383
384
385     public void error(
386             String msg, 
387             Exception e) {
388         error(msg);
389         e.printStackTrace(System.err);
390     }
391 }