package org.rascalmpl.parser;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.jar.Attributes;
public class StandAloneParser {
private static final String BAD_JAR = "Jar file not correctly set up";
private static final String EXPECTED_ARG = "Expected argument to option";
private static final String NO_PARSER = "Parser not found in jar file";
private static String rascalPath = null;
private static String startSymbol = null;
private static String input = null;
/**
* @param args
* @throws Throwable
*/
public static void main(String[] args) throws Throwable {
URL resource = StandAloneParser.class.getResource("StandAloneParser.class");
if(resource.getProtocol().equals("jar")) {
URLConnection conn = resource.openConnection();
if(conn instanceof JarURLConnection) {
Attributes mainAttributes = ((JarURLConnection) conn).getMainAttributes();
String clsName = mainAttributes.getValue("X-Rascal-Saved-Class");
if(clsName == null)
fatal(BAD_JAR, "No saved parser class");
parseOptions(args);
String classPath = System.getenv("CLASSPATH");
String[] split = classPath != null ? classPath.split(System.getProperty("path.separator")) : new String[0];
URL[] urls = new URL[split.length + 1 + (rascalPath != null ? 1 : 0)];
int i = 0;
if(rascalPath != null)
urls[i++] = new URL("file", null, rascalPath);
for(String path : split) {
urls[i++] = new URL("file", null, path);
}
urls[i++] = ((JarURLConnection) conn).getJarFileURL();
// System.out.println("URLs: " + Arrays.toString(urls));
try(URLClassLoader loader = new URLClassLoader(urls, null)) {
Class<?> clazz = loader.loadClass(clsName);
if(startSymbol == null) {
// attempt to guess start symbol from available classes
for(Class<?> f : clazz.getDeclaredClasses()) {
if(f.getSimpleName().startsWith("start__")) {
startSymbol = f.getSimpleName();
System.err.println("Using start symbol " + startSymbol);
break;
}
}
}
if(startSymbol == null)
fatal("Start symbol required", "--start STARTSYMBOL");
Class<?> INodeFlattener = loader.loadClass("org.rascalmpl.parser.gtd.result.out.INodeFlattener");
Class<?> INodeConstructorFactory = loader.loadClass("org.rascalmpl.parser.gtd.result.out.INodeConstructorFactory");
Class<?> DefaultNodeFlattener = loader.loadClass("org.rascalmpl.parser.gtd.result.out.DefaultNodeFlattener");
Class<?> UPTRNodeFactory = loader.loadClass("org.rascalmpl.parser.uptr.UPTRNodeFactory");
Class<?> ParseError = loader.loadClass("org.rascalmpl.parser.gtd.exception.ParseError");
Class<?> UndeclaredNonTerminalException = loader.loadClass("org.rascalmpl.parser.gtd.exception.UndeclaredNonTerminalException");
Method method = clazz.getMethod("parse", String.class, URI.class, char[].class, INodeFlattener, INodeConstructorFactory);
URI inputURI;
if(input != null)
inputURI = new URI("file", input, null);
else
inputURI = new URI("stdin", "//", null);
try {
Object invoke = method.invoke(clazz.newInstance(), startSymbol, inputURI, getInput(), DefaultNodeFlattener.newInstance(), UPTRNodeFactory.newInstance());
System.out.println(invoke);
}
catch(InvocationTargetException e) {
Throwable cause = e.getCause();
if(ParseError.isInstance(cause)) {
fatal(cause.toString(), null);
}
else if(UndeclaredNonTerminalException.isInstance(cause)) {
fatal(cause.getMessage(), "(perhaps start symbol doesn't exist?)");
}
throw e.getCause();
}
}
catch(NoSuchMethodException | ClassNotFoundException e) {
fatal(NO_PARSER, e.getMessage());
}
}
else {
fatal(BAD_JAR, "Not running from a JAR file");
}
}
else {
fatal(BAD_JAR, "Not running from a JAR file");
}
}
private static char[] getInput() {
InputStream inputStream = null;
try {
try {
if(input != null) {
inputStream = new FileInputStream(input);
}
else {
inputStream = System.in;
}
StringBuilder builder = new StringBuilder();
char[] buffer = new char[8192];
InputStreamReader reader = new InputStreamReader(inputStream);
while(true) {
int n = reader.read(buffer);
if(n == -1)
break;
builder.append(buffer, 0, n);
}
return builder.toString().toCharArray();
}
finally {
if(inputStream != null && inputStream != System.in)
inputStream.close();
}
}
catch (FileNotFoundException e) {
fatal("Input file not found", input);
}
catch (IOException e) {
fatal("I/O error reading input file", e.getMessage());
}
return null; // can't happen
}
private static void parseOptions(String[] args) {
int i = 0;
while(i < args.length) {
switch(args[i]) {
case "-r":
case "--rascal":
if(++i < args.length) {
rascalPath = args[i++];
}
else {
fatal(EXPECTED_ARG, "rascal path");
}
break;
case "-s":
case "--start":
if(++i < args.length) {
startSymbol = args[i++];
}
else {
fatal(EXPECTED_ARG, "start symbol");
}
break;
case "-h":
case "--help":
System.err.println("usage: java -jar parserJarFile [options] inputFile");
System.err.println("options:");
System.err.println(" -r, --rascal PATH give path to Rascal installation (either jar or dir)");
System.err.println(" -s, --start NAME name of start symbol");
System.err.println(" -h, --help print help");
System.exit(0);
break;
default:
if(input != null) {
fatal("name of input file already given", args[i]);
}
else {
input = args[i++];
}
break;
}
}
}
private static void fatal(String msg1, String msg2) {
if(msg2 == null)
System.err.println(msg1);
else
System.err.println(msg1 + ": " + msg2);
System.exit(1);
}
}