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

co.streamx.fluent.extree.expression.Interpreter Maven / Gradle / Ivy

The newest version!
package co.streamx.fluent.extree.expression;

import static co.streamx.fluent.extree.function.Functions.add;
import static co.streamx.fluent.extree.function.Functions.and;
import static co.streamx.fluent.extree.function.Functions.bitwiseAnd;
import static co.streamx.fluent.extree.function.Functions.bitwiseNot;
import static co.streamx.fluent.extree.function.Functions.bitwiseOr;
import static co.streamx.fluent.extree.function.Functions.constant;
import static co.streamx.fluent.extree.function.Functions.divide;
import static co.streamx.fluent.extree.function.Functions.equal;
import static co.streamx.fluent.extree.function.Functions.greaterThan;
import static co.streamx.fluent.extree.function.Functions.greaterThanOrEqual;
import static co.streamx.fluent.extree.function.Functions.iif;
import static co.streamx.fluent.extree.function.Functions.instanceOf;
import static co.streamx.fluent.extree.function.Functions.lessThan;
import static co.streamx.fluent.extree.function.Functions.lessThanOrEqual;
import static co.streamx.fluent.extree.function.Functions.modulo;
import static co.streamx.fluent.extree.function.Functions.multiply;
import static co.streamx.fluent.extree.function.Functions.negate;
import static co.streamx.fluent.extree.function.Functions.not;
import static co.streamx.fluent.extree.function.Functions.or;
import static co.streamx.fluent.extree.function.Functions.shiftLeft;
import static co.streamx.fluent.extree.function.Functions.shiftRight;
import static co.streamx.fluent.extree.function.Functions.subtract;
import static co.streamx.fluent.extree.function.Functions.xor;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 *
 */

final class Interpreter implements ExpressionVisitor> {

    static final Interpreter Instance = new Interpreter();
    private static final Object[] emptyArray = new Object[0];

    private Interpreter() {
    }

    private Function normalize(BiFunction source) {
        return pp -> source.apply(pp, pp);
    }

    private Function normalize(BiPredicate source) {
        return pp -> source.test(pp, pp);
    }

    private Function normalize(Predicate source) {
        return pp -> source.test(pp);
    }

    // https://stackoverflow.com/questions/3473756/java-convert-primitive-class/17836370
    private static final Class[] wrappers = {Integer.class, Double.class, Byte.class, Boolean.class,
            Character.class, Void.class, Short.class, Float.class, Long.class};

    @SuppressWarnings("unchecked")
    private static  Class wrap(final Class clazz) {
        if (!clazz.isPrimitive())
            return clazz;
        final String name = clazz.getName();
        final int c0 = name.charAt(0);
        final int c2 = name.charAt(2);
        final int mapper = (c0 + c0 + c0 + 5) & (118 - c2);
        return (Class) wrappers[mapper];
    }

    @SuppressWarnings("unchecked")
    @Override
    public Function visit(BinaryExpression e) {
        final Function first = e.getFirst().accept(this);
        final Function second = e.getSecond().accept(this);
        switch (e.getExpressionType()) {
            case ExpressionType.Add:
                return normalize(add((Function) first, (Function) second));
            case ExpressionType.BitwiseAnd:
                return normalize(bitwiseAnd((Function) first, (Function) second));
            case ExpressionType.LogicalAnd:
                return normalize(and((Function) first, (Function) second));
            case ExpressionType.ArrayIndex:
                return t -> Array.get(first.apply(t), (Integer) second.apply(t));
            // return new Function() {
            // // @Override
            // public Object invoke(Object[] t) throws Throwable {
            // return Array.get(first.invoke(t), (Integer) second
            // .invoke(t));
            // }
            // };
            // case ExpressionType.Coalesce:
            // return coalesce((Function) first,
            // (Function) second);
            case ExpressionType.Conditional:
                return iif((Function) e.getOperator().accept(this), first, second);
            case ExpressionType.Divide:
                return normalize(divide((Function) first, (Function) second));
            case ExpressionType.Equal:
                return normalize(equal(first, second));
            case ExpressionType.ExclusiveOr:
                return normalize(xor((Function) first, (Function) second));
            case ExpressionType.GreaterThan:
                return normalize(
                        greaterThan((Function) first, (Function) second));
            case ExpressionType.GreaterThanOrEqual:
                return normalize(greaterThanOrEqual((Function) first,
                        (Function) second));
            case ExpressionType.LeftShift:
                return normalize(shiftLeft((Function) first, (Function) second));
            case ExpressionType.LessThan:
                return normalize(lessThan((Function) first, (Function) second));
            case ExpressionType.LessThanOrEqual:
                return normalize(
                        lessThanOrEqual((Function) first, (Function) second));
            case ExpressionType.Modulo:
                return normalize(modulo((Function) first, (Function) second));
            case ExpressionType.Multiply:
                return normalize(multiply((Function) first, (Function) second));
            case ExpressionType.NotEqual:
                return normalize(equal(first, second).negate());
            case ExpressionType.BitwiseOr:
                return normalize(bitwiseOr((Function) first, (Function) second));
            case ExpressionType.LogicalOr:
                return normalize(or((Function) first, (Function) second));
            // case ExpressionType.Power:
            // return power((Function) first,
            // (Function) second);
            case ExpressionType.RightShift:
                return normalize(shiftRight((Function) first, (Function) second));
            case ExpressionType.Subtract:
                return normalize(subtract((Function) first, (Function) second));
            case ExpressionType.InstanceOf:
                return normalize(instanceOf(first, (Class) second.apply(null)));
            default:
                throw new IllegalArgumentException(ExpressionType.toString(e.getExpressionType()));
        }
    }

