/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Anya Helene Bagge - anya@ii.uib.no (Univ. Bergen)
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.parser;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.List;
import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.interpreter.Configuration;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.asserts.ImplementationError;
import org.rascalmpl.interpreter.control_exceptions.Throw;
import org.rascalmpl.interpreter.env.GlobalEnvironment;
import org.rascalmpl.interpreter.env.ModuleEnvironment;
import org.rascalmpl.interpreter.load.StandardLibraryContributor;
import org.rascalmpl.interpreter.utils.JavaBridge;
import org.rascalmpl.interpreter.utils.Profiler;
import org.rascalmpl.parser.gtd.IGTD;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.values.ValueFactoryFactory;
import org.rascalmpl.values.uptr.ITree;
public class ParserGenerator {
private final Evaluator evaluator;
private final JavaBridge bridge;
private final IValueFactory vf;
private static final String packageName = "org.rascalmpl.java.parser.object";
private static final boolean debug = false;
public ParserGenerator(IRascalMonitor monitor, PrintWriter out, List<ClassLoader> loaders, IValueFactory factory, Configuration config) {
GlobalEnvironment heap = new GlobalEnvironment();
ModuleEnvironment scope = new ModuleEnvironment("$parsergenerator$", heap);
this.evaluator = new Evaluator(ValueFactoryFactory.getValueFactory(), out, out, scope,heap);
this.evaluator.getConfiguration().setRascalJavaClassPathProperty(config.getRascalJavaClassPathProperty());
this.evaluator.getConfiguration().setGeneratorProfiling(config.getGeneratorProfilingProperty());
evaluator.addRascalSearchPathContributor(StandardLibraryContributor.getInstance());
this.evaluator.setBootstrapperProperty(true);
this.bridge = new JavaBridge(loaders, factory, config);
this.vf = factory;
monitor.startJob("Loading parser generator", 100, 139);
try {
evaluator.doImport(monitor, "lang::rascal::grammar::ParserGenerator");
evaluator.doImport(monitor, "lang::rascal::grammar::ConcreteSyntax");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Modules");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Priorities");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Regular");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Keywords");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Literals");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Parameters");
evaluator.doImport(monitor, "lang::rascal::grammar::definition::Symbols");
evaluator.doImport(monitor, "Ambiguity");
}
finally {
monitor.endJob(true);
}
}
public void setGeneratorProfiling(boolean f) {
evaluator.getConfiguration().setGeneratorProfiling(f);
}
public IValue diagnoseAmbiguity(IConstructor parseForest) {
return evaluator.call("diagnose", parseForest);
}
/**
* Generate a parser from a Rascal syntax definition (a set of production rules).
*
* @param monitor a progress monitor; this method will contribute 100 work units
* @param loc a location for error reporting
* @param name the name of the parser for use in code generation and for later reference
* @param imports a set of syntax definitions (which are imports in the Rascal grammar)
* @return
*/
public Class<IGTD<IConstructor, IConstructor, ISourceLocation>> getParser(IRascalMonitor monitor, ISourceLocation loc, String name, IMap definition) {
monitor.startJob("Generating parser:" + name, 100, 90);
try {
monitor.event("Importing and normalizing grammar:" + name, 30);
IConstructor grammar = getGrammar(monitor, name, definition);
debugOutput(grammar, System.getProperty("java.io.tmpdir") + "/grammar.trm");
String normName = name.replaceAll("::", "_");
monitor.event("Generating java source code for parser: " + name,30);
IString classString = (IString) evaluator.call(monitor, "generateObjectParser", vf.string(packageName), vf.string(normName), grammar);
debugOutput(classString.getValue(), System.getProperty("java.io.tmpdir") + "/parser.java");
monitor.event("Compiling generated java code: " + name, 30);
return bridge.compileJava(loc, packageName + "." + normName, classString.getValue());
} catch (ClassCastException e) {
throw new ImplementationError("parser generator:" + e.getMessage(), e);
} catch (Throw e) {
throw new ImplementationError("parser generator: " + e.getMessage() + e.getTrace());
} finally {
monitor.endJob(true);
}
}
private void debugOutput(Object thing, String file) {
if (debug) {
String classString = thing.toString();
FileOutputStream s = null;
try {
System.err.println("Writing parser to " + file);
s = new FileOutputStream(file);
s.write(classString.getBytes());
s.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (s != null) {
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public IConstructor getGrammar(IRascalMonitor monitor, String main, IMap definition) {
return (IConstructor) evaluator.call(monitor, "modules2grammar", vf.string(main), definition);
}
public IConstructor getExpandedGrammar(IRascalMonitor monitor, String main, IMap definition) {
IConstructor g = getGrammar(monitor, main, definition);
monitor.event("Expanding keywords", 10);
g = (IConstructor) evaluator.call(monitor, "expandKeywords", g);
monitor.event("Adding regular productions",10);
g = (IConstructor) evaluator.call(monitor, "makeRegularStubs", g);
monitor.event("Expanding regulars", 10);
g = (IConstructor) evaluator.call(monitor, "expandRegularSymbols", g);
monitor.event("Expanding parametrized symbols");
g = (IConstructor) evaluator.call(monitor, "expandParameterizedSymbols", g);
monitor.event("Defining literals");
g = (IConstructor) evaluator.call(monitor, "literals", g);
return g;
}
public ISet getNestingRestrictions(IRascalMonitor monitor,
IConstructor g) {
return (ISet) evaluator.call(monitor, "doNotNest", g);
}
/**
* Produces the name generated by the parser generator for a parse method for the given symbol
*/
public String getParserMethodName(IConstructor symbol) {
return ((IString) evaluator.call((IRascalMonitor) null, "getParserMethodName", symbol)).getValue();
}
/**
* Converts the parse tree of a symbol to a UPTR symbol
*/
public IConstructor symbolTreeToSymbol(IConstructor symbol) {
return (IConstructor) evaluator.call((IRascalMonitor) null,"sym2symbol", symbol);
}
/**
* Generate a parser from a Rascal syntax definition (a set of production rules).
*
* @param monitor a progress monitor; this method will contribute 100 work units
* @param loc a location for error reporting
* @param name the name of the parser for use in code generation and for later reference
* @param definition a map of syntax definitions (which are imports in the Rascal grammar)
* @return A parser class, ready for instantiation
*/
public Class<IGTD<IConstructor, ITree, ISourceLocation>> getNewParser(IRascalMonitor monitor, ISourceLocation loc, String name, IMap definition) {
monitor.startJob("Generating parser:" + name, 100, 130);
Profiler profiler = evaluator.getConfiguration().getGeneratorProfilingProperty() ? new Profiler(evaluator) : null;
try {
monitor.event("Importing and normalizing grammar:" + name, 30);
if (profiler != null) {
profiler.start();
}
IConstructor grammar = getGrammar(monitor, name, definition);
debugOutput(grammar, System.getProperty("java.io.tmpdir") + "/grammar.trm");
return getNewParser(monitor, loc, name, grammar);
} catch (ClassCastException e) {
throw new ImplementationError("parser generator:" + e.getMessage(), e);
} catch (Throw e) {
throw new ImplementationError("parser generator: " + e.getMessage() + e.getTrace());
} finally {
monitor.endJob(true);
if (profiler != null) {
profiler.pleaseStop();
evaluator.getStdOut().println("PROFILE:");
profiler.report();
profiler = null;
}
}
}
/**
* Generate a parser from a Rascal grammar.
*
* @param monitor a progress monitor; this method will contribute 100 work units
* @param loc a location for error reporting
* @param name the name of the parser for use in code generation and for later reference
* @param grammar a grammar
* @return A parser class, ready for instantiation
*/
public Class<IGTD<IConstructor, ITree, ISourceLocation>> getNewParser(IRascalMonitor monitor, ISourceLocation loc, String name, IConstructor grammar) {
monitor.startJob("Generating parser:" + name, 100, 60);
try {
String normName = name.replaceAll("::", "_").replaceAll("\\\\", "_");
monitor.event("Generating java source code for parser: " + name,30);
IString classString = (IString) evaluator.call(monitor, "newGenerate", vf.string(packageName), vf.string(normName), grammar);
debugOutput(classString, System.getProperty("java.io.tmpdir") + "/parser.java");
monitor.event("Compiling generated java code: " + name, 30);
return bridge.compileJava(loc, packageName + "." + normName, classString.getValue());
} catch (ClassCastException e) {
throw new ImplementationError("parser generator:" + e.getMessage(), e);
} catch (Throw e) {
throw new ImplementationError("parser generator: " + e.getMessage() + e.getTrace());
} finally {
monitor.endJob(true);
}
}
/**
* Save a generated parser class to a jar file
*
* @param parserClass The parser class
* @param outStream An output stream
* @throws IOException on IO error
*/
public void saveToJar(Class<IGTD<IConstructor, ITree, ISourceLocation>> parserClass, OutputStream outStream) throws IOException {
bridge.saveToJar("", parserClass, StandAloneParser.class, outStream, false);
}
public String createHole(IConstructor part, int size) {
return ((IString) evaluator.call("createHole", part, vf.integer(size))).getValue();
}
}