Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

g595.ulyanin.task4 #334

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions homework-g595-ulyanin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,66 @@

<dependencies>
<dependency>
<groupId>ru.mipt.java2016</groupId>
<artifactId>homework-base</artifactId>
<version>1.0.0</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.4.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>ru.mipt.java2016</groupId>
<artifactId>homework-tests</artifactId>
<artifactId>homework-base</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<version>RELEASE</version>
</dependency>

<dependency>
<groupId>ru.mipt.java2016</groupId>
<artifactId>homework-tests</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<java.version>1.8</java.version>
</properties>

<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,6 +14,72 @@

public class ShuntingYardCalculator implements Calculator {

private HashMap<String, Double> variablesValues = new HashMap<>();
private HashMap<String, Function> functions = new HashMap<>();
private HashSet<String> defaultFunctions;

private class Function {
private String name;
private ArrayList<String> arguments;
private String expression;
private ArrayList<Token> postfix;


Function(String functionName, ArrayList<String> arguments, String expression) throws ParsingException {
this.name = functionName;
this.arguments = arguments;
this.expression = expression;
this.postfix = infixToPostfix(splitExpressionToTokens(expression));
}

public Double apply(ArrayList<Double> argumentValues) throws ParsingException {
ArrayList<Token> newPostfix = replaceWithArguments(postfix, argumentValues);
return calculatePostfix(newPostfix);
}

private ArrayList<Token> replaceWithArguments(ArrayList<Token> postfixToReplace, ArrayList<Double> argValues) {
ArrayList<Token> 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");
Expand All @@ -29,14 +94,33 @@ private static ArrayList<Token> splitExpressionToTokens(String expression) throw
return tokenizer.getTokens(expression);
}

private static ArrayList<Token> infixToPostfix(ArrayList<Token> tokens) throws ParsingException {
private ArrayList<Token> infixToPostfix(ArrayList<Token> tokens) throws ParsingException {

ArrayList<Token> postfix = new ArrayList<>();
Stack<TokenOperator> 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()) {
Expand All @@ -63,13 +147,15 @@ private static ArrayList<Token> infixToPostfix(ArrayList<Token> 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;
}
Expand All @@ -83,23 +169,55 @@ private static ArrayList<Token> infixToPostfix(ArrayList<Token> tokens) throws P
return postfix;
}

private static double calculatePostfix(ArrayList<Token> postfix) throws ParsingException {
private double calculatePostfix(ArrayList<Token> postfix) throws ParsingException {
Stack<Double> 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<Double> 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());
Expand All @@ -111,4 +229,68 @@ private static double calculatePostfix(ArrayList<Token> 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<String> 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<String> 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<String> getVariableList() {
return new ArrayList<>(variablesValues.keySet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading