summaryrefslogtreecommitdiff
path: root/src/net/sourceforge/plantuml/tim/expression
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/sourceforge/plantuml/tim/expression')
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/Expression.java40
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/Knowledge.java46
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/ReversePolishInterpretor.java126
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/ShuntingYard.java153
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/TValue.java218
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/Token.java90
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/TokenIterator.java45
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/TokenOperator.java138
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/TokenStack.java223
-rw-r--r--src/net/sourceforge/plantuml/tim/expression/TokenType.java117
10 files changed, 1196 insertions, 0 deletions
diff --git a/src/net/sourceforge/plantuml/tim/expression/Expression.java b/src/net/sourceforge/plantuml/tim/expression/Expression.java
new file mode 100644
index 0000000..ca65b9f
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/Expression.java
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+
+public class Expression {
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/Knowledge.java b/src/net/sourceforge/plantuml/tim/expression/Knowledge.java
new file mode 100644
index 0000000..8a102f5
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/Knowledge.java
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import net.sourceforge.plantuml.tim.TFunction;
+import net.sourceforge.plantuml.tim.TFunctionSignature;
+
+public interface Knowledge {
+
+ public TValue getVariable(String name);
+
+ public TFunction getFunction(TFunctionSignature signature);
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/ReversePolishInterpretor.java b/src/net/sourceforge/plantuml/tim/expression/ReversePolishInterpretor.java
new file mode 100644
index 0000000..b45b2c5
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/ReversePolishInterpretor.java
@@ -0,0 +1,126 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.tim.EaterExceptionLocated;
+import net.sourceforge.plantuml.tim.EaterException;
+import net.sourceforge.plantuml.tim.TContext;
+import net.sourceforge.plantuml.tim.TFunction;
+import net.sourceforge.plantuml.tim.TFunctionSignature;
+import net.sourceforge.plantuml.tim.TMemory;
+
+public class ReversePolishInterpretor {
+
+ private final TValue result;
+ private boolean trace = false;
+
+ public ReversePolishInterpretor(TokenStack queue, Knowledge knowledge, TMemory memory, TContext context)
+ throws EaterException, EaterExceptionLocated {
+ this(null, queue, knowledge, memory, context);
+ }
+
+ public ReversePolishInterpretor(LineLocation location, TokenStack queue, Knowledge knowledge, TMemory memory,
+ TContext context) throws EaterException, EaterExceptionLocated {
+
+ final Deque<TValue> stack = new ArrayDeque<TValue>();
+ if (trace)
+ System.err.println("ReversePolishInterpretor::queue=" + queue);
+ for (TokenIterator it = queue.tokenIterator(); it.hasMoreTokens();) {
+ final Token token = it.nextToken();
+ if (trace)
+ System.err.println("rpn " + token);
+ if (token.getTokenType() == TokenType.NUMBER) {
+ stack.addFirst(TValue.fromNumber(token));
+ } else if (token.getTokenType() == TokenType.QUOTED_STRING) {
+ stack.addFirst(TValue.fromString(token));
+ } else if (token.getTokenType() == TokenType.JSON_DATA) {
+ stack.addFirst(TValue.fromJson(token.getJson()));
+ } else if (token.getTokenType() == TokenType.OPERATOR) {
+ final TValue v2 = stack.removeFirst();
+ final TValue v1 = stack.removeFirst();
+ final TokenOperator op = token.getTokenOperator();
+ if (op == null) {
+ throw EaterException.unlocated("bad op");
+ }
+ final TValue tmp = op.operate(v1, v2);
+ stack.addFirst(tmp);
+ } else if (token.getTokenType() == TokenType.OPEN_PAREN_FUNC) {
+ final int nb = Integer.parseInt(token.getSurface());
+ final Token token2 = it.nextToken();
+ if (token2.getTokenType() != TokenType.FUNCTION_NAME) {
+ throw EaterException.unlocated("rpn43");
+ }
+ if (trace)
+ System.err.println("token2=" + token2);
+ final TFunction function = knowledge.getFunction(new TFunctionSignature(token2.getSurface(), nb));
+ if (trace)
+ System.err.println("function=" + function);
+ if (function == null) {
+ throw EaterException.unlocated("Unknow built-in function " + token2.getSurface());
+ }
+ if (function.canCover(nb) == false) {
+ throw EaterException
+ .unlocated("Bad number of arguments for " + function.getSignature().getFunctionName());
+ }
+ final List<TValue> args = new ArrayList<TValue>();
+ for (int i = 0; i < nb; i++) {
+ args.add(0, stack.removeFirst());
+ }
+ if (trace)
+ System.err.println("args=" + args);
+ if (location == null) {
+ throw EaterException.unlocated("rpn44");
+ }
+ final TValue r = function.executeReturn(context, memory, location, args);
+ if (trace)
+ System.err.println("r=" + r);
+ stack.addFirst(r);
+ } else {
+ throw EaterException.unlocated("rpn41");
+ }
+ }
+ result = stack.removeFirst();
+ }
+
+ public final TValue getResult() {
+ return result;
+ }
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/ShuntingYard.java b/src/net/sourceforge/plantuml/tim/expression/ShuntingYard.java
new file mode 100644
index 0000000..4a0265c
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/ShuntingYard.java
@@ -0,0 +1,153 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
+// https://en.cppreference.com/w/c/language/operator_precedence
+public class ShuntingYard {
+
+ final private TokenStack ouputQueue = new TokenStack();
+ final private Deque<Token> operatorStack = new ArrayDeque<Token>();
+
+ private static final boolean TRACE = false;
+
+ private void traceMe() {
+ if (TRACE == false)
+ return;
+ System.err.println("-------------------");
+ System.err.println("operatorStack=" + operatorStack);
+ System.err.println("ouputQueue=" + ouputQueue);
+ System.err.println("");
+ }
+
+ public ShuntingYard(TokenIterator it, Knowledge knowledge) {
+
+ while (it.hasMoreTokens()) {
+ final Token token = it.nextToken();
+ traceMe();
+ if (TRACE)
+ System.err.println("token=" + token);
+ if (token.getTokenType() == TokenType.NUMBER || token.getTokenType() == TokenType.QUOTED_STRING) {
+ ouputQueue.add(token);
+ } else if (token.getTokenType() == TokenType.FUNCTION_NAME) {
+ operatorStack.addFirst(token);
+ } else if (token.getTokenType() == TokenType.PLAIN_TEXT) {
+ final String name = token.getSurface();
+ final TValue variable = knowledge.getVariable(name);
+ if (variable == null) {
+ ouputQueue.add(new Token("undefined", TokenType.QUOTED_STRING, null));
+ } else {
+ ouputQueue.add(variable.toToken());
+ }
+ } else if (token.getTokenType() == TokenType.OPERATOR) {
+ while ((thereIsAFunctionAtTheTopOfTheOperatorStack(token) //
+ || thereIsAnOperatorAtTheTopOfTheOperatorStackWithGreaterPrecedence(token) //
+ || theOperatorAtTheTopOfTheOperatorStackHasEqualPrecedenceAndIsLeftAssociative(token)) //
+ && theOperatorAtTheTopOfTheOperatorStackIsNotALeftParenthesis(token)) {
+ ouputQueue.add(operatorStack.removeFirst());
+ }
+ // push it onto the operator stack.
+ operatorStack.addFirst(token);
+ } else if (token.getTokenType() == TokenType.OPEN_PAREN_FUNC) {
+ operatorStack.addFirst(token);
+ } else if (token.getTokenType() == TokenType.OPEN_PAREN_MATH) {
+ operatorStack.addFirst(token);
+ } else if (token.getTokenType() == TokenType.CLOSE_PAREN_FUNC) {
+ final Token first = operatorStack.removeFirst();
+ ouputQueue.add(first);
+ } else if (token.getTokenType() == TokenType.CLOSE_PAREN_MATH) {
+ while (operatorStack.peekFirst().getTokenType() != TokenType.OPEN_PAREN_MATH) {
+ ouputQueue.add(operatorStack.removeFirst());
+ }
+ if (operatorStack.peekFirst().getTokenType() == TokenType.OPEN_PAREN_MATH) {
+ operatorStack.removeFirst();
+ }
+ } else if (token.getTokenType() == TokenType.COMMA) {
+ while (operatorStack.peekFirst() != null
+ && operatorStack.peekFirst().getTokenType() != TokenType.OPEN_PAREN_FUNC) {
+ ouputQueue.add(operatorStack.removeFirst());
+ }
+ } else {
+ throw new UnsupportedOperationException(token.toString());
+ }
+ }
+
+ while (operatorStack.isEmpty() == false) {
+ final Token token = operatorStack.removeFirst();
+ ouputQueue.add(token);
+ }
+
+ // System.err.println("ouputQueue=" + ouputQueue);
+ }
+
+ private boolean thereIsAFunctionAtTheTopOfTheOperatorStack(Token token) {
+ final Token top = operatorStack.peekFirst();
+ return top != null && top.getTokenType() == TokenType.FUNCTION_NAME;
+ }
+
+ private boolean thereIsAnOperatorAtTheTopOfTheOperatorStackWithGreaterPrecedence(Token token) {
+ final Token top = operatorStack.peekFirst();
+ if (top != null && top.getTokenType() == TokenType.OPERATOR
+ && top.getTokenOperator().getPrecedence() > token.getTokenOperator().getPrecedence()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean theOperatorAtTheTopOfTheOperatorStackHasEqualPrecedenceAndIsLeftAssociative(Token token) {
+ final Token top = operatorStack.peekFirst();
+ if (top != null && top.getTokenType() == TokenType.OPERATOR && top.getTokenOperator().isLeftAssociativity()
+ && top.getTokenOperator().getPrecedence() == token.getTokenOperator().getPrecedence()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean theOperatorAtTheTopOfTheOperatorStackIsNotALeftParenthesis(Token token) {
+ final Token top = operatorStack.peekFirst();
+ if (top != null && top.getTokenType() == TokenType.OPEN_PAREN_MATH) {
+ return true;
+ }
+ return true;
+ }
+
+ public TokenStack getQueue() {
+ return this.ouputQueue;
+ }
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/TValue.java b/src/net/sourceforge/plantuml/tim/expression/TValue.java
new file mode 100644
index 0000000..921f8d6
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/TValue.java
@@ -0,0 +1,218 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import net.sourceforge.plantuml.json.JsonValue;
+
+public class TValue {
+
+ private final int intValue;
+ private final String stringValue;
+ private final JsonValue jsonValue;
+
+ private TValue(int value) {
+ this.intValue = value;
+ this.stringValue = null;
+ this.jsonValue = null;
+ }
+
+ private TValue(String stringValue) {
+ if (stringValue == null) {
+ throw new IllegalArgumentException();
+ }
+ this.intValue = 0;
+ this.jsonValue = null;
+ this.stringValue = stringValue;
+ }
+
+ public TValue(JsonValue json) {
+ this.jsonValue = json;
+ this.intValue = 0;
+ this.stringValue = null;
+ }
+
+ public static TValue fromInt(int v) {
+ return new TValue(v);
+ }
+
+ public static TValue fromBoolean(boolean b) {
+ return new TValue(b ? 1 : 0);
+ }
+
+ public static TValue fromJson(JsonValue json) {
+ return new TValue(json);
+ }
+
+ @Override
+ public String toString() {
+ if (jsonValue != null) {
+ return jsonValue.toString();
+ }
+ if (stringValue == null) {
+ return "" + intValue;
+ }
+ return stringValue;
+ }
+
+ public static TValue fromString(Token token) {
+ if (token.getTokenType() != TokenType.QUOTED_STRING) {
+ throw new IllegalArgumentException();
+ }
+ return new TValue(token.getSurface());
+ }
+
+ public static TValue fromString(String s) {
+ return new TValue(s);
+ }
+
+ public static TValue fromNumber(Token token) {
+ if (token.getTokenType() != TokenType.NUMBER) {
+ throw new IllegalArgumentException();
+ }
+ return new TValue(Integer.parseInt(token.getSurface()));
+ }
+
+ public TValue add(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return new TValue(this.intValue + v2.intValue);
+ }
+ return new TValue(toString() + v2.toString());
+ }
+
+ public TValue minus(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return new TValue(this.intValue - v2.intValue);
+ }
+ return new TValue(toString() + v2.toString());
+ }
+
+ public TValue multiply(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return new TValue(this.intValue * v2.intValue);
+ }
+ return new TValue(toString() + v2.toString());
+ }
+
+ public TValue dividedBy(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return new TValue(this.intValue / v2.intValue);
+ }
+ return new TValue(toString() + v2.toString());
+ }
+
+ public boolean isNumber() {
+ return this.jsonValue == null && this.stringValue == null;
+ }
+
+ public boolean isJson() {
+ return this.jsonValue != null;
+ }
+
+ public Token toToken() {
+ if (isNumber()) {
+ return new Token(toString(), TokenType.NUMBER, null);
+ }
+ if (isJson()) {
+ return new Token(toString(), TokenType.JSON_DATA, jsonValue);
+ }
+ return new Token(toString(), TokenType.QUOTED_STRING, null);
+ }
+
+ public TValue greaterThanOrEquals(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return fromBoolean(this.intValue >= v2.intValue);
+ }
+ return fromBoolean(toString().compareTo(v2.toString()) >= 0);
+ }
+
+ public TValue greaterThan(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return fromBoolean(this.intValue > v2.intValue);
+ }
+ return fromBoolean(toString().compareTo(v2.toString()) > 0);
+ }
+
+ public TValue lessThanOrEquals(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return fromBoolean(this.intValue <= v2.intValue);
+ }
+ return fromBoolean(toString().compareTo(v2.toString()) <= 0);
+ }
+
+ public TValue lessThan(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return fromBoolean(this.intValue < v2.intValue);
+ }
+ return fromBoolean(toString().compareTo(v2.toString()) < 0);
+ }
+
+ public TValue equalsOperation(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return fromBoolean(this.intValue == v2.intValue);
+ }
+ return fromBoolean(toString().compareTo(v2.toString()) == 0);
+ }
+
+ public TValue notEquals(TValue v2) {
+ if (this.isNumber() && v2.isNumber()) {
+ return fromBoolean(this.intValue != v2.intValue);
+ }
+ return fromBoolean(toString().compareTo(v2.toString()) != 0);
+ }
+
+ public boolean toBoolean() {
+ if (this.isNumber()) {
+ return this.intValue != 0;
+ }
+ return toString().length() > 0;
+ }
+
+ public int toInt() {
+ return this.intValue;
+ }
+
+ public TValue logicalAnd(TValue v2) {
+ return fromBoolean(this.toBoolean() && v2.toBoolean());
+ }
+
+ public TValue logicalOr(TValue v2) {
+ return fromBoolean(this.toBoolean() || v2.toBoolean());
+ }
+
+ public JsonValue toJson() {
+ return jsonValue;
+ }
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/Token.java b/src/net/sourceforge/plantuml/tim/expression/Token.java
new file mode 100644
index 0000000..245dd07
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/Token.java
@@ -0,0 +1,90 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import net.sourceforge.plantuml.json.JsonValue;
+
+public class Token {
+
+ private final String surface;
+ private final JsonValue json;
+ private final TokenType tokenType;
+
+ @Override
+ public String toString() {
+ return tokenType + "{" + surface + "}";
+ }
+
+ public Token(char surface, TokenType tokenType, JsonValue json) {
+ this("" + surface, tokenType, json);
+ }
+
+ public Token(String surface, TokenType tokenType, JsonValue json) {
+ this.surface = surface;
+ this.tokenType = tokenType;
+ this.json = json;
+ }
+
+ public TokenOperator getTokenOperator() {
+ if (this.tokenType != TokenType.OPERATOR) {
+ throw new IllegalStateException();
+ }
+ final char ch2 = surface.length() > 1 ? surface.charAt(1) : 0;
+ return TokenOperator.getTokenOperator(surface.charAt(0), ch2);
+ }
+
+ public final String getSurface() {
+ return surface;
+ }
+
+ public final TokenType getTokenType() {
+ return tokenType;
+ }
+
+ public Token muteToFunction() {
+ if (this.tokenType != TokenType.PLAIN_TEXT) {
+ throw new IllegalStateException();
+ }
+ return new Token(surface, TokenType.FUNCTION_NAME, null);
+ }
+
+ public JsonValue getJson() {
+ if (this.tokenType != TokenType.JSON_DATA) {
+ throw new IllegalStateException();
+ }
+ return json;
+ }
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/TokenIterator.java b/src/net/sourceforge/plantuml/tim/expression/TokenIterator.java
new file mode 100644
index 0000000..5f3e8a2
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/TokenIterator.java
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+public interface TokenIterator {
+
+ public Token nextToken();
+
+ public Token peekToken();
+
+ public boolean hasMoreTokens();
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/TokenOperator.java b/src/net/sourceforge/plantuml/tim/expression/TokenOperator.java
new file mode 100644
index 0000000..97964ab
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/TokenOperator.java
@@ -0,0 +1,138 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+
+//https://en.cppreference.com/w/c/language/operator_precedence
+
+public enum TokenOperator {
+ MULTIPLICATION(100 - 3, "*") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.multiply(v2);
+ }
+ },
+ DIVISION(100 - 3, "/") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.dividedBy(v2);
+ }
+ },
+ ADDITION(100 - 4, "+") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.add(v2);
+ }
+ },
+ SUBSTRACTION(100 - 4, "-") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.minus(v2);
+ }
+ },
+ LESS_THAN(100 - 6, "<") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.lessThan(v2);
+ }
+ },
+ GREATER_THAN(100 - 6, ">") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.greaterThan(v2);
+ }
+ },
+ LESS_THAN_OR_EQUALS(100 - 6, "<=") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.lessThanOrEquals(v2);
+ }
+ },
+ GREATER_THAN_OR_EQUALS(100 - 6, ">=") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.greaterThanOrEquals(v2);
+ }
+ },
+ EQUALS(100 - 7, "==") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.equalsOperation(v2);
+ }
+ },
+ NOT_EQUALS(100 - 7, "!=") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.notEquals(v2);
+ }
+ },
+ LOGICAL_AND(100 - 11, "&&") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.logicalAnd(v2);
+ }
+ },
+ LOGICAL_OR(100 - 12, "||") {
+ public TValue operate(TValue v1, TValue v2) {
+ return v1.logicalOr(v2);
+ }
+ },
+ ;
+
+ private final int precedence;
+ private final String display;
+
+ private TokenOperator(int precedence, String display) {
+ this.precedence = precedence;
+ this.display = display;
+ }
+
+ public boolean isLeftAssociativity() {
+ return true;
+ }
+
+ public static TokenOperator getTokenOperator(char ch, char ch2) {
+ for (TokenOperator op : TokenOperator.values()) {
+ if (op.display.length() == 2 && op.display.charAt(0) == ch && op.display.charAt(1) == ch2) {
+ return op;
+ }
+ }
+ for (TokenOperator op : TokenOperator.values()) {
+ if (op.display.length() == 1 && op.display.charAt(0) == ch) {
+ return op;
+ }
+ }
+ return null;
+ }
+
+ public final int getPrecedence() {
+ return precedence;
+ }
+
+ public abstract TValue operate(TValue v1, TValue v2);
+
+ public final String getDisplay() {
+ return display;
+ }
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/TokenStack.java b/src/net/sourceforge/plantuml/tim/expression/TokenStack.java
new file mode 100644
index 0000000..6547bf6
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/TokenStack.java
@@ -0,0 +1,223 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.tim.Eater;
+import net.sourceforge.plantuml.tim.EaterExceptionLocated;
+import net.sourceforge.plantuml.tim.EaterException;
+import net.sourceforge.plantuml.tim.TContext;
+import net.sourceforge.plantuml.tim.TMemory;
+
+public class TokenStack {
+
+ final private List<Token> tokens;
+
+ public TokenStack() {
+ this(new ArrayList<Token>());
+ }
+
+ private TokenStack(List<Token> list) {
+ this.tokens = list;
+ }
+
+ public int size() {
+ return tokens.size();
+ }
+
+ public TokenStack subTokenStack(int i) {
+ return new TokenStack(Collections.unmodifiableList(tokens.subList(i, tokens.size())));
+ }
+
+ @Override
+ public String toString() {
+ return tokens.toString();
+ }
+
+ public void add(Token token) {
+ this.tokens.add(token);
+ }
+
+ public TokenStack withoutSpace() {
+ final TokenStack result = new TokenStack();
+ for (Token token : tokens) {
+ if (token.getTokenType() != TokenType.SPACES) {
+ result.add(token);
+ }
+ }
+ return result;
+ }
+
+ static public TokenStack eatUntilCloseParenthesisOrComma(Eater eater) throws EaterException {
+ final TokenStack result = new TokenStack();
+ int level = 0;
+ while (true) {
+ eater.skipSpaces();
+ final char ch = eater.peekChar();
+ if (ch == 0) {
+ throw EaterException.unlocated("until001");
+ }
+ if (level == 0 && (ch == ',' || ch == ')')) {
+ return result;
+ }
+ final Token token = TokenType.eatOneToken(eater, false);
+ final TokenType type = token.getTokenType();
+ if (type == TokenType.OPEN_PAREN_MATH) {
+ level++;
+ } else if (type == TokenType.CLOSE_PAREN_MATH) {
+ level--;
+ }
+ result.add(token);
+ }
+ }
+
+ static public void eatUntilCloseParenthesisOrComma(TokenIterator it) throws EaterException {
+ int level = 0;
+ while (true) {
+ final Token ch = it.peekToken();
+ if (ch == null) {
+ throw EaterException.unlocated("until002");
+ }
+ final TokenType typech = ch.getTokenType();
+ if (level == 0 && (typech == TokenType.COMMA || typech == TokenType.CLOSE_PAREN_MATH)
+ || typech == TokenType.CLOSE_PAREN_FUNC) {
+ return;
+ }
+ final Token token = it.nextToken();
+ final TokenType type = token.getTokenType();
+ if (type == TokenType.OPEN_PAREN_MATH || type == TokenType.OPEN_PAREN_FUNC) {
+ level++;
+ } else if (type == TokenType.CLOSE_PAREN_MATH || type == TokenType.CLOSE_PAREN_FUNC) {
+ level--;
+ }
+ }
+ }
+
+ private int countFunctionArg(TokenIterator it) throws EaterException {
+ // return 42;
+ final TokenType type1 = it.peekToken().getTokenType();
+ if (type1 == TokenType.CLOSE_PAREN_MATH || type1 == TokenType.CLOSE_PAREN_FUNC) {
+ return 0;
+ }
+ int result = 1;
+ while (it.hasMoreTokens()) {
+ eatUntilCloseParenthesisOrComma(it);
+ final Token token = it.nextToken();
+ final TokenType type = token.getTokenType();
+ if (type == TokenType.CLOSE_PAREN_MATH || type == TokenType.CLOSE_PAREN_FUNC) {
+ return result;
+ } else if (type == TokenType.COMMA) {
+ result++;
+ } else {
+ throw EaterException.unlocated("count13");
+ }
+ }
+ throw EaterException.unlocated("count12");
+ }
+
+ public void guessFunctions() throws EaterException {
+ final Deque<Integer> open = new ArrayDeque<Integer>();
+ final Map<Integer, Integer> parens = new HashMap<Integer, Integer>();
+ for (int i = 0; i < tokens.size(); i++) {
+ final Token token = tokens.get(i);
+ if (token.getTokenType().equals(TokenType.OPEN_PAREN_MATH)) {
+ open.addFirst(i);
+ } else if (token.getTokenType().equals(TokenType.CLOSE_PAREN_MATH)) {
+ parens.put(open.pollFirst(), i);
+ }
+ }
+ // System.err.println("before=" + toString());
+ // System.err.println("guessFunctions2" + parens);
+ for (Map.Entry<Integer, Integer> ids : parens.entrySet()) {
+ final int iopen = ids.getKey();
+ final int iclose = ids.getValue();
+ assert tokens.get(iopen).getTokenType() == TokenType.OPEN_PAREN_MATH;
+ assert tokens.get(iclose).getTokenType() == TokenType.CLOSE_PAREN_MATH;
+ if (iopen > 0 && tokens.get(iopen - 1).getTokenType() == TokenType.PLAIN_TEXT) {
+ tokens.set(iopen - 1, new Token(tokens.get(iopen - 1).getSurface(), TokenType.FUNCTION_NAME, null));
+ final int nbArg = countFunctionArg(subTokenStack(iopen + 1).tokenIterator());
+ tokens.set(iopen, new Token("" + nbArg, TokenType.OPEN_PAREN_FUNC, null));
+ tokens.set(iclose, new Token(")", TokenType.CLOSE_PAREN_FUNC, null));
+ }
+ }
+ // System.err.println("after=" + toString());
+ }
+
+ class InternalIterator implements TokenIterator {
+
+ private int pos = 0;
+
+ public Token peekToken() {
+ return tokens.get(pos);
+ }
+
+ public Token nextToken() {
+ if (hasMoreTokens() == false) {
+ return null;
+ }
+ return tokens.get(pos++);
+ }
+
+ public boolean hasMoreTokens() {
+ return pos < tokens.size();
+ }
+
+ }
+
+ public TokenIterator tokenIterator() {
+ return new InternalIterator();
+ }
+
+ public TValue getResult(LineLocation location, TContext context, TMemory memory) throws EaterException, EaterExceptionLocated {
+ final Knowledge knowledge = context.asKnowledge(memory);
+ final TokenStack tmp = withoutSpace();
+ tmp.guessFunctions();
+ final TokenIterator it = tmp.tokenIterator();
+ final ShuntingYard shuntingYard = new ShuntingYard(it, knowledge);
+ final ReversePolishInterpretor rpn = new ReversePolishInterpretor(location, shuntingYard.getQueue(), knowledge,
+ memory, context);
+ return rpn.getResult();
+
+ }
+
+}
diff --git a/src/net/sourceforge/plantuml/tim/expression/TokenType.java b/src/net/sourceforge/plantuml/tim/expression/TokenType.java
new file mode 100644
index 0000000..f39951b
--- /dev/null
+++ b/src/net/sourceforge/plantuml/tim/expression/TokenType.java
@@ -0,0 +1,117 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info: http://plantuml.com
+ *
+ * If you like this project or if you find it useful, you can support us at:
+ *
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ *
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ *
+ * Original Author: Arnaud Roques
+ *
+ */
+package net.sourceforge.plantuml.tim.expression;
+
+import net.sourceforge.plantuml.tim.Eater;
+import net.sourceforge.plantuml.tim.EaterException;
+import net.sourceforge.plantuml.tim.TLineType;
+
+public enum TokenType {
+ QUOTED_STRING, JSON_DATA, OPERATOR, OPEN_PAREN_MATH, COMMA, CLOSE_PAREN_MATH, NUMBER, PLAIN_TEXT, SPACES, FUNCTION_NAME, OPEN_PAREN_FUNC, CLOSE_PAREN_FUNC;
+
+ private boolean isSingleChar1() {
+ return this == OPEN_PAREN_MATH || this == COMMA || this == CLOSE_PAREN_MATH;
+ }
+
+ private static boolean isPlainTextBreak(char ch, char ch2) {
+ final TokenType tmp = fromChar(ch, ch2);
+ if (tmp.isSingleChar1() || tmp == TokenType.OPERATOR || tmp == SPACES) {
+ return true;
+ }
+ return false;
+ }
+
+ private static TokenType fromChar(char ch, char ch2) {
+ TokenType result = PLAIN_TEXT;
+ if (TLineType.isQuote(ch)) {
+ result = QUOTED_STRING;
+ } else if (TokenOperator.getTokenOperator(ch, ch2) != null) {
+ result = OPERATOR;
+ } else if (ch == '(') {
+ result = OPEN_PAREN_MATH;
+ } else if (ch == ')') {
+ result = CLOSE_PAREN_MATH;
+ } else if (ch == ',') {
+ result = COMMA;
+ } else if (TLineType.isLatinDigit(ch)) {
+ result = NUMBER;
+ } else if (TLineType.isSpaceChar(ch)) {
+ result = SPACES;
+ }
+ return result;
+ }
+
+ final static public Token eatOneToken(Eater eater, boolean manageColon) throws EaterException {
+ final char ch = eater.peekChar();
+ if (ch == 0) {
+ return null;
+ }
+ if (manageColon && ch == ':') {
+ return null;
+ }
+ final TokenOperator tokenOperator = TokenOperator.getTokenOperator(ch, eater.peekCharN2());
+ if (TLineType.isQuote(ch)) {
+ return new Token(eater.eatAndGetQuotedString(), TokenType.QUOTED_STRING, null);
+ } else if (tokenOperator != null) {
+ if (tokenOperator.getDisplay().length() == 1) {
+ return new Token(eater.eatOneChar(), TokenType.OPERATOR, null);
+ }
+ return new Token("" + eater.eatOneChar() + eater.eatOneChar(), TokenType.OPERATOR, null);
+ } else if (ch == '(') {
+ return new Token(eater.eatOneChar(), TokenType.OPEN_PAREN_MATH, null);
+ } else if (ch == ')') {
+ return new Token(eater.eatOneChar(), TokenType.CLOSE_PAREN_MATH, null);
+ } else if (ch == ',') {
+ return new Token(eater.eatOneChar(), TokenType.COMMA, null);
+ } else if (TLineType.isLatinDigit(ch)) {
+ return new Token(eater.eatAndGetNumber(), TokenType.NUMBER, null);
+ } else if (TLineType.isSpaceChar(ch)) {
+ return new Token(eater.eatAndGetSpaces(), TokenType.SPACES, null);
+ }
+ return new Token(eatAndGetTokenPlainText(eater), TokenType.PLAIN_TEXT, null);
+ }
+
+ static private String eatAndGetTokenPlainText(Eater eater) throws EaterException {
+ final StringBuilder result = new StringBuilder();
+ while (true) {
+ final char ch = eater.peekChar();
+ if (ch == 0 || TokenType.isPlainTextBreak(ch, eater.peekCharN2())) {
+ return result.toString();
+ }
+ result.append(eater.eatOneChar());
+ }
+ }
+
+}