Charj: better names for ast modification passes
[charm.git] / src / langs / charj / src / charj / translator / Translator.java
1
2 package charj.translator;
3
4 import java.io.*;
5 import java.nio.*;
6 import java.nio.channels.*;
7 import java.nio.charset.*;
8 import java.util.*;
9 import org.antlr.runtime.*;
10 import org.antlr.runtime.tree.*;
11 import org.antlr.stringtemplate.*;
12
13
14 /**
15  * Driver class for lexing, parsing, and output.
16  * Takes in file names, parses them and generates code
17  * for .ci and .cc files in a .charj directory. Invokes
18  * charmc on these outputs and moves any resulting .o file 
19  * to the appropriate directory.
20  */
21 public class Translator {
22
23     public static final String templateFile = "charj/translator/Charj.stg";
24
25     // variables controlled by command-line arguments
26     private String m_charmc;
27     private boolean m_debug;
28     private boolean m_verbose;
29     private boolean m_errorCondition;
30     private boolean m_printAST;
31     private boolean m_translate_only;
32
33     // library locations to search for classes
34     private String m_stdlib;
35     private List<String> m_usrlibs;
36
37     private String m_basename;
38     private SymbolTable m_symtab;
39     private CommonTree m_ast;
40     private CommonTreeNodeStream m_nodes;
41     private CommonTokenStream m_tokens;
42
43     public Translator(
44             String _charmc,
45             boolean _debug,
46             boolean _verbose,
47             boolean _printAST,
48             boolean _translate_only,
49             String _stdlib,
50             List<String> _usrlibs)
51     {
52         m_charmc = _charmc;
53         m_debug = _debug;
54         m_verbose = _verbose;
55         m_printAST = _printAST;
56         m_translate_only = _translate_only;
57         m_stdlib = _stdlib;
58         m_usrlibs = _usrlibs;
59         m_symtab = new SymbolTable(this);
60         m_errorCondition = false;
61     }
62
63     public boolean debug()      { return m_debug; }
64     public boolean verbose()    { return m_verbose; }
65     public String basename()    { return m_basename; }
66
67     public static TreeAdaptor m_adaptor = new CommonTreeAdaptor() {
68         public Object create(Token token) {
69             return new CharjAST(token);
70         }
71         
72         public Object dupNode(Object t) {
73             if (t == null) {
74                 return null;
75             }
76             CharjAST orig = (CharjAST)t;
77             CharjAST node = (CharjAST)create(orig.token);
78             node.def = orig.def;
79             node.symbolType = orig.symbolType;
80             node.scope = orig.scope;
81             return node;
82         }
83     };
84
85     public String translate(String filename) throws Exception {
86         m_basename = filename.substring(0, filename.lastIndexOf("."));
87         m_basename = m_basename.substring(m_basename.lastIndexOf("/") + 1);
88
89         ANTLRFileStream input = new ANTLRFileStream(filename);
90             
91         CharjLexer lexer = new CharjLexer(input);
92         m_tokens = new CommonTokenStream(lexer);
93
94         // Use lexer tokens to feed tree parser
95         CharjParser parser = new CharjParser(m_tokens);
96         parser.setTreeAdaptor(m_adaptor);
97         CharjParser.charjSource_return r = parser.charjSource();
98
99         // Create node stream for AST traversals
100         m_ast = (CommonTree)r.getTree();
101         m_nodes = new CommonTreeNodeStream(m_ast);
102         m_nodes.setTokenStream(m_tokens);
103         m_nodes.setTreeAdaptor(m_adaptor);
104
105         // do AST rewriting and semantic checking
106         if (m_printAST) printAST("Before Preanalysis Pass", "before_preanalysis.html");
107         preAnalysisPass();
108         if (m_printAST) printAST("After Preanalysis Pass", "after_preanalysis.html");
109
110         resolveTypes();
111         if (m_printAST) printAST("After Type Resolution", "after_types.html");
112
113         initPupCollect();
114         if (m_printAST) printAST("After Collector Pass", "after_collector.html");
115
116         postAnalysisPass();
117         if (m_printAST) printAST("After Postanalysis Pass", "after_postanalysis.html");
118
119                 m_nodes = new CommonTreeNodeStream(m_ast);
120         m_nodes.setTokenStream(m_tokens);
121         m_nodes.setTreeAdaptor(m_adaptor);
122
123         // emit code for .ci, .h, and .cc based on rewritten AST
124         String ciOutput = translationPass(OutputMode.ci);
125         writeTempFile(filename, ciOutput, OutputMode.ci);
126
127         String hOutput = translationPass(OutputMode.h);
128         writeTempFile(filename, hOutput, OutputMode.h);
129         
130         String ccOutput = translationPass(OutputMode.cc);
131         writeTempFile(filename, ccOutput, OutputMode.cc);
132
133         if (!m_translate_only) compileTempFiles(filename, m_charmc);
134
135         // Build a string representing all emitted code. This will be printed
136         // by the main driver if requested via command-line argument. 
137         String ciHeader = "-----CI----------------------------\n";
138         String hHeader  = "-----H-----------------------------\n";
139         String ccHeader = "-----CC----------------------------\n";
140         String footer   = "-----------------------------------\n";
141         return ciHeader + ciOutput + hHeader + hOutput + 
142             ccHeader + ccOutput + footer;
143     }
144
145     private void preAnalysisPass() throws
146         RecognitionException, IOException, InterruptedException
147     {
148         m_nodes.reset();
149         CharjPreAnalysis pass = new CharjPreAnalysis(m_nodes);
150         pass.setTreeAdaptor(m_adaptor);
151         m_ast = (CommonTree)pass.charjSource().getTree();
152         m_nodes = new CommonTreeNodeStream(m_ast);
153         m_nodes.setTokenStream(m_tokens);
154         m_nodes.setTreeAdaptor(m_adaptor);
155     }
156
157     private void postAnalysisPass() throws
158         RecognitionException, IOException, InterruptedException
159     {
160         m_nodes.reset();
161         CharjPostAnalysis pass = new CharjPostAnalysis(m_nodes);
162         pass.setTreeAdaptor(m_adaptor);
163         m_ast = (CommonTree)pass.charjSource(m_symtab).getTree();
164         m_nodes = new CommonTreeNodeStream(m_ast);
165         m_nodes.setTokenStream(m_tokens);
166         m_nodes.setTreeAdaptor(m_adaptor);
167     }
168
169     private ClassSymbol semanticPass() throws
170         RecognitionException, IOException, InterruptedException
171     {
172         m_nodes.reset();
173         CharjSemantics sem = new CharjSemantics(m_nodes);
174         return sem.charjSource(m_symtab);
175     }
176
177     private void resolveTypes() throws
178         RecognitionException, IOException, InterruptedException
179     {
180         m_nodes.reset();
181         if (m_verbose) System.out.println("\nDefiner Phase\n----------------");
182         SymbolDefiner definer = new SymbolDefiner(m_nodes, m_symtab);
183         definer.downup(m_ast);
184         m_nodes.reset();
185         definer.downup(m_ast);
186         if (m_verbose) System.out.println("\nResolver Phase\n----------------");
187         if (m_printAST) printAST("After Type Definition", "after_definition.html");
188         m_nodes.reset();
189         SymbolResolver resolver = new SymbolResolver(m_nodes, m_symtab);
190         resolver.downup(m_ast);
191     }
192
193     private void initPupCollect() throws
194         RecognitionException, IOException, InterruptedException
195     {
196         m_nodes.reset();
197         if (m_verbose) System.out.println("\nInitPupCollector Phase\n----------------");
198         InitPUPCollector collector = new InitPUPCollector(m_nodes);
199         collector.downup(m_ast);
200     }
201
202     private String translationPass(OutputMode m) throws
203         RecognitionException, IOException, InterruptedException
204     {
205         m_nodes.reset();
206         CharjEmitter emitter = new CharjEmitter(m_nodes);
207         StringTemplateGroup templates = getTemplates(templateFile);
208         emitter.setTemplateLib(templates);
209         StringTemplate st = 
210             (StringTemplate)emitter.charjSource(m_symtab, m).getTemplate();
211         return st.toString();
212     }
213
214     public static StringTemplateGroup getTemplates(String templateFile) {
215         StringTemplateGroup templates = null;
216         try {
217             ClassLoader loader = Thread.currentThread().getContextClassLoader();
218             InputStream istream = loader.getResourceAsStream(templateFile);
219             BufferedReader reader = 
220                 new BufferedReader(new InputStreamReader(istream));
221             templates = new StringTemplateGroup(reader);
222             reader.close();
223         } catch(IOException ex) {
224             System.err.println(ex.getMessage());
225             ex.printStackTrace(System.err);
226         }
227         return templates;
228     }
229
230     public File findPackage(String packageName) {
231         String packageDir = packageName.replace(".", "/");
232         File p = new File(packageDir);
233         if ( debug() ) System.out.println(
234                 " [charj] findPackage " + packageName + 
235                 " trying " + p.getAbsoluteFile());
236        
237         // check current directory
238         if ( p.exists() ) {
239             return p;
240         }
241
242         // look in user libs if any
243         if ( m_usrlibs != null ) {
244             for (String lib : m_usrlibs) {
245                 p = new File(lib, packageDir);
246                 if (debug() ) System.out.println(
247                         " \tnot found, now trying " + p.getAbsoluteFile());
248                 if ( p.exists() ) {
249                     return p;
250                 }
251             }
252         }
253
254         // look in standard lib
255         p = new File(m_stdlib, packageDir);
256         if ( debug() ) System.out.println(
257                 " \tnot found, now trying " + p.getAbsoluteFile());
258         if ( p.exists() ) {
259             return p;
260         }
261
262         return null;
263     }
264
265     /** Load a class from disk looking in lib/package
266      *  Side-effect: add class to symtab. This is used by ClassSymbol to
267      *  load unknown types from disk. packageName comes from the output
268      *  of PackageScope.getFullyQualifiedName
269      */
270     public ClassSymbol loadType(String packageName, String typeName) {
271         if (debug()) System.out.println(
272                 " [charj] loadType(" + typeName + ") from " + packageName);
273         
274         ClassSymbol cs = null;
275         try {
276             String packageDir = ".";
277             if ( packageName!=null ) {
278                 packageDir = packageName.replace(".", "/");
279             }
280             String fullName = packageDir + "/" + typeName + ".cj";
281                 
282             ClassLoader cl = Thread.currentThread().getContextClassLoader();
283             boolean fileExists = (cl.getResource(fullName) != null);
284             if (!fileExists) {
285                 if (debug()) System.out.println(
286                         " \tloadType(" + typeName + "): not found");
287                 return null;
288             }
289
290             if (debug()) System.out.println(
291                     " \tloadType(" + typeName + "): parsing " + 
292                     packageName + "." + typeName);
293             
294             ANTLRInputStream fs = new ANTLRInputStream(
295                     cl.getResourceAsStream(fullName));
296             fs.name = packageDir + "/" + typeName + ".cj";
297             CharjLexer lexer = new CharjLexer(fs);
298             
299             cs = semanticPass();
300         } catch (Exception e) {
301             e.printStackTrace();
302         }
303         return cs;
304     }
305
306     /**
307      * Read the given file name in as a string.
308      */
309     public static String readFile(String path) throws IOException {
310       FileInputStream stream = new FileInputStream(new File(path));
311       try {
312         FileChannel fc = stream.getChannel();
313         MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
314         return Charset.defaultCharset().decode(bb).toString();
315       }
316       finally {
317         stream.close();
318       }
319     }
320
321     /**
322      * Utility function to write a generated .ci, .cc, or .h
323      * file to disk. Takes a .cj filename and writes a .cc,
324      * .ci, or .h file to the .charj directory depending on
325      * the OutputMode.
326      */
327     private void writeTempFile(
328             String filename, 
329             String output,
330             OutputMode m) throws
331         IOException
332     {
333         int lastDot = filename.lastIndexOf(".");
334         filename = filename.substring(0, lastDot) + m.extension();
335         writeTempFile(filename, output);
336         return;
337     }
338
339     private void writeTempFile(
340             String filename,
341             String output) throws
342         IOException
343     {
344         if (m_verbose) System.out.println(" [charjc] create: " + filename);
345         FileWriter fw = new FileWriter(filename);
346         fw.write(output);
347         fw.close();
348         return;
349     }
350
351     /**
352      * Compiles the .cc and .ci files generated from the given filename.
353      * The given charmc string includes all options to be passed to charmc.
354      * Any generated .o file is moved back to the initial directory.
355      */
356     private void compileTempFiles(
357             String filename,
358             String charmc) throws
359         IOException, InterruptedException
360     {
361         int lastDot = filename.lastIndexOf(".");
362         int lastSlash = filename.lastIndexOf("/");
363         String baseDirectory = filename.substring(0, lastSlash + 1);
364         if (baseDirectory.equals("")) {
365             baseDirectory = "./";
366         }
367         String moduleName = filename.substring(lastSlash + 1, lastDot);
368         String baseTempFilename = moduleName;
369
370         // Compile interface file
371         String cmd = charmc + " " + baseTempFilename + ".ci";
372         File currentDir = new File(".");
373         int retVal = exec(cmd, currentDir);
374         if (retVal != 0) {
375             error("Could not compile generated interface file.");
376             return;
377         }
378
379         // Move decl.h and def.h into temp directory.
380         // charmxi/charmc doesn't offer control over where to generate these
381         cmd = "touch " + baseTempFilename + ".decl.h " +
382             baseTempFilename + ".def.h";
383         retVal = exec(cmd, currentDir);
384         if (retVal != 0) {
385             error("Could not touch .decl.h and .def.h files.");
386             return;
387         }
388
389         // Compile c++ output
390         cmd = charmc + " -I" + m_stdlib + "/charj/libs -c " +
391             baseTempFilename + ".cc" + " -o " + baseTempFilename + ".o";
392         retVal = exec(cmd, currentDir);
393         if (retVal != 0) {
394             error("Could not compile generated C++ file");
395             return;
396         }
397     }
398
399     /**
400      * Utility function to execute a given command line.
401      */
402     private int exec(String cmd, File outputDir) throws
403         IOException, InterruptedException
404     {
405         if (m_verbose) System.out.println(" [charjc] exec: " + cmd);
406         Process p = Runtime.getRuntime().exec(cmd, null, outputDir);
407         StreamEmitter stdout = new StreamEmitter(
408                 p.getInputStream(), System.out);
409         StreamEmitter stderr = new StreamEmitter(
410                 p.getErrorStream(), System.err);
411         stdout.start();
412         stderr.start();
413         p.waitFor();
414         stdout.join();
415         stderr.join();
416         int retVal = p.exitValue();
417         return retVal;
418     }
419
420     /**
421      * Print a representation of the Charj AST. If message is not null,
422      * it is printed, along with an ASCII representation of the tree,
423      * to stdout. If filename is not null, an html temp file containin
424      * the representation is printed to filename.
425      */
426     public void printAST(String message, String filename) throws IOException
427     {
428         if (filename != null) {
429             ASTHTMLPrinter htmlPrinter = new ASTHTMLPrinter();
430             TreeTraverser.visit((CharjAST)m_ast, htmlPrinter);
431             writeTempFile(filename, htmlPrinter.output());
432         }
433
434         if (message != null) {
435             String header = "----------\n" + "AST: " + message + "\n----------\n";
436             String footer = "\n----------\n";
437             String body = null;
438             if (m_ast != null) {
439                 body = m_ast.toStringTree();
440             } else {
441                 body = "Null tree, no AST available";
442             }
443             System.out.println(header + body + footer);
444         }
445     }
446     
447     public void error(
448             BaseRecognizer recog, 
449             String msg, 
450             CharjAST node) 
451     {
452         String sourceName = "";
453         if (recog == null) {
454             sourceName = "<anonymous>";
455         } else {
456             sourceName = recog.getSourceName();
457         }
458         error(sourceName, msg, node);
459     } 
460
461     private void error(
462             String sourceName, 
463             String msg, 
464             CharjAST node) 
465     {
466         m_errorCondition = true;
467         String linecol = ":";
468         if ( node!=null ) {
469             CommonToken t = (CommonToken)node.getToken();
470             linecol = ": line " + t.getLine() + ":" + 
471                 t.getCharPositionInLine();
472         }
473         System.err.println(sourceName + linecol + " " + msg);
474         System.err.flush();
475     }
476
477     private void error(
478             String sourceName, 
479             String msg) {
480         error(sourceName, msg, (CharjAST)null);
481     }
482
483
484     public void error(String msg) {
485         error(" [charjc] error", msg, (CharjAST)null);
486     }
487
488
489     public void error(
490             String msg, 
491             Exception e) {
492         error(msg);
493         e.printStackTrace(System.err);
494     }
495 }