root/trunk/ProjectFortress/src/com/sun/fortress/Shell.java @ 2404

Revision 2404, 23.3 KB (checked in by mspiegel, 16 months ago)

[static analysis] Another refactoring of Shell.java. Static phases are now declared in package com.sun.fortress.compiler.phases.

Line 
1/*******************************************************************************
2  Copyright 2008 Sun Microsystems, Inc.,
3  4150 Network Circle, Santa Clara, California 95054, U.S.A.
4  All rights reserved.
5
6  U.S. Government Rights - Commercial software.
7  Government users are subject to the Sun Microsystems, Inc. standard
8  license agreement and applicable provisions of the FAR and its supplements.
9
10  Use is subject to license terms.
11
12  This distribution may include materials developed by third parties.
13
14  Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered
15  trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
16  ******************************************************************************/
17
18package com.sun.fortress;
19
20import com.sun.fortress.repository.CacheBasedRepository;
21import com.sun.fortress.repository.FortressRepository;
22import com.sun.fortress.repository.GraphRepository;
23import com.sun.fortress.repository.ProjectProperties;
24import java.io.*;
25import java.util.*;
26
27import edu.rice.cs.plt.tuple.Option;
28import edu.rice.cs.plt.tuple.OptionUnwrapException;
29import edu.rice.cs.plt.iter.IterUtil;
30
31import com.sun.fortress.compiler.*;
32import com.sun.fortress.exceptions.shell.UserError;
33import com.sun.fortress.exceptions.StaticError;
34import com.sun.fortress.exceptions.WrappedException;
35import com.sun.fortress.exceptions.ProgramError;
36import com.sun.fortress.exceptions.FortressException;
37import com.sun.fortress.exceptions.shell.RepositoryError;
38import com.sun.fortress.compiler.Parser;
39import com.sun.fortress.compiler.phases.PhaseOrder;
40import com.sun.fortress.nodes.Api;
41import com.sun.fortress.nodes.APIName;
42import com.sun.fortress.nodes.CompilationUnit;
43import com.sun.fortress.nodes.Component;
44import com.sun.fortress.nodes_util.NodeFactory;
45import com.sun.fortress.nodes_util.ASTIO;
46import com.sun.fortress.interpreter.Driver;
47import com.sun.fortress.syntax_abstractions.parser.PreParser;
48import com.sun.fortress.useful.Path;
49import com.sun.fortress.useful.Debug;
50import com.sun.fortress.useful.Useful;
51import com.sun.fortress.tools.FortressAstToConcrete;
52
53public final class Shell {
54    static boolean test;
55
56    /* set this statically if you only want to run up to a certain phase */
57    private static PhaseOrder finalPhase = PhaseOrder.CODEGEN;
58
59    private final FortressRepository _repository;
60    private static final CacheBasedRepository defaultRepository =
61        new CacheBasedRepository(ProjectProperties.ANALYZED_CACHE_DIR);
62    public static FortressRepository CURRENT_INTERPRETER_REPOSITORY = null;
63
64    public Shell(FortressRepository repository) { _repository = repository; }
65
66    public FortressRepository getRepository() {
67        return _repository;
68    }
69
70    private static void setPhase( PhaseOrder phase ){
71        finalPhase = phase;
72    }
73
74    /**
75     * This is used to communicate, clumsily, with parsers generated by syntax expansion.
76     * The interface should be improved.
77     */
78    public static void setCurrentInterpreterRepository( FortressRepository g ){
79        CURRENT_INTERPRETER_REPOSITORY = g;
80    }
81
82    private static GraphRepository specificRepository(Path p, CacheBasedRepository cache ){
83        GraphRepository fr = new GraphRepository( p, cache );
84        CURRENT_INTERPRETER_REPOSITORY = fr;
85        return fr;
86    }
87
88    public static GraphRepository specificRepository(Path p) {
89        return specificRepository( p, defaultRepository );
90    }
91
92    /* Helper method to print usage message.*/
93    private static void printUsageMessage() {
94        System.err.println("Usage:");
95        System.err.println(" compile [-out file] [-debug [type]* [#]] somefile.fs{s,i}");
96        System.err.println(" [run] [-test] [-debug [type]* [#]] somefile.fss arg...");
97        System.err.println(" parse [-out file] [-debug [type]* [#]] somefile.fs{s,i}");
98        System.err.println(" unparse [-out file] [-debug [type]* [#]] somefile.tf{s,i}");
99        System.err.println(" disambiguate [-out file] [-debug [type]* [#]] somefile.fs{s,i}");
100        System.err.println(" desugar [-out file] [-debug [type]* [#]] somefile.fs{s,i}");
101        System.err.println(" grammar [-out file] [-debug [type]* [#]] somefile.fs{s,i}");
102        System.err.println(" typecheck [-out file] [-debug [type]* [#]] somefile.fs{s,i}");
103        System.err.println(" help");
104    }
105
106    private static void printHelpMessage() {
107        System.err.println
108        ("Invoked as script: fortress args\n"+
109         "Invoked by java: java ... com.sun.fortress.Shell args\n"+
110         "\n"+
111         "fortress compile [-out file] [-debug [type]* [#]] somefile.fs{s,i}\n"+
112         "  Compile somefile. If compilation succeeds no message will be printed.\n"+
113         "\n"+
114         "fortress [run] [-test] [-debug [type]* [#]] somefile.fss arg ...\n"+
115         "  Runs somefile.fss through the Fortress interpreter, passing arg ... to the\n"+
116         "  run method of somefile.fss.\n"+
117         "\n"+
118         "fortress parse [-out file] [-debug [type]* [#]] somefile.fs{i,s}\n"+
119         "  Parses a file. If parsing succeeds the message \"Ok\" will be printed.\n"+
120         "  If -out file is given, a message about the file being written to will be printed.\n"+
121         "\n"+
122         "fortress unparse [-out file] [-debug [type]* [#]] somefile.tf{i,s}\n"+
123         "  Convert a parsed file back to Fortress source code. The output will be dumped to stdout if -out is not given.\n"+
124         "  If -out file is given, a message about the file being written to will be printed.\n"+
125         "\n"+
126         "fortress disambiguate [-out file] [-debug [type]* [#]] somefile.fs{i,s}\n"+
127         "  Disambiguates a file.\n"+
128         "  If -out file is given, a message about the file being written to will be printed.\n"+
129         "\n"+
130         "fortress desugar [-out file] [-debug [#]] somefile.fs{i,s}\n"+
131         "  Desugars a file.\n"+
132         "  If -out file is given, a message about the file being written to will be printed.\n"+
133         "\n"+
134         "fortress grammar [-out file] [-debug [#]] somefile.fs{i,s}\n"+
135         "  Rewrites syntax grammars in a file.\n"+
136         "  If -out file is given, a message about the file being written to will be printed.\n"+
137         "\n"+
138         "fortress typecheck [-out file] [-debug [#]] somefile.fs{i,s}\n"+
139         "  Typechecks a file. If type checking succeeds no message will be printed.\n"+
140         "\n"+
141         "More details on each flag:\n"+
142         "   -out file : dumps the processed abstract syntax tree to a file.\n"+
143         "   -test : first runs test functions associated with the program.\n"+
144         "   -debug : enables debugging to the maximum level (99) for all \n"+
145         "            debugging types and prints java stack traces.\n"+
146         "   -debug # : sets debugging to the specified level, where # is a number, \n"+
147         "            and sets all debugging types on.\n"+
148         "   -debug types : sets debugging types to the specified types with \n"+
149         "            the maximum debugging level. \n" +
150         "   -debug types # : sets debugging to the specified level, where # is a number, \n"+
151         "            and the debugging types to the specified types. \n" +
152         "   The acceptable debugging types are:\n"+
153         "            " + Debug.typeStrings() + "\n"+
154         "\n"
155        );
156    }
157
158    private static void turnOnTypeChecking(){
159        com.sun.fortress.compiler.StaticChecker.typecheck = true;
160    }
161
162    /* Main entry point for the fortress shell.*/
163    public static void main(String[] tokens) throws InterruptedException, Throwable {
164        if (tokens.length == 0) {
165            printUsageMessage();
166            System.exit(-1);
167        }
168
169        // Now match the assembled string.
170        try {
171            String what = tokens[0];
172            List<String> args = Arrays.asList(tokens).subList(1, tokens.length);
173            if (what.equals("compile")) {
174                compile(args, Option.<String>none());
175            } else if (what.equals("run")) {
176                run(args);
177            } else if ( what.equals("parse" ) ){
178                parse(args, Option.<String>none());
179            } else if ( what.equals("unparse" ) ){
180                unparse(args, Option.<String>none());
181            } else if ( what.equals( "disambiguate" ) ){
182                setPhase( PhaseOrder.DISAMBIGUATE );
183                compile(args, Option.<String>none());
184            } else if ( what.equals( "desugar" ) ){
185                setPhase( PhaseOrder.DESUGAR );
186                compile(args, Option.<String>none());
187            } else if ( what.equals( "grammar" ) ){
188                setPhase( PhaseOrder.GRAMMAR );
189                compile(args, Option.<String>none());
190            } else if (what.equals("typecheck")) {
191                /* TODO: remove the next line once type checking is permanently turned on */
192                turnOnTypeChecking();
193                setPhase( PhaseOrder.TYPECHECK );
194                compile(args, Option.<String>none());
195            } else if (what.contains(ProjectProperties.COMP_SOURCE_SUFFIX)
196                       || (what.startsWith("-") && tokens.length > 1)) {
197                // no "run" command.
198                run(Arrays.asList(tokens));
199            } else if (what.equals("help")) {
200                printHelpMessage();
201
202            } else { printUsageMessage(); }
203        }
204        catch (UserError error) {
205            System.err.println(error.getMessage());
206        }
207        catch (IOException error) {
208            System.err.println(error.getMessage());
209        }
210    }
211
212    /**
213     * Parse a file. If the file parses ok it will say "Ok".
214     * If you want a dump then give -out somefile.
215     */
216    private static void parse(List<String> args, Option<String> out)
217        throws UserError, InterruptedException, IOException {
218        if (args.size() == 0) {
219            throw new UserError("Need a file to parse");
220        }
221        String s = args.get(0);
222        List<String> rest = args.subList(1, args.size());
223
224        if (s.startsWith("-")) {
225            if (s.equals("-debug")){
226                rest = Debug.parseOptions(rest);
227            }
228            if (s.equals("-out") && ! rest.isEmpty() ){
229                out = Option.<String>some(rest.get(0));
230                rest = rest.subList( 1, rest.size() );
231            }
232            if (s.equals("-noPreparse")) ProjectProperties.noPreparse = true;
233
234            parse( rest, out );
235        } else {
236            parse( s, out );
237        }
238    }
239
240    /**
241     * UnParse a file.
242     * If you want a dump then give -out somefile.
243     */
244    private static void unparse(List<String> args, Option<String> out)
245        throws UserError, InterruptedException, IOException {
246        if (args.size() == 0) {
247            throw new UserError("Need a file to unparse");
248        }
249        String s = args.get(0);
250        List<String> rest = args.subList(1, args.size());
251
252        if (s.startsWith("-")) {
253            if (s.equals("-debug")){
254                rest = Debug.parseOptions(rest);
255            }
256            if (s.equals("-out") && ! rest.isEmpty() ){
257                out = Option.<String>some(rest.get(0));
258                rest = rest.subList( 1, rest.size() );
259            }
260            if (s.equals("-noPreparse")) ProjectProperties.noPreparse = true;
261
262            unparse( rest, out );
263        } else {
264            unparse( s, out );
265        }
266    }
267
268    private static void unparse( String file, Option<String> out ){
269        try{
270            String code = ASTIO.readJavaAst( file ).unwrap().accept( new FortressAstToConcrete() );
271            if ( out.isSome() ){
272                try{
273                    BufferedWriter writer = Useful.filenameToBufferedWriter(out.unwrap());
274                    writer.write(code);
275                    writer.close();
276                    System.out.println( "Dumped code to " + out.unwrap() );
277                } catch ( IOException e ){
278                    System.err.println( "Error while writing " + out.unwrap() );
279                }
280            } else {
281                System.out.println( code );
282            }
283        } catch ( IOException i ){
284            i.printStackTrace();
285        } catch ( OptionUnwrapException o ){
286            o.printStackTrace();
287        }
288    }
289
290    private static void parse( String file, Option<String> out){
291        try{
292            CompilationUnit unit = Parser.parseFile(cuName(file), new File(file));
293            System.out.println( "Ok" );
294            if ( out.isSome() ){
295                try{
296                    ASTIO.writeJavaAst(unit, out.unwrap());
297                    System.out.println( "Dumped parse tree to " + out.unwrap() );
298                } catch ( IOException e ){
299                    System.err.println( "Error while writing " + out.unwrap() );
300                }
301            }
302        } catch (ProgramError e) {
303            System.err.println(e.getMessage());
304            e.printInterpreterStackTrace(System.err);
305            if (Debug.isOnMax()) {
306                e.printStackTrace();
307            } else {
308                System.err.println("Turn on -debug for Java-level error dump.");
309            }
310            System.exit(1);
311        } catch ( FileNotFoundException f ){
312            System.err.println( file + " not found" );
313        } catch ( IOException ie ){
314            System.err.println( "Error while reading " + file );
315        } catch ( StaticError s ){
316            System.err.println(s);
317        }
318    }
319
320    private static boolean isApi(String file){
321        return file.endsWith(ProjectProperties.API_SOURCE_SUFFIX);
322    }
323
324    private static boolean isComponent(String file){
325        return file.endsWith(ProjectProperties.COMP_SOURCE_SUFFIX);
326    }
327
328    private static APIName cuName( String file ){
329        if ( file.endsWith( ProjectProperties.COMP_SOURCE_SUFFIX ) ||
330             file.endsWith( ProjectProperties.API_SOURCE_SUFFIX ) ){
331            return NodeFactory.makeAPIName(file.substring( 0, file.lastIndexOf(".") ));
332        }
333        return NodeFactory.makeAPIName(file);
334    }
335
336    public static boolean checkCompilationUnitName(String filename,
337                                                   String cuname) {
338        String file = filename.substring( 0, filename.lastIndexOf(".") );
339        file = file.replace('/','.');
340        file = file.replace('\\','.');
341        String regex = "(.*\\.)?" + cuname.replaceAll("\\.", "\\.") + "$";
342        Debug.debug( Debug.Type.REPOSITORY, 3, "Checking file name " + file + " vs cuname " + regex );
343        return file.matches( regex );
344    }
345
346    /**
347     * Compile a file.
348     * If you want a dump then give -out somefile.
349     */
350    private static void compile(List<String> args, Option<String> out)
351        throws UserError, InterruptedException, IOException {
352        if (args.size() == 0) {
353            throw new UserError("Need a file to compile");
354        }
355        String s = args.get(0);
356        List<String> rest = args.subList(1, args.size());
357
358        if (s.startsWith("-")) {
359            if (s.equals("-debug")){
360                rest = Debug.parseOptions(rest);
361            }
362            if (s.equals("-out") && ! rest.isEmpty() ){
363                out = Option.<String>some(rest.get(0));
364                rest = rest.subList( 1, rest.size() );
365            }
366            if (s.equals("-noPreparse")) ProjectProperties.noPreparse = true;
367            compile(rest, out);
368        } else {
369            try {
370                APIName name = trueApiName( s );
371                Path path = sourcePath( s, name );
372
373                /*
374                Debug.debug( Debug.Type.REPOSITORY, 2, "True api name is " + name );
375                Debug.debug( Debug.Type.REPOSITORY, 2, "Path is " + s );
376                Path path = ProjectProperties.SOURCE_PATH;
377                String source = new File( s ).getCanonicalPath().substring( 0, s.length() - (name.toString().length() + 4) );
378                path = path.prepend( source );
379                Debug.debug( Debug.Type.REPOSITORY, 2, "Source path is " + source );
380                Debug.debug( Debug.Type.REPOSITORY, 2, "Lookup path is " + path );
381                */
382                /*
383                if (s.contains("/")) {
384                    String head = s.substring(0, s.lastIndexOf("/"));
385                    s = s.substring(s.lastIndexOf("/")+1, s.length());
386                    path = path.prepend(head);
387                }
388                */
389                Iterable<? extends StaticError> errors = compile(path, name.toString() + (s.endsWith(".fss") ? ".fss" : ".fsi"), out );
390                if ( errors.iterator().hasNext() ){
391                    for (StaticError error: errors) {
392                        System.err.println(error);
393                    }
394                }
395            } catch (RepositoryError error) {
396                System.err.println(error);
397            }
398        }
399    }
400
401    private static APIName trueApiName( String path ) throws IOException {
402        return PreParser.apiName( NodeFactory.makeAPIName(path), new File(path).getCanonicalFile() );
403    }
404
405    /**
406     * Compile a file.
407     */
408    public static Iterable<? extends StaticError> compile(Path path, String file) {
409        return compile(path, file, Option.<String>none());
410    }
411
412    private static Iterable<? extends StaticError> compile(Path path, String file, Option<String> out) {
413        GraphRepository bcr = specificRepository( path, defaultRepository );
414
415        Debug.debug( Debug.Type.FORTRESS, 2, "Compiling file ", file );
416        APIName name = cuName(file);
417        try {
418            if ( isApi(file) ) {
419                // FIXME: The following line is executed for its side effects
420                Api a = (Api) bcr.getApi(name).ast();
421                if ( out.isSome() )
422                    ASTIO.writeJavaAst(defaultRepository.getApi(name).ast(), out.unwrap());
423            } else if (isComponent(file)) {
424                // FIXME: The following line is executed for its side effects
425                Component c = (Component) bcr.getComponent(name).ast();
426                if ( out.isSome() )
427                    ASTIO.writeJavaAst(defaultRepository.getComponent(name).ast(), out.unwrap());
428            } else {
429                System.out.println( "Don't know what kind of file " + file +
430                                    " is. Append .fsi or .fss." );
431            }
432        } catch (ProgramError pe) {
433            Iterable<? extends StaticError> se = pe.getStaticErrors();
434            if (se == null) {
435                return IterUtil.singleton(new WrappedException(pe, Debug.isOnMax()));
436            }
437            else {
438                return se;
439            }
440        } catch (RepositoryError ex) {
441            throw ex;
442        } catch ( FileNotFoundException ex ){
443            throw new WrappedException(ex);
444        } catch ( IOException e ){
445            throw new WrappedException(e);
446        } catch (StaticError ex) {
447             return IterUtil.singleton(new WrappedException(ex, Debug.isOnMax()));
448        }
449
450        if (bcr.verbose())
451            System.err.println("Compiling done.");
452
453        return IterUtil.empty();
454    }
455
456    /**
457     * Run a file.
458     */
459    private static void run(List<String> args)
460        throws UserError, IOException, Throwable {
461        if (args.size() == 0) {
462            throw new UserError("Need a file to run");
463        }
464        String s = args.get(0);
465        List<String> rest = args.subList(1, args.size());
466
467        if (s.startsWith("-")) {
468            if (s.equals("-debug")){
469                rest = Debug.parseOptions(rest);
470            }
471            if (s.equals("-test")) {
472                test = true;
473            }
474            if (s.equals("-noPreparse")) ProjectProperties.noPreparse = true;
475
476            run(rest);
477        } else {
478            run(s, rest);
479        }
480    }
481
482    private static void run(String fileName, List<String> args)
483        throws UserError, Throwable {
484        try {
485            /*
486            Path path = ProjectProperties.SOURCE_PATH;
487            if (fileName.contains("/")) {
488                String head = fileName.substring(0, fileName.lastIndexOf("/"));
489                fileName = fileName.substring(fileName.lastIndexOf("/")+1, fileName.length());
490                path = path.prepend(head);
491            }
492            APIName componentName = cuName(fileName);
493            */
494
495            APIName name = trueApiName( fileName );
496            Path path = sourcePath(fileName, name);
497           
498            GraphRepository bcr = specificRepository( path, defaultRepository );
499            Iterable<? extends StaticError> errors = IterUtil.empty();
500
501            try {
502                CompilationUnit cu = bcr.getLinkedComponent(name).ast();
503                Driver.runProgram(bcr, cu, test, args);
504            } catch (Throwable th) {
505                // TODO FIXME what is the proper treatment of errors/exceptions etc.?
506                if (th instanceof FortressException) {
507                    FortressException pe = (FortressException) th;
508                    if (pe.getStaticErrors() != null)
509                        errors = pe.getStaticErrors();
510                }
511                if (th instanceof RuntimeException)
512                    throw (RuntimeException) th;
513                if (th instanceof Error)
514                    throw (Error) th;
515                throw new WrappedException(th, Debug.isOnMax());
516            }
517
518            for (StaticError error: errors) {
519                System.err.println(error);
520            }
521            // If there are no errors, all components will have been written to disk by the CacheBasedRepository.
522        } catch ( StaticError e ){
523            System.err.println(e);
524            if ( Debug.isOnMax() ){
525                e.printStackTrace();
526            }
527        } catch (RepositoryError e) {
528            System.err.println(e.getMessage());
529        } catch (FortressException e) {
530            System.err.println(e.getMessage());
531            e.printInterpreterStackTrace(System.err);
532            if (Debug.isOnMax()) {
533                e.printStackTrace();
534            } else {
535                System.err.println("Turn on -debug for Java-level error dump.");
536            }
537            System.exit(1);
538        }
539    }
540
541    /* find the api name for a file and chop it off the source path.
542     * what remains from the source path is the directory that contains
543     * the file( including sub-directories )
544     */
545    private static Path sourcePath( String file, APIName name ) throws IOException {
546        Debug.debug( Debug.Type.REPOSITORY, 2, "True api name is " + name );
547        String fullPath = new File(file).getCanonicalPath();
548        Debug.debug( Debug.Type.REPOSITORY, 2, "Path is " + fullPath );
549        Path path = ProjectProperties.SOURCE_PATH;
550        /* the path to the file is /absolute/path/a/b/c/foo.fss and the apiname is
551         * a.b.c.foo, so we need to take off the apiname plus four more characters,
552         * ".fss" or ".fsi"
553         */
554        String source = fullPath.substring( 0, fullPath.length() - (name.toString().length() + 4) );
555        path = path.prepend( source );
556        Debug.debug( Debug.Type.REPOSITORY, 2, "Source path is " + source );
557        Debug.debug( Debug.Type.REPOSITORY, 2, "Lookup path is " + path );
558        return path;
559    }
560
561    /* run all the analysis available */
562    public static AnalyzeResult analyze(final FortressRepository repository,
563                                        final GlobalEnvironment env,
564                                        List<Api> apis,
565                                        List<Component> components,
566                                        final long lastModified) throws StaticError {           
567        AnalyzeResult result = finalPhase.makePhase(repository,env,apis,components,lastModified).run();
568        return result;
569    }
570
571
572}
Note: See TracBrowser for help on using the browser.