    @Override
    public Function visit(ConstantExpression e) {
        return constant(e.getValue());
    }

    @Override
    public Function visit(NewArrayInitExpression newArrayInitExpression) {
        List> args = newArrayInitExpression.getInitializers()
                .stream()
                .map(i -> (Function) i.accept(this))
                .collect(Collectors.toList());

        Class componentType = newArrayInitExpression.getComponentType();

        return pp -> {

            Object r = Array.newInstance(componentType, args.size());

            for (int index = 0; index < args.size(); index++) {
                Array.set(r, index, args.get(index).apply(pp));
            }

            return r;
        };
    }

    @Override
    public Function visit(InvocationExpression e) {

        InvocableExpression target = e.getTarget();
        Function m = target.accept(this);
        Function x;
        if (target.getExpressionType() == ExpressionType.Lambda) {
            x = (Object[] pp) -> {
                Function f1 = (Function) m.apply(pp);
                return f1.apply(emptyArray);
            };
        } else {
            x = m;
        }

        int size = e.getArguments().size();
        List> ppe = new ArrayList<>(size);
        for (Expression p : e.getArguments())
            ppe.add(p.accept(this));

        Function params = pp -> {

            if (target.getExpressionType() == ExpressionType.FieldAccess)
                return pp; // field: no arguments, just the instance

            Object[] r = new Object[ppe.size()];
            int index = 0;
            for (Function pe : ppe) {
                r[index++] = pe.apply(pp);
            }

            // for MethodAccess we need both outer and inner scope arguments
            return (target.getExpressionType() == ExpressionType.MethodAccess
                    || target.getExpressionType() == ExpressionType.Delegate) ? new Object[]{pp, r} : r;
        };

        return x.compose(params);
    }

    @Override
    public Function visit(LambdaExpression e) {

        Function f = e.getBody().accept(this);

        List locals = e.getLocals();
        int size = locals.size();
        if (size > 0) {
            List> ple = new ArrayList<>(size);
            for (Expression p : locals)
                ple.add(p != null ? p.accept(this) : null);

            f = f.compose((Object[] pp) -> {
                int originalLength = pp.length;
                pp = Arrays.copyOf(pp, originalLength + size);
                for (int index = 0; index < size; index++) {
                    Function le = ple.get(index);
                    if (le == null)
                        continue;
                    pp[index + originalLength] = le.apply(pp);
                }
                return pp;
            });
        }
        return toClosure(f.compose(visitParameters(e)));
    }

    private static Function toClosure(Function f) {
        return (Function>) (Object[] captured) -> (Object[] p) -> f
                .apply(concat(captured, p));
    }

    private static  T[] concat(T[] first,
                                  T[] second) {
        if (first == null || first.length == 0)
            return second;
        if (second == null || second.length == 0)
            return first;
        T[] result = Arrays.copyOf(first, first.length + second.length);
        System.arraycopy(second, 0, result, first.length, second.length);
        return result;
    }

    private Function visitParameters(InvocableExpression invocable) {
        List parameters = invocable.getParameters();
        int size = parameters.size();
        List> ppe = new ArrayList<>(size);
        for (ParameterExpression p : parameters)
            ppe.add(p.accept(this));

        Function params = pp -> {
            Object[] r = new Object[ppe.size()];
            int index = 0;
            for (Function pe : ppe) {
                r[parameters.get(index++).getIndex()] = pe.apply(pp);
            }
            return r;
        };

        return params;
    }

