From d282ea8b8ca960539464f7521e5ac7fdc33b2ec3 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Tue, 8 Jun 2021 18:08:05 +0300 Subject: [PATCH] Implement expression computation by AST in StringCalc --- StringCalc/.gitignore | 1 + StringCalc/.idea/compiler.xml | 22 - .../.idea/copyright/profiles_settings.xml | 3 - StringCalc/.idea/description.html | 1 - StringCalc/.idea/misc.xml | 8 +- StringCalc/.idea/modules.xml | 2 +- StringCalc/.idea/project-template.xml | 3 - StringCalc/.idea/vcs.xml | 6 + StringCalc/.idea/workspace.xml | 413 ++---------------- .../{UnnamedCalc.iml => StringCalc.iml} | 3 +- .../production/UnnamedCalc/main/Main.class | Bin 2539 -> 0 bytes StringCalc/src/main/ASTNode.java | 25 ++ StringCalc/src/main/Main.java | 187 +++++--- StringCalc/src/main/OperatorType.java | 23 + StringCalc/src/main/Token.java | 22 + 15 files changed, 243 insertions(+), 476 deletions(-) create mode 100644 StringCalc/.gitignore delete mode 100644 StringCalc/.idea/compiler.xml delete mode 100644 StringCalc/.idea/copyright/profiles_settings.xml delete mode 100644 StringCalc/.idea/description.html delete mode 100644 StringCalc/.idea/project-template.xml create mode 100644 StringCalc/.idea/vcs.xml rename StringCalc/{UnnamedCalc.iml => StringCalc.iml} (97%) delete mode 100644 StringCalc/out/production/UnnamedCalc/main/Main.class create mode 100644 StringCalc/src/main/ASTNode.java create mode 100644 StringCalc/src/main/OperatorType.java create mode 100644 StringCalc/src/main/Token.java diff --git a/StringCalc/.gitignore b/StringCalc/.gitignore new file mode 100644 index 0000000..89f9ac0 --- /dev/null +++ b/StringCalc/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/StringCalc/.idea/compiler.xml b/StringCalc/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/StringCalc/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/StringCalc/.idea/copyright/profiles_settings.xml b/StringCalc/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/StringCalc/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/StringCalc/.idea/description.html b/StringCalc/.idea/description.html deleted file mode 100644 index db5f129..0000000 --- a/StringCalc/.idea/description.html +++ /dev/null @@ -1 +0,0 @@ -Simple Java application that includes a class with main() method \ No newline at end of file diff --git a/StringCalc/.idea/misc.xml b/StringCalc/.idea/misc.xml index e29b6f0..0548357 100644 --- a/StringCalc/.idea/misc.xml +++ b/StringCalc/.idea/misc.xml @@ -1,12 +1,6 @@ - - - - - - + \ No newline at end of file diff --git a/StringCalc/.idea/modules.xml b/StringCalc/.idea/modules.xml index e111f7a..655730a 100644 --- a/StringCalc/.idea/modules.xml +++ b/StringCalc/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/StringCalc/.idea/project-template.xml b/StringCalc/.idea/project-template.xml deleted file mode 100644 index 1f08b88..0000000 --- a/StringCalc/.idea/project-template.xml +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/StringCalc/.idea/vcs.xml b/StringCalc/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/StringCalc/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/StringCalc/.idea/workspace.xml b/StringCalc/.idea/workspace.xml index fc48f52..3cd100e 100644 --- a/StringCalc/.idea/workspace.xml +++ b/StringCalc/.idea/workspace.xml @@ -1,240 +1,50 @@ - - - - - - - \ No newline at end of file diff --git a/StringCalc/UnnamedCalc.iml b/StringCalc/StringCalc.iml similarity index 97% rename from StringCalc/UnnamedCalc.iml rename to StringCalc/StringCalc.iml index d5c0743..c90834f 100644 --- a/StringCalc/UnnamedCalc.iml +++ b/StringCalc/StringCalc.iml @@ -8,5 +8,4 @@ - - + \ No newline at end of file diff --git a/StringCalc/out/production/UnnamedCalc/main/Main.class b/StringCalc/out/production/UnnamedCalc/main/Main.class deleted file mode 100644 index 958209eda85138e0d5ed28a84030f8a48efee496..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2539 zcmaJ?TT>KA6#jZ|vn<1Hxrq^35p}ufswhD~B0*M(fEN@+G-geO+>2vz@Io~<`o#ywqKfMBQ z3||M(fHMlt7GV${1>nLt1w#RNaYhQm0n}hbPUoe;g#dQrVgQ#gs^GF8R{|)(m>*Z= zfopz@`*Gcm2`SxBa8tp@0o3A_JpYNDG$~HXRZOmKE6^283HW*vM#Ab9aEF`61U&tz z+qyv6P{Pnhva^%Ad0CrGQWG3X#kAy@W+tSZS9+}JL`I-^XjV%Ykzr2VbRh+Sn()NX zjCN0pBsC)*8MVxW5%0D`Xl9&;D!17dP%<&iFm#jq&gra`NJd5-rF5PoFp1_J*8)YU zv~Frv%B1&-!qb)(yTh}JVJF1hBI%{iG_`pF*F-q!`lE@sp;=jzl^odyz_vMV1yyLYmY~EL7)@o(m>x|? z))Yxj9FljXB7{A>7yI4{FI7QY#WWJqV1~>qFp%(7+(A;sEDRMXq*dI7sp5TfD#)m? zkQHd$;KE=ssmHZspBc~2>V`Ei7t_<0UFf}Jb13&R_ac=0G50FBnp?`f$h{2Zp1uC| z^`qBMLPj>pFd^Jy68!=*x;9Hf?FetX zA^Xg4=yR6rV}Yu0vtw4+i|qZRZp5u=yZ6bQVk_0}a7XrqC)_MM!=1_!)<*aX?Bk8_ zm3Bdhsz6N&rlnDmusz|Zgi@VHSs31a3ewpO!|yNLb}G?b2sp1Im~@KN-<-|*W?)LT z*_f8hGGo4Gk^dVwk7y~_I7#sE$Z?0B0a*Dht}0Nigx~gI>u8q>sH6ttvYDsm#xh`=I46K9U>A>+`aWJ zuf7)xEx(~;6(!@r(q)wW(o%oy2YQ0I$SKSb$&c&&Mmdh=_j>Z*3yY$?HCVocimQvF zxiuJcRt>GeN@rEw8mw|w#VlL(uc+zruw-1+E@4Nfx6ae$b9E?nz9rPzp#hz7WEEuW zvm6qK)OF^+*Xn#S;?D2T;qFitahNjWQr_>8a)>gcQ?Bt!xtuaFQ1k+dl+-j8YG$M+Sg0{Ny=}GL6})GsjjA>lbp^XtP@mU5 zc8kTgk2j7yWn7QV7L#`i?BoqKkfR~qXd^4ogmYx*CD!2zIdFr#xJA5EWI%#;4hpY z(>(O@C%Vv0R=LCj^pK;hl6XRnwiBIPNglGrxRN|{t+UVBJHEcx!MzW7q!ZQu&{^dX yR}p{yWlnwtrxf(@Pbm1%#X)~%Wo7E_ii(OiD18I9{BI)Y=V=#C)8l}>iv9 operators = new Stack<>(); - Stack numbers = new Stack<>(); - line = scanner.nextLine(); - - char[] lineArray = new char[line.length()]; - + line = line.replace(" ", ""); + line = line.replace("+-", "-"); + line = line.replace("--", "+"); + char[] lineArray; lineArray = line.toCharArray(); - - System.out.println(calcExpression(lineArray, operators, numbers)); + List tokens = tokenize(lineArray); + System.out.println("[\n"+tokens.stream() + .map(item -> "\t"+item) + .collect(Collectors.joining(",\n"))+"\n]"); + ASTNode node = parse(tokens); + System.out.println(node.toString()); + int result = evaluateAST(node); + System.out.println(result); } - static float calcExpression(char[] charArr, Stack operators, Stack numbers) { - for(int i = 0; i < charArr.length; i++) { + static List tokenize(char[] charArr) { + List tokens = new ArrayList<>(); + StringBuilder tmpNumStr = new StringBuilder(); + boolean isLastCharOperator = false; + for (int i = 0; i < charArr.length; i++) { + char c = charArr[i]; + Token token = new Token(TokenType.Unknown, null); + if (Character.isDigit(c) || c == '-' && isLastCharOperator) { + isLastCharOperator = false; + tmpNumStr.append(c); + // if next char is also digit, then, instead of creating token, parse next digit + // if it's the end of array, then just create token and finish + if (i+1 != charArr.length) { + if(Character.isDigit(charArr[i+1])) { + continue; + } + } + token = new Token(TokenType.Number, Integer.parseInt(tmpNumStr.toString())); + tmpNumStr = new StringBuilder(); + } else { + switch (c) { + case '(': { + token = new Token(TokenType.Operator, OperatorType.LeftParenthesis); + break; + } + case '+': { + token = new Token(TokenType.Operator, OperatorType.Add); + break; + } + case '-': { + token = new Token(TokenType.Operator, OperatorType.Sub); + break; + } + case '*': { + token = new Token(TokenType.Operator, OperatorType.Mul); + break; + } + case '/': { + token = new Token(TokenType.Operator, OperatorType.Div); + break; + } + case ')': { + token = new Token(TokenType.Operator, OperatorType.RightParenthesis); + break; + } + } + isLastCharOperator = true; + } + tokens.add(token); + } - String operatorTemp; - float numberTemp; + return tokens; + } - if (charArr == null) - throw new IllegalArgumentException("Num не должен быть null"); + static ASTNode parse(List tokens) { + Stack output = new Stack<>(); + Stack ops = new Stack<>(); + for (Token token : tokens) { + // https://en.wikipedia.org/wiki/Shunting-yard_algorithm - switch(charArr[i]) { - case '(': { - break; - } - case '+': { - operators.push("+"); - break; - } - case '-': { - operators.push("-"); - break; - } - case '*': { - operators.push("*"); - break; - } - case '/': { - operators.push("/"); - break; - } - case ')': { - operatorTemp = operators.pop(); - numberTemp = numbers.pop(); + if (token.type == TokenType.Number) { + output.add(new ASTNode(token)); + continue; + } - switch(operatorTemp) { - case "+": { - numberTemp = numbers.pop() + numberTemp; - break; - } - case "-": { - numberTemp = numbers.pop() - numberTemp; - break; - } - case "*": { - numberTemp = numbers.pop() * numberTemp; - break; - } - case "/": { - numberTemp = numbers.pop() / numberTemp; + if (token.type == TokenType.Operator) { + if (token.value == OperatorType.LeftParenthesis) { + ops.push(token); + continue; + } + + while (ops.size() != 0) { + if (ops.peek().value == OperatorType.LeftParenthesis) { + break; + } + + if (token.value != OperatorType.RightParenthesis) { + if (OperatorType.getPrecedence((OperatorType) ops.peek().value) < OperatorType.getPrecedence((OperatorType) token.value)) { break; } } - numbers.push(numberTemp); - break; + Token op = ops.pop(); + ASTNode right = output.pop(); + ASTNode left = output.pop(); + output.push(new ASTNode(op, left, right)); } - default: { - numbers.push(Float.parseFloat(String.valueOf(charArr[i]))); + if (token.value == OperatorType.RightParenthesis) { + ops.pop(); + } else { + ops.push(token); } } } - return numbers.pop(); + while(ops.size() != 0) { + Token op = ops.pop(); + ASTNode right = output.pop(); + ASTNode left = output.pop(); + output.push(new ASTNode(op, left, right)); + } + return output.pop(); + } + + static int evaluateAST(ASTNode node) { + int val = 0; + if (node.token.type == TokenType.Operator) { + switch ((OperatorType) node.token.value) { + case Add: + val = evaluateAST(node.left) + evaluateAST(node.right); + break; + case Sub: + val = evaluateAST(node.left) - evaluateAST(node.right); + break; + case Mul: + val = evaluateAST(node.left) * evaluateAST(node.right); + break; + case Div: + val = evaluateAST(node.left) / evaluateAST(node.right); + break; + } + } else { + val = (int) node.token.value; + } + return val; } } \ No newline at end of file diff --git a/StringCalc/src/main/OperatorType.java b/StringCalc/src/main/OperatorType.java new file mode 100644 index 0000000..735fbd9 --- /dev/null +++ b/StringCalc/src/main/OperatorType.java @@ -0,0 +1,23 @@ +package main; + +public enum OperatorType { + Add, + Sub, + Mul, + Div, + LeftParenthesis, + RightParenthesis; + + public static int getPrecedence(OperatorType type) { + switch (type) { + case Add: + case Sub: + return 0; + case Mul: + case Div: + return 1; + default: + return -1; + } + } +} diff --git a/StringCalc/src/main/Token.java b/StringCalc/src/main/Token.java new file mode 100644 index 0000000..5eb017e --- /dev/null +++ b/StringCalc/src/main/Token.java @@ -0,0 +1,22 @@ +package main; + +enum TokenType { + Number, + Operator, + Unknown +} + +class Token { + TokenType type; + Object value; + + public Token(TokenType type, Object value) { + this.type = type; + this.value = value; + } + + @Override + public String toString() { + return "Token{type="+type+", value="+value+"}"; + } +} \ No newline at end of file