KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > percederberg > grammatica > Grammatica


1 /*
2  * Grammatica.java
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2.1
7  * of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307, USA.
18  *
19  * Copyright (c) 2003-2005 Per Cederberg. All rights reserved.
20  */

21
22 package net.percederberg.grammatica;
23
24 import java.io.BufferedReader JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileNotFoundException JavaDoc;
27 import java.io.FileReader JavaDoc;
28 import java.io.IOException JavaDoc;
29
30 import net.percederberg.grammatica.output.CSharpParserGenerator;
31 import net.percederberg.grammatica.output.JavaParserGenerator;
32 import net.percederberg.grammatica.output.VisualBasicParserGenerator;
33 import net.percederberg.grammatica.parser.Analyzer;
34 import net.percederberg.grammatica.parser.Node;
35 import net.percederberg.grammatica.parser.ParseException;
36 import net.percederberg.grammatica.parser.Parser;
37 import net.percederberg.grammatica.parser.ParserCreationException;
38 import net.percederberg.grammatica.parser.ParserLogException;
39 import net.percederberg.grammatica.parser.Token;
40 import net.percederberg.grammatica.parser.Tokenizer;
41
42 /**
43  * The main application. This class provides the command-line
44  * interface for invoking the application. See separate documentation
45  * for information on usage and command-line parameters.
46  *
47  * @author Per Cederberg, <per at percederberg dot net>
48  * @version 1.5
49  */

