Show HN: JLSP – A Java Expression Parser

4 months ago 2
addOperator('^', (a, b, extra) -> { double base = Math.abs(a); int sign = 1; if (extra[0] && extra[1]) { if (b != Math.floor(b)) throw new ArithmeticException("cannot raise negative number to fractional power"); if ((long) b % 2 != 0) sign = -sign; } else if (!extra[0] && a < 0) sign = -sign; if (extra[0] && extra[2]) sign = -sign; return sign * Math.pow(base, b);}, 10); addOperator('*', (a, b) -> a * b, 5); addOperator('/', (a, b) -> a / b, 5); addOperator('%', (a, b) -> a % b, 5); addOperator('+', (a, b) -> a + b, 0); addOperator('-', (a, b) -> a - b, 0); addComma('.'); addDelimiter(',');
addFunction("pi", (caller, inOperationOrder, a) -> Math.PI); addFunction("e", (caller, inOperationOrder, a) -> Math.E); addFunction("phi", (caller, inOperationOrder, a) -> (1 + Math.sqrt(5)) / 2); addFunction("tau", (caller, inOperationOrder, a) -> 2 * Math.PI); addFunction("sqrt2", (caller, inOperationOrder, a) -> Math.sqrt(2)); addFunction("sqrt3", (caller, inOperationOrder, a) -> Math.sqrt(3)); addFunction("ln2", (caller, inOperationOrder, a) -> Math.log(2)); addFunction("ln10", (caller, inOperationOrder, a) -> Math.log(10)); addFunction("log2e", (caller, inOperationOrder, a) -> 1 / Math.log(2)); addFunction("log10e", (caller, inOperationOrder, a) -> 1 / Math.log(10)); addFunction("inf", (caller, inOperationOrder, a) -> Double.POSITIVE_INFINITY); addFunction("nan", (caller, inOperationOrder, a) -> Double.NaN);

'B-b-but PI isn't a function'
It is defined like an 0-arg function here for clarity. In theory, with this setup, you could use 1+pi+pi() where simple pi would be translated 1 + p*i + pi() where p and i are two individual separate entities that can be replaced.
'B-b-but isn't that confusing?'
Keen observation. Too bad though. Just use π instead

