JBBPExpressionEvaluator implements JBBPIntegerValueEvaluator { @Override public int eval(final JBBPBitInputStream inStream, final int currentCompiledBlockOffset, final JBBPCompiledBlock compiledBlockData, final JBBPNamedNumericFieldMap fieldMap) { final int[] stack = new int[this.maxStackDepth]; int stackDepth = 0; final JBBPIntCounter counter = new JBBPIntCounter(); while (counter.get() < this.compiledExpression.length) { final int code = this.compiledExpression[counter.getAndIncrement()]; switch (code) { case CODE_EXTVAR: case CODE_VAR: { final int index = JBBPUtils.unpackInt(this.compiledExpression, counter); final int value; if (code == CODE_EXTVAR) { value = "$".equals(this.externalValueNames[index]) ? (int) inStream.getCounter() : fieldMap.getExternalFieldValue(this.externalValueNames[index], compiledBlockData, this); } else { final JBBPNamedFieldInfo namedField = compiledBlockData.getNamedFields()[index]; final JBBPNumericField numericField = fieldMap.get(namedField); if (numericField == null) { throw new java.lang.ArithmeticException("Can't find field '" + namedField.getFieldName() + "' among numeric fields"); } else { value = fieldMap.get(namedField).getAsInt(); } } stack[stackDepth++] = value; } break; case CODE_CONST: { stack[stackDepth++] = JBBPUtils.unpackInt(this.compiledExpression, counter); } break; case CODE_ADD: { final int top = stack[--stackDepth]; stack[stackDepth - 1] += top; } break; case CODE_AND: { final int top = stack[--stackDepth]; stack[stackDepth - 1] &= top; } break; case CODE_OR: { final int top = stack[--stackDepth]; stack[stackDepth - 1] |= top; } break; case CODE_XOR: { final int top = stack[--stackDepth]; stack[stackDepth - 1] ^= top; } break; case CODE_MINUS: { final int top = stack[--stackDepth]; stack[stackDepth - 1] -= top; } break; case CODE_UNARYMINUS: { stack[stackDepth - 1] = -stack[stackDepth - 1]; } break; case CODE_UNARYPLUS: { } break; case CODE_NOT: { stack[stackDepth - 1] = ~stack[stackDepth - 1]; } break; case CODE_DIV: { final int top = stack[--stackDepth]; stack[stackDepth - 1] /= top; } break; case CODE_MUL: { final int top = stack[--stackDepth]; stack[stackDepth - 1] *= top; } break; case CODE_MOD: { final int top = stack[--stackDepth]; stack[stackDepth - 1] %= top; } break; case CODE_LSHIFT: { final int top = stack[--stackDepth]; stack[stackDepth - 1] <<= top; } break; case CODE_RSHIFT: { final int top = stack[--stackDepth]; stack[stackDepth - 1] >>= top; } break; case CODE_RSIGNSHIFT: { final int top = stack[--stackDepth]; stack[stackDepth - 1] >>>= top; } break; default: throw new Error("Detected unsupported operation, contact developer"); } } return stack[0]; } JBBPExpressionEvaluator(final String expression, final List<JBBPNamedFieldInfo> namedFields, final byte[] compiledData); static boolean hasExpressionOperators(final String str); int getMaxStackDepth(); @Override int eval(final JBBPBitInputStream inStream, final int currentCompiledBlockOffset, final JBBPCompiledBlock compiledBlockData, final JBBPNamedNumericFieldMap fieldMap); @Override void visitItems(final JBBPCompiledBlock block, final int currentCompiledBlockOffset, final ExpressionEvaluatorVisitor visitor); @Override String toString();  }
@Test public void testExpression_Error_MulWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123*", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_SubWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123-", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_AddWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123+", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_ModWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123%", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_DivWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123/", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_AndWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123&", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_OrWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123|", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Error_XorWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("123^", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_Constant() { JBBPExpressionEvaluator expr = new JBBPExpressionEvaluator("5678", null, null); assertEquals(5678, expr.eval(null, 0, null, null)); }
@Test public void testExpression_Variable() { final JBBPNamedFieldInfo info = new JBBPNamedFieldInfo("hello", "hello", 0); final byte[] compiled = new byte[] {JBBPCompiler.CODE_INT | JBBPCompiler.FLAG_NAMED}; final List<JBBPNamedFieldInfo> list = Collections.singletonList(info); JBBPExpressionEvaluator expr = new JBBPExpressionEvaluator("hello", list, compiled); final JBBPNamedNumericFieldMap map = new JBBPNamedNumericFieldMap(); map.putField(new JBBPFieldInt(info, 1234)); assertEquals(1234, expr.eval(null, 0, JBBPCompiledBlock.prepare().setCompiledData(compiled).setNamedFieldData(list).setSource("no source").build(), map)); }
@Test public void testExpression_ExpressionWithVariable() { final JBBPNamedFieldInfo info = new JBBPNamedFieldInfo("hello", "hello", 0); final byte[] compiled = new byte[] {JBBPCompiler.CODE_INT | JBBPCompiler.FLAG_NAMED}; final List<JBBPNamedFieldInfo> list = Collections.singletonList(info); JBBPExpressionEvaluator expr = new JBBPExpressionEvaluator("2*hello*6/4+3*2-11%3&hello-~hello", list, compiled); final JBBPNamedNumericFieldMap map = new JBBPNamedNumericFieldMap(); map.putField(new JBBPFieldInt(info, 8)); assertEquals(2 * 8 * 6 / 4 + 3 * 2 - 11 % 3 & 8 - ~8, expr.eval(null, 0, JBBPCompiledBlock.prepare().setCompiledData(compiled).setNamedFieldData(list).setSource("no source").build(), map)); }
@Test public void testExpression_unaryMinus() { JBBPExpressionEvaluator expr = new JBBPExpressionEvaluator("-567", null, null); assertEquals(-567, expr.eval(null, 0, null, null)); }
@Test public void testExpression_LeftShift_ErrorWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("1234<<", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_RightShift_ErrorWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("1234 >>", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_RightSignShift_ErrorWithoutSecondArgument() { assertThrows(JBBPEvalException.class, () -> new JBBPExpressionEvaluator("1234 >>>", null, null).eval(null, 0, null, null)); }
@Test public void testExpression_NotAfterMinus() throws Exception { JBBPExpressionEvaluator expr = new JBBPExpressionEvaluator("60|7-~17%1", null, null); assertEquals(60 | 7 - ~17 % 1, expr.eval(null, 0, null, null)); }