All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.intelie.pipes.util.AnalyzingCompilerContext Maven / Gradle / Ivy

There is a newer version: 0.25.5
Show newest version
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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy