/**
* Class Input reads a line from the system console (System.in).
* It displays a numbered prompt.
*/
import java.util.Scanner;
public class Input {
// Static members and methods
private static int lineNo = 0;
static Scanner sc = new Scanner(System.in).useDelimiter("\\n");
public static Input readInput() {
return new Input();
}
// Instance members and methods
private final String line;
private Input() {
lineNo++;
System.out.printf("[%d]=> ", lineNo);
this.line = sc.next();
}
public String getLine() {
return this.line;
}
public int getLineNum() {
return this.lineNo;
}
}
/**
* This class serves only to store the result of the calculation
* in the form of a message string. A display method is provided
* to print the message to console (System.out).
*/
public class Result {
private final String message;
public Result(String m) {
this.message = m;
}
public void display() {
System.out.println(message);
}
}
/**
* Class Statement is the workhorse of the calculator.
* It initializes the hashtable to store the command and its associated
* lambda function. To run the command, its name is used to lookup the table
* to retrieve the lambda, which is then applied to the arguments.
*
* There is a lot of code to handle errors, which are due to:
* (a) unknown command, (b) wrong type of arguments, (c) too many or too few
* arguments. Such code clutters the main code and obscures the key idea.
* There is room for improvement!
*/
import java.util.Scanner;
import java.util.function.Function;
import java.util.NoSuchElementException;
import java.util.InputMismatchException;
import java.util.Hashtable;
public class Statement {
// static members and methods
// This stores the function for each command
private static Hashtable < String, Function < Double[], Double >> commandTable;
// This stores the number of arguments for each command
private static Hashtable < String, Integer > argsTable;
public static Statement parse(Input input) {
return new Statement(input.getLine());
}
/**
* Error input, return 0.0
* Accept a double array with 0 elements = 0 argument
*/
private static final Function < Double[], Double >
error = x - > 0.0;
/**
* Addition
* Accept a double array with 2 elements = 2 arguments
* Example: add 1 2
*/
private static final Function < Double[], Double >
addition = x - > x[0] + x[1];
/**
* Multiplication
* Accept a double array with 2 elements = 2 arguments
* Example: mult 3 4
*/
private static final Function < Double[], Double >
multiplication = x - > x[0] * x[1];
/**
* Reciprocal
* Accept a double array with 1 element = 1 argument
* Example: recip 5
*/
private static final Function < Double[], Double >
reciprocal = x - > 1.0 / x[0];
/**
* Percentage after Multiplication
* Accept a double array with 2 elements = 2 arguments
* Example: % 6 7
*/
private static final Function < Double[], Double >
percentage = x - > x[0] * x[1] / 100.0;
// Initialization
public static void initialize() {
commandTable = new Hashtable < > ();
argsTable = new Hashtable < > ();
/**
* Error input, return 0.0
* Accept a double array with 0 elements = 0 argument
*/
commandTable.put("err", error);
argsTable.put("err", 0);
/**
* Addition
* Accept a double array with 2 elements = 2 arguments
*/
commandTable.put("add", addition);
argsTable.put("add", 2);
/**
* Multiplication
* Accept a double array with 2 elements = 2 arguments
*/
commandTable.put("mult", multiplication);
argsTable.put("mult", 2);
/**
* Reciprocal
* Accept a double array with 1 element = 1 argument
*/
commandTable.put("recip", reciprocal);
argsTable.put("recip", 1);
/**
* Percentage after Multiplication
* Accept a double array with 2 elements = 2 arguments
*/
commandTable.put("%", percentage);
argsTable.put("%", 2);
}
// Instance member and methods
private String command;
private Double[] arguments;
private String message;
// Input Method
public Statement(String input) {
/**
* Input format: <command> <arg1> [<arg2>]
*
* Example:
* add 1 2
* mult 3 4
* recip 5
* % 6 7
*/
Scanner sc = new Scanner(input);
// Parse command
String c = sc.next();
// If the command is not err, add, mult, recip or %
if (!commandTable.containsKey(c)) {
this.command = "err";
this.message = String.format("Bad command! %s", c);
return;
}
this.command = c;
// Parse arguments
int numberArgs = argsTable.get(this.command);
this.arguments = new Double[numberArgs];
try {
for (int i = 0; i < numberArgs; i++) {
this.arguments[i] = sc.nextDouble();
}
if (sc.hasNext()) {
this.command = "err";
this.message = "Too many arguments!";
}
} catch (InputMismatchException e) {
this.command = "err";
this.message = "Bad arguments!";
} catch (NoSuchElementException e) {
this.command = "err";
this.message = "Too few arguments!";
}
}
public Result evaluate() {
if (!this.command.equals("err")) {
// commandTable.get(this.command): Function object
// this.arguments: Double[] object
double r = commandTable.get(this.command).apply(this.arguments);
this.message = String.format("%f", r);
}
return new Result(this.message);
}
}
/**
* Calc.java implements a simple, command-line calculator.
* User enters command in the form: <command> <arg1> <arg2>
* Example: add 3 5
* Currently, only 4 command are implemented: add, mult, recip, %,
* denoting addition, multiplcation, reciprocal, and percentage, respectively.
* To compile, put all the files in the same directory:
* Calc.java, Input.java, Statement.java, Results.java
* Then type:
* javac *.java
* java Calc
*/
import java.util.NoSuchElementException;
public class Calc {
public static void main(String[] args) {
// Initialize the calculator
Statement.initialize();
try {
while (true) {
Input input = Input.readInput();
Statement statement = Statement.parse(input);
Result result = statement.evaluate();
result.display();
}
} catch (NoSuchElementException e) {
// break out of while loop; end program
}
}
}