50 public class Grammatica extends Object JavaDoc {
51
52     /**
53      * The command-line help output.
54      */

55     private static final String JavaDoc COMMAND_HELP =
56         "Generates source code for a C#, Java or Visual Basic parser from\n" +
57         "a grammar file. This program comes with ABSOLUTELY NO WARRANTY;\n" +
58         "for details see the LICENSE.txt file.\n" +
59         "\n" +
60         "Syntax: Grammatica <grammarfile> <action> [<options>]\n" +
61         "\n" +
62         "Actions:\n" +
63         " --debug\n" +
64         " Debugs the grammar by validating it and printing the\n" +
65         " internal representation.\n" +
66         " --tokenize <file>\n" +
67         " Debugs the grammar by using it to tokenize the specified\n" +
68         " file. No code has to be generated for this.\n" +
69         " --parse <file>\n" +
70         " Debugs the grammar by using it to parse the specified\n" +
71         " file. No code has to be generated for this.\n" +
72         " --profile <file>\n" +
73         " Profiles the grammar by using it to parse the specified\n" +
74         " file and printing a statistic summary.\n" +
75         " --csoutput <dir>\n" +
76         " Creates a C# parser for the grammar (in source code).\n" +
77         " The specified directory will be used as output directory\n" +
78         " for the source code files.\n" +
79         " --javaoutput <dir>\n" +
80         " Creates a Java parser for the grammar (in source code).\n" +
81         " The specified directory will be used as the base output\n" +
82         " directory for the source code files.\n" +
83         " --vboutput <dir>\n" +
84         " Creates a Visual Basic (.NET) parser for the grammar (in\n" +
85         " source code). The specified directory will be used as\n" +
86         " output directory for the source code files.\n" +
87         "\n" +
88         "C# Output Options:\n" +
89         " --csnamespace <package>\n" +
90         " Sets the C# namespace to use in generated source code\n" +
91         " files. By default no namespace declaration is included.\n" +
92         " --csclassname <name>\n" +
93         " Sets the C# class name prefix to use in generated source\n" +
94         " code files. By default the grammar file name is used.\n" +
95         " --cspublic\n" +
96         " Sets public access for all C# types generated. By default\n" +
97         " type access is internal.\n" +
98         "\n" +
99         "Java Output Options:\n" +
100         " --javapackage <package>\n" +
101         " Sets the Java package to use in generated source code\n" +
102         " files. By default no package declaration is included.\n" +
103         " --javaclassname <name>\n" +
104         " Sets the Java class name prefix to use in generated source\n" +
105         " code files. By default the grammar file name is used.\n" +
106         " --javapublic\n" +
107         " Sets public access for all Java types. By default type\n" +
108         " access is package local.\n" +
109         "\n" +
110         "Visual Basic Output Options:\n" +
111         " --vbnamespace <package>\n" +
112         " Sets the namespace to use in generated source code files.\n" +
113         " By default no namespace declaration is included.\n" +
114         " --vbclassname <name>\n" +
115         " Sets the class name prefix to use in generated source code\n" +
116         " files. By default the grammar file name is used.\n" +
117         " --vbpublic\n" +
118         " Sets public access for all types generated. By default type\n" +
119         " access is friend.";
120
121     /**
122      * The internal error message.
123      */

124     private static final String JavaDoc INTERNAL_ERROR =
125         "INTERNAL ERROR: An internal error in Grammatica has been found.\n" +
126         " Please report this error to the maintainers (see the web\n" +
127         " site for instructions). Be sure to include the Grammatica\n" +
128         " version number, as well as the information below:\n";
129
130     /**
131      * The application entry point.
132      *
133      * @param args the command-line parameters
134      */

135     public static void main(String JavaDoc[] args) {
136         Grammar grammar = null;
137
138         // Parse command-line arguments
139
if (args.length == 1 && args[0].equals("--help")) {
140             printHelp(null);
141             System.exit(1);
142         }
143         if (args.length < 2) {
144             printHelp("Missing grammar file and/or action");
145             System.exit(1);
146         }
147
148         // Read grammar file
149
try {
150             grammar = new Grammar(new File JavaDoc(args[0]));
151         } catch (FileNotFoundException JavaDoc e) {
152             printError(args[0], e);
153             System.exit(1);
154         } catch (ParserLogException e) {
155             printError(args[0], e);
156             System.exit(1);
157         } catch (GrammarException e) {
158             printError(e);
159             System.exit(1);
160         } catch (SecurityException JavaDoc e) {
161             throw e;
162         } catch (RuntimeException JavaDoc e) {
163             printInternalError(e);
164             System.exit(2);
165         }
166
167         // Check action parameter
168
try {
169             if (args[1].equals("--debug")) {
170                 debug(grammar);
171             } else if (args.length < 3) {
172                 printHelp("missing action file parameter");
173                 System.exit(1);
174             } else if (args[1].equals("--tokenize")) {
175                 tokenize(grammar, new File JavaDoc(args[2]));
176             } else if (args[1].equals("--parse")) {
177                 parse(grammar, new File JavaDoc(args[2]));
178             } else if (args[1].equals("--profile")) {
179                 profile(grammar, new File JavaDoc(args[2]));
180             } else if (args[1].equals("--javaoutput")) {
181                 writeJavaCode(args, grammar);
182             } else if (args[1].equals("--csoutput")) {
183                 writeCSharpCode(args, grammar);
184             } else if (args[1].equals("--vboutput")) {
185                 writeVisualBasicCode(args, grammar);
186             } else {
187                 printHelp("unrecognized option: " + args[1]);
188                 System.exit(1);
189             }
190         } catch (SecurityException JavaDoc e) {
191             throw e;
192         } catch (RuntimeException JavaDoc e) {
193             printInternalError(e);
194             System.exit(2);
195         }
196     }
197
198     /**
199      * Prints command-line help information.
200      *
201      * @param error an optional error message, or null
202      */

203     private static void printHelp(String JavaDoc error) {
204         System.err.println(COMMAND_HELP);
205         System.err.println();
206         if (error != null) {
207             System.err.print("Error: ");
208             System.err.println(error);
209             System.err.println();
210         }
211     }
212
213     /**
214      * Prints a general error message.
215      *
216      * @param e the detailed exception
217      */

218     private static void printError(Exception JavaDoc e) {
219         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
220
221         buffer.append("Error: ");
222         buffer.append(e.getMessage());
223         System.err.println(buffer.toString());
224     }
225
226     /**
227      * Prints a file not found error message.
228      *
229      * @param file the file name not found
230      * @param e the detailed exception
231      */

232     private static void printError(String JavaDoc file, FileNotFoundException JavaDoc e) {
233         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
234
235         buffer.append("Error: couldn't open file:");
236         buffer.append("\n ");
237         buffer.append(file);
238         System.err.println(buffer.toString());
239     }
240
241     /**
242      * Prints a parse error message.
243      *
244      * @param file the input file name
245      * @param e the detailed exception
246      */

247     private static void printError(String JavaDoc file, ParseException e) {
248         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
249         String JavaDoc line;
250
251         // Handle normal parse error
252
buffer.append("Error: in ");
253         buffer.append(file);
254         if (e.getLine() > 0) {
255             buffer.append(": line ");
256             buffer.append(e.getLine());
257         }
258         buffer.append(":\n");
259         buffer.append(linebreakString(e.getErrorMessage(), " ", 70));
260         line = readLines(file, e.getLine(), e.getLine());
261         if (line != null) {
262             buffer.append("\n\n");
263             buffer.append(line);
264             for (int i = 1; i < e.getColumn(); i++) {
265                 if (line.charAt(i - 1) == '\t') {
266                     buffer.append("\t");
267                 } else {
268                     buffer.append(" ");
269                 }
270             }
271             buffer.append("^");
272         }
273         System.err.println(buffer.toString());
274     }
275
276     /**
277      * Prints a list of parse error messages.
278      *
279      * @param file the input file name
280      * @param e the parser log exception
281      */

282     private static void printError(String JavaDoc file, ParserLogException e) {
283         for (int i = 0; i < e.getErrorCount(); i++) {
284             printError(file, e.getError(i));
285         }
286     }
287
288     /**
289      * Prints a grammar error message.
290      *
291      * @param e the detailed exception
292      */

293     private static void printError(GrammarException e) {
294         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
295         String JavaDoc lines;
296
297         buffer.append("Error: in ");
298         buffer.append(e.getFile());
299         if (e.getStartLine() > 0) {
300             if (e.getStartLine() == e.getEndLine()) {
301                 buffer.append(": line ");
302                 buffer.append(e.getStartLine());
303             } else {
304                 buffer.append(": lines ");
305                 buffer.append(e.getStartLine());
306                 buffer.append("-");
307                 buffer.append(e.getEndLine());
308             }
309         }
310         buffer.append(":\n");
311         buffer.append(linebreakString(e.getErrorMessage(), " ", 70));
312         lines = readLines(e.getFile(), e.getStartLine(), e.getEndLine());
313         if (lines != null) {
314             buffer.append("\n\n");
315             buffer.append(lines);
316         }
317         System.err.println(buffer.toString());
318     }
319
320     /**
321      * Prints an internal error message. This type of error should
322      * only be reported when run-time exceptions occur, such as null
323      * pointer and the likes. All these error should be reported as
324      * bugs to the program maintainers.
325      *
326      * @param e the exception to be reported
327      */

328     private static void printInternalError(Exception JavaDoc e) {
329         System.err.println(INTERNAL_ERROR);
330         e.printStackTrace();
331     }
332
333     /**
334      * Breaks a string into multiple lines. This method will also add
335      * a prefix to each line in the resulting string. The prefix
336      * length will be taken into account when breaking the line. Line
337      * breaks will only be inserted as replacements for space
338      * characters.
339      *
340      * @param str the string to line break
341      * @param prefix the prefix to add to each line
342      * @param length the maximum line length
343      *
344      * @return the new formatted string
345      */

346     private static String JavaDoc linebreakString(String JavaDoc str,
347                                           String JavaDoc prefix,
348                                           int length) {
349
350         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
351         int pos;
352
353         while (str.length() + prefix.length() > length) {
354             pos = str.lastIndexOf(' ', length - prefix.length());
355             if (pos < 0) {
356                 pos = str.indexOf(' ');
357                 if (pos < 0) {
358                     break;
359                 }
360             }
361             buffer.append(prefix);
362             buffer.append(str.substring(0, pos));
363             str = str.substring(pos + 1);
364             buffer.append("\n");
365         }
366         buffer.append(prefix);
367         buffer.append(str);
368         return buffer.toString();
369     }
370
371     /**
372      * Reads a number of lines from a file. In the file couldn't be
373      * opened or read correctly, null will be returned.
374      *
375      * @param file the name of the file to read
376      * @param start the first line number to read, from one (1)
377      * @param end the last line number to read, from one (1)
378      *
379      * @return the lines read including newline characters
380      */

381     private static String JavaDoc readLines(String JavaDoc file, int start, int end) {
382         BufferedReader JavaDoc input;
383         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
384         String JavaDoc str;
385
386         // Check invalid line number
387
if (start < 1 || end < start) {
388             return null;
389         }
390
391         // Read line from file
392
try {
393             input = new BufferedReader JavaDoc(new FileReader JavaDoc(file));
394             for (int i = 0; i < end; i++) {
395                 str = input.readLine();
396                 if (str == null) {
397                     input.close();
398                     return null;
399                 } else if (start <= i + 1) {
400                     buffer.append(str);
401                     buffer.append("\n");
402                 }
403             }
404             input.close();
405         } catch (IOException JavaDoc e) {
406             return null;
407         }
408
409         return buffer.toString();
410     }
411
412     /**
413      * Debugs a grammar by printing the internal representation.
414      *
415      * @param grammar the grammar to use
416      */

417     private static void debug(Grammar grammar) {
418         Tokenizer tokenizer = null;
419         Parser parser = null;
420
421         // Create tokenizer and parser
422
try {
423             tokenizer = grammar.createTokenizer(null);
424             parser = grammar.createParser(tokenizer);
425         } catch (GrammarException e) {
426             printInternalError(e);
427             System.exit(2);
428         }
429
430         // Print tokenizer and parser
431
System.out.println("Contents of " + grammar.getFileName() + ":");
432         System.out.println();
433         System.out.println("Token Declarations:");
434         System.out.println("-------------------");
435         System.out.print(tokenizer);
436         System.out.println("Production Declarations:");
437         System.out.println("------------------------");
438         System.out.print(parser);
439     }
440
441     /**
442      * Tokenizes the specified file with the token patterns from the
443      * grammar.
444      *
445      * @param grammar the grammar to use
446      * @param file the file to parse
447      */

448     private static void tokenize(Grammar grammar, File JavaDoc file) {
449         Tokenizer tokenizer;
450         Token token;
451
452         try {
453             tokenizer = grammar.createTokenizer(new FileReader JavaDoc(file));
454             System.out.println("Tokens from " + file + ":");
455             while ((token = tokenizer.next()) != null) {
456                 System.out.println(token);
457             }
458         } catch (FileNotFoundException JavaDoc e) {
459             printError(file.toString(), e);
460             System.exit(1);
461         } catch (GrammarException e) {
462             printInternalError(e);
463             System.exit(2);
464         } catch (ParseException e) {
465             printError(file.toString(), e);
466             System.exit(1);
467         }
468     }
469
470     /**
471      * Parses the specified file with the grammar.
472      *
473      * @param grammar the grammar to use
474      * @param file the file to parse
475      */

476     private static void parse(Grammar grammar, File JavaDoc file) {
477         Tokenizer tokenizer;
478         Analyzer analyzer;
479         Parser parser;
480
481         try {
482             tokenizer = grammar.createTokenizer(new FileReader JavaDoc(file));
483             analyzer = new TreePrinter(System.out);
484             parser = grammar.createParser(tokenizer, analyzer);
485             System.out.println("Parse tree from " + file + ":");
486             parser.parse();
487         } catch (FileNotFoundException JavaDoc e) {
488             printError(file.toString(), e);
489             System.exit(1);
490         } catch (GrammarException e) {
491             printInternalError(e);
492             System.exit(2);
493         } catch (ParserCreationException e) {
494             printInternalError(e);
495             System.exit(2);
496         } catch (ParserLogException e) {
497             printError(file.toString(), e);
498             System.exit(1);
499         }
500     }
501
502     /**
503      * Parses the specified file with the grammar and prints
504      * profiling information.
505      *
506      * @param grammar the grammar to use
507      * @param file the file to parse
508      */

509     private static void profile(Grammar grammar, File JavaDoc file) {
510         Tokenizer tokenizer;
511         Parser parser;
512         Node node;
513         long time;
514         int counter;
515
516         // Profile tokenizer
517
try {
518             tokenizer = grammar.createTokenizer(new FileReader JavaDoc(file));
519             System.out.println("Tokenizing " + file);
520             time = System.currentTimeMillis();
521             counter = 0;
522             while (tokenizer.next() != null) {
523                 counter++;
524             }
525             time = System.currentTimeMillis() - time + 1;
526             System.out.println(" Time elapsed: " + time + " millisec");
527             System.out.println(" Tokens found: " + counter);
528             System.out.println(" Average speed: " + (counter / time) +
529                                " tokens/millisec");
530             System.out.println();
531         } catch (FileNotFoundException JavaDoc e) {
532             printError(file.toString(), e);
533             System.exit(1);
534         } catch (GrammarException e) {
535             printInternalError(e);
536             System.exit(2);
537         } catch (ParseException e) {
538             printError(file.toString(), e);
539             System.exit(1);
540         }
541
542         // Profile parser
543
try {
544             tokenizer = grammar.createTokenizer(new FileReader JavaDoc(file));
545             parser = grammar.createParser(tokenizer);
546             System.out.println("Parsing " + file);
547             time = System.currentTimeMillis();
548             node = parser.parse();
549             time = System.currentTimeMillis() - time + 1;
550             counter = 1 + node.getDescendantCount();
551             System.out.println(" Time elapsed: " + time + " millisec");
552             System.out.println(" Nodes found: " + counter);
553             System.out.println(" Average speed: " + (counter / time) +
554                                " nodes/millisec");
555             System.out.println();
556         } catch (FileNotFoundException JavaDoc e) {
557             printError(file.toString(), e);
558             System.exit(1);
559         } catch (GrammarException e) {
560             printInternalError(e);
561             System.exit(2);
562         } catch (ParserCreationException e) {
563             printInternalError(e);
564             System.exit(2);
565         } catch (ParserLogException e) {
566             printError(file.toString(), e);
567             System.exit(1);
568         }
569     }
570
571     /**
572      * Parses the command-line arguments and generates the Java source
573      * code for a parser.
574      *
575      * @param args the command-line arguments
576      * @param grammar the grammar to use
577      */

578     private static void writeJavaCode(String JavaDoc[] args, Grammar grammar) {
579         JavaParserGenerator gen = new JavaParserGenerator(grammar);
580
581         // Read command-line arguments
582
for (int i = 1; i < args.length; i++) {
583             if (args[i].equals("--javaoutput")) {
584                 gen.setBaseDir(new File JavaDoc(args[++i]));
585             } else if (args[i].equals("--javapackage")) {
586                 gen.setBasePackage(args[++i]);
587             } else if (args[i].equals("--javaclassname")) {
588                 gen.setBaseName(args[++i]);
589             } else if (args[i].equals("--javapublic")) {
590                 gen.setPublicAccess(true);
591             } else {
592                 printHelp("unrecognized option: " + args[i]);
593                 System.exit(1);
594             }
595         }
596
597         // Write parser source code
598
try {
599             System.out.println("Writing Java parser source code...");
600             gen.write();
601             System.out.println("Done.");
602         } catch (IOException JavaDoc e) {
603             printError(e);
604             System.exit(1);
605         }
606     }
607
608     /**
609      * Parses the command-line arguments and generates the C# source
610      * code for a parser.
611      *
612      * @param args the command-line arguments
613      * @param grammar the grammar to use
614      */

615     private static void writeCSharpCode(String JavaDoc[] args, Grammar grammar) {
616         CSharpParserGenerator gen = new CSharpParserGenerator(grammar);
617
618         // Read command-line arguments
619
for (int i = 1; i < args.length; i++) {
620             if (args[i].equals("--csoutput")) {
621                 gen.setBaseDir(new File JavaDoc(args[++i]));
622             } else if (args[i].equals("--csnamespace")) {
623                 gen.setNamespace(args[++i]);
624             } else if (args[i].equals("--csclassname")) {
625                 gen.setBaseName(args[++i]);
626             } else if (args[i].equals("--cspublic")) {
627                 gen.setPublicAccess(true);
628             } else {
629                 printHelp("unrecognized option: " + args[i]);
630                 System.exit(1);
631             }
632         }
633
634         // Write parser source code
635
try {
636             System.out.println("Writing C# parser source code...");
637             gen.write();
638             System.out.println("Done.");
639         } catch (IOException JavaDoc e) {
640             printError(e);
641             System.exit(1);
642         }
643     }
644
645     /**
646      * Parses the command-line arguments and generates the Visual
647      * Basic source code for a parser.
648      *
649      * @param args the command-line arguments
650      * @param grammar the grammar to use
651      */

652     private static void writeVisualBasicCode(String JavaDoc[] args, Grammar grammar) {
653         VisualBasicParserGenerator gen;
654
655         // Read command-line arguments
656
gen = new VisualBasicParserGenerator(grammar);
657         for (int i = 1; i < args.length; i++) {
658             if (args[i].equals("--vboutput")) {
659                 gen.setBaseDir(new File JavaDoc(args[++i]));
660             } else if (args[i].equals("--vbnamespace")) {
661                 gen.setNamespace(args[++i]);
662             } else if (args[i].equals("--vbclassname")) {
663                 gen.setBaseName(args[++i]);
664             } else if (args[i].equals("--vbpublic")) {
665                 gen.setPublicAccess(true);
666             } else {
667                 printHelp("unrecognized option: " + args[i]);
668                 System.exit(1);
669             }
670         }
671
672         // Write parser source code
673
try {
674             System.out.println("Writing Visual Basic parser source code...");
675             gen.write();
676             System.out.println("Done.");
677         } catch (IOException JavaDoc e) {
678             printError(e);
679             System.exit(1);
680         }
681     }
682 }
683
Popular Tags