/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* JFlex 1.5 *
* Copyright (C) 1998-2014 Gerwin Klein <lsf@jflex.de> *
* All rights reserved. *
* *
* License: BSD *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package jflex;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jflex.gui.MainFrame;
import jflex.unicode.UnicodeProperties;
/**
* This is the main class of JFlex controlling the scanner generation process.
* It is responsible for parsing the commandline, getting input files,
* starting up the GUI if necessary, etc.
*
* @author Gerwin Klein
* @version JFlex 1.5, $Revision: 799 $, $Date: 2014-03-21 16:42:55 +0100 (Fri, 21 Mar 2014) $
*/
public class Main {
/** JFlex version */
final public static String version = "1.5.1"; //$NON-NLS-1$
/**
* Generates a scanner for the specified input file.
*
* @param inputFile a file containing a lexical specification
* to generate a scanner for.
*/
public static void generate(File inputFile) {
Out.resetCounters();
Timer totalTime = new Timer();
Timer time = new Timer();
LexScan scanner = null;
LexParse parser = null;
FileReader inputReader = null;
totalTime.start();
try {
Out.println(ErrorMessages.READING, inputFile.toString());
inputReader = new FileReader(inputFile);
scanner = new LexScan(inputReader);
scanner.setFile(inputFile);
parser = new LexParse(scanner);
}
catch (FileNotFoundException e) {
Out.error(ErrorMessages.CANNOT_OPEN, inputFile.toString());
throw new GeneratorException();
}
try {
NFA nfa = (NFA) parser.parse().value;
Out.checkErrors();
if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.NFA_IS)+
Out.NL+nfa+Out.NL);
if (Options.dot)
nfa.writeDot(Emitter.normalize("nfa.dot", null)); //$NON-NLS-1$
Out.println(ErrorMessages.NFA_STATES, nfa.numStates);
time.start();
DFA dfa = nfa.getDFA();
time.stop();
Out.time(ErrorMessages.DFA_TOOK, time);
dfa.checkActions(scanner, parser);
nfa = null;
if (Options.dump) Out.dump(ErrorMessages.get(ErrorMessages.DFA_IS)+
Out.NL+dfa+Out.NL);
if (Options.dot)
dfa.writeDot(Emitter.normalize("dfa-big.dot", null)); //$NON-NLS-1$
Out.checkErrors();
time.start();
dfa.minimize();
time.stop();
Out.time(ErrorMessages.MIN_TOOK, time);
if (Options.dump)
Out.dump(ErrorMessages.get(ErrorMessages.MIN_DFA_IS)+
Out.NL+dfa);
if (Options.dot)
dfa.writeDot(Emitter.normalize("dfa-min.dot", null)); //$NON-NLS-1$
time.start();
Emitter e = new Emitter(inputFile, parser, dfa);
e.emit();
time.stop();
Out.time(ErrorMessages.WRITE_TOOK, time);
totalTime.stop();
Out.time(ErrorMessages.TOTAL_TIME, totalTime);
}
catch (ScannerException e) {
Out.error(e.file, e.message, e.line, e.column);
throw new GeneratorException();
}
catch (MacroException e) {
Out.error(e.getMessage());
throw new GeneratorException();
}
catch (IOException e) {
Out.error(ErrorMessages.IO_ERROR, e.toString());
throw new GeneratorException();
}
catch (OutOfMemoryError e) {
Out.error(ErrorMessages.OUT_OF_MEMORY);
throw new GeneratorException();
}
catch (GeneratorException e) {
throw new GeneratorException();
}
catch (Exception e) {
e.printStackTrace();
throw new GeneratorException();
}
}
public static List<File> parseOptions(String argv[]) throws SilentExit {
List<File> files = new ArrayList<File>();
for (int i = 0; i < argv.length; i++) {
if ( argv[i].equals("-d") || argv[i].equals("--outdir") ) { //$NON-NLS-1$ //$NON-NLS-2$
if ( ++i >= argv.length ) {
Out.error(ErrorMessages.NO_DIRECTORY);
throw new GeneratorException();
}
Options.setDir(argv[i]);
continue;
}
if ( argv[i].equals("--skel") || argv[i].equals("-skel") ) { //$NON-NLS-1$ //$NON-NLS-2$
if ( ++i >= argv.length ) {
Out.error(ErrorMessages.NO_SKEL_FILE);
throw new GeneratorException();
}
Options.setSkeleton(new File(argv[i]));
continue;
}
if ( argv[i].equals("-jlex") || argv[i].equals("--jlex") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.jlex = true;
continue;
}
if ( argv[i].equals("-v") || argv[i].equals("--verbose") || argv[i].equals("-verbose") ) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Options.verbose = true;
Options.progress = true;
continue;
}
if ( argv[i].equals("-q") || argv[i].equals("--quiet") || argv[i].equals("-quiet") ) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Options.verbose = false;
Options.progress = false;
continue;
}
if ( argv[i].equals("--dump") || argv[i].equals("-dump") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.dump = true;
continue;
}
if ( argv[i].equals("--time") || argv[i].equals("-time") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.time = true;
continue;
}
if ( argv[i].equals("--version") || argv[i].equals("-version") ) { //$NON-NLS-1$ //$NON-NLS-2$
Out.println(ErrorMessages.THIS_IS_JFLEX, version);
throw new SilentExit();
}
if ( argv[i].equals("--dot") || argv[i].equals("-dot") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.dot = true;
continue;
}
if ( argv[i].equals("--help") || argv[i].equals("-h") || argv[i].equals("/h") ) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
printUsage();
throw new SilentExit();
}
if ( argv[i].equals("--info") || argv[i].equals("-info") ) { //$NON-NLS-1$ //$NON-NLS-2$
Out.printSystemInfo();
throw new SilentExit();
}
if ( argv[i].equals("--nomin") || argv[i].equals("-nomin") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.no_minimize = true;
continue;
}
if ( argv[i].equals("--pack") || argv[i].equals("-pack") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.gen_method = Options.PACK;
continue;
}
if ( argv[i].equals("--table") || argv[i].equals("-table") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.gen_method = Options.TABLE;
continue;
}
if ( argv[i].equals("--switch") || argv[i].equals("-switch") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.gen_method = Options.SWITCH;
continue;
}
if ( argv[i].equals("--nobak") || argv[i].equals("-nobak") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.no_backup = true;
continue;
}
if ( argv[i].equals("--legacydot") || argv[i].equals("-legacydot") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.legacy_dot = true;
continue;
}
// TODO: In the JFlex version after 1.6, --inputstreamctor will be removed.
if ( argv[i].equals("--inputstreamctor") || argv[i].equals("-inputstreamctor") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.emitInputStreamCtor = true;
continue;
}
// TODO: In the JFlex version after 1.6, --noinputstreamctor will be removed.
if ( argv[i].equals("--noinputstreamctor") || argv[i].equals("-noinputstreamctor") ) { //$NON-NLS-1$ //$NON-NLS-2$
Options.emitInputStreamCtor = false;
continue;
}
if ( argv[i].equals("--uniprops") || argv[i].equals("-uniprops") ) { //$NON-NLS-1$ //$NON-NLS-2$
if ( ++i >= argv.length ) {
Out.error(ErrorMessages.PROPS_ARG_REQUIRES_UNICODE_VERSION,
UnicodeProperties.UNICODE_VERSIONS);
throw new GeneratorException();
}
String unicodeVersion = argv[i];
try {
printUnicodePropertyValuesAndAliases(unicodeVersion);
} catch (UnicodeProperties.UnsupportedUnicodeVersionException e) {
Out.error(ErrorMessages.UNSUPPORTED_UNICODE_VERSION_SUPPORTED_ARE,
UnicodeProperties.UNICODE_VERSIONS);
throw new GeneratorException();
}
throw new SilentExit();
}
if ( argv[i].startsWith("-") ) { //$NON-NLS-1$
Out.error(ErrorMessages.UNKNOWN_COMMANDLINE, argv[i]);
printUsage();
throw new SilentExit();
}
// if argv[i] is not an option, try to read it as file
File f = new File(argv[i]);
if ( f.isFile() && f.canRead() )
files.add(f);
else {
Out.error("Sorry, couldn't open \""+f+"\""); //$NON-NLS-2$
throw new GeneratorException();
}
}
return files;
}
/**
* Prints one Unicode property value per line, along with its aliases, if any, for
* the given unicodeVersion.
*
* @param unicodeVersion The Unicode version to print property values and aliases for
* @throws UnicodeProperties.UnsupportedUnicodeVersionException if unicodeVersion
* is not supported
*/
private static void printUnicodePropertyValuesAndAliases(String unicodeVersion)
throws UnicodeProperties.UnsupportedUnicodeVersionException {
Pattern versionPattern = Pattern.compile("(\\d+)(?:\\.(\\d+))?(?:\\.\\d+)?");
Matcher matcher = versionPattern.matcher(unicodeVersion);
if (!matcher.matches()) {
throw new UnicodeProperties.UnsupportedUnicodeVersionException();
}
String underscoreVersion
= matcher.group(1) + (null == matcher.group(2) ? "_0" : "_" + matcher.group(2));
String[] propertyValues;
String[] propertyValueAliases;
try {
Class<?> clazz = Class.forName("jflex.unicode.data.Unicode_" + underscoreVersion);
Field field = clazz.getField("propertyValues");
propertyValues = (String[])field.get(null);
field = clazz.getField("propertyValueAliases");
propertyValueAliases = (String[])field.get(null);
} catch (Exception e) {
throw new UnicodeProperties.UnsupportedUnicodeVersionException();
}
SortedMap<String,SortedSet<String>> propertyValuesToAliases
= new TreeMap<String, SortedSet<String>>();
for (String value : propertyValues) {
propertyValuesToAliases.put(value, new TreeSet<String>());
}
for (int i = 0 ; i < propertyValueAliases.length ; i += 2) {
String alias = propertyValueAliases[i];
String value = propertyValueAliases[i + 1];
SortedSet<String> aliases = propertyValuesToAliases.get(value);
if (null == aliases) {
aliases = new TreeSet<String>();
propertyValuesToAliases.put(value, aliases);
}
aliases.add(alias);
}
for (Map.Entry<String,SortedSet<String>> entry : propertyValuesToAliases.entrySet()) {
String value = entry.getKey();
SortedSet<String> aliases = entry.getValue();
Out.print(value);
if (aliases.size() > 0) {
for (String alias : aliases) {
Out.print(", " + alias);
}
}
Out.println("");
}
}
public static void printUsage() {
Out.println(""); //$NON-NLS-1$
Out.println("Usage: jflex <options> <input-files>");
Out.println("");
Out.println("Where <options> can be one or more of");
Out.println("-d <directory> write generated file to <directory>");
Out.println("--skel <file> use external skeleton <file>");
Out.println("--switch (DEPRECATED - will be removed in JFlex 1.6)");
Out.println("--table (DEPRECATED - will be removed in JFlex 1.6)");
Out.println("--pack set default code generation method (default)");
Out.println("--jlex strict JLex compatibility");
Out.println("--legacydot dot (.) metachar matches [^\\n] instead of");
Out.println(" [^\\n\\r\\u000B\\u000C\\u0085\\u2028\\u2029]");
Out.println("--inputstreamctor include a scanner constructor taking InputStream (default)");
Out.println("--noinputstreamctor don't include a scanner constructor taking InputStream");
Out.println("--nomin skip minimization step");
Out.println("--nobak don't create backup files");
Out.println("--dump display transition tables");
Out.println("--dot write graphviz .dot files for the generated automata (alpha)");
Out.println("--verbose");
Out.println("-v display generation progress messages (default)");
Out.println("--quiet");
Out.println("-q display errors only");
Out.println("--time display generation time statistics");
Out.println("--version print the version number of this copy of jflex");
Out.println("--info print system + JDK information");
Out.println("--uniprops <ver> print all supported properties for Unicode version <ver>");
Out.println("--help");
Out.println("-h print this message");
Out.println("");
Out.println(ErrorMessages.THIS_IS_JFLEX, version);
Out.println("Have a nice day!");
}
public static void generate(String argv[]) throws SilentExit {
List<File> files = parseOptions(argv);
if (files.size() > 0) {
for (File file : files)
generate(file);
}
else {
new MainFrame();
}
}
/**
* Starts the generation process with the files in <code>argv</code> or
* pops up a window to choose a file, when <code>argv</code> doesn't have
* any file entries.
*
* @param argv the commandline.
*/
public static void main(String argv[]) {
try {
generate(argv);
}
catch (GeneratorException e) {
Out.statistics();
System.exit(1);
}
catch (SilentExit e) {
System.exit(1);
}
}
}