addFunction("abs", (caller, inOperationOrder, a) -> Math.abs(processEntity(caller, a[0], inOperationOrder))); addFunction("round", (caller, inOperationOrder, a) -> (double) Math.round(processEntity(caller, a[0], inOperationOrder))); addFunction("floor", (caller, inOperationOrder, a) -> Math.floor(processEntity(caller, a[0], inOperationOrder))); addFunction("ceil", (caller, inOperationOrder, a) -> Math.ceil(processEntity(caller, a[0], inOperationOrder))); addFunction("mod", (caller, inOperationOrder, a) -> processEntity(caller, a[0], inOperationOrder) % processEntity(caller, a[1], inOperationOrder)); addFunction("sqrt", (caller, inOperationOrder, a) -> Math.sqrt(processEntity(caller, a[0], inOperationOrder))); addFunction("pow", (caller, inOperationOrder, a) -> Math.pow(processEntity(caller, a[0], inOperationOrder), processEntity(caller, a[1], inOperationOrder))); addFunction("exp", (caller, inOperationOrder, a) -> Math.exp(processEntity(caller, a[0], inOperationOrder))); addFunction("log", (caller, inOperationOrder, a) -> Math.log(processEntity(caller, a[0], inOperationOrder))); addFunction("log10", (caller, inOperationOrder, a) -> Math.log10(processEntity(caller, a[0], inOperationOrder))); addFunction("log2", (caller, inOperationOrder, a) -> Math.log(processEntity(caller, a[0], inOperationOrder)) / Math.log(2)); addFunction("sin", (caller, inOperationOrder, a) -> Math.sin(processEntity(caller, a[0], inOperationOrder))); addFunction("cos", (caller, inOperationOrder, a) -> Math.cos(processEntity(caller, a[0], inOperationOrder))); addFunction("tan", (caller, inOperationOrder, a) -> Math.tan(processEntity(caller, a[0], inOperationOrder))); addFunction("asin", (caller, inOperationOrder, a) -> Math.asin(processEntity(caller, a[0], inOperationOrder))); addFunction("acos", (caller, inOperationOrder, a) -> Math.acos(processEntity(caller, a[0], inOperationOrder))); addFunction("atan", (caller, inOperationOrder, a) -> Math.atan(processEntity(caller, a[0], inOperationOrder))); addFunction("atan2", (caller, inOperationOrder, a) -> Math.atan2(processEntity(caller, a[0], inOperationOrder), processEntity(caller, a[1], inOperationOrder))); addFunction("sinh", (caller, inOperationOrder, a) -> Math.sinh(processEntity(caller, a[0], inOperationOrder))); addFunction("cosh", (caller, inOperationOrder, a) -> Math.cosh(processEntity(caller, a[0], inOperationOrder))); addFunction("tanh", (caller, inOperationOrder, a) -> Math.tanh(processEntity(caller, a[0], inOperationOrder))); addFunction("asinh", (caller, inOperationOrder, a) -> Math.log(processEntity(caller, a[0], inOperationOrder) + Math.sqrt(Math.pow(processEntity(caller, a[0], inOperationOrder), 2) + 1))); addFunction("acosh", (caller, inOperationOrder, a) -> Math.log(processEntity(caller, a[0], inOperationOrder) + Math.sqrt(Math.pow(processEntity(caller, a[0], inOperationOrder), 2) - 1))); addFunction("atanh", (caller, inOperationOrder, a) -> 0.5 * Math.log((1 + processEntity(caller, a[0], inOperationOrder)) / (1 - processEntity(caller, a[0], inOperationOrder)))); addFunction("fact", (caller, inOperationOrder, a) -> { int val = (int) processEntity(caller, a[0], inOperationOrder); if (val < 0) throw new IllegalArgumentException("factorial undefined for negative"); long result = 1L; for (int i = 1; i <= val; i++) result *= i; return (double) result; }); addFunction("min", (caller, inOperationOrder, a) -> { double result = Double.POSITIVE_INFINITY; for (int i = 0; i < a.length; i++) { double val = processEntity(caller, a[i], inOperationOrder); if (val < result) result = val; } return result; }); addFunction("max", (caller, inOperationOrder, a) -> { double result = Double.NEGATIVE_INFINITY; for (int i = 0; i < a.length; i++) { double val = processEntity(caller, a[i], inOperationOrder); if (val > result) result = val; } return result; }); addFunction("avg", (caller, inOperationOrder, a) -> { double sum = 0; for (int i = 0; i < a.length; i++) sum += processEntity(caller, a[i], inOperationOrder); return sum / a.length; }); addFunction("sum", (caller, inOperationOrder, a) -> { double total = 0; for (int i = 0; i < a.length; i++) total += processEntity(caller, a[i], inOperationOrder); return total; }); addFunction("median", (caller, inOperationOrder, a) -> { double[] vals = new double[a.length]; for (int i = 0; i < a.length; i++) vals[i] = processEntity(caller, a[i], inOperationOrder); Arrays.sort(vals); int mid = a.length / 2; return a.length % 2 == 1 ? vals[mid] : (vals[mid - 1] + vals[mid]) / 2.0; }); addFunction("sign", (caller, inOperationOrder, a) -> Math.signum(processEntity(caller, a[0], inOperationOrder))); addFunction("deg", (caller, inOperationOrder, a) -> Math.toDegrees(processEntity(caller, a[0], inOperationOrder))); addFunction("rad", (caller, inOperationOrder, a) -> Math.toRadians(processEntity(caller, a[0], inOperationOrder))); addFunction("clamp", (caller, inOperationOrder, a) -> { double val = processEntity(caller, a[0], inOperationOrder); double min = processEntity(caller, a[1], inOperationOrder); double max = processEntity(caller, a[2], inOperationOrder); return Math.max(min, Math.min(max, val)); }); addFunction("brt", (caller, inOperationOrder, a) -> Math.cbrt(processEntity(caller, a[0], inOperationOrder))); addFunction("nthroot", (caller, inOperationOrder, a) -> Math.pow(processEntity(caller, a[0], inOperationOrder), 1.0 / processEntity(caller, a[1], inOperationOrder))); addFunction("hypot", (caller, inOperationOrder, a) -> Math.hypot(processEntity(caller, a[0], inOperationOrder), processEntity(caller, a[1], inOperationOrder))); addFunction("root", (caller, inOperationOrder, a) -> Math.pow(processEntity(caller, a[0], inOperationOrder), 1.0 / processEntity(caller, a[1], inOperationOrder))); addFunction("bitand", (caller, inOperationOrder, a) -> (double)((long)processEntity(caller, a[0], inOperationOrder) & (long)processEntity(caller, a[1], inOperationOrder))); addFunction("bitor", (caller, inOperationOrder, a) -> (double)((long)processEntity(caller, a[0], inOperationOrder) | (long)processEntity(caller, a[1], inOperationOrder))); addFunction("bitxor", (caller, inOperationOrder, a) -> (double)((long)processEntity(caller, a[0], inOperationOrder) ^ (long)processEntity(caller, a[1], inOperationOrder))); addFunction("bitnot", (caller, inOperationOrder, a) -> (double)(~(long)processEntity(caller, a[0], inOperationOrder))); addFunction("shl", (caller, inOperationOrder, a) -> (double) ((long) processEntity(caller, a[0], inOperationOrder) << (int) (long) processEntity(caller, a[1], inOperationOrder))); addFunction("shr", (caller, inOperationOrder, a) -> (double) ((long) processEntity(caller, a[0], inOperationOrder) >> (int) (long) processEntity(caller, a[1], inOperationOrder))); addFunction("sec", (caller, inOperationOrder, a) -> 1.0 / Math.cos(processEntity(caller, a[0], inOperationOrder))); addFunction("csc", (caller, inOperationOrder, a) -> 1.0 / Math.sin(processEntity(caller, a[0], inOperationOrder))); addFunction("cot", (caller, inOperationOrder, a) -> 1.0 / Math.tan(processEntity(caller, a[0], inOperationOrder))); addFunction("normalize_angle", (caller, inOperationOrder, a) -> { double angle = processEntity(caller, a[0], inOperationOrder); double twoPi = 2 * Math.PI; return ((angle % twoPi) + twoPi) % twoPi; }); addFunction("wrap", (caller, inOperationOrder, a) -> { double val = processEntity(caller, a[0], inOperationOrder); double min = processEntity(caller, a[1], inOperationOrder); double max = processEntity(caller, a[2], inOperationOrder); double range = max - min; return ((val - min) % range + range) % range + min; }); Random randInstance = new Random(); addFunction("rand", (caller, inOperationOrder, a) -> randInstance.nextDouble()); addFunction("randint", (caller, inOperationOrder, a) -> { int min = (int)processEntity(caller, a[0], inOperationOrder); int max = (int)processEntity(caller, a[1], inOperationOrder); return min + randInstance.nextInt(max - min + 1); }); addFunction("randrange", (caller, inOperationOrder, a) -> { double min = processEntity(caller, a[0], inOperationOrder); double max = processEntity(caller, a[1], inOperationOrder); return min + (max - min) * randInstance.nextDouble(); }); addFunction("seed", (caller, inOperationOrder, a) -> { randInstance.setSeed((long)processEntity(caller, a[0], inOperationOrder)); return 0.0; }); addFunction("noise", (caller, inOperationOrder, a) -> { double x = processEntity(caller, a[0], inOperationOrder); return (Math.sin(x * 12.9898 + 78.233) + 1.0) * 0.5; }); addFunction("dot", (caller, inOperationOrder, a) -> processEntity(caller, a[0], inOperationOrder) * processEntity(caller, a[1], inOperationOrder)); addFunction("cross", (caller, inOperationOrder, a) -> processEntity(caller, a[0], inOperationOrder) * processEntity(caller, a[1], inOperationOrder)); addFunction("length", (caller, inOperationOrder, a) -> Math.abs(processEntity(caller, a[0], inOperationOrder))); addFunction("normalize", (caller, inOperationOrder, a) -> { double val = processEntity(caller, a[0], inOperationOrder); return val == 0 ? 0 : val / Math.abs(val); }); addFunction("distance", (caller, inOperationOrder, a) -> { double dx = processEntity(caller, a[0], inOperationOrder) - processEntity(caller, a[1], inOperationOrder); return Math.abs(dx); }); addFunction("angle", (caller, inOperationOrder, a) -> { double x = processEntity(caller, a[0], inOperationOrder); double y = processEntity(caller, a[1], inOperationOrder); return Math.atan2(y, x); }); addFunction("lerp", (caller, inOperationOrder, a) -> { double a0 = processEntity(caller, a[0], inOperationOrder); double a1 = processEntity(caller, a[1], inOperationOrder); double t = processEntity(caller, a[2], inOperationOrder); return a0 + (a1 - a0) * t; }); addFunction("mix", (caller, inOperationOrder, a) -> { double a0 = processEntity(caller, a[0], inOperationOrder); double a1 = processEntity(caller, a[1], inOperationOrder); double t = processEntity(caller, a[2], inOperationOrder); return a0 + (a1 - a0) * t; });
Read Entire Article