    @Override
    public Function visit(DelegateExpression e) {
        final Function f = e.getDelegate().accept(this);

        Function params = visitParameters(e);

        return t -> {
            InvocableExpression l = (InvocableExpression) f.apply((Object[]) t[0]);
            Function f1 = (Function) l.accept(this).apply(params.apply((Object[]) t[1]));
            return l.getExpressionType() == ExpressionType.Lambda ? f1.apply(emptyArray) : f1;
        };
    }

    @Override
    public Function visit(BlockExpression e) {

        List> ff = new ArrayList<>();
        for (Expression s : e.getExpressions())
            ff.add(s.accept(this));
        
        return t -> {
            Object result = null;
            for (Function f : ff)
                result = f.apply(t);
            return result;
        };
    }

    @Override
    public Function visit(MemberExpression e) {
        final Member m = e.getMember();

        if (!Modifier.isPublic(m.getModifiers()) && m instanceof AccessibleObject) {
            AccessibleObject ao = (AccessibleObject) m;
            try {
                if (!ao.isAccessible())
                    ao.setAccessible(true);
            } catch (Exception ee) {
                // suppress
            }
        }

        Expression ei = e.getInstance();
        final Function instance = ei != null ? ei.accept(this) : null;

        Function params = visitParameters(e);

        Function field = t -> {
            try {
                return ((Field) m).get(instance == null ? null : instance.apply(t));
            } catch (IllegalArgumentException | IllegalAccessException ex) {
                throw new RuntimeException(ex);
            }
        };

        Function method = t -> {
            Object inst;
            if (instance != null) {
                inst = instance.apply((Object[]) t[0]);
            } else
                inst = null;
            try {
                Object[] pp = params.apply((Object[]) t[1]);
                return ((Method) m).invoke(inst, pp);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                throw new RuntimeException(ex);
            }
        };

        Function ctor = t -> {
            try {
                return ((Constructor) m).newInstance(params.apply(t));
            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                     | InvocationTargetException ex) {
                throw new RuntimeException(ex);
            }
        };

        Function member;

        if (m instanceof Field)
            member = field;
        else if (m instanceof Method)
            member = method;
        else
            member = ctor;

        return member;// .compose(params);
    }

    @Override
    public Function visit(ParameterExpression e) {
        final int index = e.getIndex();
        return t -> t[index];
    }

    @SuppressWarnings("unchecked")
    @Override
    public Function visit(UnaryExpression e) {
        final Function first = e.getFirst().accept(this);
        switch (e.getExpressionType()) {
            case ExpressionType.ArrayLength:
                return t -> Array.getLength(first.apply(t));
            case ExpressionType.BitwiseNot:
                return (Function) bitwiseNot((Function) first);
            case ExpressionType.Convert:
                final Class to = e.getResultType();
                if (to.isPrimitive() || Number.class.isAssignableFrom(to))
                    return t -> {
                        Object source = first.apply(t);
                        if (source instanceof Number) {
                            Number result = (Number) source;
                            if (to.isPrimitive()) {
                                if (to == Integer.TYPE)
                                    return result.intValue();
                                if (to == Long.TYPE)
                                    return result.longValue();
                                if (to == Float.TYPE)
                                    return result.floatValue();
                                if (to == Double.TYPE)
                                    return result.doubleValue();
                                if (to == Byte.TYPE)
                                    return result.byteValue();
                                if (to == Character.TYPE)
                                    return (char) result.intValue();
                                if (to == Short.TYPE)
                                    return result.shortValue();
                            } else if (result != null) {
                                if (to == BigInteger.class)
                                    return BigInteger.valueOf(result.longValue());
                                if (to == BigDecimal.class)
                                    return BigDecimal.valueOf(result.doubleValue());
                            }
                        }
                        if (source instanceof Character) {
                            if (to == Character.TYPE)
                                return (char) source;
                            if (to == Integer.TYPE)
                                return (int) (char) source;
                            if (to == Long.TYPE)
                                return (long) (char) source;
                            if (to == Float.TYPE)
                                return (float) (char) source;
                            if (to == Double.TYPE)
                                return (double) (char) source;
                        }
                        return wrap(to).cast(source);
                    };

                return first;
            case ExpressionType.IsNull:
                return first.andThen(r -> r == null);
            case ExpressionType.IsNonNull:
                return first.andThen(r -> r != null);
            case ExpressionType.LogicalNot:
                return normalize(not((Function) first));
            case ExpressionType.Negate:
                return (Function) negate((Function) first);
            // case ExpressionType.UnaryPlus:
            // return abs((Function) first);
            default:
                throw new IllegalArgumentException(ExpressionType.toString(e.getExpressionType()));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy