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

net.intelie.pipes.ast.AstStack Maven / Gradle / Ivy

There is a newer version: 0.25.5
Show newest version
package net.intelie.pipes.ast;

import net.intelie.pipes.generated.PipesTokenManager;
import net.intelie.pipes.types.Type;
import net.intelie.pipes.util.Preconditions;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class AstStack {
    private final ArrayDeque stack = new ArrayDeque<>(128);
    private final Supplier token;

    public AstStack(Supplier token) {
        this.token = token;
    }

    public SourceLocation pop() {
        return pop(get());
    }

    public void pushCall(SourceLocation.Type type, String... expectedFunctions) {
        Preconditions.checkArgument(expectedFunctions.length > 0, "must expect functions");
        internalPush(type, validator(CallNode.class, node -> {
            Preconditions.checkArgument(Arrays.asList(expectedFunctions).contains(node.getName()),
                    "Function not expected here: %s", node.getName());
        }));
    }

    public void pushCallAny(SourceLocation.Type type) {
        internalPush(type, validator(CallNode.class, node -> {
        }));
    }


    public void pushLiteral(SourceLocation.Type type) {
        internalPush(type, validator(LiteralNode.class, node -> {
        }));
    }

    public void pushAny(SourceLocation.Type type) {
        internalPush(type, node -> {
        });
    }


    private  Consumer validator(Class clazz, Consumer consumer) {
        return node -> {
            Preconditions.checkArgument(clazz.isInstance(node), "Must be %s (%s)", clazz, node);
            consumer.accept((T) node);
        };
    }

    private void internalPush(SourceLocation.Type type, Consumer validator) {
        stack.addLast(new Item(
                PipesTokenManager.toLocationBegin(token.get()).withType(type),
                validator));
    }

    public AstNode validate(AstNode node) {
        stack.getLast().validator.accept(node);

        return checkCast(node);
    }

    private AstNode checkCast(AstNode node) {
        //doing this check to make AST string representation recompilation idempotent
        //this way, e.g., null$ always compile to the same AST string
        if (!(node instanceof CallNode))
            return node;
        CallNode call = (CallNode) node;

        if (call.getArgs().size() != 1 || !(call.getArgs().get(0) instanceof LiteralNode))
            return node;
        Object value = ((LiteralNode) call.getArgs().get(0)).getValue();

        if (Type.NUMBER.name().equals(call.getName()))
            return new LiteralNode(node.getLocation(), Type.NUMBER, value);
        if (Type.STRING.name().equals(call.getName()))
            return new LiteralNode(node.getLocation(), Type.STRING, value);

        return node;
    }


    public AstNode fn(String name, List args) {
        return validate(new CallNode(get(), name, args));
    }

    public AstNode fn(String name, AstNode... args) {
        return fn(name, Arrays.asList(args));
    }

    public AstNode fnCopy(String name, AstNode arg) {
        return new CallNode(arg.getLocation().withType(SourceLocation.Type.NONE), name, arg);
    }

    public AstNode fnVirt(String name, AstNode... args) {
        return new CallNode(pointLocation(), name, Arrays.asList(args));
    }

    public AstNode lit(net.intelie.pipes.types.Type type, Object value) {
        return validate(new LiteralNode(get(), type, value));
    }

    public AstNode litVirt(net.intelie.pipes.types.Type type, Object value) {
        return new LiteralNode(pointLocation(), type, value);
    }

    public AstNode rawVirt(Object obj) {
        return new RawNode(pointLocation(), obj);
    }

    public  T pop(T obj) {
        Preconditions.checkState(!stack.isEmpty(), "BUG: ast stack is empty");
        Item removed = stack.removeLast();
        return obj;
    }

    public  T checkEmpty(T obj) {
        Preconditions.checkState(stack.isEmpty(), "BUG: ast stack should be empty");
        return obj;
    }

    public SourceLocation get() {
        SourceLocation begin = stack.getLast().begin;
        SourceLocation end = endLocation();
        if (begin.comesAfter(end))
            return begin;

        return new SourceLocation(begin, end);
    }

    private SourceLocation endLocation() {
        return PipesTokenManager.toLocationEnd(token.get());
    }

    private SourceLocation pointLocation() {
        return PipesTokenManager.toLocationPoint(token.get());
    }

    public AstNode fnPop(String name, List args) {
        return pop(fn(name, args));
    }

    public AstNode fnPop(String name, AstNode... args) {
        return pop(fn(name, args));
    }

    public AstNode litPop(net.intelie.pipes.types.Type type, Object value) {
        return pop(lit(type, value));
    }

    private static class Item {
        private final SourceLocation begin;
        private final Consumer validator;

        public Item(SourceLocation begin, Consumer validator) {
            this.begin = begin;
            this.validator = validator;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy