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