/** Our internal representation of a BinOp * in the Z0 language. * See http://www.radford.edu/itec380/2022spring-ibarland/Homeworks/Project/ * * @author Ian Barland * @version 2021.Apr.05 */ import java.util.*; import java.util.function.*; import java.util.stream.*; public class BinOp extends Expr { String op; Expr left, right; static final String START_TOKEN = "~"; public static final List>> // Pre-Java-9? Then `List.of` won't exist; see the comment below, instead. OP_FUNCS = List.of( new Pair<>( "sum", Double::sum ) , new Pair<>( "dif", (a,b) -> a-b ) , new Pair<>( "prd", (a,b) -> a*b ) ); /* // If the above doesn't compile (Pre-Java-9, OR your compiler can't infer the type of `(a,b) -> a+b` (netbeans??)): // Here's what to do: // (a) delete the lines above; we won't declare OP_FUNCS at all. // (b) in the next line, declare `OPS` to be a List and fill it with the valid operator-names. // (c) in evalBinOp below: */ public static final List OPS = OP_FUNCS.stream().map( Pair::getFirst ).collect(Collectors.toList()); // see next lines if compiler-error //OPS = List.of( "sum", "dif", "prd" ); // <-- Alternately just use this, if `OP_FUNCS` not declared. // //OPS = new ArrayList(); // <-- or, pre-Java-9, these 4 lines //OPS.add("sum"); //OPS.add("dif"); //OPS.add("prd"); BinOp( String _op, Expr _left, Expr _right ) { this.op = _op; this.left = _left; this.right = _right; } @Override public String toString( /* BinOp this */ ) { return START_TOKEN + this.op + " " + this.left.toString() + " " + this.right.toString() ; } public static BinOp parse(java.util.Scanner s, String punctuation) { UtilIan.verifyToken( UtilIan.nextSplittingBy(s,punctuation), START_TOKEN); // Consume (&verify) opening punctuation. String op = UtilIan.nextSplittingBy(s,punctuation); if (!(OPS.contains(op))) throw new InputMismatchException(String.format("Unknown operator \"%s\".",op)); Expr lefty = Expr.parse(s,punctuation); Expr righty = Expr.parse(s,punctuation); // NOTE: recur with `Expr.parse` -- not `parse`=`BinOp.parse` which is NOT what we want! return new BinOp(op, lefty, righty ); } public Value eval( /* BinOp this */) { String theOp = this.op; double leftVal = ((Num)(this.left .eval())).doubleValue(); double rightVal = ((Num)(this.right.eval())).doubleValue(); return evalBinOp( theOp, leftVal, rightVal ); } /** Evaluate Z's `op` w/ `l` and `r` */ static Value evalBinOp( String op, double l, double r ) { Pair> opFunc = OP_FUNCS.stream() // didn't declare OP_FUNCS? See comment below. .filter(pr -> pr.getFirst().equals(op) ) .findFirst() .orElseThrow( () -> new InputMismatchException(String.format("Unknown op `%s`; must be one of %s.", op, OPS)) ); return new Num( opFunc.getSecond().apply(l,r) ); /* If OP_FUNCS wasn't defined, delete the above two statments, and instead use one of the following two: if (op.equals("sum")) { return new Num(l + r); } else if (op.equals("dif")) { return new Num(l - r); } else if (op.equals("prd")) { return new Num(l * r); } else { throw new RuntimeException(String.format("Unknown op `%s`; must be one of %s.", op, OPS)); } // Or Java 14: return switch(op) { case "sum" -> new Num(l+r); case "dif" -> new Num(l-r); case "prd" -> new Num(l*r); default -> throw new RuntimeException(String.format("Unknown op `%s`; must be one of %s.", op, OPS)); }; */ } @Override public boolean equals( /* BinOp this, */ Object that) { if (this==that) { return true; } else if (that==null) { return false; } else if (this.getClass() != that.getClass()) { return false; } else { BinOp thatt = (BinOp) that; return this.left.equals(thatt.left) && this.op.equals(thatt.op) && this.right.equals(thatt.right); } } @Override public int hashCode() { if (cachedHash == null) { int hashSoFar = 0; hashSoFar += this.left.hashCode(); hashSoFar *= 31; hashSoFar += this.op.hashCode(); hashSoFar *= 31; hashSoFar += this.right.hashCode(); cachedHash = hashSoFar; } return cachedHash; } private Integer cachedHash = null; }