net.intelie.pipes.util.AnalyzingCompilerContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pipes-api Show documentation
Show all versions of pipes-api Show documentation
Intelie Pipes' API classes and interfaces
package net.intelie.pipes.util;
import net.intelie.pipes.*;
import net.intelie.pipes.Module;
import net.intelie.pipes.ast.AstNode;
import net.intelie.pipes.ast.CallNode;
import net.intelie.pipes.ast.SourceLocation;
import net.intelie.pipes.filters.Filter;
import net.intelie.pipes.types.Metadata;
import net.intelie.pipes.types.RowFields;
import java.util.*;
public class AnalyzingCompilerContext extends CompilerContext.Forwarding {
private final Map objs;
private final Map exceptions;
private final Analyzer analyzer;
public AnalyzingCompilerContext(CompilerContext context, Analyzer analyzer) {
super(context);
this.analyzer = analyzer;
this.objs = new IdentityHashMap<>();
this.exceptions = new IdentityHashMap<>();
}
public CompilationResult get(AstNode node) {
return objs.get(node);
}
public PipeException getException(AstNode node) {
return exceptions.get(node);
}
private AnalyzingCompilerContext(CompilerContext context, CompilerContext forwarding, Analyzer analyzer,
Map compiled, Map exceptions) {
super(context, forwarding);
this.analyzer = analyzer;
this.objs = compiled;
this.exceptions = exceptions;
}
@Override
public Forwarding wrap(CompilerContext context, CompilerContext forward) {
return new AnalyzingCompilerContext(context, forward, analyzer, objs, exceptions);
}
@Override
public CompilationResult compile(AstNode node) throws PipeException {
try {
CompilationResult result = delegate().compile(node);
objs.put(node, result);
return result;
} catch (PipeException ex) {
exceptions.put(node, ex);
throw ex;
}
}
public static List analyze(PipeCompiler compiler, String query) {
return analyze(AnalyzingCompilerContext::defaultAnalyzer, compiler, query);
}
public static List analyze(Analyzer analyzer, PipeCompiler compiler, String query) {
try {
AnalyzingCompilerContext context = new AnalyzingCompilerContext(compiler.getContext(), analyzer);
PipeCompiler newCompiler = compiler.withContext(context);
AstNode node = newCompiler.parse(query);
try {
newCompiler.compile(node);
return context.visitNode(node);
} catch (PipeException e) {
List nodes = context.visitNode(node);
nodes.addAll(visitException("markerror", nodes.size(), e));
return nodes;
}
} catch (PipeException e) {
List nodes = visitException("error", 0, e);
nodes.addAll(visitException("markerror", nodes.size(), e));
return nodes;
}
}
private static List visitException(String type, int offset, PipeException e) {
ArrayList list = new ArrayList<>();
visitException(type, list, e);
for (int i = 0; i < list.size(); i++) {
NodeInfo info = list.get(i);
if (i > 0)
info.down = info.right = offset + i - 1;
if (i + 1 < list.size())
info.up = info.left = offset + i + 1;
}
return list;
}
private static void visitException(String headerType, List list, PipeException ex) {
String type = ex.level().toString().toLowerCase(Locale.ROOT);
list.add(buildInfo(ex.removedLocation(), headerType,
String.format((Locale) null, "error\n%s\n\n%s", type, ex.getMessage())));
for (PipeException cause : ex.causes())
visitException(headerType, list, cause);
}
public List visitNode(AstNode node) {
List list = new ArrayList<>();
visitNode(list, new ArrayList<>(), 0, node, -1);
for (NodeInfo info : list) {
if (info.left < 0) info.left = info.up;
if (info.right < 0) info.right = info.up;
}
return list;
}
private void visitNode(List list, List levels, int level, AstNode node, int parent) {
while (level >= levels.size())
levels.add(-1);
int left = levels.get(level);
NodeInfo info = buildInfo(node);
if (info != null) {
int index = list.size();
list.add(info);
if (parent >= 0) {
list.get(info.up = parent).setDownIfNone(index);
}
if (left >= 0)
list.get(info.left = left).setRightIfNone(index);
levels.set(level, index);
parent = index;
}
if (node instanceof CallNode) {
List args = new ArrayList<>(((CallNode) node).getArgs());
args.sort(Comparator.comparing(x -> x.getLocation().getPosition()));
for (AstNode arg : args)
visitNode(list, levels, level + (info != null ? 1 : 0), arg, parent);
}
}
public NodeInfo buildInfo(AstNode node) {
SourceLocation location = node.getLocation();
if (location == null || location.getLength() == 0) return null;
SourceLocation.Type type = location.getType();
if (type == null || type == SourceLocation.Type.NONE) return null;
return analyzer.analyze(node, get(node), getException(node));
}
public static NodeInfo defaultAnalyzer(AstNode node, CompilationResult result, PipeException ex) {
List lines = new ArrayList<>();
Object obj = result != null ? result.result() : null;
String type;
if (ex != null) {
lines.add(type = "error");
lines.add(ex.level().toString().toLowerCase(Locale.ROOT));
lines.add("");
lines.add(ex.getMessage());
} else if (obj == null) {
lines.add(type = "uncompiled");
lines.add(Classes.toStringClassOf(node));
lines.add("This AST node was never compiled directly, so we cannot display any type information about it.");
lines.add(String.valueOf(node));
} else if (obj instanceof Expression) {
Expression expr = (Expression) obj;
type = expr.level().cleanName(false);
lines.add(expr.level().cleanName(true));
lines.add(functionDescription(result));
lines.add(expr.type().toString());
lines.add(String.format((Locale) null,
"weight: %s / ttl: %s",
expr.weight(), expr.ttl()));
lines.add(compiledRepr(obj));
} else if (obj instanceof Pipe) {
Pipe pipe = (Pipe) obj;
Metadata metadata = pipe.metadata();
lines.add(type = "pipe");
lines.add(functionDescription(result));
RowFields fields = metadata.getRowFields();
if (fields != null) {
lines.add((fields.group().size() > 0 ? "[" + fields.group() + "] " : "") + fields.select());
} else {
lines.add("raw type: " + metadata.type().toString());
}
String windowStr = metadata.window().toString();
if (!"".equals(windowStr))
lines.add("window: " + windowStr);
lines.add("output: " + Iterables.join(", ", metadata.output()));
lines.add("weight: " + metadata.weight());
if (!metadata.custom().isEmpty())
lines.add("custom: " + metadata.custom());
lines.add(compiledRepr(obj));
} else if (obj instanceof Module) {
lines.add(type = "module");
lines.add(functionDescription(result));
List functionList = ListModule.makeReprList(((Module) obj).functions());
lines.add(functionList.size() + " function" + (functionList.size() != 1 ? "s" : ""));
lines.add(Iterables.join("\n", functionList));
} else if (obj instanceof Function) {
Function function = (Function) obj;
lines.add(type = "function");
lines.add(functionDescription(result));
lines.add(function.description());
lines.add(function.help().usage());
} else if (obj instanceof Filter) {
lines.add(type = "filter");
lines.add(functionDescription(result));
lines.add(obj.toString());
} else {
lines.add(type = "object");
lines.add(functionDescription(result));
lines.add(Classes.toStringClassOf(obj));
lines.add(compiledRepr(obj));
}
SourceLocation location = node.getLocation();
return new NodeInfo(type,
location.getBeginLine(), location.getBeginColumn(),
location.getEndLine(), location.getEndColumn(),
Iterables.join("\n", lines));
}
private static String compiledRepr(Object obj) {
return "compiled: " + truncate(String.valueOf(obj), 45, 15);
}
public static String functionDescription(CompilationResult result) {
String repr = result.function() != null ?
result.function().description() :
"literal";
return truncate(repr, 30, 10);
}
private static String truncate(String repr, int before, int after) {
if (repr.length() > before + after + 3)
return repr.substring(0, before) + "..." + repr.substring(repr.length() - after);
return repr;
}
private static NodeInfo buildInfo(SourceLocation location, String type, String body) {
if (location == null)
return new NodeInfo(type,
1, 1, 1, 1,
body);
return new NodeInfo(type,
location.getBeginLine(), location.getBeginColumn(),
location.getEndLine(), location.getEndColumn(),
body);
}
public interface Analyzer {
NodeInfo analyze(AstNode node, CompilationResult result, PipeException exception);
}
public static class NodeInfo {
private final String type;
private final int beginLine;
private final int beginColumn;
private final int endLine;
private final int endColumn;
private final String body;
private int left = -1;
private int right = -1;
private int up = -1;
private int down = -1;
public NodeInfo(String type, int beginLine, int beginColumn, int endLine, int endColumn, String body) {
this.type = type;
this.beginLine = beginLine;
this.beginColumn = beginColumn;
this.endLine = endLine;
this.endColumn = endColumn;
this.body = body;
}
public void setDownIfNone(int index) {
if (down < 0) down = index;
}
public void setRightIfNone(int index) {
if (right < 0) right = index;
}
}
}