first commit
This commit is contained in:
239
java/expression/generic/GenericParser.java
Normal file
239
java/expression/generic/GenericParser.java
Normal file
@@ -0,0 +1,239 @@
|
||||
package expression.generic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Doschennikov Nikita (me@fymio.us)
|
||||
*/
|
||||
public class GenericParser {
|
||||
|
||||
private static final List<String> VARIABLES = List.of("x", "y", "z");
|
||||
|
||||
private String src;
|
||||
private int pos;
|
||||
|
||||
public <T> GenericExpr<T> parse(String expression) {
|
||||
this.src = expression;
|
||||
this.pos = 0;
|
||||
GenericExpr<T> result = parseMinMax();
|
||||
skipWhitespace();
|
||||
if (pos < src.length()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected character '" +
|
||||
src.charAt(pos) +
|
||||
"' at position " +
|
||||
pos
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private <T> GenericExpr<T> parseMinMax() {
|
||||
GenericExpr<T> left = parseAddSub();
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
if (matchKeyword("min")) {
|
||||
GenericExpr<T> right = parseAddSub();
|
||||
GenericExpr<T> l = left;
|
||||
left = (type, vars) ->
|
||||
type.min(
|
||||
l.evaluate(type, vars),
|
||||
right.evaluate(type, vars)
|
||||
);
|
||||
} else if (matchKeyword("max")) {
|
||||
GenericExpr<T> right = parseAddSub();
|
||||
GenericExpr<T> l = left;
|
||||
left = (type, vars) ->
|
||||
type.max(
|
||||
l.evaluate(type, vars),
|
||||
right.evaluate(type, vars)
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
private <T> GenericExpr<T> parseAddSub() {
|
||||
GenericExpr<T> left = parseMulDiv();
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
if (pos < src.length() && src.charAt(pos) == '+') {
|
||||
pos++;
|
||||
GenericExpr<T> right = parseMulDiv();
|
||||
GenericExpr<T> l = left;
|
||||
left = (type, vars) ->
|
||||
type.add(
|
||||
l.evaluate(type, vars),
|
||||
right.evaluate(type, vars)
|
||||
);
|
||||
} else if (
|
||||
pos < src.length() && src.charAt(pos) == '-' && !nextIsKeyword()
|
||||
) {
|
||||
pos++;
|
||||
GenericExpr<T> right = parseMulDiv();
|
||||
GenericExpr<T> l = left;
|
||||
left = (type, vars) ->
|
||||
type.subtract(
|
||||
l.evaluate(type, vars),
|
||||
right.evaluate(type, vars)
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
private <T> GenericExpr<T> parseMulDiv() {
|
||||
GenericExpr<T> left = parseUnary();
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
if (pos < src.length() && src.charAt(pos) == '*') {
|
||||
pos++;
|
||||
GenericExpr<T> right = parseUnary();
|
||||
GenericExpr<T> l = left;
|
||||
left = (type, vars) ->
|
||||
type.multiply(
|
||||
l.evaluate(type, vars),
|
||||
right.evaluate(type, vars)
|
||||
);
|
||||
} else if (pos < src.length() && src.charAt(pos) == '/') {
|
||||
pos++;
|
||||
GenericExpr<T> right = parseUnary();
|
||||
GenericExpr<T> l = left;
|
||||
left = (type, vars) ->
|
||||
type.divide(
|
||||
l.evaluate(type, vars),
|
||||
right.evaluate(type, vars)
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
private <T> GenericExpr<T> parseUnary() {
|
||||
skipWhitespace();
|
||||
if (pos >= src.length()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected end of expression at position " + pos
|
||||
);
|
||||
}
|
||||
if (src.charAt(pos) == '-') {
|
||||
pos++;
|
||||
skipWhitespace();
|
||||
if (pos < src.length() && Character.isDigit(src.charAt(pos))) {
|
||||
return parseNumber(true);
|
||||
}
|
||||
GenericExpr<T> operand = parseUnary();
|
||||
return (type, vars) -> type.negate(operand.evaluate(type, vars));
|
||||
}
|
||||
if (matchKeyword("count")) {
|
||||
GenericExpr<T> operand = parseUnary();
|
||||
return (type, vars) -> type.count(operand.evaluate(type, vars));
|
||||
}
|
||||
return parsePrimary();
|
||||
}
|
||||
|
||||
private <T> GenericExpr<T> parsePrimary() {
|
||||
skipWhitespace();
|
||||
if (pos >= src.length()) {
|
||||
throw new IllegalArgumentException("Unexpected end of expression");
|
||||
}
|
||||
char c = src.charAt(pos);
|
||||
|
||||
if (c == '(') {
|
||||
pos++;
|
||||
GenericExpr<T> inner = parseMinMax();
|
||||
skipWhitespace();
|
||||
expect();
|
||||
return inner;
|
||||
}
|
||||
|
||||
if (Character.isDigit(c)) {
|
||||
return parseNumber(false);
|
||||
}
|
||||
|
||||
if (Character.isLetter(c)) {
|
||||
int start = pos;
|
||||
while (
|
||||
pos < src.length() && Character.isLetterOrDigit(src.charAt(pos))
|
||||
) {
|
||||
pos++;
|
||||
}
|
||||
String name = src.substring(start, pos);
|
||||
int idx = VARIABLES.indexOf(name);
|
||||
if (idx >= 0) {
|
||||
return (type, vars) -> vars.get(idx);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown variable '" + name + "' at position " + start
|
||||
);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected character '" + c + "' at position " + pos
|
||||
);
|
||||
}
|
||||
|
||||
private <T> GenericExpr<T> parseNumber(boolean negative) {
|
||||
int start = pos;
|
||||
while (pos < src.length() && Character.isDigit(src.charAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
if (start == pos) {
|
||||
throw new IllegalArgumentException(
|
||||
"Expected digit at position " + pos
|
||||
);
|
||||
}
|
||||
String numStr = src.substring(start, pos);
|
||||
long val = Long.parseLong(numStr);
|
||||
if (negative) val = -val;
|
||||
if (val < Integer.MIN_VALUE || val > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Integer overflow: " + val);
|
||||
}
|
||||
int intVal = (int) val;
|
||||
return (type, vars) -> type.fromInt(intVal);
|
||||
}
|
||||
|
||||
private boolean matchKeyword(String keyword) {
|
||||
int i = pos;
|
||||
for (char ch : keyword.toCharArray()) {
|
||||
if (i >= src.length() || src.charAt(i) != ch) return false;
|
||||
i++;
|
||||
}
|
||||
if (
|
||||
i < src.length() && Character.isLetterOrDigit(src.charAt(i))
|
||||
) return false;
|
||||
pos = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean nextIsKeyword() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void skipWhitespace() {
|
||||
while (pos < src.length() && Character.isWhitespace(src.charAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
private void expect() {
|
||||
if (pos >= src.length() || src.charAt(pos) != ')') {
|
||||
throw new IllegalArgumentException(
|
||||
"Expected '" +
|
||||
')' +
|
||||
"' at position " +
|
||||
pos +
|
||||
(pos < src.length()
|
||||
? ", got '" + src.charAt(pos) + "'"
|
||||
: ", got end of input")
|
||||
);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user