/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.redgeek.android.eventrend.synthetic; import java.util.ArrayList; import net.redgeek.android.eventrend.synthetic.Tokenizer.Token; import net.redgeek.android.eventrend.synthetic.Tokenizer.TokenID; import net.redgeek.android.eventrend.util.DateUtil; public class AST { public enum Opcode { GROUPING, PLUS, MINUS, MULTIPLY, DIVIDE, DELTA, } private boolean mValid; private Operation mRoot; private ArrayList<String> mDependents; private Tokenizer mTokenizer; private Tokenizer.Token mError; public AST() { setup(); } public AST(String input) { setup(); generate(input); } private void setup() { mValid = false; mRoot = null; mError = new Tokenizer.Token(); mTokenizer = new Tokenizer(); mDependents = new ArrayList<String>(); } public Operation getRoot() { return mRoot; } public boolean isValid() { return mValid; } public Tokenizer.Token getErrorToken() { return mError; } public ArrayList<String> getDependentNames() { return mDependents; } public boolean generate(String input) { mTokenizer.setInput(input); mRoot = parseString(); if (mRoot != null) mValid = true; return mValid; } public String toString() { if (mRoot == null || mValid == false) return "<parse error>"; return collapseSpaces(mRoot.toString()); } public static String opcodeToString(Opcode opcode) { switch (opcode) { case PLUS: return Tokenizer.PLUS; case MINUS: return Tokenizer.MINUS; case MULTIPLY: return Tokenizer.MULTIPLY; case DIVIDE: return Tokenizer.DIVIDE; case DELTA: return Tokenizer.DELTA; default: return ""; } } public static Opcode tokenToOpcode(TokenID t) { if (t == TokenID.PLUS) return Opcode.PLUS; else if (t == TokenID.MINUS) return Opcode.MINUS; else if (t == TokenID.MULTIPLY) return Opcode.MULTIPLY; else if (t == TokenID.DIVIDE) return Opcode.DIVIDE; else // if (t == TokenID.DELTA) return Opcode.DELTA; } public static interface Operand<T> { void setValue(T value); T getValue(); String toString(); } public static class TimeSeriesOperand implements Operand<String> { private String mCategoryName = null; public TimeSeriesOperand() { } public TimeSeriesOperand(String seriesId) { mCategoryName = seriesId; } public void setValue(String seriesId) { mCategoryName = seriesId; } public String getValue() { return mCategoryName; } public String toString() { return Tokenizer.SERIES + " " + Tokenizer.SERIES_DELIM + Tokenizer.escape(mCategoryName) + Tokenizer.SERIES_DELIM; } } public static class FloatOperand implements Operand<Float> { private Float mValue = null; public FloatOperand() { } public FloatOperand(Float value) { mValue = value; } public void setValue(Float value) { mValue = value; } public Float getValue() { return mValue; } public String toString() { return mValue.toString(); } } public static class LongOperand implements Operand<Long> { private Long mValue = null; public LongOperand() { } public LongOperand(Long value) { mValue = value; } public void setValue(Long value) { mValue = value; } public Long getValue() { return mValue; } public String toString() { return mValue.toString(); } } public static class UnitsOperand implements Operand<DateUtil.Period> { private DateUtil.Period mPeriod = null; public UnitsOperand() { } public UnitsOperand(DateUtil.Period period) { mPeriod = period; } public void setValue(DateUtil.Period period) { mPeriod = period; } public DateUtil.Period getValue() { return mPeriod; } public String toString() { return DateUtil.mapPeriodToString(mPeriod); } } public static class DeltaOperand implements Operand<String> { private String mAxis = null; public DeltaOperand() { } public DeltaOperand(String axis) { mAxis = axis; } public void setValue(String axis) { mAxis = axis; } public String getValue() { return mAxis; } public String toString() { return mAxis; } } public static class GroupOperand implements Operand<Operation> { private Operation mOp = null; public GroupOperand() { } public GroupOperand(Operation op) { mOp = op; } public void setValue(Operation op) { mOp = op; } public Operation getValue() { return mOp; } public String toString() { return Tokenizer.GROUP_START + mOp.toString() + Tokenizer.GROUP_END; } } // ***** Operations ***** // public static interface Operation { Opcode getOpcode(); void setOpcode(Opcode opcode); String toString(); } public static class UnaryOperation<U> implements Operation { private Opcode mOpcode; private U mOperand = null; public UnaryOperation() { } public UnaryOperation(U operand) { mOperand = operand; } public Opcode getOpcode() { return mOpcode; } public U getOperand() { return mOperand; } public void setOpcode(Opcode opcode) { mOpcode = opcode; } public void setOperand(U operand) { mOperand = operand; } public String toString() { return opcodeToString(mOpcode) + " " + mOperand.toString(); } } public static class BinaryOperation<L, R> implements Operation { private Opcode mOpcode; private L mOperandLeft = null; private R mOperandRight = null; public BinaryOperation() { } public BinaryOperation(L left, R right) { mOperandLeft = left; mOperandRight = right; } public Opcode getOpcode() { return mOpcode; } public L getOperandLeft() { return mOperandLeft; } public R getOperandRight() { return mOperandRight; } public void setOpcode(Opcode opcode) { mOpcode = opcode; } public void setOperandLeft(L operand) { mOperandLeft = operand; } public void setOperandRight(R operand) { mOperandRight = operand; } public String toString() { return mOperandLeft.toString() + " " + opcodeToString(mOpcode) + " " + mOperandRight.toString(); } } // ***** Tree Building ***** // private Operation parseString() { Operation expr; Token t = new Tokenizer.Token(); expr = getExpression(t); if (expr == null || t.mTokenID == Tokenizer.TokenID.UNKNOWN) { mError.set(t); return null; } return expr; } private Operation getExpression(Tokenizer.Token out) { Tokenizer.Token op; Operand left, right; left = getOperand(out); if (left == null) return null; op = getOperator(out.mTokenID); out.set(op); if (op.mTokenID == Tokenizer.TokenID.EOF) { UnaryOperation group = new UnaryOperation(); group.setOpcode(Opcode.GROUPING); group.setOperand(left); return group; } if (op.mTokenID == Tokenizer.TokenID.UNKNOWN) return null; right = getOperand(out); if (right == null || out.mTokenID == Tokenizer.TokenID.EOF) return null; out.mTokenID = Tokenizer.TokenID.COMPOSITE; BinaryOperation binop = new BinaryOperation(); binop.setOperandLeft(left); binop.setOpcode(tokenToOpcode(op.mTokenID)); binop.setOperandRight(right); return binop; } private Operand getOperand(Tokenizer.Token out) { Tokenizer.Token t, id, end, paren; t = mTokenizer.getNextToken(); out.set(t); if (out.mTokenID == Tokenizer.TokenID.EOF) return null; if (out.mTokenID == Tokenizer.TokenID.GROUP_START) { Operation subop = getExpression(out); if (subop == null) return null; paren = mTokenizer.getNextToken(); out.set(paren); if (paren.mTokenID != Tokenizer.TokenID.GROUP_END) return null; GroupOperand group = new GroupOperand(); group.setValue(subop); out.mTokenID = Tokenizer.TokenID.COMPOSITE; return group; } else if (out.mTokenID == Tokenizer.TokenID.LONG_VALUE) { return new LongOperand(Long.valueOf(out.mValue)); } else if (out.mTokenID == Tokenizer.TokenID.FLOAT_VALUE) { return new FloatOperand(Float.valueOf(out.mValue)); } else if (out.mTokenID == Tokenizer.TokenID.SERIES) { id = mTokenizer.getNextToken(); out.set(id); if (id.mTokenID != Tokenizer.TokenID.STRING_VALUE) return null; out.mTokenID = Tokenizer.TokenID.SERIES; TimeSeriesOperand ts = new TimeSeriesOperand(id.mValue); mDependents.add(id.mValue); return ts; } else if (out.mTokenID == Tokenizer.TokenID.DELTA_TIMESTAMP || out.mTokenID == Tokenizer.TokenID.DELTA_VALUE) { DeltaOperand prev = new DeltaOperand(out.mValue); return prev; } else if (out.mTokenID == Tokenizer.TokenID.PERIOD_CONSTANT) { DateUtil.Period p = DateUtil.mapStringToPeriod(out.mValue); UnitsOperand units = new UnitsOperand(p); return units; } return null; } private Tokenizer.Token getOperator(TokenID bindingType) { Tokenizer.Token op = mTokenizer.getNextToken(); if (op.mTokenID == Tokenizer.TokenID.EOF) return op; if (bindingType == Tokenizer.TokenID.SERIES) { // series can use the DELTA operator, but not scalars if (op.mTokenID != Tokenizer.TokenID.PLUS && op.mTokenID != Tokenizer.TokenID.MINUS && op.mTokenID != Tokenizer.TokenID.MULTIPLY && op.mTokenID != Tokenizer.TokenID.DIVIDE && op.mTokenID != Tokenizer.TokenID.DELTA) { op.mTokenID = Tokenizer.TokenID.UNKNOWN; } } else if (bindingType == Tokenizer.TokenID.DELTA) { // DELTA takes one of two arguments: if (op.mTokenID != Tokenizer.TokenID.DELTA_TIMESTAMP && op.mTokenID != Tokenizer.TokenID.DELTA_VALUE) { op.mTokenID = Tokenizer.TokenID.UNKNOWN; } } else if (op.mTokenID != Tokenizer.TokenID.PLUS && op.mTokenID != Tokenizer.TokenID.MINUS && op.mTokenID != Tokenizer.TokenID.MULTIPLY && op.mTokenID != Tokenizer.TokenID.DIVIDE) { op.mTokenID = Tokenizer.TokenID.UNKNOWN; } return op; } private String collapseSpaces(String in) { boolean addSpace; int j; char c; String out = ""; for (int inIdx = 0; inIdx < in.length(); inIdx++) { j = inIdx; addSpace = false; c = in.charAt(inIdx); if (c == ' ') { for (j = inIdx; j < in.length(); j++) { c = in.charAt(j); if (c != ' ') { addSpace = true; break; } } } if (j >= in.length()) break; if (inIdx > 0 && addSpace == true) out += ' '; inIdx = j; out += c; } return out; } }