/*******************************************************************************
* Copyright (c) 2009-2016 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.library.experiments.Compiler.RVM.Interpreter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.interpreter.Evaluator; // TODO: remove import: YES
import org.rascalmpl.interpreter.control_exceptions.Throw; // TODO: remove import: LATER
import org.rascalmpl.interpreter.env.GlobalEnvironment; // TODO: remove import: YES
import org.rascalmpl.interpreter.env.ModuleEnvironment; // TODO: remove import: YES
import org.rascalmpl.interpreter.load.StandardLibraryContributor; // remove import: NO
import org.rascalmpl.interpreter.types.RascalTypeFactory;
import org.rascalmpl.interpreter.utils.JavaBridge; // remove import: NO
import org.rascalmpl.parser.gtd.IGTD;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.RascalValueFactory;
public class ParserGenerator {
private Evaluator evaluator;
private final JavaBridge bridge;
private final IValueFactory vf;
private final TypeFactory tf;
private static RVMCore rvmParserGenerator;
private Function getParserMethodNameFunction;
private Function newGenerateFunction;
private Function createHoleFunction;
private static final String packageName = "org.rascalmpl.java.parser.object";
private static final boolean debug = false;
private static final boolean useCompiledParserGenerator = true;
static {
}
public ParserGenerator(RascalExecutionContext rex) throws IOException {
this.vf = rex.getValueFactory();
this.tf = TypeFactory.getInstance();
this.bridge = new JavaBridge(rex.getClassLoaders(), rex.getValueFactory(), rex.getConfiguration());
if(useCompiledParserGenerator && rvmParserGenerator == null) {
RascalExecutionContext rex2 =
RascalExecutionContextBuilder.normalContext(rex.getPathConfig(), System.out, System.err)
.forModule("$parsergenerator$")
.jvm(true) // options for complete repl
.build();
rvmParserGenerator = RVMCore.readFromFileAndInitialize(rex.getParserGenerator(), rex2);
}
if(useCompiledParserGenerator){
Type symSort = RascalTypeFactory.getInstance().nonTerminalType(vf.constructor(RascalValueFactory.Symbol_Sort, vf.string("Sym")));
getParserMethodNameFunction = rvmParserGenerator.getFunction("getParserMethodName", tf.stringType(), tf.tupleType(symSort));
if(getParserMethodNameFunction == null){
throw new CompilerError("Function getParserMethodName not found");
}
newGenerateFunction = rvmParserGenerator.getFunction("newGenerate", tf.stringType(),
tf.tupleType(tf.stringType(), tf.stringType(),
tf.abstractDataType(rex.getTypeStore(), "Grammar")));
if(newGenerateFunction == null){
throw new CompilerError("Function newGenerate not found");
}
Type cpLex = RascalTypeFactory.getInstance().nonTerminalType(vf.constructor(RascalValueFactory.Symbol_Lex, vf.string("ConcretePart")));
createHoleFunction = rvmParserGenerator.getFunction("createHole", tf.stringType(),
tf.tupleType(cpLex, tf.integerType()));
if(createHoleFunction == null){
throw new CompilerError("Function createHole not found");
}
} else {
GlobalEnvironment heap = new GlobalEnvironment();
ModuleEnvironment scope = new ModuleEnvironment("$parsergenerator$", heap);
this.evaluator = new Evaluator(ValueFactoryFactory.getValueFactory(), rex.getStdOut(), rex.getStdErr(), scope, heap);
evaluator.addRascalSearchPathContributor(StandardLibraryContributor.getInstance()); this.evaluator.setBootstrapperProperty(true);
IRascalMonitor monitor = rex.getMonitor();
monitor.startJob("Compiled -- Loading interpreted parser generator, 2", 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);
}
}
}
private void debugOutput(String classString, String file) {
if (debug) {
System.err.println("Saving debug output in " + file);
try (FileOutputStream s = new FileOutputStream(file)) {
s.write(classString.getBytes());
s.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public IConstructor convertMapToGrammar(IMap definition) {
TypeFactory TF = TypeFactory.getInstance();
TypeStore TS = RascalValueFactory.getStore(); //new TypeStore();
Type Grammar = TF.abstractDataType(TS, "Grammar");
Type Symbol = TF.abstractDataType(TS, "Symbol");
Type Production = TF.abstractDataType(TS, "Production");
Type grammar = TF.constructor(TS, Grammar, "grammar", TF.setType(Symbol), "starts", TF.mapType(Symbol, Production), "rules");
return vf.constructor(grammar, vf.set(), definition);
}
/**
* Produces the name generated by the parser generator for a parse method for the given symbol
* @param rex TODO
*/
public String getParserMethodName(IConstructor symbol, RascalExecutionContext rex) {
if(useCompiledParserGenerator){
return ((IString) rvmParserGenerator.executeRVMFunction(getParserMethodNameFunction, new IValue[]{ symbol }, new HashMap<String, IValue>())).getValue();
} else {
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)
* @param rex TODO
* @return A parser class, ready for instantiation
*/
public Class<IGTD<IConstructor, ITree, ISourceLocation>> getNewParser(IRascalMonitor monitor, ISourceLocation loc, String name, IMap definition, RascalExecutionContext rex) {
return getNewParser(loc, name, convertMapToGrammar(definition), rex);
}
/**
* Generate a parser from a Rascal grammar.
* @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
* @param rex TODO
*
* @return A parser class, ready for instantiation
*/
public Class<IGTD<IConstructor, ITree, ISourceLocation>> getNewParser(ISourceLocation loc, String name, IConstructor grammar, RascalExecutionContext rex) {
rex.startJob("Compiled -- Generating new parser:" + name, 100, 60);
try {
String normName = name.replaceAll("::", "_").replaceAll("\\\\", "_");
rex.event("Generating java source code for parser: " + name,30);
IString classString;
if(useCompiledParserGenerator){
classString = (IString) rvmParserGenerator.executeRVMFunction(newGenerateFunction, new IValue[]{ vf.string(packageName), vf.string(normName), grammar }, new HashMap<String, IValue>());
} else {
classString = (IString) evaluator.call(rex.getMonitor(), "newGenerate", vf.string(packageName), vf.string(normName), grammar);
}
debugOutput(grammar.toString(), System.getProperty("java.io.tmpdir") + "/grammar.trm");
debugOutput(classString.getValue(), System.getProperty("java.io.tmpdir") + "/parser.java");
rex.event("Compiling generated java code: " + name, 30);
return bridge.compileJava(loc, packageName + "." + normName, this.getClass(), classString.getValue());
} catch (ClassCastException e) {
throw new CompilerError("parser generator:" + e.getMessage(), e);
} catch (Throw e) {
throw new CompilerError("parser generator: " + e.getMessage(), e);
} catch (Thrown e) {
throw new CompilerError("parser generator: " + e.getMessage(), e);
} catch (Throwable e) {
throw new CompilerError("parser generator: " + e.getMessage(), e);
} finally {
rex.endJob(true);
}
}
public String createHole(IConstructor part, int size, RascalExecutionContext rex) {
if(useCompiledParserGenerator){
return ((IString) rvmParserGenerator.executeRVMFunction(createHoleFunction, new IValue[]{ part, vf.integer(size) }, new HashMap<String, IValue>())).getValue();
} else {
return ((IString) evaluator.call("createHole", part, vf.integer(size))).getValue();
}
}
}