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

edu.washington.cs.knowitall.logic.LogicExpression Maven / Gradle / Ivy

package edu.washington.cs.knowitall.logic;

import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;

import com.google.common.base.Function;
import com.google.common.base.Predicate;

import edu.washington.cs.knowitall.logic.Expression.Apply;
import edu.washington.cs.knowitall.logic.Expression.Arg;
import edu.washington.cs.knowitall.logic.Expression.Op;
import edu.washington.cs.knowitall.logic.Expression.Paren;
import edu.washington.cs.knowitall.logic.LogicException.ApplyLogicException;
import edu.washington.cs.knowitall.logic.LogicException.CompileLogicException;
import edu.washington.cs.knowitall.logic.LogicException.TokenizeLogicException;

/**
 * A logic expression engine that operates over user specified objects.
 *
 * @author Michael Schmitz 
 *
 * @param    the type of the base expressions
 */
public class LogicExpression implements Predicate {
    private final Apply expression;

    /***
     *
     * @param input an infix representation of the logic expression.
     * @throws TokenizeLogicException
     * @throws CompileLogicException
     */
    protected LogicExpression(List> expressions)
            throws TokenizeLogicException, CompileLogicException {
        // put in reverse polish notation
        List> rpn = rpn(expressions);

        // compile the expression
        expression = buildAst(rpn);
    }

    /***
     * Compile an infix list of tokens into an expression tree.
     * @param rpn a list of tokens in infix form.
     * @return an expression tree.
     */
    public static  LogicExpression compile(
            final List> expressions) {
        return new LogicExpression(expressions);
    }

    /***
     * Helper factory method to instantiate a LogicExpression.
     * @param  input  The string to parse.
     * @param  factoryDelegate  The factory to build tokens.
     * @return  a new LogicExpression
     */
    public static  LogicExpression compile(final String input,
            final Function> factoryDelegate) {
        return new LogicExpressionParser() {
            @Override
            public Arg factory(String argument) {
                return factoryDelegate.apply(argument);
            }
        }.parse(input);
    }

    @Override
    public String toString() {
        if (this.isEmpty()) {
            return "(empty)";
        }
        else {
            return expression.toString();
        }
    }


    /***
     * If the expression is empty, it returns true for all inputs.
     * @return true iff the expression is empty.
     */
    public boolean isEmpty() {
        return this.expression == null;
    }

    @Override
    public boolean apply(E entity) {
        if (this.isEmpty()) {
            return true;
        }
        else {
            return this.expression.apply(entity);
        }
    }

    /***
     * Compile a rpn list of tokens into an expression tree.
     * @param rpn a list of tokens in infix form.
     * @return an expression tree.
     */
    public static  Apply buildAst(List> rpn) {
        if (rpn.isEmpty()) {
            return null;
        }

        Stack> stack = new Stack>();
        for (Expression tok : rpn) {
            if (tok instanceof Arg) {
                stack.push((Arg) tok);
            } else if (tok instanceof Op) {
                try {
                    if (tok instanceof Op.Mon){
                       Apply sub = stack.pop();

                        Op.Mon mon = (Op.Mon) tok;

                        mon.sub = sub;

                        stack.push(mon);
                    }
                    if (tok instanceof Op.Bin) {
                        Apply arg2 = stack.pop();
                        Apply arg1 = stack.pop();

                        Op.Bin bin = (Op.Bin) tok;

                        bin.left = arg1;
                        bin.right = arg2;

                        stack.push(bin);
                    }
                }
                catch (EmptyStackException e) {
                    throw new CompileLogicException(
                            "No argument for operator (stack empty): "
                            + tok.toString());
                }
            }
        }

        if (stack.size() > 1) {
            throw new ApplyLogicException(
                    "Stack has multiple elements after apply: " + stack.toString());
        }

        if (stack.size() == 0) {
            throw new ApplyLogicException(
                    "Stack has zero elements after apply.");
        }

        if (!(stack.peek() instanceof Apply)) {
            throw new ApplyLogicException(
                    "Stack contains non-appliable tokens after apply: " + stack.toString());
        }

        return (stack.pop());
    }

    /***
     * Return a list of the arguments contained in the expression.
     * @return
     */
    public List getArgs() {
        List args = new ArrayList();
        getArgs(this.expression, args);

        return args;
    }

    /***
     * Private helper method to recursively find arguments.
     * @param apply the expression tree to search.
     * @param args the resulting list of arguments.
     */
    private void getArgs(Apply apply, List args) {
        if (apply instanceof Op.Bin) {
            Op.Bin bin = (Op.Bin) apply;

            getArgs(bin.left, args);
            getArgs(bin.right, args);
        }
        else if (apply instanceof Arg.Pred) {
            args.add(((Arg.Pred)apply).getDescription());
        }
    }

    /***
     * Converts an infix logic representation into a postfix logic representation.
     * @param tokens a list of tokens in infix form.
     * @return a list of tokens in postfix (rpn) form.
     * @throws CompileLogicException
     */
    public List> rpn(List> tokens)
            throws CompileLogicException {
        // intermediate storage
        Stack> stack = new Stack>();

        // final rpn output
        LinkedList> output = new LinkedList>();

        for (Expression tok : tokens) {
            if (tok instanceof Paren.L) {
                stack.push(tok);
            } else if (tok instanceof Paren.R) {
                Expression top;
                do {
                    top = stack.pop();

                    if (!(top instanceof Paren.L)) {
                        output.offer(top);
                    }

                } while (!(top instanceof Paren.L));

            } else if (tok instanceof Op.Mon) {
                stack.push(tok);
            } else if (tok instanceof Op.Bin) {
                // higher precedence
                while (!stack.isEmpty() && stack.peek() instanceof Op
                        && ((Op)stack.peek()).preceeds((Op)tok)) {
                    output.offer(stack.pop());
                }

                stack.push(tok);
            } else if (tok instanceof Arg) {
                output.offer(tok);
            }
        }

        // empty out items remaining ni the stack
        while (!stack.isEmpty()) {
            Expression top = stack.pop();

            if (top instanceof Paren.L || top instanceof Paren.R) {
                throw new CompileLogicException("Unbalanced parentheses.");
            }

            output.offer(top);
        }

        return output;
    }


    /***
     * Iteractively interpret logic statements from stdin such as "true | (true & false)".
     * @param args
     */
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);

        while (scan.hasNextLine()) {
            String line = scan.nextLine();

            LogicExpression expr = LogicExpressionParsers.trivial.parse(line);

            System.out.println("string: " + expr.toString());
            System.out.println("value:  " + expr.apply(null));
            System.out.println();
        }

        scan.close();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy