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