From 900fb0aee0b68cbb88f578f20019738b4416f436 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Mon, 4 Nov 2019 10:27:29 +0100 Subject: Add query language interpreter code --- .../mimuw/cloudatlas/interpreter/Interpreter.java | 493 +++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Interpreter.java (limited to 'src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Interpreter.java') diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Interpreter.java b/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Interpreter.java new file mode 100644 index 0000000..4634459 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Interpreter.java @@ -0,0 +1,493 @@ +/** + * Copyright (c) 2014, University of Warsaw + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package pl.edu.mimuw.cloudatlas.interpreter; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import pl.edu.mimuw.cloudatlas.interpreter.query.PrettyPrinter; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.AliasedSelItemC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.AscOrderC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExpr; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExprAddC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExprDivC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExprModC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExprMulC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExprNegC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BasicExprSubC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BoolExpr; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BoolExprBasicExprC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BoolExprCmpC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.BoolExprRegExpC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.CondExpr; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.CondExprAndC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.CondExprBoolExprC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.CondExprNotC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.CondExprOrC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.DescOrderC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EBoolC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.ECondExprC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EDblC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EFunC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EIdentC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EIntC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EStmtC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.EStrC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.NoNullsC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.NoOrderByC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.NoOrderC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.NoWhereC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.NullFirstsC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Nulls; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.NullsLastC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Order; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.OrderBy; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.OrderByC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.OrderItem; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.OrderItemC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Program; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.ProgramC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOp; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOpEqC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOpGeC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOpGtC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOpLeC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOpLtC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.RelOpNeC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.SelItem; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.SelItemC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Statement; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.StatementC; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Where; +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.WhereC; +import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.TypePrimitive; +import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueBoolean; +import pl.edu.mimuw.cloudatlas.model.ValueDouble; +import pl.edu.mimuw.cloudatlas.model.ValueInt; +import pl.edu.mimuw.cloudatlas.model.ValueString; +import pl.edu.mimuw.cloudatlas.model.ZMI; + +public class Interpreter { + private static class ValuesPair { + public final Result left; + public final Result right; + + public ValuesPair(Result left, Result right) { + this.left = left; + this.right = right; + } + } + + private final ZMI zmi; + + public Interpreter(ZMI zmi) { + this.zmi = zmi; + } + + private static Boolean getBoolean(Value value) { + if(value.getType().isCompatible(TypePrimitive.BOOLEAN)) { + Boolean b = ((ValueBoolean)value).getValue(); + return b == null? false : b.booleanValue(); + } + throw new InvalidTypeException(TypePrimitive.BOOLEAN, value.getType()); + } + + public List interpretProgram(Program program) { + return program.accept(new ProgramInterpreter(), zmi); + } + + public List interpretStatement(Statement statement) { + return statement.accept(new StatementInterpreter(), zmi); + } + + public class ProgramInterpreter implements Program.Visitor, ZMI> { + public List visit(ProgramC program, ZMI zmi) { + List results = new ArrayList(); + for(Statement s : program.liststatement_) { + try { + List l = s.accept(new StatementInterpreter(), zmi); + for(QueryResult qr : l) + if(qr.getName() == null) + throw new IllegalArgumentException("All items in top-level SELECT must be aliased."); + results.addAll(l); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(s), exception); + } + } + return results; + } + } + + public class StatementInterpreter implements Statement.Visitor, ZMI> { + public List visit(StatementC statement, ZMI zmi) { + Table table = new Table(zmi); + try { + table = statement.where_.accept(new WhereInterpreter(), table); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(statement.where_), exception); + } + try { + table = statement.orderby_.accept(new OrderByInterpreter(), table); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(statement.orderby_), exception); + } + List ret = new ArrayList(); + + for(SelItem selItem : statement.listselitem_) { + try { + QueryResult qr = selItem.accept(new SelItemInterpreter(), table); + if(qr.getName() != null) { + for(QueryResult qrRet : ret) + if(qr.getName().getName().equals(qrRet.getName().getName())) + throw new IllegalArgumentException("Alias collision."); + } + ret.add(qr); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(selItem), exception); + } + } + + return ret; + } + } + + public class WhereInterpreter implements Where.Visitor { + public Table visit(NoWhereC where, Table table) { + return table; + } + + public Table visit(WhereC where, Table table) { + Table result = new Table(table); + for(TableRow row : table) { + Environment env = new Environment(row, table.getColumns()); + Value value = where.condexpr_.accept(new CondExprInterpreter(), env).getValue(); + if(getBoolean(value)) + result.appendRow(row); + } + return result; + } + } + + public class OrderByInterpreter implements OrderBy.Visitor { + public Table visit(NoOrderByC orderBy, Table table) { + return table; + } + + public Table visit(OrderByC orderBy, Table table) { + for(OrderItem item : orderBy.listorderitem_) { + try { + table = item.accept(new OrderItemInterpreter(), table); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(item), exception); + } + } + return table; + } + } + + public class OrderItemInterpreter implements OrderItem.Visitor { + public Table visit(final OrderItemC orderItem, final Table table) { + Comparator comparator = new Comparator() { + @Override + public int compare(TableRow row1, TableRow row2) { + Environment env1 = new Environment(row1, table.getColumns()); + Result expr1 = orderItem.condexpr_.accept(new CondExprInterpreter(), env1); + Environment env2 = new Environment(row2, table.getColumns()); + Result expr2 = orderItem.condexpr_.accept(new CondExprInterpreter(), env2); + ValuesPair pair = new ValuesPair(expr1, expr2); + int result = orderItem.nulls_.accept(new NullsInterpreter(), pair); + if(result == 0) + result = orderItem.order_.accept(new OrderInterpreter(), pair); + return result; + } + }; + table.sort(comparator); + return table; + } + } + + public class OrderInterpreter implements Order.Visitor { + private int compareAsc(ValuesPair pair) { + if(getBoolean(pair.left.isEqual(pair.right).getValue())) + return 0; + if(getBoolean(pair.left.isLowerThan(pair.right).getValue())) + return -1; + return 1; + } + + public Integer visit(AscOrderC order, ValuesPair pair) { + return compareAsc(pair); + } + + public Integer visit(DescOrderC order, ValuesPair pair) { + return -compareAsc(pair); + } + + public Integer visit(NoOrderC order, ValuesPair pair) { + return compareAsc(pair); + } + } + + public class NullsInterpreter implements Nulls.Visitor { + private Integer nullsFirst(ValuesPair pair) { + if(pair.left.getValue().isNull()) { + if(pair.right.getValue().isNull()) + return 0; + return -1; + } + if(pair.right.getValue().isNull()) + return 1; + return 0; + } + + public Integer visit(NoNullsC nulls, ValuesPair pair) { + return nullsFirst(pair); + } + + public Integer visit(NullFirstsC nulls, ValuesPair pair) { + return nullsFirst(pair); + } + + public Integer visit(NullsLastC nulls, ValuesPair pair) { + return -nullsFirst(pair); + } + } + + public class SelItemInterpreter implements SelItem.Visitor { + public QueryResult visit(SelItemC selItem, Table table) { + Environment env = null; // TODO + Result result = selItem.condexpr_.accept(new CondExprInterpreter(), env); + return new QueryResult(result.getValue()); + } + + public QueryResult visit(AliasedSelItemC selItem, Table table) { + Environment env = null; // TODO + Result result = selItem.condexpr_.accept(new CondExprInterpreter(), env); + return new QueryResult(new Attribute(selItem.qident_), result.getValue()); + } + } + + public class BoolExprInterpreter implements BoolExpr.Visitor { + public Result visit(BoolExprCmpC expr, Environment env) { + try { + Result left = expr.basicexpr_1.accept(new BasicExprInterpreter(), env); + Result right = expr.basicexpr_2.accept(new BasicExprInterpreter(), env); + return expr.relop_.accept(new RelOpInterpreter(), new ValuesPair(left, right)); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(BoolExprRegExpC expr, Environment env) { + try { + Result left = expr.basicexpr_.accept(new BasicExprInterpreter(), env); + return (new ResultSingle(new ValueString(expr.string_))).regExpr(left); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(BoolExprBasicExprC expr, Environment env) { + return expr.basicexpr_.accept(new BasicExprInterpreter(), env); + } + } + + public class CondExprInterpreter implements CondExpr.Visitor { + public Result visit(CondExprOrC expr, Environment env) { + try { + Result left = expr.condexpr_1.accept(new CondExprInterpreter(), env); + Result right = expr.condexpr_2.accept(new CondExprInterpreter(), env); + return left.or(right); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(CondExprAndC expr, Environment env) { + // TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + public Result visit(CondExprNotC expr, Environment env) { + try { + return expr.condexpr_.accept(new CondExprInterpreter(), env).negate(); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(CondExprBoolExprC expr, Environment env) { + return expr.boolexpr_.accept(new BoolExprInterpreter(), env); + } + } + + public class BasicExprInterpreter implements BasicExpr.Visitor { + public Result visit(BasicExprAddC expr, Environment env) { + try { + Result left = expr.basicexpr_1.accept(new BasicExprInterpreter(), env); + Result right = expr.basicexpr_2.accept(new BasicExprInterpreter(), env); + return left.addValue(right); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(BasicExprSubC expr, Environment env) { + try { + Result left = expr.basicexpr_1.accept(new BasicExprInterpreter(), env); + Result right = expr.basicexpr_2.accept(new BasicExprInterpreter(), env); + return left.subtract(right); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(BasicExprMulC expr, Environment env) { + // TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + public Result visit(BasicExprDivC expr, Environment env) { + try { + Result left = expr.basicexpr_1.accept(new BasicExprInterpreter(), env); + Result right = expr.basicexpr_2.accept(new BasicExprInterpreter(), env); + return left.divide(right); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(BasicExprModC expr, Environment env) { + try { + Result left = expr.basicexpr_1.accept(new BasicExprInterpreter(), env); + Result right = expr.basicexpr_2.accept(new BasicExprInterpreter(), env); + return left.modulo(right); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(BasicExprNegC expr, Environment env) { + try { + return expr.basicexpr_.accept(new BasicExprInterpreter(), env).negate(); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public Result visit(EIdentC expr, Environment env) { + return env.getIdent(expr.qident_); + } + + public Result visit(EFunC expr, Environment env) { + try { + List arguments = new ArrayList(expr.listcondexpr_.size()); + for(CondExpr arg : expr.listcondexpr_) + arguments.add(arg.accept(new CondExprInterpreter(), env)); + + return Functions.getInstance().evaluate(expr.qident_, arguments); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + + public ResultSingle visit(EStrC expr, Environment env) { + return new ResultSingle(new ValueString(expr.string_)); + } + + public ResultSingle visit(EBoolC expr, Environment env) { + ValueBoolean value; + if(expr.qbool_.compareTo("true") == 0) + value = new ValueBoolean(true); + else if(expr.qbool_.compareTo("false") == 0) + value = new ValueBoolean(false); + else + throw new InternalInterpreterException("Incorrect boolean constant: " + PrettyPrinter.print(expr)); + return new ResultSingle(value); + } + + public ResultSingle visit(EIntC expr, Environment env) { + try { + return new ResultSingle(new ValueInt(Long.parseLong(expr.qinteger_))); + } catch(NumberFormatException exception) { + throw new InternalInterpreterException(exception.getMessage()); + } + } + + public ResultSingle visit(EDblC expr, Environment env) { + try { + return new ResultSingle(new ValueDouble(Double.parseDouble(expr.qdouble_))); + } catch(NumberFormatException exception) { + throw new InternalInterpreterException(exception.getMessage()); + } + } + + public Result visit(ECondExprC expr, Environment env) { + return expr.condexpr_.accept(new CondExprInterpreter(), env); + } + + public ResultSingle visit(EStmtC expr, Environment env) { + try { + List l = expr.statement_.accept(new StatementInterpreter(), zmi); + if(l.size() != 1) + throw new IllegalArgumentException("Nested queries must SELECT exactly one item."); + return new ResultSingle(l.get(0).getValue()); + } catch(Exception exception) { + throw new InsideQueryException(PrettyPrinter.print(expr), exception); + } + } + } + + public class RelOpInterpreter implements RelOp.Visitor { + public Result visit(RelOpGtC op, ValuesPair pair) { + return pair.left.isLowerThan(pair.right).negate().and(pair.left.isEqual(pair.right).negate()); + } + + public Result visit(RelOpEqC op, ValuesPair pair) { + // TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + public Result visit(RelOpNeC op, ValuesPair pair) { + return pair.left.isEqual(pair.right).negate(); + } + + public Result visit(RelOpLtC op, ValuesPair pair) { + return pair.left.isLowerThan(pair.right); + } + + public Result visit(RelOpLeC op, ValuesPair pair) { + // TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + public Result visit(RelOpGeC op, ValuesPair pair) { + return pair.left.isLowerThan(pair.right).negate(); + } + } +} -- cgit v1.2.3