Created
December 28, 2014 19:30
-
-
Save akameco/a0f8e84931377ba0e77a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package compilir; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.util.*; | |
public class Calculator { | |
// 変数の名前と値の組を格納する HashMap (値は整数限定) | |
private HashMap<String, Double> symbolMap; | |
public Calculator() { | |
symbolMap = new HashMap<>(); | |
System.out.println("式の値を求めます (Ctrl-d で終了)"); | |
BufferedReader reader = new BufferedReader(new InputStreamReader( | |
System.in)); | |
while (true) { | |
System.out.print("代入文を入力してください: "); | |
String line; | |
try { | |
line = reader.readLine(); | |
} catch (IOException e) { | |
break; | |
} | |
if (line == null) // 入力終了 (Ctrl-d) | |
break; | |
// 文字列 line を Token に分解しリストに | |
LinkedList<Token> inputList = stringToTokenList(line); | |
// 代入文を仮定しているので先頭は左辺の変数 | |
String variableName = inputList.poll().toString(); | |
// 次の "=" を取り除いておく | |
inputList.poll(); | |
// 右辺 ("=" の次) から逆ポーランド記法に変換 | |
LinkedList<Token> reversePolishList = reverse(inputList); | |
// 逆ポーランド記法に変換した結果を表示 | |
System.out.print("逆ポーランド記法による表現: "); | |
for (Token token : reversePolishList) | |
System.out.print(token.toString() + " "); | |
System.out.println(); | |
// 逆ポーランド記法の式の値を計算 | |
Double value = calculate(reversePolishList); | |
// 変数に代入 | |
symbolMap.put(variableName, value); | |
System.out.println(" " + variableName + " <- " + value); | |
} | |
} | |
public static void main(String[] args) { | |
new Calculator(); | |
} | |
/** | |
* 入力された文字列からトークンのリストを生成 | |
*/ | |
private LinkedList<Token> stringToTokenList(String string) { | |
LinkedList<Token> inputList = new LinkedList<>(); | |
StringTokenizer st = new StringTokenizer(string, " ^;=()+-*/%", true); | |
while (st.hasMoreTokens()) { | |
String tokenString = st.nextToken(); | |
if (!tokenString.equals(" ")) | |
inputList.add(TokenFactory.newInstance(tokenString)); | |
} | |
return inputList; | |
} | |
/** | |
* 入力されたトークンのリストから逆ポーランド記法のリストを生成 | |
*/ | |
private LinkedList<Token> reverse(LinkedList<Token> inputList) { | |
LinkedList<Token> reversePolishList = new LinkedList<>(); | |
inputList.add(TokenFactory.newInstance(";")); // 終了記号 | |
Stack<Token> tokenStack = new Stack<>(); | |
tokenStack.push(TokenFactory.newInstance("^")); // 開始記号 | |
do { | |
System.out.println("stack: " + tokenStack); | |
Token a = inputList.poll(); // 先頭を取り出す | |
if (a.getKind() == Token.OPERAND) { | |
// オペランド(定数か変数)だったらすぐに出力 | |
reversePolishList.add(a); | |
System.out.println(" 出力: " + reversePolishList); | |
} else { | |
Token s = tokenStack.peek(); | |
while (s.compareTo(a) == -1) { | |
System.out.println(" 比較: \"" + s + "\" > \"" + a + "\""); | |
// a よりも優先度の高い演算子がスタックにあったら出力 | |
reversePolishList.add(tokenStack.pop()); | |
System.out.println("stack: " + tokenStack); | |
System.out.println(" 出力: " + reversePolishList); | |
s = tokenStack.peek(); | |
} | |
if (s.compareTo(a) == 1) { | |
System.out.println(" 比較: \"" + s + "\" < \"" + a + "\""); | |
// a より優先度の低い演算子がスタックの先頭だったら | |
// a をスタックに積む | |
tokenStack.push(a); | |
System.out.println("stack: " + tokenStack); | |
} else if (s.compareTo(a) == 0) { | |
System.out.println(" 比較: \"" + s + "\" == \"" + a + "\""); | |
tokenStack.pop(); // 取り出して捨てる | |
System.out.println("stack: " + tokenStack); | |
} | |
} | |
} while (!tokenStack.empty()); | |
return reversePolishList; | |
} | |
/** | |
* 逆ポーランド記法のトークンリストを演算 | |
*/ | |
private Double calculate(List<Token> reversePolishList) { | |
// スタックを用意 (とりあえず中身はInteger) | |
// 中身は Integer だがオートボクシングにより int のように使える | |
Stack<Double> stack = new Stack<>(); | |
for (Token token : reversePolishList) { | |
// 現在のスタックを表示 | |
System.out.print(stack); | |
// 2項演算子だったら、前に数値が2つ push されているはずなので、 | |
// それらを pop して演算し、結果の値を push する | |
if (token instanceof BinaryOperator) { | |
double operand2 = stack.pop(); | |
double operand1 = stack.pop(); | |
System.out.println(" 演算: " + operand1 + ", " + operand2); | |
stack.push(((BinaryOperator) token).calc(operand1, operand2)); | |
} else if (token instanceof DoubleConstant) { // 数値 | |
System.out.println(" 値を push: " + token.toString()); | |
// 文字列から数値に変換して push | |
stack.push(((DoubleConstant) token).doubleValue()); | |
} else { // 演算子でも数値でもないものは識別子 | |
System.out.println(" 識別子: " + token.toString()); | |
// すでに値が入っている変数であれば、その値を stack に push | |
if (symbolMap.containsKey(token.toString())) { | |
Double value = symbolMap.get(token.toString()); | |
stack.push(value); | |
} else { | |
System.err.println("未定義: " + token); | |
} | |
} | |
} | |
// 最終的に stack には計算された値1つが入っている | |
return stack.pop(); | |
} | |
// 2項演算子の抽象クラス。2引数の calc メソッドを持つ | |
abstract static class BinaryOperator extends Token { | |
public BinaryOperator(String string) { | |
super(string); | |
} | |
public abstract double calc(double operand1, double operand2); | |
} | |
abstract static class LogicOperator extends Token { | |
public LogicOperator(String string) { | |
super(string); | |
} | |
} | |
// 2項演算子 / | |
static class Div extends BinaryOperator { | |
public Div(String string) { | |
super(string); | |
kind = DIV; | |
} | |
public double calc(double operand1, double operand2) { | |
return operand1 / operand2; | |
} | |
} | |
// 識別子(変数名) | |
static class Identifier extends Token { | |
public Identifier(String string) { | |
super(string); | |
kind = OPERAND; | |
} | |
} | |
// 整数定数 | |
// static class IntConstant extends Token { | |
// private int value; | |
// | |
// public IntConstant(String string) { | |
// super(string); | |
// try { | |
// value = Integer.parseInt(string); | |
// } catch (Exception e) { | |
// e.printStackTrace(); | |
// } | |
// kind = OPERAND; | |
// } | |
// | |
// public int intValue() { | |
// return value; | |
// } | |
// } | |
// 実数 | |
static class DoubleConstant extends Token { | |
private double value; | |
public DoubleConstant(String string) { | |
super(string); | |
try { | |
value = Double.parseDouble(string); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
kind = OPERAND; | |
} | |
public double doubleValue() { | |
return value; | |
} | |
} | |
// 左括弧 | |
static class LParen extends Token { | |
public LParen(String string) { | |
super(string); | |
kind = LPAREN; | |
} | |
} | |
// 2項演算子 * | |
static class Mul extends BinaryOperator { | |
public Mul(String string) { | |
super(string); | |
kind = MUL; | |
} | |
public double calc(double operand1, double operand2) { | |
return operand1 * operand2; | |
} | |
} | |
// 右括弧 | |
static class RParen extends Token { | |
public RParen(String string) { | |
super(string); | |
kind = RPAREN; | |
} | |
} | |
// 特殊記号 ^ ; = | |
static class SpecialSymbol extends Token { | |
public SpecialSymbol(String string) { | |
super(string); | |
if (string.equals("^")) | |
kind = START; | |
else if (string.equals(";")) | |
kind = END; | |
// "=" は扱わないので何もしない | |
} | |
} | |
// 2項演算子 - | |
static class Sub extends BinaryOperator { | |
public Sub(String string) { | |
super(string); | |
kind = SUB; | |
} | |
public double calc(double operand1, double operand2) { | |
return operand1 - operand2; | |
} | |
} | |
// 2項演算子 + | |
static class Sum extends BinaryOperator { | |
public Sum(String string) { | |
super(string); | |
kind = SUM; | |
} | |
public double calc(double operand1, double operand2) { | |
return operand1 + operand2; | |
} | |
} | |
// 2項演算子 % | |
static class Rem extends BinaryOperator { | |
public Rem(String string) { | |
super(string); | |
kind = REM; | |
} | |
@Override | |
public double calc(double operand1, double operand2) { | |
return operand1 % operand2; | |
} | |
} | |
// トークンを表す抽象クラス | |
// トークン間の比較のために Comparable インタフェースを実装 | |
abstract static class Token implements Comparable<Token> { | |
// トークンの種類を表す定数 | |
// 値は演算子順位行列の添字と対応していれば何でもよく、順不同 | |
public static final int START = 0; // 始 | |
public static final int SUM = 1; // + | |
public static final int SUB = 2; // - | |
public static final int MUL = 3; // * | |
public static final int DIV = 4; // / | |
public static final int LPAREN = 5; // ( | |
public static final int RPAREN = 6; // ) | |
public static final int OPERAND = 7; // 識別子(変数)または定数 | |
public static final int END = 8; // 終 | |
public static final int REM = 9; // % | |
public static final int TRUE = 10; // true | |
public static final int FALSE = 11; // false | |
// トークンの種類 | |
protected int kind; | |
// トークンの文字列表現 | |
private String string; | |
// コンストラクタ | |
public Token(String string) { | |
this.string = string; | |
} | |
public String toString() { | |
return string; | |
} | |
public int getKind() { | |
return kind; | |
} | |
// トークン間の比較 | |
@SuppressWarnings("NullableProblems") | |
public int compareTo(Token anotherToken) { | |
int result = 1; // default は "<" | |
int left = this.kind; | |
int right = anotherToken.getKind(); | |
if (left == START) { // 開始記号 "^" | |
if (right == END) { | |
result = 0; // "=" 相殺 | |
} else if (right == RPAREN) { | |
result = 9; // "(" がスタックになく ")" がきたら error | |
} | |
} else if (left == LPAREN) { // "(" | |
if (right == RPAREN) { // ")" | |
result = 0; // "=" 相殺 | |
} else if (right == END) { | |
result = 9; // "(" がスタックにあるのに ";" がきたら error | |
} | |
} else if (left == SUM || left == SUB) { // "+" "-" | |
if (right == SUM || right == SUB | |
|| right == RPAREN || right == END) { | |
result = -1; // ">" | |
} | |
} else if (left == MUL || left == DIV || left == REM) { // "*" "/" "%" | |
if (right == SUM || right == SUB || right == MUL || | |
right == DIV || right == RPAREN || right == END || right == REM) { | |
result = -1; // ">" | |
} | |
} else { // 識別子 | |
if (right == SUM || right == SUB || right == MUL | |
|| right == DIV || right == RPAREN || right == END || right == REM) { | |
result = -1; // ">" | |
} else { | |
result = 9; // error | |
} | |
} | |
return result; | |
} | |
} | |
/** | |
* Token のサブクラスのインスタンスを生成するための Factory クラス | |
*/ | |
static class TokenFactory { | |
public static Token newInstance(String string) { | |
Token instance; | |
if (string.equals("^") || string.equals(";") || string.equals("=")) | |
instance = new SpecialSymbol(string); | |
else if (string.equals("+")) | |
instance = new Sum(string); | |
else if (string.equals("-")) | |
instance = new Sub(string); | |
else if (string.equals("*")) | |
instance = new Mul(string); | |
else if (string.equals("/")) | |
instance = new Div(string); | |
else if (string.equals("%")) | |
instance = new Rem(string); | |
else if (string.equals("(")) | |
instance = new LParen(string); | |
else if (string.equals(")")) | |
instance = new RParen(string); | |
else if (string.matches("(\\d+)|(\\d+\\.?\\d+)")) // 数字の並びは整数定数及び実数 | |
instance = new DoubleConstant(string); | |
else | |
// 識別子(変数) | |
instance = new Identifier(string); | |
return instance; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment