package compiler;
import static util.StringUtils.builderAppend;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import files.RelativeSourcePath;
import compiler.macros.MacroInterface;
import driver.CompilationDriver;
import compiler.java.CompiledClass;
import compiler.java.DynamicJavaCompiler;
import compiler.java.StringJavaFileObject;
import driver.Context;
/**
* A macro compiler turns the user-defined macro expansion code into a class,
* and loads this class in order to be able to expand the defined macro.
*
* TODO Code might be cleaner if using dynamicQuote().
*/
public class MacroCompiler
{
/*****************************************************************************
* The package in which to place macro expander classes.
*/
public static final String MACRO_PKG = "compiler.macros";
/****************************************************************************/
private final DynamicJavaCompiler compiler = DynamicJavaCompiler.get();
/*****************************************************************************
* Same as {@link #compile(String, List, List, String)}, but fills in
* some defaults.
*/
public MacroInterface compile(String macroName, String macroBody)
{
Context ctx = Context.get();
Set<String> captureNames = ctx.captureNames;
return compile(
macroName, ctx.currentFile.imports(), captureNames, macroBody);
}
/*****************************************************************************
* Converts user-defined expansion code into a class. $macroName should be
* the user-defined macro name. $imports should be a list of fully qualified
* classes to import.
*/
private MacroInterface compile(
String macroName,
Collection<String> imports,
Collection<String> captures,
String macroBody)
{
StringBuilder code = new StringBuilder("package " + MACRO_PKG + ";\n\n");
for (String imp : imports) {
builderAppend(code, imp, "\n");
}
code.append("import parser.Match;\n");
code.append("\npublic class ");
code.append(macroName);
code.append("Macro implements MacroInterface {\n ");
code.append("public Match expand(Match input) {\n");
for (String captureName : captures)
{
builderAppend(code,
" Match[] ", captureName,
" = input.getCaptures(\"", captureName, "\");\n");
}
builderAppend(code, macroBody, "\n }\n}");
String name = "compiler.macros." + macroName + "Macro";
List<CompiledClass> compClasses =
compiler.compile(new StringJavaFileObject(
RelativeSourcePath.make(name), code.toString()));
CompiledClass macroCompClass = null;
for (CompiledClass compClass : compClasses) {
if (compClass.name.equals(name)) {
macroCompClass = compClass;
break;
}
}
CompilationDriver.dumpAndOrLoadClass(macroCompClass);
Class<?> klass = macroCompClass.klass();
try {
return (MacroInterface) klass.newInstance();
}
catch (Exception e) {
throw new Error("Error when instantiating macro expansion code for macro: "
+ macroName + ".", e);
}
}
}