diff --git a/homework-g595-ulyanin/pom.xml b/homework-g595-ulyanin/pom.xml
index 66d384245..7517dbd0b 100644
--- a/homework-g595-ulyanin/pom.xml
+++ b/homework-g595-ulyanin/pom.xml
@@ -15,26 +15,66 @@
- ru.mipt.java2016
- homework-base
- 1.0.0
+ org.springframework.boot
+ spring-boot-dependencies
+ 1.4.2.RELEASE
+ pom
+ import
+
+ org.springframework.boot
+ spring-boot-starter-web
+ RELEASE
+
ru.mipt.java2016
- homework-tests
+ homework-base
1.0.0
- test
+
com.google.guava
guava
19.0
+
org.springframework.security
spring-security-core
RELEASE
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+ RELEASE
+
+
+
+ ru.mipt.java2016
+ homework-tests
+ 1.0.0
+ test
+
+
+
+ 1.8
+
+
+
+
+ spring-releases
+ https://repo.spring.io/libs-release
+
+
+
+
+
+ spring-releases
+ https://repo.spring.io/libs-release
+
+
\ No newline at end of file
diff --git a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/ShuntingYardCalculator.java b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/ShuntingYardCalculator.java
index fe13d7b7a..794576eca 100644
--- a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/ShuntingYardCalculator.java
+++ b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/ShuntingYardCalculator.java
@@ -4,8 +4,7 @@
import ru.mipt.java2016.homework.base.task1.Calculator;
import ru.mipt.java2016.homework.base.task1.ParsingException;
-import java.util.ArrayList;
-import java.util.Stack;
+import java.util.*;
/**
* Implementation of Calculator using Shunting Yard algorithm
@@ -15,6 +14,72 @@
public class ShuntingYardCalculator implements Calculator {
+ private HashMap variablesValues = new HashMap<>();
+ private HashMap functions = new HashMap<>();
+ private HashSet defaultFunctions;
+
+ private class Function {
+ private String name;
+ private ArrayList arguments;
+ private String expression;
+ private ArrayList postfix;
+
+
+ Function(String functionName, ArrayList arguments, String expression) throws ParsingException {
+ this.name = functionName;
+ this.arguments = arguments;
+ this.expression = expression;
+ this.postfix = infixToPostfix(splitExpressionToTokens(expression));
+ }
+
+ public Double apply(ArrayList argumentValues) throws ParsingException {
+ ArrayList newPostfix = replaceWithArguments(postfix, argumentValues);
+ return calculatePostfix(newPostfix);
+ }
+
+ private ArrayList replaceWithArguments(ArrayList postfixToReplace, ArrayList argValues) {
+ ArrayList newPostfix = new ArrayList<>();
+ for (Token token : postfixToReplace) {
+ int argN = arguments.indexOf(token.data);
+ if (argN == -1) {
+ newPostfix.add(token);
+ } else {
+ newPostfix.add(new Token(Double.toString(argValues.get(argN)), Token.TokenType.NUMBER));
+ }
+ }
+ return newPostfix;
+ }
+
+ public int getArity() {
+ return arguments.size();
+ }
+
+ }
+
+
+
+ public ShuntingYardCalculator() {
+ try {
+ functions.put("sin",
+ new Function("sin", new ArrayList<>(Arrays.asList("x")), "sin(x)")
+ );
+ functions.put("abs",
+ new Function("abs", new ArrayList<>(Arrays.asList("x")), "abs(x)")
+ );
+ functions.put("max",
+ new Function("max", new ArrayList<>(Arrays.asList("x,y")), "max(x,y)")
+ );
+ } catch (ParsingException e) {
+ e.printStackTrace();
+ }
+ this.defaultFunctions = new HashSet<>(functions.keySet());
+ }
+
+
+ public boolean isLocalVariable(Token token) {
+ return variablesValues.containsKey(token.data);
+ }
+
public double calculate(String expression) throws ParsingException {
if (expression == null) {
throw new ParsingException("Null expression");
@@ -29,14 +94,33 @@ private static ArrayList splitExpressionToTokens(String expression) throw
return tokenizer.getTokens(expression);
}
- private static ArrayList infixToPostfix(ArrayList tokens) throws ParsingException {
+ private ArrayList infixToPostfix(ArrayList tokens) throws ParsingException {
ArrayList postfix = new ArrayList<>();
Stack operatorStack = new Stack<>();
boolean mayBeUnaryOperator = true;
Token lastToken = null;
for (Token token : tokens) {
- if (token.isOperatorToken()) {
+ // variables:
+// if (lastToken != null && !token.isOpenBraceToken() && lastToken.isFunctionToken()) {
+// postfix.add(operatorStack.pop());
+// }
+
+ if (token.isNumberToken()) {
+ postfix.add(token);
+ mayBeUnaryOperator = false;
+ } else if (token.isFunctionToken()) {
+ operatorStack.add(new TokenOperator(token.getData(), Token.TokenType.FUNCTION));
+ mayBeUnaryOperator = false;
+ } else if (token.isArgumentSeparatorToken()) {
+ while (!operatorStack.isEmpty() && !operatorStack.peek().isOpenBraceToken()) {
+ postfix.add(operatorStack.pop());
+ }
+ if (operatorStack.isEmpty()) {
+ throw new ParsingException("missing ',' or '(' in function declaration");
+ }
+ mayBeUnaryOperator = true; // because max(0, -4) is possible
+ } else if (token.isOperatorToken()) {
// operator case
TokenOperator currentOperator = new TokenOperator(token.getData(), mayBeUnaryOperator);
while (!operatorStack.isEmpty()) {
@@ -63,13 +147,15 @@ private static ArrayList infixToPostfix(ArrayList tokens) throws P
postfix.add(operatorStack.pop());
}
if (operatorStack.isEmpty()) {
- throw new ParsingException("there are no '(' before ')'");
+ throw new ParsingException("there is no '(' before ')'");
+ }
+ if (operatorStack.peek().isFunctionToken()) {
+ postfix.add(operatorStack.pop());
}
operatorStack.pop();
mayBeUnaryOperator = false;
} else {
- postfix.add(token);
- mayBeUnaryOperator = false;
+ throw new ParsingException("unexpected token type");
}
lastToken = token;
}
@@ -83,23 +169,55 @@ private static ArrayList infixToPostfix(ArrayList tokens) throws P
return postfix;
}
- private static double calculatePostfix(ArrayList postfix) throws ParsingException {
+ private double calculatePostfix(ArrayList postfix) throws ParsingException {
Stack operands = new Stack<>();
for (Token token : postfix) {
if (token instanceof TokenOperator) {
- if (!((TokenOperator) token).isUnary()) {
+ if (token.isFunctionToken()) {
+ Double var;
+ if (variablesValues.containsKey(token.data)) {
+ var = variablesValues.get(token.data);
+ } else if (defaultFunctions.contains(token.data)) {
+ double var1 = operands.pop();
+ if (token.data.equals("sin")) {
+ var = Math.sin(var1);
+ } else if (token.data.equals("abs")) {
+ var = Math.abs(var1);
+ } else if (token.data.equals("max")) {
+ double var2 = operands.pop();
+ var = Math.max(var1, var2);
+ } else {
+ throw new ParsingException("unmapped function " + token.data);
+ }
+ } else {
+ Function f = functions.get(token.data);
+ if (f == null) {
+ throw new ParsingException("unknown function name " + token.data);
+ }
+ int arity = f.getArity();
+ ArrayList arguments = new ArrayList<>();
+ for (int i = 0; i < arity; ++i) {
+ arguments.add(operands.pop());
+ }
+ Collections.reverse(arguments);
+ var = f.apply(arguments);
+ }
+ operands.push(var);
+ } else if (!((TokenOperator) token).isUnary()) {
if (operands.size() < 2) {
throw new ParsingException("there are no two operands to binary operator " + token.getData());
}
Double var2 = operands.pop();
Double var1 = operands.pop();
operands.push(((TokenOperator) token).apply(var1, var2));
- } else {
+ } else if (token.isOperatorToken()) {
if (operands.size() < 1) {
throw new ParsingException("there are no operands to unary operator" + token.getData());
}
Double var = operands.pop();
operands.push(((TokenOperator) token).apply(var));
+ } else {
+ throw new ParsingException("unexpected tokenOperatorType");
}
} else {
operands.push(token.getValue());
@@ -111,4 +229,68 @@ private static double calculatePostfix(ArrayList postfix) throws ParsingE
}
return result;
}
+
+ public String getVariableValue(String variableName) throws ParsingException {
+ if (!variablesValues.containsKey(variableName)) {
+ throw new ParsingException("invalid variable name");
+ }
+ return Double.toString(variablesValues.get(variableName));
+ }
+
+ public String addVariable(String variableName, String valueExpression) throws ParsingException {
+ variablesValues.put(variableName, calculate(valueExpression));
+ return getVariableValue(variableName);
+ }
+
+ public void deleteVariable(String variableName) throws ParsingException {
+ if (!variablesValues.containsKey(variableName)) {
+ throw new ParsingException("variable " + variableName + " does not exist");
+ }
+ variablesValues.remove(variableName);
+ }
+
+ public void addFunction(String functionName, ArrayList params, String expression) throws ParsingException {
+ if (defaultFunctions.contains(functionName)) {
+ throw new ParsingException("trying to redefine default function " + functionName);
+ }
+ Function f = new Function(functionName, params, expression);
+ functions.put(functionName, f);
+ }
+
+ public void deleteFunction(String functionName) throws ParsingException {
+ if (!functions.containsKey(functionName)) {
+ throw new ParsingException("function " + functionName + " does not exist");
+ }
+ if (defaultFunctions.contains(functionName)) {
+ throw new ParsingException("trying to delete default function");
+ }
+ functions.remove(functionName);
+ }
+
+ public ArrayList getFunctionList() {
+ return new ArrayList<>(functions.keySet());
+ }
+
+ public String getFunctionDescription(String functionName) throws ParsingException {
+ if (!functions.containsKey(functionName)) {
+ throw new ParsingException("function " + functionName + " does not exist");
+ }
+ Function f = functions.get(functionName);
+ StringBuilder function = new StringBuilder();
+ function.append(functionName);
+ function.append('(');
+ for (int i = 0; i < f.getArity(); ++i) {
+ if (i != 0) {
+ function.append(',');
+ }
+ function.append(f.arguments.get(i));
+ }
+ function.append(')');
+ function.append(" -> " + f.expression);
+ return function.toString();
+ }
+
+ public ArrayList getVariableList() {
+ return new ArrayList<>(variablesValues.keySet());
+ }
}
diff --git a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Token.java b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Token.java
index a5253302a..44e43003c 100644
--- a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Token.java
+++ b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Token.java
@@ -9,7 +9,7 @@
public class Token {
- public enum TokenType { OPERATOR, NUMBER, BRACE_OPEN, BRACE_CLOSE }
+ public enum TokenType { OPERATOR, NUMBER, BRACE_OPEN, BRACE_CLOSE, FUNCTION, ARGS_SEPARATOR }
protected String data;
protected TokenType type;
@@ -39,6 +39,14 @@ public boolean isCloseBraceToken() {
return type == TokenType.BRACE_CLOSE;
}
+ public boolean isArgumentSeparatorToken() {
+ return type == TokenType.ARGS_SEPARATOR;
+ }
+
+ public boolean isFunctionToken() {
+ return type == TokenType.FUNCTION;
+ }
+
public double getValue() throws ParsingException {
if (type != TokenType.NUMBER) {
throw new ParsingException("can not cast not a number to double");
diff --git a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/TokenOperator.java b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/TokenOperator.java
index b44c21009..8a68fd2a2 100644
--- a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/TokenOperator.java
+++ b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/TokenOperator.java
@@ -29,7 +29,8 @@ public int getPrecedence() throws ParsingException {
final String ops = "()+-*/";
int index = ops.indexOf(data);
if (index == -1) {
- throw new ParsingException("unknown operator " + data);
+ return 4;
+// throw new ParsingException("unknown operator " + data);
}
if (isSubtruct() && isUnary()) {
return 3;
diff --git a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Tokenizer.java b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Tokenizer.java
index 2917919bb..afa85fb4f 100644
--- a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Tokenizer.java
+++ b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task1/Tokenizer.java
@@ -5,7 +5,8 @@
import java.util.ArrayList;
/**
- * Created by ulyanin on 11.10.16.
+ * @author ulyanin
+ * @since 11.10.16.
*/
public class Tokenizer {
private int currentPosition;
@@ -36,6 +37,10 @@ public ArrayList getTokens(String expr) throws ParsingException {
currentToken = new Token(String.valueOf(c), Token.TokenType.BRACE_CLOSE);
} else if (isOperator(c)) {
currentToken = new Token(String.valueOf(c), Token.TokenType.OPERATOR);
+ } else if (isFunctionArgumentSeparator(c)) {
+ currentToken = new Token(String.valueOf(c), Token.TokenType.ARGS_SEPARATOR);
+ } else if (isFunctionName(c)) {
+ currentToken = new Token(readFunctionName(), Token.TokenType.FUNCTION);
} else {
throw new ParsingException("unknown symbol '" + c + "'");
}
@@ -44,6 +49,8 @@ public ArrayList getTokens(String expr) throws ParsingException {
return result;
}
+
+
private char getCurrentChar() {
return expression.charAt(currentPosition);
}
@@ -72,11 +79,19 @@ public static boolean isCloseBrace(char c) {
return c == ')';
}
+ public static boolean isFunctionArgumentSeparator(char c) {
+ return c == ',';
+ }
+
public static boolean isOperator(char c) {
return c == '+' || c == '*' || c == '-' || c == '/';
}
- private String readNumberToken() {
+ private boolean isFunctionName(char c) {
+ return Character.isLetter(c) || Character.isDigit(c) || c == '_';
+ }
+
+ private String readNumberToken() throws ParsingException {
StringBuilder token = new StringBuilder();
while (characterExist() && isNumberCharacter(getCurrentChar())) {
token.append(getCurrentChar());
@@ -87,4 +102,16 @@ private String readNumberToken() {
}
return token.toString();
}
+
+ private String readFunctionName() throws ParsingException {
+ StringBuilder token = new StringBuilder();
+ while (characterExist() && isFunctionName(getCurrentChar())) {
+ token.append(getCurrentChar());
+ readNextCharacter();
+ }
+ if (characterExist()) {
+ unreadCharacter();
+ }
+ return token.toString();
+ }
}
diff --git a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task4/RestCalculatorController.java b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task4/RestCalculatorController.java
new file mode 100644
index 000000000..559ec1198
--- /dev/null
+++ b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task4/RestCalculatorController.java
@@ -0,0 +1,84 @@
+package ru.mipt.java2016.homework.g595.ulyanin.task4;
+
+import org.springframework.web.bind.annotation.*;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+import ru.mipt.java2016.homework.g595.ulyanin.task1.ShuntingYardCalculator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by ulyanin on 20.12.16.
+ */
+@RestController
+public class RestCalculatorController {
+ private ShuntingYardCalculator calculator = new ShuntingYardCalculator();
+
+ @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.GET)
+ @ResponseBody
+ public String getVariableValue(@PathVariable String variableName) {
+ try {
+ return String.valueOf(calculator.getVariableValue(variableName));
+ } catch (ParsingException err) {
+ return "Incorrect expression";
+ }
+ }
+
+ @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.PUT)
+ public String putValueOfVariable(@PathVariable String variableName,
+ @RequestBody String valueExpression) throws ParsingException {
+ System.out.println(variableName);
+ System.out.println(valueExpression);
+ return calculator.addVariable(variableName, valueExpression);
+ }
+
+ @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.DELETE)
+ public void deleteVariable(@PathVariable String variableName) throws ParsingException {
+ calculator.deleteVariable(variableName);
+ }
+
+ @RequestMapping(path = "/variable", method = RequestMethod.GET)
+ @ResponseBody
+ public List getVariableList() {
+ return calculator.getVariableList();
+ }
+
+ @RequestMapping(path = "/function/{functionName}", method = RequestMethod.GET)
+ @ResponseBody
+ public String getFunctionDescription(@PathVariable String functionName) throws ParsingException {
+ return calculator.getFunctionDescription(functionName);
+ }
+
+ @RequestMapping(path = "/function/{functionName}", method = RequestMethod.PUT)
+ public String putFunction(@PathVariable String functionName,
+ @RequestParam(value = "args") List params,
+ @RequestBody String expression) throws ParsingException {
+ calculator.addFunction(functionName, new ArrayList<>(params), expression);
+ return "add function ok";
+ }
+
+ @RequestMapping(path = "/function/{functionName}", method = RequestMethod.DELETE)
+ public String deleteFunction(@PathVariable String functionName) throws ParsingException {
+ calculator.deleteFunction(functionName);
+ return "delete ok";
+ }
+
+ @RequestMapping(path = "/function", method = RequestMethod.GET)
+ @ResponseBody
+ public List getFunctionList() {
+ return calculator.getFunctionList();
+ }
+
+ @RequestMapping(path = "/eval", method = RequestMethod.POST)
+ @ResponseBody
+ public String calculate(@RequestBody String expression) {
+ System.out.println(expression);
+ try {
+ return String.valueOf(calculator.calculate(expression));
+ } catch (ParsingException e) {
+ e.printStackTrace();
+ return e.getMessage();
+ }
+ }
+}
+
diff --git a/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task4/Service.java b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task4/Service.java
new file mode 100644
index 000000000..53774362b
--- /dev/null
+++ b/homework-g595-ulyanin/src/main/java/ru/mipt/java2016/homework/g595/ulyanin/task4/Service.java
@@ -0,0 +1,19 @@
+package ru.mipt.java2016.homework.g595.ulyanin.task4;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Created by ulyanin on 19.12.16.
+ */
+
+@Configuration
+@SpringBootApplication
+public class Service extends SpringBootServletInitializer {
+ public static void main(String[] args) {
+ SpringApplication.run(Service.class, args);
+ }
+}
+