pax_global_header00006660000000000000000000000064131031377300014510gustar00rootroot0000000000000052 comment=d7fc247e53512e2a84fe63ef521ef478be744442 parsington-parsington-1.0.1/000077500000000000000000000000001310313773000160555ustar00rootroot00000000000000parsington-parsington-1.0.1/.gitignore000066400000000000000000000001021310313773000200360ustar00rootroot00000000000000# Maven # /target/ # Eclipse # /.project /.classpath /.settings/ parsington-parsington-1.0.1/.travis.yml000066400000000000000000000011041310313773000201620ustar00rootroot00000000000000language: java jdk: openjdk6 branches: only: master install: true script: ".travis/build.sh" after_success: ".travis/notify.sh Travis-Success" after_failure: ".travis/notify.sh Travis-Failure" env: global: - secure: ZuGY1ziNimZG6yhkEjUGmS4hCgcq5TxuZ4hTdlp8xJimnrdp9JCVFprAwldq/gUH7LPEoMuwq+cOlr7DnFHfGfVMmx+grreQ7HmucvEHWpET/vyfSa6/P+mJa2AVRLmnPutP6O0OkgD+NsZ8cqd3EAwgQUM1okZ3lx1XhgU2MRg= - secure: YDcd0PGzvzwVmqv1iyLuSpoRt3B2I/MaCnye8E+0NCvvF4IBGVG60dUsEAumraFo5Spvk4gPzPYGCVgrN6BbGUjZ4h4fXoXPDDgb+2wNX6HcyJB3DdIMx/065AThlkmgzT+v7x+AwBZlBXh+G1NiblsDn35s/eQYGPk78Xd6sWw= parsington-parsington-1.0.1/.travis/000077500000000000000000000000001310313773000174435ustar00rootroot00000000000000parsington-parsington-1.0.1/.travis/build.sh000077500000000000000000000003511310313773000211000ustar00rootroot00000000000000#!/bin/sh dir="$(dirname "$0")" if [ "$TRAVIS_SECURE_ENV_VARS" = true \ -a "$TRAVIS_PULL_REQUEST" = false \ -a "$TRAVIS_BRANCH" = master ] then mvn -Pdeploy-to-imagej deploy --settings "$dir/settings.xml" else mvn install fi parsington-parsington-1.0.1/.travis/notify.sh000077500000000000000000000002421310313773000213100ustar00rootroot00000000000000#!/bin/sh curl -fs "https://jenkins.imagej.net/job/$1/buildWithParameters?token=$TOKEN_NAME&repo=$TRAVIS_REPO_SLUG&commit=$TRAVIS_COMMIT&pr=$TRAVIS_PULL_REQUEST" parsington-parsington-1.0.1/.travis/settings.xml000066400000000000000000000005031310313773000220230ustar00rootroot00000000000000 imagej.releases travis ${env.MAVEN_PASS} imagej.snapshots travis ${env.MAVEN_PASS} parsington-parsington-1.0.1/LICENSE.txt000066400000000000000000000025071310313773000177040ustar00rootroot00000000000000Copyright (c) 2015 - 2016, Board of Regents of the University of Wisconsin-Madison. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. parsington-parsington-1.0.1/README.md000066400000000000000000000106321310313773000173360ustar00rootroot00000000000000[![](https://img.shields.io/maven-central/v/org.scijava/parsington.svg)](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.scijava%22%20AND%20a%3A%22parsington%22) [![](https://travis-ci.org/scijava/parsington.svg?branch=master)](https://travis-ci.org/scijava/parsington) # Parsington Parsington is an infix-to-postfix (or infix-to-syntax-tree) expression parser for mathematical expressions written in Java. It is simple yet fancy, handling (customizable) operators, functions, variables and constants in a similar way to what the Java language itself supports. Parsington is part of the [SciJava](http://scijava.org/) project for scientific computing in Java. ## Rationale Expression parsers are as old as the hills; what makes this one different? * __No dependencies.__ * __[Available on Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.scijava%22%20AND%20a%3A%22parsington%22).__ * __Permissive BSD-2 license.__ See [LICENSE.txt](LICENSE.txt). * __Separation of concerns.__ Parsington is a _parser_, not an _evaluator_. Once you have the postfix queue and/or syntax tree, what you do with it is your business (though there is a [small evaluation API in the eval subpackage](src/main/java/org/scijava/parse/eval) if that appeals to you). In general, there is no assumption that your variables will consist of any particular data type, numerical or otherwise. * __Clean, well-commented codebase with unit tests.__ 'Nuff said! ## History The [ImageJ Ops](https://github.com/imagej/imagej-ops) project needed an expression parser so that it could be more awesome. But not one limited to primitive doubles, or one that conflated parsing with evaluation, or one licensed in a restrictive way. Just a simple infix parser: a nice shunting yard implementation, or maybe some lovely recursive descent. Something on GitHub, with no dependencies, available on Maven Central. But surprisingly, there was only tumbleweed. So Parsington was born, and all our problems are now over! ## Usage In your POM ``: ```xml org.scijava parsington 1.0.0 ``` ### Postfix queues To parse an infix expression to a postfix queue: ```java LinkedList queue = new ExpressionParser().parsePostfix("a+b*c^f(1,2)'"); // queue = [a, b, c, f, 1, 2, (2), , ^, ', *, +] ``` ### Suffix trees To parse an infix expression to a suffix tree: ```java SuffixTree tree = new ExpressionParser().parseTree("a+b*c^f(1,2)'"); ``` ``` +-------+ | + | +---+---+ | +------+------+ | | +---+---+ +---+---+ | a | | * | +-------+ +---+---+ | +------+------+ | | +---+---+ +---+---+ | b | | ' | +-------+ +---+---+ | | +---+---+ | ^ | +---+---+ | +------+------+ | | +---+---+ +---+---+ | c | | | +-------+ +---+---+ | +------+------+ | | +---+---+ +---+---+ | f | | (2) | +-------+ +---+---+ | +------+------+ | | +---+---+ +---+---+ | 1 | | 2 | +-------+ +-------+ ``` ### Evaluation To evaluate an expression involving basic types: ```java Object result = new DefaultEvaluator().evaluate("6.5*7.8^2.3"); ``` ### Interactive console There is also an [interactive console shell](src/main/java/org/scijava/parse/Main.java) you can play with. Run it easily using [jrun](https://github.com/ctrueden/jrun): ``` jrun org.scijava:parsington ``` Or run from source, after cloning this repository: ```shell mvn java -jar target/parsington-*-SNAPSHOT.jar ``` ``` > 6.5*7.8^2.3 732.3706691398969 > postfix('6.5*7.8^2.3') 6.5 7.8 2.3 ^ * > tree('6.5*7.8^2.3') '*' - '6.5' - '^' -- '7.8' -- '2.3' > 2*3,4 ^ Misplaced separator or mismatched groups at index 4 ``` parsington-parsington-1.0.1/pom.xml000066400000000000000000000050721310313773000173760ustar00rootroot00000000000000 4.0.0 org.scijava pom-scijava 11.5.0 parsington 1.0.1 Parsington: The SciJava Expression Parser A general-purpose mathematical expression parser, which converts infix expression strings into postfix queues and/or syntax trees. https://github.com/scijava/parsington 2015 Simplified BSD License repo ctrueden Curtis Rueden http://imagej.net/User:Rueden founder lead developer debugger reviewer support maintainer None scm:git:git://github.com/scijava/parsington scm:git:git@github.com:scijava/parsington parsington-1.0.1 https://github.com/scijava/parsington GitHub Issues https://github.com/scijava/parsington/issues Travis CI https://travis-ci.org/scijava/parsington 1.6 org.scijava.parse.Main org.scijava.parse bsd_2 Parsington: the SciJava mathematical expression parser. Board of Regents of the University of Wisconsin-Madison. junit junit test parsington-parsington-1.0.1/src/000077500000000000000000000000001310313773000166445ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/000077500000000000000000000000001310313773000175705ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/java/000077500000000000000000000000001310313773000205115ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/java/org/000077500000000000000000000000001310313773000213005ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/java/org/scijava/000077500000000000000000000000001310313773000227205ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/java/org/scijava/parse/000077500000000000000000000000001310313773000240325ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/java/org/scijava/parse/ExpressionParser.java000066400000000000000000000331041310313773000302120ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.LinkedList; import java.util.List; /** * A parser for mathematical expressions, using Dijkstra's famous shunting-yard * algorithm. *

* It is important to note that this parser does not attempt to * evaluate the expression in any way; rather, it only provides the * parsed tree according to the desired operators. *

* * @author Curtis Rueden */ public class ExpressionParser { private final List operators; /** Creates an expression parser with the default set of operators. */ public ExpressionParser() { this(Operators.standardList()); } /** * Creates an expression parser with the given set of operators. * * @param operators The collection of operators available to expressions. */ public ExpressionParser(final Collection operators) { this.operators = new ArrayList(operators); // NB: Ensure operators with longer symbols come first. // This prevents e.g. '-' from being matched before '-=' and '--'. Collections.sort(this.operators, new Comparator() { @Override public int compare(final Operator o1, final Operator o2) { final String t1 = o1.getToken(); final String t2 = o2.getToken(); final int len1 = t1.length(); final int len2 = t2.length(); if (len1 > len2) return -1; // o1 is longer, so o1 comes first. if (len1 < len2) return 1; // o2 is longer, so o2 comes first. return t1.compareTo(t2); } }); } // -- ExpressionParser methods -- /** * Parses the given mathematical expression into a syntax tree. * * @param expression The mathematical expression to parse. * @return Parsed hierarchy of tokens. * @throws IllegalArgumentException if the syntax of the expression is * incorrect. */ public SyntaxTree parseTree(final String expression) { return new ParseOperation(expression).parseTree(); } /** * Parses the given mathematical expression into a queue in Reverse Polish * notation (i.e., postfix notation). * * @param expression The mathematical expression to parse. * @return Parsed queue of tokens in postfix notation. * @throws IllegalArgumentException if the syntax of the expression is * incorrect. */ public LinkedList parsePostfix(final String expression) { return new ParseOperation(expression).parsePostfix(); } // -- Helper classes -- /** A stateful parsing operation. */ private class ParseOperation { private final String expression; private final Position pos = new Position(); private final Deque stack = new ArrayDeque(); private final LinkedList outputQueue = new LinkedList(); /** * State flag for parsing context. *
    *
  • If true, we are expecting an infix or postfix operator next.
  • *
  • If false, we are expecting a prefix operator or "noun" token (e.g., * variable or literal) next.
  • *
*/ private boolean infix; public ParseOperation(final String expression) { this.expression = expression; } /** Parses the expression into a syntax tree. */ public SyntaxTree parseTree() { return new SyntaxTree(parsePostfix()); } /** * Parses the expression into an output queue in Reverse * Polish notation (i.e., postfix notation). */ public LinkedList parsePostfix() { // While there are tokens to be read... while (pos.get() < expression.length()) { // PROTIP: Put a breakpoint here, and watch the expression // parser do its thing piece by piece in your debugger! parseWhitespace(); // If next token is a literal, add it to the output queue. final Object literal = parseLiteral(); if (literal != null) { outputQueue.add(literal); // Update the state flag. infix = true; continue; } // If next token is a function argument separator (i.e., a comma)... final Character separator = parseComma(); if (separator != null) { handleGroupSeparator(); continue; } // If next token is a statement separator (i.e., a semicolon)... final Character semicolon = parseSemicolon(); if (semicolon != null) { // Flush the stack and begin a new statement. flushStack(); infix = false; continue; } // If the token is an operator... final Operator o1 = parseOperator(); if (o1 != null) { if (Tokens.isGroup(o1) && infix) { // NB: Group initiator symbol following a "noun" token; // we infer an implicit function operator between them. handleOperator(new Function(o1.getPrecedence())); } handleOperator(o1); continue; } // If the token is a group terminator... final Group group = parseGroupTerminator(); if (group != null) { handleGroupTerminator(group); continue; } // If next token is a variable, add it to the output queue. final Variable variable = parseVariable(); if (variable != null) { outputQueue.add(variable); // Update the state flag. infix = true; continue; } pos.die("Invalid character"); } // No more tokens to read! flushStack(); return outputQueue; } public char currentChar() { return futureChar(0); } public char futureChar(final int offset) { return pos.ch(expression, offset); } // -- State methods -- public boolean isPrefixOK() { return !infix; } public boolean isPostfixOK() { return infix; } public boolean isInfixOK() { return infix; } // -- Parsing methods -- /** Skips past any whitespace to the next interesting character. */ public void parseWhitespace() { while (Character.isWhitespace(currentChar())) pos.inc(); } /** * Attempts to parse a numeric literal. * * @return The parsed literal, or null if the next token is not one. * @see Literals#parseLiteral */ public Object parseLiteral() { // Only accept a literal in the appropriate context. // This avoids confusing e.g. the unary and binary minus // operators, or a quoted string with a quote operator. if (infix) return null; return Literals.parseLiteral(expression, pos); } /** * Attempts to parse a variable. * * @return The parsed variable name, or null if the next token is not one. */ public Variable parseVariable() { final int length = parseIdentifier(); if (length == 0) return null; return new Variable(parseToken(length)); } /** * Attempts to parse an identifier, as defined by * {@link Character#isUnicodeIdentifierStart(char)} and * {@link Character#isUnicodeIdentifierPart(char)}. * * @return The length of the parsed identifier, or 0 if the next * token is not one. */ public int parseIdentifier() { // Only accept an identifier in the appropriate context. if (infix) return 0; if (!Character.isUnicodeIdentifierStart(currentChar())) return 0; int length = 0; while (true) { final char next = futureChar(length); if (next == '\0') break; if (!Character.isUnicodeIdentifierPart(next)) break; length++; } return length; } /** * Attempts to parse an operator. * * @return The parsed operator, or null if the next token is not one. */ public Operator parseOperator() { for (final Operator op : operators) { final String symbol = op.getToken(); if (operatorMatches(op, symbol)) return op; } return null; } /** * Attempts to parse a group terminator symbol. * * @return The group, or null if the next token is not a group terminator. */ public Group parseGroupTerminator() { for (final Operator op : operators) { if (!(op instanceof Group)) continue; final Group group = (Group) op; final String symbol = group.getTerminator(); if (operatorMatches(op, symbol)) return group; } return null; } /** * Attempts to parse a comma character. * * @return The comma, or null if the next token is not one. */ public Character parseComma() { // Only accept a comma in the appropriate context. if (!infix) return null; return parseChar(','); } /** * Attempts to parse a semicolon character. * * @return The semicolon, or null if the next token is not one. */ public Character parseSemicolon() { return parseChar(';'); } /** * Attempts to parse the given character. * * @return The character, or null if the next token is not that character. */ public Character parseChar(final char c) { if (currentChar() == c) { pos.inc(); return c; } return null; } /** * Parses a token of the given length. * * @return The parsed token. */ public String parseToken(final int length) { final int offset = pos.get(); final String token = expression.substring(offset, offset + length); pos.inc(length); return token; } // -- Object methods -- @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(expression); sb.append("\n"); for (int i = 0; i < pos.get(); i++) { sb.append(" "); } sb.append("^"); return sb.toString(); } // -- Helper methods -- private void handleOperator(final Operator o1) { // While there is an operator token, o2, at the top of the stack... final double p1 = o1.getPrecedence(); while (!stack.isEmpty() && Tokens.isOperator(stack.peek()) && !Tokens.isGroup(stack.peek())) { final Operator o2 = (Operator) stack.peek(); final double p2 = o2.getPrecedence(); // ...and o1 has lower precedence than o2... if (o1.isLeftAssociative() && p1 <= p2 || // o1.isRightAssociative() && p1 < p2) { // Pop o2 off the stack, onto the output queue. outputQueue.add(stack.pop()); } else break; } // Push o1 onto the stack. stack.push(o1.instance()); // Update the state flag. if (o1.isPrefix() || o1.isInfix()) infix = false; else if (o1.isPostfix()) infix = true; else pos.fail("Impenetrable operator '" + o1 + "'"); } private void handleGroupSeparator() { // Pop from stack to output queue until function found. while (true) { if (stack.isEmpty()) { pos.die("Misplaced separator or mismatched groups"); } if (Tokens.isGroup(stack.peek())) { // Count the completed argument in the group's arity. ((Group) stack.peek()).incArity(); break; } outputQueue.add(stack.pop()); } // Update the state flag. infix = false; } private void handleGroupTerminator(final Group group) { // Pop from stack to output queue until matching group found. while (true) { if (stack.isEmpty()) { // No group found: mismatched group symbols! pos.die("Mismatched group terminator '" + group.getTerminator() + "'"); } // If token is a group... if (Tokens.isMatchingGroup(stack.peek(), group)) { // Count the completed argument in the group's arity. if (infix) ((Group) stack.peek()).incArity(); // Pop the group onto the output queue. outputQueue.add(stack.pop()); break; } outputQueue.add(stack.pop()); } // Update the state flag. infix = true; } private void flushStack() { // While there are still operator tokens in the stack... while (!stack.isEmpty()) { final Object token = stack.pop(); // There shouldn't be any groups left. if (Tokens.isGroup(token)) pos.die("Mismatched groups"); // Pop the operator onto the output queue. outputQueue.add(token); } } private boolean operatorMatches(final Operator op, final String symbol) { if (!expression.startsWith(symbol, pos.get())) return false; // Ensure the operator is appropriate to the current context. if (isPrefixOK() && op.isPrefix() || // isPostfixOK() && op.isPostfix() || // isInfixOK() && op.isInfix()) { pos.inc(symbol.length()); return true; } return false; } } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Function.java000066400000000000000000000042121310313773000264610ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * A function is an implicit binary operator between two "noun" * tokens—typically between a variable on the left and a group on the * right, in which case the function's precedence is inferred from the group. *

* Examples: *

*
    *
  • {@code f()} → {@code f (0) }
  • *
  • {@code f(a)} → {@code f a (1) }
  • *
  • {@code f(a, b)} → {@code f a b (2) }
  • *
  • {@code f(g(a))} → {@code f g a (1) (1) }
  • *
* * @author Curtis Rueden */ public class Function extends Operator { public Function(final double precedence) { super("", 2, Associativity.LEFT, precedence); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Group.java000066400000000000000000000067321310313773000260010ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * A group is a special N-ary operator delineated by a left-hand symbol and a * right-hand symbol, with comma-separated arguments. *

* Typically, these are various forms of parentheses, although in principle any * pair of two distinct symbols is allowed. *

* * @author Curtis Rueden */ public class Group extends Operator { private final String terminator; private int arity; public Group(final String initiator, final String terminator, final double precedence) { super(initiator, 0, Associativity.NONE, precedence); this.terminator = terminator; } // -- Group methods -- public String getTerminator() { return terminator; } public void incArity() { arity++; } /** * Returns true iff the given group is the same as this one, in terms of token * (lefthand symbol), terminator (righthand symbol) and precedence. *

* Note that this method intentionally does not compare arity; the idea is * that if you have a {@link Group} and call {@link #instance} to duplicate * it, that copy will match this one, even though the copy initially starts at * arity 0. *

*/ public boolean matches(final Group g) { return getToken().equals(g.getToken()) && getTerminator().equals(g.getTerminator()) && getPrecedence() == g.getPrecedence(); } // -- Operator methods -- @Override public int getArity() { return arity; } @Override public boolean isInfix() { return true; } @Override public boolean isPrefix() { return true; } /** * Creates an instance of a group operator, using this one as a template. *

* The created group will have the same initiator and terminator symbols, as * well as the same precedence. But it will begin as a nullary group until * {@link #incArity} is called. *

*/ @Override public Group instance() { return new Group(getToken(), getTerminator(), getPrecedence()); } // -- Object methods -- @Override public String toString() { return getToken() + getArity() + getTerminator(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Literals.java000066400000000000000000000500301310313773000264520ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import java.math.BigDecimal; import java.math.BigInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Utility methods for parsing literals from strings. These methods largely * conform to the Java specification's ideas of what constitutes a numeric or * string literal. * * @author Curtis Rueden */ public final class Literals { private static final Pattern HEX = Pattern .compile("(([-+]?)0[Xx]([0-9a-fA-F]+)([Ll]?)).*"); private static final Pattern BINARY = Pattern .compile("(([-+]?)0[Bb]([01]+)([Ll]?)).*"); private static final Pattern OCTAL = Pattern .compile("(([-+]?)0([0-7]+)([Ll]?)).*"); private static final Pattern DECIMAL = Pattern .compile("(([-+]?[0-9]+(\\.[0-9]*)?([Ee][0-9]+)?)([Dd]|[Ff]|[Ll])?).*"); private Literals() { // NB: Prevent instantiation of utility class. } /** * Parses a boolean literal (i.e., true and false). * * @param s The string from which the boolean literal should be parsed. * @return The parsed boolean value—either {@link Boolean#TRUE} or * {@link Boolean#FALSE}— or null if the string does not begin * with a boolean literal. */ public static Boolean parseBoolean(final CharSequence s) { return parseBoolean(s, new Position()); } /** * Parses a string literal which is enclosed in single or double quotes. *

* For literals in double quotes, this parsing mechanism is intended to be as * close as possible to the numeric literals supported by the Java programming * language itself. Literals in single quotes are completely verbatim, with no * escaping performed. *

* * @param s The string from which the string literal should be parsed. * @return The parsed string value, unescaped according to Java conventions. * Returns null if the string does not begin with a single or double * quote. */ public static String parseString(final CharSequence s) { return parseString(s, new Position()); } /** * Parses a hexidecimal literal (e.g., {@code 0xfedcba9876543210}). * * @param s The string from which the numeric literal should be parsed. * @return The parsed numeric value—an {@link Integer} if sufficiently * small, or a {@link Long} if needed or if the {@code L} suffix is * given; or a {@link BigInteger} if the value is too large even for * {@code long}. */ public static Number parseHex(final CharSequence s) { return parseHex(s, new Position()); } /** * Parses a binary literal (e.g., {@code 0b010101000011}). * * @param s The string from which the numeric literal should be parsed. * @return The parsed numeric value—an {@link Integer} if sufficiently * small, or a {@link Long} if needed or if the {@code L} suffix is * given; or a {@link BigInteger} if the value is too large even for * {@code long}. */ public static Number parseBinary(final CharSequence s) { return parseBinary(s, new Position()); } /** * Parses an octal literal (e.g., {@code 01234567}). * * @param s The string from which the numeric literal should be parsed. * @return The parsed numeric value—an {@link Integer} if sufficiently * small, or a {@link Long} if needed or if the {@code L} suffix is * given; or a {@link BigInteger} if the value is too large even for * {@code long}. */ public static Number parseOctal(final CharSequence s) { return parseOctal(s, new Position()); } /** * Parses a decimal literal (integer or otherwise; e.g., {@code 1234567890}, * {@code 1234.0987} or {@code 1.2e34}). * * @param s The string from which the numeric literal should be parsed. * @return The parsed numeric value, of a type consistent with Java's support * for numeric primitives—or for values outside the normal range * of Java primitives, {@link BigInteger} or {@link BigDecimal} as * appropriate. Returns null if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseDecimal(final CharSequence s) { return parseDecimal(s, new Position()); } /** * Parses a numeric literal of any known type. *

* This parsing mechanism is intended to be as close as possible to the * numeric literals supported by the Java programming language itself. *

* * @param s The string from which the numeric literal should be parsed. * @return The parsed numeric value, of a type consistent with Java's support * for numeric primitives—or for values outside the normal range * of Java primitives, {@link BigInteger} or {@link BigDecimal} as * appropriate. Returns null if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseNumber(final CharSequence s) { return parseNumber(s, new Position()); } /** * Parses a literal of any known type (booleans, strings and numbers). * * @param s The string from which the literal should be parsed. * @return The parsed value, of a type consistent with Java's support for * literals: either {@link Boolean}, {@link String} or a concrete * {@link Number} subclass. Returns null if the string does * not match the syntax of a known literal. * @see #parseBoolean(CharSequence) * @see #parseString(CharSequence) * @see #parseNumber(CharSequence) */ public static Object parseLiteral(final CharSequence s) { return parseLiteral(s, new Position()); } /** * Parses a boolean literal (i.e., true and false). * * @param s The string from which the boolean literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed boolean value—either {@link Boolean#TRUE} or * {@link Boolean#FALSE}— or null if the string does not begin * with a boolean literal. */ public static Boolean parseBoolean(final CharSequence s, final Position pos) { if (isWord(s, pos, "true")) { pos.inc(4); return Boolean.TRUE; } if (isWord(s, pos, "false")) { pos.inc(5); return Boolean.FALSE; } return null; } /** * Parses a string literal which is enclosed in single or double quotes. *

* For literals in double quotes, this parsing mechanism is intended to be as * close as possible to the numeric literals supported by the Java programming * language itself. Literals in single quotes are completely verbatim, with no * escaping performed. *

* * @param s The string from which the string literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed string value, unescaped according to Java conventions. * Returns null if the string does not begin with a single or double * quote. */ public static String parseString(final CharSequence s, final Position pos) { final char quote = pos.ch(s); if (quote != '"' && quote != '\'') return null; int index = pos.get() + 1; boolean escaped = false; final StringBuilder sb = new StringBuilder(); while (true) { if (index >= s.length()) pos.die("Unclosed string literal"); final char c = s.charAt(index); if (escaped) { escaped = false; if (isOctal(c)) { // octal sequence String octal = "" + c; final char c1 = pos.ch(s, index + 1); if (isOctal(c1)) { octal += c1; if (c >= '0' && c <= '3') { final char c2 = pos.ch(s, index + 2); if (isOctal(c2)) octal += c2; } } sb.append((char) Integer.parseInt(octal, 8)); index += octal.length(); continue; } switch (c) { case 'b': // backspace sb.append('\b'); break; case 't': // tab sb.append('\t'); break; case 'n': // linefeed sb.append('\n'); break; case 'f': // form feed sb.append('\f'); break; case 'r': // carriage return sb.append('\r'); break; case '"': // double quote sb.append('"'); break; case '\\': // backslash sb.append('\\'); break; case 'u': // unicode sequence final char u1 = hex(s, pos, index + 1); final char u2 = hex(s, pos, index + 2); final char u3 = hex(s, pos, index + 3); final char u4 = hex(s, pos, index + 4); sb.append((char) Integer.parseInt("" + u1 + u2 + u3 + u4, 16)); index += 4; break; default: // invalid escape pos.die("Invalid escape sequence"); } } else if (c == '\\' && quote == '"') escaped = true; else if (c == quote) break; else sb.append(c); index++; } pos.set(index + 1); return sb.toString(); } /** * Parses a hexidecimal literal (e.g., {@code 0xfedcba9876543210}). * * @param s The string from which the numeric literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed numeric value—an {@link Integer} if sufficiently * small, or a {@link Long} if needed or if the {@code L} suffix is * given; or a {@link BigInteger} if the value is too large even for * {@code long}; or {@code null} if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseHex(final CharSequence s, final Position pos) { return parseInteger(HEX, s, pos, 16); } /** * Parses a binary literal (e.g., {@code 0b010101000011}). * * @param s The string from which the numeric literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed numeric value—an {@link Integer} if sufficiently * small, or a {@link Long} if needed or if the {@code L} suffix is * given; or a {@link BigInteger} if the value is too large even for * {@code long}; or {@code null} if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseBinary(final CharSequence s, final Position pos) { return parseInteger(BINARY, s, pos, 2); } /** * Parses an octal literal (e.g., {@code 01234567}). * * @param s The string from which the numeric literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed numeric value—an {@link Integer} if sufficiently * small, or a {@link Long} if needed or if the {@code L} suffix is * given; or a {@link BigInteger} if the value is too large even for * {@code long}; or {@code null} if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseOctal(final CharSequence s, final Position pos) { return parseInteger(OCTAL, s, pos, 8); } /** * Parses a decimal literal (e.g., {@code 1234.0987} or {@code 1.2e34}). * * @param s The string from which the numeric literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed numeric value, of a type consistent with Java's support * for numeric primitives—or for values outside the normal range * of Java primitives, {@link BigInteger} or {@link BigDecimal} as * appropriate. Returns null if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseDecimal(final CharSequence s, final Position pos) { if (!isNumberSyntax(s, pos)) return null; final Matcher m = matcher(DECIMAL, s, pos); if (!m.matches()) return null; final String number = m.group(2); final String force = m.group(5); final boolean forceLong = "l".equalsIgnoreCase(force); final boolean forceFloat = "f".equalsIgnoreCase(force); final boolean forceDouble = "d".equalsIgnoreCase(force); Number result = null; if (!forceFloat && !forceDouble) { result = parseInteger(number, forceLong, 10); } if (result == null && !forceLong) { result = parseDecimal(number, forceFloat, forceDouble); } return verifyResult(result, m, pos); } /** * Parses a numeric literal of any known type. *

* This parsing mechanism is intended to be as close as possible to the * numeric literals supported by the Java programming language itself. *

* * @param s The string from which the numeric literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed numeric value, of a type consistent with Java's support * for numeric primitives—or for values outside the normal range * of Java primitives, {@link BigInteger} or {@link BigDecimal} as * appropriate. Returns null if the string does not begin with the * numeric literal telltale of a 0-9 digit with optional minus. */ public static Number parseNumber(final CharSequence s, final Position pos) { final Number hex = parseHex(s, pos); if (hex != null) return hex; final Number binary = parseBinary(s, pos); if (binary != null) return binary; final Number octal = parseOctal(s, pos); if (octal != null) return octal; final Number decimal = parseDecimal(s, pos); if (decimal != null) return decimal; return null; } /** * Parses a literal of any known type (booleans, strings and numbers). * * @param s The string from which the literal should be parsed. * @param pos The offset from which the literal should be parsed. If parsing * is successful, the position will be advanced to the next index * after the parsed literal. * @return The parsed value, of a type consistent with Java's support for * literals: either {@link Boolean}, {@link String} or a concrete * {@link Number} subclass. Returns null if the string does * not match the syntax of a known literal. * @see #parseBoolean(CharSequence, Position) * @see #parseString(CharSequence, Position) * @see #parseNumber(CharSequence, Position) */ public static Object parseLiteral(final CharSequence s, final Position pos) { final Boolean bool = parseBoolean(s, pos); if (bool != null) return bool; final String str = parseString(s, pos); if (str != null) return str; final Number num = parseNumber(s, pos); if (num != null) return num; return null; } // -- Helper methods -- private static boolean isOctal(final char c) { return c >= '0' && c <= '7'; } private static char hex(final CharSequence s, final Position pos, final int index) { final char c = pos.ch(s, index); if (c >= '0' && c <= '9') return c; if (c >= 'a' && c <= 'f') return c; if (c >= 'A' && c <= 'F') return c; pos.die("Invalid unicode sequence"); return '\0'; // NB: Unreachable. } private static boolean isNumberSyntax(final CharSequence s, final Position pos) { final int i = pos.get(); final boolean sign = s.charAt(i) == '-' || s.charAt(i) == '+'; final char digit = s.charAt(sign ? i + 1 : i); return digit >= '0' && digit <= '9'; } private static Number parseInteger(final Pattern p, final CharSequence s, final Position pos, final int base) { if (!isNumberSyntax(s, pos)) return null; final Matcher m = matcher(p, s, pos); if (!m.matches()) return null; String sign = m.group(2); if (sign == null) sign = ""; final String number = sign + m.group(3); final boolean forceLong = !m.group(4).isEmpty(); final Number result = parseInteger(number, forceLong, base); return verifyResult(result, m, pos); } private static Number parseInteger(final String number, final boolean forceLong, final int base) { if (!forceLong) { // Try to fit it into an int. try { return Integer.parseInt(number, base); } catch (final NumberFormatException exc) { // NB: No action needed. } } // Try to fit it into a long. try { return Long.parseLong(number, base); } catch (final NumberFormatException exc) { // NB: No action needed. } if (!forceLong) { // Try to treat it as a BigInteger. try { return new BigInteger(number, base); } catch (final NumberFormatException exc) { // NB: No action needed. } } return null; } private static Number parseDecimal(final String number, final boolean forceFloat, final boolean forceDouble) { if (forceFloat) { // Try to fit it into a flaot. try { return Float.parseFloat(number); } catch (final NumberFormatException exc) { // NB: No action needed. } } else { // Try to fit it into a double. try { return Double.parseDouble(number); } catch (final NumberFormatException exc) { // NB: No action needed. } } if (!forceDouble && !forceFloat) { // Try to treat it as a BigDecimal. try { return new BigDecimal(number); } catch (final NumberFormatException exc) { // NB: No action needed. } } return null; } private static Matcher matcher(final Pattern p, final CharSequence s, final Position pos) { return p.matcher(sub(s, pos)); } private static CharSequence sub(final CharSequence s, final Position pos) { return pos.get() == 0 ? s : new SubSequence(s, pos.get()); } private static Number verifyResult(final Number result, final Matcher m, final Position pos) { if (result == null) pos.die("Illegal numeric literal"); pos.inc(m.group(1).length()); return result; } private static boolean isWord(final CharSequence s, final Position pos, final String word) { if (s.length() - pos.get() < word.length()) return false; for (int i=0; i= 'a' && next <= 'z') return false; if (next >= 'A' && next <= 'Z') return false; if (next >= '0' && next <= '9') return false; if (next == '_') return false; return true; } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Main.java000066400000000000000000000036211310313773000255630ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import java.io.IOException; import org.scijava.parse.eval.EvaluatorConsole; /** * Launches the console-driven expression evaluator. * * @author Curtis Rueden * @see EvaluatorConsole */ public final class Main { private Main() { // Prevent instantiation of utility class. } // -- Main method -- public static void main(final String[] args) throws IOException { new EvaluatorConsole().showConsole(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Operator.java000066400000000000000000000067661310313773000265070ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * A mathematical operator is a "verb": a special infix (in the case of binary * or greater arity) or prefix (in the case of unary) symbol which defines a * relation between "nouns" (i.e.: literals and variables). * * @author Curtis Rueden */ public class Operator extends Token implements Comparable { public enum Associativity { EITHER, LEFT, RIGHT, NONE } private final int arity; private final Associativity associativity; private final double precedence; public Operator(final String symbol, final int arity, final Associativity associativity, final double precedence) { super(symbol); this.arity = arity; this.associativity = associativity; this.precedence = precedence; } // -- Operator methods -- /** 1 for unary, 2 for binary, etc. */ public int getArity() { return arity; } public Associativity getAssociativity() { return associativity; } public boolean isLeftAssociative() { final Associativity a = getAssociativity(); return a == Associativity.LEFT || a == Associativity.EITHER; } public boolean isRightAssociative() { final Associativity a = getAssociativity(); return a == Associativity.RIGHT || a == Associativity.EITHER; } /** True iff the operator is an infix operator (e.g., {@code a-b}). */ public boolean isInfix() { return getArity() > 1; } /** True iff the operator is a prefix operator (e.g., {@code -a}). */ public boolean isPrefix() { return getArity() == 1 && isRightAssociative(); } /** True iff the operator is a postfix operator (e.g., {@code a'}). */ public boolean isPostfix() { return getArity() == 1 && isLeftAssociative(); } public double getPrecedence() { return precedence; } public Operator instance() { // NB: Properties are immutable, so instance can be reused. return this; } // -- Comparable methods -- @Override public int compareTo(final Operator that) { final double thisP = getPrecedence(); final double thatP = that.getPrecedence(); if (thisP == thatP) return 0; return thisP < thatP ? -1 : 1; } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Operators.java000066400000000000000000000167711310313773000266670ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import static org.scijava.parse.Operator.Associativity.LEFT; import static org.scijava.parse.Operator.Associativity.RIGHT; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.scijava.parse.Operator.Associativity; /** * A collection of standard {@link Operator}s. This set of operators was * synthesized by combining Java standard operators and MATLAB standard operators. * * @author Curtis Rueden */ public final class Operators { // -- dot -- public static final Operator DOT = op(".", 2, LEFT, 16); // -- groups -- public static final Group PARENS = group("(", ")", 16); public static final Group BRACKETS = group("[", "]", 16); public static final Group BRACES = group("{", "}", 16); // -- transpose, power -- public static final Operator TRANSPOSE = op("'", 1, LEFT, 15); public static final Operator DOT_TRANSPOSE = op(".'", 1, LEFT, 15); public static final Operator POW = op("^", 2, RIGHT, 15); public static final Operator DOT_POW = op(".^", 2, RIGHT, 15); // -- postfix -- public static final Operator POST_INC = op("++", 1, LEFT, 14); public static final Operator POST_DEC = op("--", 1, LEFT, 14); // -- unary -- public static final Operator PRE_INC = op("++", 1, RIGHT, 13); public static final Operator PRE_DEC = op("--", 1, RIGHT, 13); public static final Operator POS = op("+", 1, RIGHT, 13); public static final Operator NEG = op("-", 1, RIGHT, 13); public static final Operator COMPLEMENT = op("~", 1, RIGHT, 13); public static final Operator NOT = op("!", 1, RIGHT, 13); // -- multiplicative -- public static final Operator MUL = op("*", 2, LEFT, 12); public static final Operator DIV = op("/", 2, LEFT, 12); public static final Operator MOD = op("%", 2, LEFT, 12); public static final Operator RIGHT_DIV = op("\\", 2, LEFT, 12); public static final Operator DOT_MUL = op(".*", 2, LEFT, 12); public static final Operator DOT_DIV = op("./", 2, LEFT, 12); public static final Operator DOT_RIGHT_DIV = op(".\\", 2, LEFT, 12); // -- additive -- public static final Operator ADD = op("+", 2, LEFT, 11); public static final Operator SUB = op("-", 2, LEFT, 11); // -- shift -- public static final Operator LEFT_SHIFT = op("<<", 2, LEFT, 10); public static final Operator RIGHT_SHIFT = op(">>", 2, LEFT, 10); public static final Operator UNSIGNED_RIGHT_SHIFT = op(">>>", 2, LEFT, 10); // -- colon -- public static final Operator COLON = op(":", 2, LEFT, 9); // -- relational -- public static final Operator LESS_THAN = op("<", 2, LEFT, 8); public static final Operator GREATER_THAN = op(">", 2, LEFT, 8); public static final Operator LESS_THAN_OR_EQUAL = op("<=", 2, LEFT, 8); public static final Operator GREATER_THAN_OR_EQUAL = op(">=", 2, LEFT, 8); public static final Operator INSTANCEOF = op("instanceof", 2, LEFT, 8); // -- equality -- public static final Operator EQUAL = op("==", 2, LEFT, 7); public static final Operator NOT_EQUAL = op("!=", 2, LEFT, 7); // -- bitwise AND -- public static final Operator BITWISE_AND = op("&", 2, LEFT, 6); // -- bitwise exclusive OR -- // NB: No bitwise XOR operator, because '^' is reserved for POW above. //public static final Operator BITWISE_XOR = op("^", 2, LEFT, 5); // -- bitwise inclusive OR -- public static final Operator BITWISE_OR = op("|", 2, LEFT, 4); // -- logical AND -- public static final Operator LOGICAL_AND = op("&&", 2, LEFT, 3); // -- logical OR -- public static final Operator LOGICAL_OR = op("||", 2, LEFT, 2); // -- ternary -- // NB: Ternary (? :) operator is not currently supported. // -- assignment -- public static final Operator ASSIGN = op("=", 2, RIGHT, 0); public static final Operator POW_ASSIGN = op("^=", 2, RIGHT, 0); public static final Operator DOT_POW_ASSIGN = op(".^=", 2, RIGHT, 0); public static final Operator MUL_ASSIGN = op("*=", 2, RIGHT, 0); public static final Operator DIV_ASSIGN = op("/=", 2, RIGHT, 0); public static final Operator MOD_ASSIGN = op("%=", 2, RIGHT, 0); public static final Operator RIGHT_DIV_ASSIGN = op("\\=", 2, RIGHT, 0); public static final Operator DOT_DIV_ASSIGN = op("./=", 2, RIGHT, 0); public static final Operator DOT_RIGHT_DIV_ASSIGN = op(".\\=", 2, RIGHT, 0); public static final Operator ADD_ASSIGN = op("+=", 2, RIGHT, 0); public static final Operator SUB_ASSIGN = op("-=", 2, RIGHT, 0); public static final Operator AND_ASSIGN = op("&=", 2, RIGHT, 0); public static final Operator OR_ASSIGN = op("|=", 2, RIGHT, 0); public static final Operator LEFT_SHIFT_ASSIGN = op("<<=", 2, RIGHT, 0); public static final Operator RIGHT_SHIFT_ASSIGN = op(">>=", 2, RIGHT, 0); public static final Operator UNSIGNED_RIGHT_SHIFT_ASSIGN = op(">>>=", 2, RIGHT, 0); private Operators() { // NB: Prevent instantiation of utility class. } /** Gets the standard list of operators. */ public static List standardList() { // Build the standard list from all available Operator constants. final ArrayList ops = new ArrayList(); for (final Field f : Operators.class.getFields()) { if (!isOperator(f)) continue; try { ops.add((Operator) f.get(null)); } catch (final IllegalAccessException exc) { // This should never happen. throw new IllegalStateException(exc); } } return ops; } // -- Helper methods -- private static Operator op(final String symbol, final int arity, final Associativity associativity, final double precedence) { return new Operator(symbol, arity, associativity, precedence); } private static Group group(final String leftSymbol, final String rightSymbol, final double precedence) { return new Group(leftSymbol, rightSymbol, precedence); } private static boolean isOperator(final Field f) { final int mods = f.getModifiers(); return Modifier.isStatic(mods) && Modifier.isFinal(mods) && Operator.class.isAssignableFrom(f.getType()); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Position.java000066400000000000000000000054621310313773000265100ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * A mutable parse position. Similar to {@link java.text.ParsePosition}, but * less complex. * * @author Curtis Rueden */ public class Position { private int index; public int get() { return index; } public void set(final int index) { this.index = index; } public void inc() { inc(1); } public void inc(final int count) { index += count; } public char ch(final CharSequence s) { return ch(s, 0); } public char ch(final CharSequence s, final int offset) { final int i = get() + offset; return i < s.length() ? s.charAt(i) : '\0'; } /** Throws {@link IllegalArgumentException} when syntax is incorrect. */ public void die(final String message) { throw new IllegalArgumentException(messageWithDetails(message)); } /** Throws {@link IllegalStateException} if something goes wrong. */ public void assertThat(final boolean condition, final String message) { if (condition) return; fail(message); } /** Throws {@link IllegalStateException} when something is wrong. */ public void fail(final String message) { throw new IllegalStateException(messageWithDetails(message)); } // -- Object methods -- @Override public String toString() { return "" + get(); } // -- Helper methods -- private String messageWithDetails(final String message) { return message + " at index " + get(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/SubSequence.java000066400000000000000000000065261310313773000271300ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * A {@link CharSequence} which is a by-reference subsequence of another * {@link CharSequence}. This is particularly useful for * {@link java.util.regex.Pattern regex} matching without excessive string * copying. *

* Surprisingly, core Java does not seem to have this capability (apart from * {@link javax.swing.text.Segment}, which seems misplaced in the Swing * library); all of {@link String#subSequence}, {@link StringBuffer#subSequence} * and {@link StringBuilder#subSequence} internally copy the requested string * segment. *

* * @author Curtis Rueden */ public class SubSequence implements CharSequence { private final CharSequence seq; private final int offset; private final int length; public SubSequence(final CharSequence seq, final int offset) { this(seq, offset, seq.length() - offset); } private static void outOfBounds(final String message) { throw new IndexOutOfBoundsException(message); } public SubSequence(final CharSequence seq, final int offset, final int length) { if (offset < 0) outOfBounds("Offset " + offset + " < 0"); if (offset > seq.length()) { outOfBounds("Offset " + offset + " > " + seq.length()); } if (length < 0) outOfBounds("Length " + length + " < 0"); if (offset + length > seq.length()) { outOfBounds("Offset " + offset + " + length " + length + " > " + seq.length()); } this.seq = seq; this.offset = offset; this.length = length; } // -- CharSequence methods -- @Override public int length() { return length; } @Override public char charAt(final int index) { return seq.charAt(offset + index); } @Override public SubSequence subSequence(final int start, final int end) { return new SubSequence(seq, offset + start, end - start); } // -- Object methods -- @Override public String toString() { return seq.subSequence(offset, offset + length).toString(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/SyntaxTree.java000066400000000000000000000101221310313773000267770ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; /** * A syntax * tree corresponding to an expression. * * @author Curtis Rueden */ public class SyntaxTree implements Iterable { private final Object token; private SyntaxTree[] children; /** * Creates a syntax tree built from the given postfix * token queue. This process will consume the entire queue. * * @param tokens The token queue, in postfix order. */ public SyntaxTree(final LinkedList tokens) { token = tokens.removeLast(); if (Tokens.isOperator(token)) { final Operator op = (Operator) token; final int arity = op.getArity(); if (arity > 0) children = new SyntaxTree[arity]; for (int i = children.length - 1; i >= 0; i--) { children[i] = new SyntaxTree(tokens); } } } public Object token() { return token; } public SyntaxTree child(final int index) { return children[index]; } public int count() { return children == null ? 0 : children.length; } /** Converts the syntax tree into a token queue in postfix order. */ public LinkedList postfix() { final LinkedList queue = new LinkedList(); postfix(queue); return queue; } // -- Object methods -- @Override public String toString() { return toString(""); } @Override public boolean equals(final Object o) { if (!(o instanceof SyntaxTree)) return false; final SyntaxTree tree = (SyntaxTree) o; return token.equals(tree.token) && Arrays.equals(children, tree.children); } @Override public int hashCode() { return token.hashCode() ^ children.hashCode(); } // -- Iterable methods -- @Override public Iterator iterator() { return new Iterator() { private int index; @Override public boolean hasNext() { return index < count(); } @Override public SyntaxTree next() { return child(index++); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } // -- Helper methods -- private void postfix(final LinkedList queue) { for (final SyntaxTree child : this) { child.postfix(queue); } queue.add(token()); } private String toString(final String prefix) { final StringBuilder sb = new StringBuilder(); sb.append(prefix + " '" + token + "'\n"); final String deeperPrefix = " " + prefix + "-"; for (int i = 0; i < count(); i++) { sb.append(child(i).toString(deeperPrefix)); } return sb.toString(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Token.java000066400000000000000000000041551310313773000257620ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * Base class for various types of tokens: operators, groups, functions and * variables. *

* The only exception are literals, which use the standard Java types of * {@link String}, {@link Boolean} and {@link Number} rather than extending this * class. *

* * @author Curtis Rueden */ public abstract class Token { private final String token; public Token(final String token) { this.token = token; } // -- Token methods -- /** Gets the token's sequence of characters. */ public String getToken() { return token; } // -- Object methods -- @Override public String toString() { return getToken(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Tokens.java000066400000000000000000000045201310313773000261410ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * Utility methods for working with tokens. * * @author Curtis Rueden */ public final class Tokens { private Tokens() { // NB: Prevent instantiation of utility class. } public static boolean isNumber(final Object o) { return o instanceof Number; } public static boolean isGroup(final Object o) { return o instanceof Group; } public static boolean isVariable(final Object o) { return o instanceof Variable; } public static boolean isOperator(final Object o) { return o instanceof Operator; } public static boolean isComma(final Object o) { return isCharacter(o, ','); } public static boolean isCharacter(final Object o, final Character c) { return o instanceof Character && ((Character) o).equals(c); } public static boolean isMatchingGroup(final Object o, final Group g) { return isGroup(o) && ((Group) o).matches(g); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/Variable.java000066400000000000000000000032361310313773000264260ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; /** * A "noun" token representing a variable. * * @author Curtis Rueden */ public class Variable extends Token { public Variable(final String token) { super(token); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/000077500000000000000000000000001310313773000247615ustar00rootroot00000000000000parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/AbstractEvaluator.java000066400000000000000000000063751310313773000312650ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.util.HashMap; import java.util.Map; import org.scijava.parse.ExpressionParser; import org.scijava.parse.SyntaxTree; import org.scijava.parse.Tokens; import org.scijava.parse.Variable; /** * Base class for {@link Evaluator} implementations. * * @author Curtis Rueden */ public abstract class AbstractEvaluator implements Evaluator { private final HashMap vars = new HashMap(); private final ExpressionParser parser; private boolean strict = true; public AbstractEvaluator() { this(new ExpressionParser()); } public AbstractEvaluator(final ExpressionParser parser) { this.parser = parser; } // -- Evaluator methods -- @Override public ExpressionParser getParser() { return parser; } @Override public boolean isStrict() { return strict; } @Override public void setStrict(final boolean strict) { this.strict = strict; } @Override public Object evaluate(final SyntaxTree syntaxTree) { // Convert the syntax tree to postfix. return evaluate(syntaxTree.postfix()); } @Override public Object evaluate(final String expression) { // Convert the expression to postfix. return evaluate(parser.parsePostfix(expression)); } @Override public Object value(final Object token) { return Tokens.isVariable(token) ? get((Variable) token) : token; } @Override public Object get(final Variable v) { final String name = v.getToken(); if (vars.containsKey(name)) return vars.get(name); if (strict) throw new IllegalArgumentException("Unknown variable: " + name); return new Unresolved(name); } @Override public void set(final Variable v, final Object value) { vars.put(v.getToken(), value); } @Override public void setAll(final Map map) { vars.putAll(map); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/AbstractStackEvaluator.java000066400000000000000000000072731310313773000322510ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.util.ArrayDeque; import java.util.Deque; import java.util.LinkedList; import org.scijava.parse.ExpressionParser; import org.scijava.parse.Function; import org.scijava.parse.Group; import org.scijava.parse.Operator; import org.scijava.parse.Tokens; /** * Base class for {@link StackEvaluator} implementations. * * @author Curtis Rueden */ public abstract class AbstractStackEvaluator extends AbstractEvaluator implements StackEvaluator { public AbstractStackEvaluator() { super(); } public AbstractStackEvaluator(final ExpressionParser parser) { super(parser); } // -- Evaluator methods -- @Override public Object evaluate(final LinkedList queue) { // Process the postfix token queue. final Deque stack = new ArrayDeque(); while (!queue.isEmpty()) { final Object token = queue.removeFirst(); final Object result; if (Tokens.isOperator(token)) { result = execute((Operator) token, stack); } else { // Token is a variable or a literal. result = token; } if (result == null) die(token); stack.push(result); } if (stack.isEmpty()) return null; if (stack.size() == 1) return stack.pop(); final LinkedList resultList = new LinkedList(); while (!stack.isEmpty()) { resultList.addFirst(stack.pop()); } return resultList; } // -- Helper methods -- private static final String[] ARY = { "nullary", "unary", "binary", "ternary", "quaternary", "quinary", "senary", "septenary", "octary", "nonary" }; private static String ary(final int arity) { return arity < ARY.length ? ARY[arity] : arity + "-ary"; } private static String ary(final Operator op) { return ary(op.getArity()); } private static void die(final Object token) { final StringBuilder message = new StringBuilder("Unsupported"); if (token instanceof Operator) message.append(" " + ary((Operator) token)); message.append(" " + type(token) + ": " + token); throw new IllegalArgumentException(message.toString()); } private static String type(final Object token) { if (token instanceof Function) return "function"; if (token instanceof Group) return "group"; if (token instanceof Operator) return "operator"; return "token"; } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/AbstractStandardStackEvaluator.java000066400000000000000000000210651310313773000337250ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.util.Deque; import org.scijava.parse.ExpressionParser; import org.scijava.parse.Function; import org.scijava.parse.Operator; import org.scijava.parse.Operators; import org.scijava.parse.Tokens; import org.scijava.parse.Variable; /** * Base class for stack-based evaluators which support the standard operators. * * @author Curtis Rueden */ public abstract class AbstractStandardStackEvaluator extends AbstractStackEvaluator implements StandardEvaluator { public AbstractStandardStackEvaluator() { super(); } public AbstractStandardStackEvaluator(final ExpressionParser parser) { super(parser); } // -- StandardEvaluator methods -- // -- postfix -- @Override public Object postInc(final Object a) { final Variable v = var(a); final Object value = value(v); if (value == null) return null; set(v, add(a, 1)); return value; } @Override public Object postDec(final Object a) { final Variable v = var(a); final Object value = value(v); if (value == null) return null; set(v, sub(a, 1)); return value; } // -- unary -- @Override public Object preInc(final Object a) { final Variable v = var(a); final Object result = add(a, 1); set(v, result); return result; } @Override public Object preDec(final Object a) { final Variable v = var(a); final Object result = sub(a, 1); set(v, result); return result; } // -- assignment -- @Override public Object assign(final Object a, final Object b) { final Variable v = var(a); set(v, value(b)); return v; } @Override public Object powAssign(final Object a, final Object b) { return assign(a, pow(a, b)); } @Override public Object dotPowAssign(final Object a, final Object b) { return assign(a, dotPow(a, b)); } @Override public Object mulAssign(final Object a, final Object b) { return assign(a, mul(a, b)); } @Override public Object divAssign(final Object a, final Object b) { return assign(a, div(a, b)); } @Override public Object modAssign(final Object a, final Object b) { return assign(a, mod(a, b)); } @Override public Object rightDivAssign(final Object a, final Object b) { return assign(a, rightDiv(a, b)); } @Override public Object dotDivAssign(final Object a, final Object b) { return assign(a, dotDiv(a, b)); } @Override public Object dotRightDivAssign(final Object a, final Object b) { return assign(a, dotRightDiv(a, b)); } @Override public Object addAssign(final Object a, final Object b) { return assign(a, add(a, b)); } @Override public Object subAssign(final Object a, final Object b) { return assign(a, sub(a, b)); } @Override public Object andAssign(final Object a, final Object b) { return assign(a, bitwiseAnd(a, b)); } @Override public Object orAssign(final Object a, final Object b) { return assign(a, bitwiseOr(a, b)); } @Override public Object leftShiftAssign(final Object a, final Object b) { return assign(a, leftShift(a, b)); } @Override public Object rightShiftAssign(final Object a, final Object b) { return assign(a, rightShift(a, b)); } @Override public Object unsignedRightShiftAssign(final Object a, final Object b) { return assign(a, unsignedRightShift(a, b)); } // -- StackEvaluator methods -- @Override public Object execute(final Operator op, final Deque stack) { // Pop the arguments. final int arity = op.getArity(); final Object[] args = new Object[arity]; for (int i = args.length - 1; i >= 0; i--) { args[i] = stack.pop(); } final Object a = args.length > 0 ? args[0] : null; final Object b = args.length > 1 ? args[1] : null; // Let the case logic begin! if (op instanceof Function) return function(a, b); if (op == Operators.DOT) return dot(a, b); if (Tokens.isMatchingGroup(op, Operators.PARENS)) return parens(args); if (Tokens.isMatchingGroup(op, Operators.BRACKETS)) return brackets(args); if (Tokens.isMatchingGroup(op, Operators.BRACES)) return braces(args); if (op == Operators.TRANSPOSE) return transpose(a); if (op == Operators.DOT_TRANSPOSE) return dotTranspose(a); if (op == Operators.POW) return pow(a, b); if (op == Operators.DOT_POW) return dotPow(a, b); if (op == Operators.POST_INC) return postInc(a); if (op == Operators.POST_DEC) return postDec(a); if (op == Operators.PRE_INC) return preInc(a); if (op == Operators.PRE_DEC) return preDec(a); if (op == Operators.POS) return pos(a); if (op == Operators.NEG) return neg(a); if (op == Operators.COMPLEMENT) return complement(a); if (op == Operators.NOT) return not(a); if (op == Operators.MUL) return mul(a, b); if (op == Operators.DIV) return div(a, b); if (op == Operators.MOD) return mod(a, b); if (op == Operators.RIGHT_DIV) return rightDiv(a, b); if (op == Operators.DOT_MUL) return dotMul(a, b); if (op == Operators.DOT_DIV) return dotDiv(a, b); if (op == Operators.DOT_RIGHT_DIV) return dotRightDiv(a, b); if (op == Operators.ADD) return add(a, b); if (op == Operators.SUB) return sub(a, b); if (op == Operators.LEFT_SHIFT) return leftShift(a, b); if (op == Operators.RIGHT_SHIFT) return rightShift(a, b); if (op == Operators.UNSIGNED_RIGHT_SHIFT) return unsignedRightShift(a, b); if (op == Operators.COLON) return colon(a, b); if (op == Operators.LESS_THAN) return lessThan(a, b); if (op == Operators.GREATER_THAN) return greaterThan(a, b); if (op == Operators.LESS_THAN_OR_EQUAL) return lessThanOrEqual(a, b); if (op == Operators.GREATER_THAN_OR_EQUAL) return greaterThanOrEqual(a, b); if (op == Operators.INSTANCEOF) return instanceOf(a, b); if (op == Operators.EQUAL) return equal(a, b); if (op == Operators.NOT_EQUAL) return notEqual(a, b); if (op == Operators.BITWISE_AND) return bitwiseAnd(a, b); if (op == Operators.BITWISE_OR) return bitwiseOr(a, b); if (op == Operators.LOGICAL_AND) return logicalAnd(a, b); if (op == Operators.LOGICAL_OR) return logicalOr(a, b); if (op == Operators.ASSIGN) return assign(a, b); if (op == Operators.POW_ASSIGN) return powAssign(a, b); if (op == Operators.DOT_POW_ASSIGN) return dotPowAssign(a, b); if (op == Operators.MUL_ASSIGN) return mulAssign(a, b); if (op == Operators.DIV_ASSIGN) return divAssign(a, b); if (op == Operators.MOD_ASSIGN) return modAssign(a, b); if (op == Operators.RIGHT_DIV_ASSIGN) return rightDivAssign(a, b); if (op == Operators.DOT_DIV_ASSIGN) return dotDivAssign(a, b); if (op == Operators.DOT_RIGHT_DIV_ASSIGN) return dotRightDivAssign(a, b); if (op == Operators.ADD_ASSIGN) return addAssign(a, b); if (op == Operators.SUB_ASSIGN) return subAssign(a, b); if (op == Operators.AND_ASSIGN) return andAssign(a, b); if (op == Operators.OR_ASSIGN) return orAssign(a, b); if (op == Operators.LEFT_SHIFT_ASSIGN) return leftShiftAssign(a, b); if (op == Operators.RIGHT_SHIFT_ASSIGN) return rightShiftAssign(a, b); if (op == Operators.UNSIGNED_RIGHT_SHIFT_ASSIGN) return unsignedRightShiftAssign(a, b); // Unknown operator. return null; } // -- Helper methods -- /** Casts the given token to a variable. */ private Variable var(final Object token) { if (Tokens.isVariable(token)) return (Variable) token; throw new IllegalArgumentException("Not a variable: " + token); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/DefaultEvaluator.java000066400000000000000000000501351310313773000310770ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import org.scijava.parse.ExpressionParser; import org.scijava.parse.Literals; import org.scijava.parse.Operators; import org.scijava.parse.Tokens; import org.scijava.parse.Variable; /** * An expression evaluator for most {@link Operators standard operators} with * common built-in types (i.e.: {@link Boolean}s, {@link String}s and * {@link Number}s).

Caveats

*

* This class is a big bag of case logic for various operators and types. * Looking at it, you might think: "It sure would be nice to modularize this, * with each operation in its own class, with properly declared types, and * called dynamically at runtime as appropriate." *

*

* "Great idea!" I would reply. Then I would suggest you have a look at the SciJava Ops and ImageJ Ops projects, which do * exactly that in an extensible way. *

*

* Or maybe you are thinking: "This can't possibly work as well as awesome * JVM-based scripting languages like Jython * and Groovy..." *

*

* To which I would reply: "You are absolutely right! This class is mostly just * a demonstration of an extensible, working evaluator built using the * {@link org.scijava.parse.eval} package. If your use case is only concerned * with feature-rich evaluation of standard types, then building on top of a * scripting language might make more sense." *

* * @author Curtis Rueden * @see org.scijava.parse.Main The main class, to give it a spin. */ public class DefaultEvaluator extends AbstractStandardStackEvaluator { public DefaultEvaluator() { super(); } public DefaultEvaluator(final ExpressionParser parser) { super(parser); } // -- function -- @Override public Object function(final Object a, final Object b) { final Object element = listElement(a, b); if (element != null) return element; if (Tokens.isVariable(a)) { final String name = ((Variable) a).getToken(); final Object result = callFunction(name, b); if (result != null) return result; } // NB: Unknown function type. return null; } // -- dot -- @Override public Object dot(final Object a, final Object b) { // NB: Unimplemented. return null; } // -- groups -- @Override public Object parens(final Object[] args) { if (args.length == 1) return args[0]; return Arrays.asList(args); } @Override public Object brackets(final Object[] args) { return Arrays.asList(args); } @Override public Object braces(final Object[] args) { return Arrays.asList(args); } // -- transpose, power -- @Override public Object transpose(final Object a) { // NB: Unimplemented. return null; } @Override public Object dotTranspose(final Object a) { // NB: Unimplemented. return null; } @Override public Object pow(final Object a, final Object b) { if (isD(a) && isD(b)) return pow(d(a), d(b)); if (isBI(a) && isI(b)) return pow(bi(a), i(b)); if (isBD(a) && isI(b)) return pow(bd(a), i(b)); return null; } public double pow(final double a, final double b) { return Math.pow(a, b); } public BigInteger pow(final BigInteger a, final int b) { return a.pow(b); } public BigDecimal pow(final BigDecimal a, final int b) { return a.pow(b); } @Override public Object dotPow(final Object a, final Object b) { // NB: Unimplemented. return null; } // -- unary -- @Override public Object pos(final Object a) { if (isI(a)) return pos(i(a)); if (isL(a)) return pos(l(a)); if (isF(a)) return pos(f(a)); if (isD(a)) return pos(d(a)); return value(a); } public int pos(final int num) { return +num; } public long pos(final long num) { return +num; } public float pos(final float num) { return +num; } public double pos(final double num) { return +num; } @Override public Object neg(final Object a) { if (isI(a)) return neg(i(a)); if (isL(a)) return neg(l(a)); if (isF(a)) return neg(f(a)); if (isD(a)) return neg(d(a)); if (isBI(a)) return neg(bi(a)); if (isBD(a)) return neg(bd(a)); return sub(0, a); } public int neg(final int num) { return -num; } public long neg(final long num) { return -num; } public float neg(final float num) { return -num; } public double neg(final double num) { return -num; } public BigInteger neg(final BigInteger num) { return num.negate(); } public BigDecimal neg(final BigDecimal num) { return num.negate(); } @Override public Object complement(final Object a) { if (isI(a)) return complement(i(a)); if (isL(a)) return complement(l(a)); return null; } public int complement(final int a) { return ~a; } public long complement(final long a) { return ~a; } @Override public Object not(final Object a) { return not(bool(a)); } public boolean not(final boolean a) { return !a; } // -- multiplicative -- @Override public Object mul(final Object a, final Object b) { if (isI(a) && isI(b)) return mul(i(a), i(b)); if (isL(a) && isL(b)) return mul(l(a), l(b)); if (isF(a) && isF(b)) return mul(f(a), f(b)); if (isD(a) && isD(b)) return mul(d(a), d(b)); if (isBI(a) && isBI(b)) return mul(bi(a), bi(b)); if (isBD(a) && isBD(b)) return mul(bd(a), bd(b)); return null; } public int mul(final int a, final int b) { return a * b; } public long mul(final long a, final long b) { return a * b; } public float mul(final float a, final float b) { return a * b; } public double mul(final double a, final double b) { return a * b; } public BigInteger mul(final BigInteger a, final BigInteger b) { return a.multiply(b); } public BigDecimal mul(final BigDecimal a, final BigDecimal b) { return a.multiply(b); } @Override public Object div(final Object a, final Object b) { if (isI(a) && isI(b)) return div(i(a), i(b)); if (isL(a) && isL(b)) return div(l(a), l(b)); if (isF(a) && isF(b)) return div(f(a), f(b)); if (isD(a) && isD(b)) return div(d(a), d(b)); if (isBI(a) && isBI(b)) return div(bi(a), bi(b)); if (isBD(a) && isBD(b)) return div(bd(a), bd(b)); return null; } public int div(final int a, final int b) { return a / b; } public long div(final long a, final long b) { return a / b; } public float div(final float a, final float b) { return a / b; } public double div(final double a, final double b) { return a / b; } public BigInteger div(final BigInteger a, final BigInteger b) { return a.divide(b); } public BigDecimal div(final BigDecimal a, final BigDecimal b) { return a.divide(b); } @Override public Object mod(final Object a, final Object b) { if (isI(a) && isI(b)) return mod(i(a), i(b)); if (isL(a) && isL(b)) return mod(l(a), l(b)); if (isF(a) && isF(b)) return mod(f(a), f(b)); if (isD(a) && isD(b)) return mod(d(a), d(b)); if (isBI(a) && isBI(b)) return mod(bi(a), bi(b)); if (isBD(a) && isBD(b)) return mod(bd(a), bd(b)); return null; } public int mod(final int a, final int b) { return a % b; } public long mod(final long a, final long b) { return a % b; } public float mod(final float a, final float b) { return a % b; } public double mod(final double a, final double b) { return a % b; } public BigInteger mod(final BigInteger a, final BigInteger b) { return a.remainder(b); } public BigDecimal mod(final BigDecimal a, final BigDecimal b) { return a.remainder(b); } @Override public Object rightDiv(final Object a, final Object b) { // NB: Unimplemented. return null; } @Override public Object dotMul(Object a, Object b) { // NB: Unimplemented. return null; } @Override public Object dotDiv(final Object a, final Object b) { // NB: Unimplemented. return null; } @Override public Object dotRightDiv(final Object a, final Object b) { // NB: Unimplemented. return null; } // -- additive -- @Override public Object add(final Object a, final Object b) { if (isStr(a)) return add(str(a), str(b)); if (isI(a) && isI(b)) return add(i(a), i(b)); if (isL(a) && isL(b)) return add(l(a), l(b)); if (isF(a) && isF(b)) return add(f(a), f(b)); if (isD(a) && isD(b)) return add(d(a), d(b)); if (isBI(a) && isBI(b)) return add(bi(a), bi(b)); if (isBD(a) && isBD(b)) return add(bd(a), bd(b)); return null; } public String add(final String a, final String b) { return a + b; } public int add(final int a, final int b) { return a + b; } public long add(final long a, final long b) { return a + b; } public float add(final float a, final float b) { return a + b; } public double add(final double a, final double b) { return a + b; } public BigInteger add(final BigInteger a, final BigInteger b) { return a.add(b); } public BigDecimal add(final BigDecimal a, final BigDecimal b) { return a.add(b); } @Override public Object sub(final Object a, final Object b) { if (isI(a) && isI(b)) return sub(i(a), i(b)); if (isL(a) && isL(b)) return sub(l(a), l(b)); if (isF(a) && isF(b)) return sub(f(a), f(b)); if (isD(a) && isD(b)) return sub(d(a), d(b)); if (isBI(a) && isBI(b)) return sub(bi(a), bi(b)); if (isBD(a) && isBD(b)) return sub(bd(a), bd(b)); return null; } public int sub(final int a, final int b) { return a - b; } public long sub(final long a, final long b) { return a - b; } public float sub(final float a, final float b) { return a - b; } public double sub(final double a, final double b) { return a - b; } public BigInteger sub(final BigInteger a, final BigInteger b) { return a.subtract(b); } public BigDecimal sub(final BigDecimal a, final BigDecimal b) { return a.subtract(b); } // -- shift -- @Override public Object leftShift(final Object a, final Object b) { if (isI(a) && isI(b)) return leftShift(i(a), i(b)); if (isL(a) && isL(b)) return leftShift(l(a), l(b)); if (isBI(a) && isI(b)) return leftShift(bi(a), i(b)); return null; } public int leftShift(final int a, final int b) { return a << b; } public long leftShift(final long a, final long b) { return a << b; } public BigInteger leftShift(final BigInteger a, final int b) { return a.shiftLeft(b); } @Override public Object rightShift(final Object a, final Object b) { if (isI(a) && isI(b)) return rightShift(i(a), i(b)); if (isL(a) && isL(b)) return rightShift(l(a), l(b)); if (isBI(a) && isI(b)) return rightShift(bi(a), i(b)); return null; } public int rightShift(final int a, final int b) { return a >> b; } public long rightShift(final long a, final long b) { return a >> b; } public BigInteger rightShift(final BigInteger a, final int b) { return a.shiftRight(b); } @Override public Object unsignedRightShift(final Object a, final Object b) { if (isI(a) && isI(b)) return unsignedRightShift(i(a), i(b)); if (isL(a) && isL(b)) return unsignedRightShift(l(a), l(b)); return null; } public int unsignedRightShift(final int a, final int b) { return a >>> b; } public long unsignedRightShift(final long a, final long b) { return a >>> b; } // -- colon -- @Override public Object colon(final Object a, final Object b) { // NB: Unimplemented. return null; } // -- relational -- @Override public Object lessThan(final Object a, final Object b) { if (isBool(a) && isBool(b)) return lessThan(bool(a), bool(b)); if (isStr(a) && isStr(b)) return lessThan(str(a), str(b)); if (isI(a) && isI(b)) return lessThan(i(a), i(b)); if (isL(a) && isL(b)) return lessThan(l(a), l(b)); if (isF(a) && isF(b)) return lessThan(f(a), f(b)); if (isD(a) && isD(b)) return lessThan(d(a), d(b)); if (isBI(a) && isBI(b)) return lessThan(bi(a), bi(b)); if (isBD(a) && isBD(b)) return lessThan(bd(a), bd(b)); return null; } public boolean lessThan(final Comparable a, final T b) { return a.compareTo(b) < 0; } @Override public Object greaterThan(final Object a, final Object b) { if (isBool(a) && isBool(b)) return greaterThan(bool(a), bool(b)); if (isStr(a) && isStr(b)) return greaterThan(str(a), str(b)); if (isI(a) && isI(b)) return greaterThan(i(a), i(b)); if (isL(a) && isL(b)) return greaterThan(l(a), l(b)); if (isF(a) && isF(b)) return greaterThan(f(a), f(b)); if (isD(a) && isD(b)) return greaterThan(d(a), d(b)); if (isBI(a) && isBI(b)) return greaterThan(bi(a), bi(b)); if (isBD(a) && isBD(b)) return greaterThan(bd(a), bd(b)); return null; } public boolean greaterThan(final Comparable a, final T b) { return a.compareTo(b) > 0; } @Override public Object lessThanOrEqual(final Object a, final Object b) { if (isBool(a) && isBool(b)) return lessThanOrEqual(bool(a), bool(b)); if (isStr(a) && isStr(b)) return lessThanOrEqual(str(a), str(b)); if (isI(a) && isI(b)) return lessThanOrEqual(i(a), i(b)); if (isL(a) && isL(b)) return lessThanOrEqual(l(a), l(b)); if (isF(a) && isF(b)) return lessThanOrEqual(f(a), f(b)); if (isD(a) && isD(b)) return lessThanOrEqual(d(a), d(b)); if (isBI(a) && isBI(b)) return lessThanOrEqual(bi(a), bi(b)); if (isBD(a) && isBD(b)) return lessThanOrEqual(bd(a), bd(b)); return null; } public boolean lessThanOrEqual(final Comparable a, final T b) { return a.compareTo(b) <= 0; } @Override public Object greaterThanOrEqual(final Object a, final Object b) { if (isBool(a) && isBool(b)) return greaterThanOrEqual(bool(a), bool(b)); if (isStr(a) && isStr(b)) return greaterThanOrEqual(str(a), str(b)); if (isI(a) && isI(b)) return greaterThanOrEqual(i(a), i(b)); if (isL(a) && isL(b)) return greaterThanOrEqual(l(a), l(b)); if (isF(a) && isF(b)) return greaterThanOrEqual(f(a), f(b)); if (isD(a) && isD(b)) return greaterThanOrEqual(d(a), d(b)); if (isBI(a) && isBI(b)) return greaterThanOrEqual(bi(a), bi(b)); if (isBD(a) && isBD(b)) return greaterThanOrEqual(bd(a), bd(b)); return null; } public boolean greaterThanOrEqual(final Comparable a, final T b) { return a.compareTo(b) >= 0; } @Override public Object instanceOf(final Object a, final Object b) { // NB: Unimplemented. return null; } // -- equality -- @Override public Object equal(final Object a, final Object b) { return value(a).equals(value(b)); } @Override public Object notEqual(final Object a, final Object b) { return !value(a).equals(value(b)); } // -- bitwise -- @Override public Object bitwiseAnd(final Object a, final Object b) { if (isI(a) && isI(b)) return bitwiseAnd(i(a), i(b)); if (isL(a) && isL(b)) return bitwiseAnd(l(a), l(b)); if (isBI(a) && isBI(b)) return bitwiseAnd(bi(a), bi(b)); return null; } public int bitwiseAnd(final int a, final int b) { return a & b; } public long bitwiseAnd(final long a, final long b) { return a & b; } public BigInteger bitwiseAnd(final BigInteger a, final BigInteger b) { return a.and(b); } @Override public Object bitwiseOr(final Object a, final Object b) { if (isI(a) && isI(b)) return bitwiseOr(i(a), i(b)); if (isL(a) && isL(b)) return bitwiseOr(l(a), l(b)); if (isBI(a) && isBI(b)) return bitwiseOr(bi(a), bi(b)); return null; } public int bitwiseOr(final int a, final int b) { return a | b; } public long bitwiseOr(final long a, final long b) { return a | b; } public BigInteger bitwiseOr(final BigInteger a, final BigInteger b) { return a.or(b); } // -- logical -- @Override public Object logicalAnd(final Object a, final Object b) { if (isBool(a) && isBool(b)) return logicalAnd(bool(a), bool(b)); return null; } public boolean logicalAnd(final boolean a, final boolean b) { return a && b; } @Override public Object logicalOr(final Object a, final Object b) { if (isBool(a) && isBool(b)) return logicalOr(bool(a), bool(b)); return null; } public boolean logicalOr(final boolean a, final boolean b) { return a || b; } // -- Helper methods - type matching -- private boolean is(final Object o, final Class c) { return c.isInstance(value(o)); } private boolean isBool(final Object o) { return is(o, Boolean.class); } private boolean isStr(final Object o) { return is(o, String.class); } private boolean isB(final Object o) { return is(o, Byte.class); } private boolean isS(final Object o) { return is(o, Short.class) || isB(o); } private boolean isI(final Object o) { return is(o, Integer.class) || isS(o); } private boolean isL(final Object o) { return is(o, Long.class) || isI(o); } // NB: Java allows assignment of all integer primitive types to float! private boolean isF(final Object o) { return is(o, Float.class) || isL(o); } private boolean isD(final Object o) { return is(o, Double.class) || isF(o); } private boolean isBI(final Object o) { return is(o, BigInteger.class) || isL(o); } private boolean isBD(final Object o) { return is(o, BigDecimal.class) || isBI(o) || isD(o); } // -- Helper methods - type coercion -- /** Casts the given token to the specified class, or null if incompatible. */ private T cast(final Object token, final Class type) { if (type.isInstance(token)) { @SuppressWarnings("unchecked") final T result = (T) token; return result; } return null; } /** Coerces the given token to a boolean. */ private boolean bool(final Object token) { final Boolean b = cast(value(token), Boolean.class); return b != null ? b : Boolean.valueOf(token.toString()); } /** Coerces the given token to a string. */ private String str(final Object token) { final String s = cast(value(token), String.class); return s != null ? s : token.toString(); } /** Coerces the given token to a number. */ private Number num(final Object token) { final Number n = cast(value(token), Number.class); return n != null ? n : Literals.parseNumber(token.toString()); } private int i(final Object o) { return num(o).intValue(); } private long l(final Object o) { return num(o).longValue(); } private float f(final Object o) { return num(o).floatValue(); } private double d(final Object o) { return num(o).doubleValue(); } private BigInteger bi(final Object o) { final BigInteger bi = cast(o, BigInteger.class); return bi != null ? bi : new BigInteger("" + value(o)); } private BigDecimal bd(final Object o) { final BigDecimal bd = cast(o, BigDecimal.class); return bd != null ? bd : new BigDecimal("" + value(o)); } private Object listElement(final Object a, final Object b) { final Object value; try { value = value(a); } catch (final IllegalArgumentException exc) { return null; } if (!(value instanceof List)) return null; final List list = (List) value; if (!(b instanceof List)) return null; final List indices = (List) b; if (indices.size() != 1) return null; // not a 1-D access return list.get(i(indices.get(0))); } /** Executes built-in functions. */ private Object callFunction(final String name, final Object b) { if (name.equals("postfix") && b instanceof String) { return getParser().parsePostfix((String) b); } if (name.equals("tree") && b instanceof String) { return getParser().parseTree((String) b); } return null; } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/Evaluator.java000066400000000000000000000100741310313773000275700ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.util.LinkedList; import java.util.Map; import org.scijava.parse.ExpressionParser; import org.scijava.parse.SyntaxTree; import org.scijava.parse.Variable; /** * Interface for expression evaluators. * * @author Curtis Rueden */ public interface Evaluator { /** Gets the parser used when evaluating expressions. */ ExpressionParser getParser(); /** * Gets whether the evaluator is operating in strict mode. * * @see #setStrict(boolean) */ boolean isStrict(); /** * Sets whether the evaluator is operating in strict mode. Evaluators operate * in strict mode by default. *

* When evaluating strictly, usage of an unassigned variable token in a place * where its value is needed will generate an {@link IllegalArgumentException} * with an "Unknown variable" message; in non-strict mode, such a variable * will instead be resolved to an object of type {@link Unresolved} with the * same name as the original variable. *

*

* In cases such as assignment, this may be sufficient to complete the * evaluation; for example, the expression {@code foo=bar} will complete * successfully in non-strict mode, with the variable {@code foo} containing * an object of type {@link Unresolved} and token value {@code "bar"}. But in * cases where the unresolved value is needed as an input for additional * operations, the evaluation may still ultimately fail of the operation in * question is not defined for unresolved values. For example, the * {@link DefaultEvaluator} will fail with an "Unsupported binary operator" * exception when given the expression {@code foo+bar}, since {@code foo} and * {@code bar} are unresolved variables, and the {@code +} operator cannot * handle such objects. *

*/ void setStrict(boolean strict); /** Evaluates the given infix expression, returning the result. */ Object evaluate(final String expression); /** Evaluates the given postfix token queue, returning the result. */ Object evaluate(final LinkedList queue); /** Evaluates the given syntax tree, returning the result. */ Object evaluate(final SyntaxTree syntaxTree); /** * Gets the value of the given token. For variables, returns the value of the * variable, throwing an exception if the variable is not set. For literals, * returns the token itself. */ Object value(Object token); /** Gets the value of the given variable. */ Object get(Variable v); /** Sets the value of the given variable. */ void set(Variable v, Object value); /** Assigns variables en masse. */ void setAll(Map map); } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/EvaluatorConsole.java000066400000000000000000000070421310313773000311140ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.scijava.parse.Tokens; /** * A simple console-driven expression evaluator. * * @author Curtis Rueden */ public class EvaluatorConsole { private static final String PROMPT = "> "; private final Evaluator evaluator; public EvaluatorConsole() { this(new DefaultEvaluator()); } public EvaluatorConsole(final Evaluator evaluator) { this.evaluator = evaluator; } // -- EvaluatorConsole methods -- public void showConsole() throws IOException { showConsole(new BufferedReader(new InputStreamReader(System.in))); } public void showConsole(final BufferedReader in) throws IOException { while (true) { print(PROMPT); final String line = in.readLine(); if (line == null) break; try { final Object result = evaluator.evaluate(line); if (result != null) printResult(result); } catch (final IllegalArgumentException exc) { final String msg = exc.getMessage(); if (msg == null) throw exc; // Probably a serious exception. final Matcher m = Pattern.compile(".* at index (\\d+)").matcher(msg); if (m.matches()) { // Show a helpful caret to indicate where the problem is. final int index = Integer.parseInt(m.group(1)); println(caret(index)); } println(msg); } } } private void printResult(final Object o) { if (o instanceof List) { for (final Object item : (List) o) { printResult(item); } } else if (Tokens.isVariable(o)) { println(o + " = " + evaluator.value(o)); } else println(o); } public void print(final Object o) { System.out.print(o); } public void println(final Object o) { System.out.println(o); } // -- Helper methods -- private static String caret(final int index) { final StringBuilder sb = new StringBuilder(); final int count = PROMPT.length() + index; for (int i = 0; i < count; i++) { sb.append(" "); } sb.append("^"); return sb.toString(); } } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/StackEvaluator.java000066400000000000000000000035341310313773000305610ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import java.util.Deque; import org.scijava.parse.Operator; /** * Interface for stack-based expression evaluators. * * @author Curtis Rueden */ public interface StackEvaluator extends Evaluator { /** * Executes the given {@link Operator operation} with the specified value * stack. */ Object execute(final Operator op, final Deque stack); } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/StandardEvaluator.java000066400000000000000000000166061310313773000312600ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import org.scijava.parse.Function; import org.scijava.parse.Operators; /** * Interface for expression evaluators which support the {@link Operators * standard operators}. * * @author Curtis Rueden */ public interface StandardEvaluator extends Evaluator { // -- function -- /** Applies the {@link Function} operator. */ Object function(Object a, Object b); // -- dot -- /** Applies the {@link Operators#DOT} operator. */ Object dot(Object a, Object b); // -- groups -- /** Applies the {@link Operators#PARENS} operator. */ Object parens(Object[] args); /** Applies the {@link Operators#BRACKETS} operator. */ Object brackets(Object[] args); /** Applies the {@link Operators#BRACES} operator. */ Object braces(Object[] args); // -- transpose, power -- /** Applies the {@link Operators#TRANSPOSE} operator. */ Object transpose(Object a); /** Applies the {@link Operators#DOT_TRANSPOSE} operator. */ Object dotTranspose(Object a); /** Applies the {@link Operators#POW} operator. */ Object pow(Object a, Object b); /** Applies the {@link Operators#DOT_POW} operator. */ Object dotPow(Object a, Object b); // -- postfix -- /** Applies the {@link Operators#POST_INC} operator. */ Object postInc(Object a); /** Applies the {@link Operators#POST_DEC} operator. */ Object postDec(Object a); // -- unary -- /** Applies the {@link Operators#PRE_INC} operator. */ Object preInc(Object a); /** Applies the {@link Operators#PRE_DEC} operator. */ Object preDec(Object a); /** Applies the {@link Operators#POS} operator. */ Object pos(Object a); /** Applies the {@link Operators#NEG} operator. */ Object neg(Object a); /** Applies the {@link Operators#COMPLEMENT} operator. */ Object complement(Object a); /** Applies the {@link Operators#NOT} operator. */ Object not(Object a); // -- multiplicative -- /** Applies the {@link Operators#MUL} operator. */ Object mul(Object a, Object b); /** Applies the {@link Operators#DIV} operator. */ Object div(Object a, Object b); /** Applies the {@link Operators#MOD} operator. */ Object mod(Object a, Object b); /** Applies the {@link Operators#RIGHT_DIV} operator. */ Object rightDiv(Object a, Object b); /** Applies the {@link Operators#DOT_MUL} operator. */ Object dotMul(Object a, Object b); /** Applies the {@link Operators#DOT_DIV} operator. */ Object dotDiv(Object a, Object b); /** Applies the {@link Operators#DOT_RIGHT_DIV} operator. */ Object dotRightDiv(Object a, Object b); // -- additive -- /** Applies the {@link Operators#ADD} operator. */ Object add(Object a, Object b); /** Applies the {@link Operators#SUB} operator. */ Object sub(Object a, Object b); // -- shift -- /** Applies the {@link Operators#LEFT_SHIFT} operator. */ Object leftShift(Object a, Object b); /** Applies the {@link Operators#RIGHT_SHIFT} operator. */ Object rightShift(Object a, Object b); /** Applies the {@link Operators#UNSIGNED_RIGHT_SHIFT} operator. */ Object unsignedRightShift(Object a, Object b); // -- colon -- /** Applies the {@link Operators#COLON} operator. */ Object colon(Object a, Object b); // -- relational -- /** Applies the {@link Operators#LESS_THAN} operator. */ Object lessThan(Object a, Object b); /** Applies the {@link Operators#GREATER_THAN} operator. */ Object greaterThan(Object a, Object b); /** Applies the {@link Operators#LESS_THAN_OR_EQUAL} operator. */ Object lessThanOrEqual(Object a, Object b); /** Applies the {@link Operators#GREATER_THAN_OR_EQUAL} operator. */ Object greaterThanOrEqual(Object a, Object b); /** Applies the {@link Operators#INSTANCEOF} operator. */ Object instanceOf(Object a, Object b); // -- equality -- /** Applies the {@link Operators#EQUAL} operator. */ Object equal(Object a, Object b); /** Applies the {@link Operators#NOT_EQUAL} operator. */ Object notEqual(Object a, Object b); // -- bitwise -- /** Applies the {@link Operators#BITWISE_AND} operator. */ Object bitwiseAnd(Object a, Object b); /** Applies the {@link Operators#BITWISE_OR} operator. */ Object bitwiseOr(Object a, Object b); // -- logical -- /** Applies the {@link Operators#LOGICAL_AND} operator. */ Object logicalAnd(Object a, Object b); /** Applies the {@link Operators#LOGICAL_OR} operator. */ Object logicalOr(Object a, Object b); // -- assignment -- /** Applies the {@link Operators#ASSIGN} operator. */ Object assign(Object a, Object b); /** Applies the {@link Operators#POW_ASSIGN} operator. */ Object powAssign(Object a, Object b); /** Applies the {@link Operators#DOT_POW_ASSIGN} operator. */ Object dotPowAssign(Object a, Object b); /** Applies the {@link Operators#MUL_ASSIGN} operator. */ Object mulAssign(Object a, Object b); /** Applies the {@link Operators#DIV_ASSIGN} operator. */ Object divAssign(Object a, Object b); /** Applies the {@link Operators#MOD_ASSIGN} operator. */ Object modAssign(Object a, Object b); /** Applies the {@link Operators#RIGHT_DIV_ASSIGN} operator. */ Object rightDivAssign(Object a, Object b); /** Applies the {@link Operators#DOT_DIV_ASSIGN} operator. */ Object dotDivAssign(Object a, Object b); /** Applies the {@link Operators#DOT_RIGHT_DIV_ASSIGN} operator. */ Object dotRightDivAssign(Object a, Object b); /** Applies the {@link Operators#ADD_ASSIGN} operator. */ Object addAssign(Object a, Object b); /** Applies the {@link Operators#SUB_ASSIGN} operator. */ Object subAssign(Object a, Object b); /** Applies the {@link Operators#AND_ASSIGN} operator. */ Object andAssign(Object a, Object b); /** Applies the {@link Operators#OR_ASSIGN} operator. */ Object orAssign(Object a, Object b); /** Applies the {@link Operators#LEFT_SHIFT_ASSIGN} operator. */ Object leftShiftAssign(Object a, Object b); /** Applies the {@link Operators#RIGHT_SHIFT_ASSIGN} operator. */ Object rightShiftAssign(Object a, Object b); /** Applies the {@link Operators#UNSIGNED_RIGHT_SHIFT_ASSIGN} operator. */ Object unsignedRightShiftAssign(Object a, Object b); } parsington-parsington-1.0.1/src/main/java/org/scijava/parse/eval/Unresolved.java000066400000000000000000000033521310313773000277550ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import org.scijava.parse.Token; /** * An unresolved/unknown variable value. * * @author Curtis Rueden * @see Evaluator#setStrict(boolean) */ public class Unresolved extends Token { public Unresolved(final String token) { super(token); } } parsington-parsington-1.0.1/src/test/000077500000000000000000000000001310313773000176235ustar00rootroot00000000000000parsington-parsington-1.0.1/src/test/java/000077500000000000000000000000001310313773000205445ustar00rootroot00000000000000parsington-parsington-1.0.1/src/test/java/org/000077500000000000000000000000001310313773000213335ustar00rootroot00000000000000parsington-parsington-1.0.1/src/test/java/org/scijava/000077500000000000000000000000001310313773000227535ustar00rootroot00000000000000parsington-parsington-1.0.1/src/test/java/org/scijava/parse/000077500000000000000000000000001310313773000240655ustar00rootroot00000000000000parsington-parsington-1.0.1/src/test/java/org/scijava/parse/AbstractTest.java000066400000000000000000000066371310313773000273470ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.List; /** * Base class for unit test classes. * * @author Curtis Rueden */ public class AbstractTest { // -- Helper methods -- protected void assertString(final String expected, final Object actual) { assertNotNull(actual); assertSame(expected.getClass(), actual.getClass()); assertEquals(expected, actual); } protected void assertNumber(final Number expected, final Object actual) { assertNotNull(actual); assertSame(expected.getClass(), actual.getClass()); assertEquals(expected, actual); } protected void assertGroup(final Group expected, final int arity, final Object token) { assertInstance(token, Group.class); final Group group = (Group) token; assertEquals(arity, group.getArity()); assertTrue(expected.matches(group)); } protected void assertFunction(final Object token) { assertInstance(token, Function.class); } protected void assertVariable(final String expected, final Object token) { assertInstance(token, Variable.class); assertEquals(expected, ((Variable) token).getToken()); } protected void assertInstance(final Object token, final Class c) { assertNotNull(token); assertTrue(token.getClass().getName(), c.isInstance(token)); } protected void assertCount(final int expected, final SyntaxTree tree) { assertNotNull(tree); assertEquals(expected, tree.count()); } protected void assertToken(final String expected, final Object token) { assertNotNull(token); assertEquals(expected, token.toString()); } protected void assertSameLists(final List expected, final List actual) { assertNotNull(actual); assertEquals(expected.size(), actual.size()); for (int i = 0; i < expected.size(); i++) { assertSame("Non-matching index: " + i, expected.get(i), actual.get(i)); } } } parsington-parsington-1.0.1/src/test/java/org/scijava/parse/ExpressionParserTest.java000066400000000000000000000757471310313773000311300ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import java.util.LinkedList; import java.util.List; import org.junit.Test; import org.scijava.parse.Operator.Associativity; /** * Tests {@link ExpressionParser}. * * @author Curtis Rueden */ public class ExpressionParserTest extends AbstractTest { @Test public void testStrings() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("'hello'*\"world\""); // "hello" "world" * assertNotNull(queue); assertEquals(3, queue.size()); assertString("hello", queue.pop()); assertString("world", queue.pop()); assertSame(Operators.MUL, queue.pop()); } @Test public void testNumbers() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("1.1+2L+3f+4-+5d-6"); // 1.1 2L + 3f + 4 + +5d - 6 - assertNotNull(queue); assertEquals(11, queue.size()); assertNumber(1.1, queue.pop()); assertNumber(2L, queue.pop()); assertSame(Operators.ADD, queue.pop()); assertNumber(3f, queue.pop()); assertSame(Operators.ADD, queue.pop()); assertNumber(4, queue.pop()); assertSame(Operators.ADD, queue.pop()); assertNumber(+5d, queue.pop()); assertSame(Operators.SUB, queue.pop()); assertNumber(6, queue.pop()); assertSame(Operators.SUB, queue.pop()); } /** Tests each individual standard operator. */ @Test public void testOperatorsIndividual() { final ExpressionParser p = new ExpressionParser(); assertBinary(p, "a", Operators.DOT, "b", "a.b"); assertUnary(p, "a", Operators.TRANSPOSE, "a'"); assertUnary(p, "a", Operators.DOT_TRANSPOSE, "a.'"); assertBinary(p, "a", Operators.POW, "b", "a^b"); assertBinary(p, "a", Operators.DOT_POW, "b", "a.^b"); assertUnary(p, "a", Operators.PRE_INC, "++a"); assertUnary(p, "a", Operators.POST_INC, "a++"); assertUnary(p, "a", Operators.PRE_DEC, "--a"); assertUnary(p, "a", Operators.POST_DEC, "a--"); assertUnary(p, "a", Operators.POS, "+a"); assertUnary(p, "a", Operators.NEG, "-a"); assertUnary(p, "a", Operators.COMPLEMENT, "~a"); assertUnary(p, "a", Operators.NOT, "!a"); assertBinary(p, "a", Operators.MUL, "b", "a*b"); assertBinary(p, "a", Operators.DIV, "b", "a/b"); assertBinary(p, "a", Operators.MOD, "b", "a%b"); assertBinary(p, "a", Operators.ADD, "b", "a+b"); assertBinary(p, "a", Operators.SUB, "b", "a-b"); assertBinary(p, "a", Operators.LEFT_SHIFT, "b", "a<>b"); assertBinary(p, "a", Operators.UNSIGNED_RIGHT_SHIFT, "b", "a>>>b"); assertBinary(p, "a", Operators.COLON, "b", "a:b"); assertBinary(p, "a", Operators.LESS_THAN, "b", "ab"); assertBinary(p, "a", Operators.LESS_THAN_OR_EQUAL, "b", "a<=b"); assertBinary(p, "a", Operators.GREATER_THAN_OR_EQUAL, "b", "a>=b"); assertBinary(p, "a", Operators.INSTANCEOF, "b", "a instanceof b"); assertBinary(p, "a", Operators.EQUAL, "b", "a==b"); assertBinary(p, "a", Operators.NOT_EQUAL, "b", "a!=b"); assertBinary(p, "a", Operators.BITWISE_AND, "b", "a&b"); assertBinary(p, "a", Operators.BITWISE_OR, "b", "a|b"); assertBinary(p, "a", Operators.LOGICAL_AND, "b", "a&&b"); assertBinary(p, "a", Operators.LOGICAL_OR, "b", "a||b"); assertBinary(p, "a", Operators.ASSIGN, "b", "a=b"); assertBinary(p, "a", Operators.POW_ASSIGN, "b", "a^=b"); assertBinary(p, "a", Operators.DOT_POW_ASSIGN, "b", "a.^=b"); assertBinary(p, "a", Operators.MUL_ASSIGN, "b", "a*=b"); assertBinary(p, "a", Operators.DIV_ASSIGN, "b", "a/=b"); assertBinary(p, "a", Operators.MOD_ASSIGN, "b", "a%=b"); assertBinary(p, "a", Operators.RIGHT_DIV_ASSIGN, "b", "a\\=b"); assertBinary(p, "a", Operators.DOT_DIV_ASSIGN, "b", "a./=b"); assertBinary(p, "a", Operators.DOT_RIGHT_DIV_ASSIGN, "b", "a.\\=b"); assertBinary(p, "a", Operators.ADD_ASSIGN, "b", "a+=b"); assertBinary(p, "a", Operators.SUB_ASSIGN, "b", "a-=b"); assertBinary(p, "a", Operators.AND_ASSIGN, "b", "a&=b"); assertBinary(p, "a", Operators.OR_ASSIGN, "b", "a|=b"); assertBinary(p, "a", Operators.LEFT_SHIFT_ASSIGN, "b", "a<<=b"); assertBinary(p, "a", Operators.RIGHT_SHIFT_ASSIGN, "b", "a>>=b"); assertBinary(p, "a", Operators.UNSIGNED_RIGHT_SHIFT_ASSIGN, "b", "a>>>=b"); } /** Tests all the numeric operators in a single expression. */ @Test public void testMathOperators() { final String expression = "(a|=b)|(c&=d)&(e>>>=f)>>>(g>>=h)>>(i<<=j)<<(k-=l)-(m+=n)" + "+(o.\\=p).\\(q./=r)./(s\\=t)\\(u%=v)%(w/=x)/(y*=z)" + "*(aa.^=bb).^(cc^=dd)^f(~ee--,-ff++,+--gg',++hh.')"; final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix(expression); // a b |= (1) c d &= (1) e f >>>= (1) g h >>= (1) >>> i j <<= (1) >> k l // -= (1) m n += (1) - o p .\= (1) q r ./= (1) .\ s t \= (1) ./ u v %= // (1) \ w x /= (1) % y z *= (1) / aa bb .^= (1) cc dd ^= (1) f ee -- ~ // ff ++ - gg ' -- + hh .' ++ (4) ^ .^ * + << & | assertNotNull(queue); assertEquals(91, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.OR_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("c", queue.pop()); assertVariable("d", queue.pop()); assertSame(Operators.AND_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("e", queue.pop()); assertVariable("f", queue.pop()); assertSame(Operators.UNSIGNED_RIGHT_SHIFT_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("g", queue.pop()); assertVariable("h", queue.pop()); assertSame(Operators.RIGHT_SHIFT_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.UNSIGNED_RIGHT_SHIFT, queue.pop()); assertVariable("i", queue.pop()); assertVariable("j", queue.pop()); assertSame(Operators.LEFT_SHIFT_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.RIGHT_SHIFT, queue.pop()); assertVariable("k", queue.pop()); assertVariable("l", queue.pop()); assertSame(Operators.SUB_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("m", queue.pop()); assertVariable("n", queue.pop()); assertSame(Operators.ADD_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.SUB, queue.pop()); assertVariable("o", queue.pop()); assertVariable("p", queue.pop()); assertSame(Operators.DOT_RIGHT_DIV_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("q", queue.pop()); assertVariable("r", queue.pop()); assertSame(Operators.DOT_DIV_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.DOT_RIGHT_DIV, queue.pop()); assertVariable("s", queue.pop()); assertVariable("t", queue.pop()); assertSame(Operators.RIGHT_DIV_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.DOT_DIV, queue.pop()); assertVariable("u", queue.pop()); assertVariable("v", queue.pop()); assertSame(Operators.MOD_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.RIGHT_DIV, queue.pop()); assertVariable("w", queue.pop()); assertVariable("x", queue.pop()); assertSame(Operators.DIV_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.MOD, queue.pop()); assertVariable("y", queue.pop()); assertVariable("z", queue.pop()); assertSame(Operators.MUL_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.DIV, queue.pop()); assertVariable("aa", queue.pop()); assertVariable("bb", queue.pop()); assertSame(Operators.DOT_POW_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("cc", queue.pop()); assertVariable("dd", queue.pop()); assertSame(Operators.POW_ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("f", queue.pop()); assertVariable("ee", queue.pop()); assertSame(Operators.POST_DEC, queue.pop()); assertSame(Operators.COMPLEMENT, queue.pop()); assertVariable("ff", queue.pop()); assertSame(Operators.POST_INC, queue.pop()); assertSame(Operators.NEG, queue.pop()); assertVariable("gg", queue.pop()); assertSame(Operators.TRANSPOSE, queue.pop()); assertSame(Operators.PRE_DEC, queue.pop()); assertSame(Operators.POS, queue.pop()); assertVariable("hh", queue.pop()); assertSame(Operators.DOT_TRANSPOSE, queue.pop()); assertSame(Operators.PRE_INC, queue.pop()); assertGroup(Operators.PARENS, 4, queue.pop()); assertFunction(queue.pop()); assertSame(Operators.POW, queue.pop()); assertSame(Operators.DOT_POW, queue.pop()); assertSame(Operators.MUL, queue.pop()); assertSame(Operators.ADD, queue.pop()); assertSame(Operators.LEFT_SHIFT, queue.pop()); assertSame(Operators.BITWISE_AND, queue.pop()); assertSame(Operators.BITWISE_OR, queue.pop()); } /** Tests all the boolean operators in a single expression. */ public void testLogicOperators() { final String expression = "ad && e<=f || g>=h && i==j || k!=l && m instanceof n || !o"; final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix(expression); // a b |= c d &= e f >>>= g h >>= >>> i j <<= >> k l -= m n += - o p .\= // q r ./= .\ s t \= ./ u v %= \ w x /= % y z *= / aa bb .^= cc dd ^= ee // -- ~ ff ++ - gg ' -- + hh .' ++ f ^ .^ * + << & | assertNotNull(queue); assertEquals(30, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.LESS_THAN, queue.pop()); assertVariable("c", queue.pop()); assertVariable("d", queue.pop()); assertSame(Operators.GREATER_THAN, queue.pop()); assertVariable("e", queue.pop()); assertVariable("f", queue.pop()); assertSame(Operators.LESS_THAN_OR_EQUAL, queue.pop()); assertSame(Operators.LOGICAL_AND, queue.pop()); assertSame(Operators.LOGICAL_OR, queue.pop()); assertVariable("g", queue.pop()); assertVariable("h", queue.pop()); assertSame(Operators.GREATER_THAN_OR_EQUAL, queue.pop()); assertVariable("i", queue.pop()); assertVariable("j", queue.pop()); assertSame(Operators.EQUAL, queue.pop()); assertSame(Operators.LOGICAL_AND, queue.pop()); assertSame(Operators.LOGICAL_OR, queue.pop()); assertVariable("k", queue.pop()); assertVariable("l", queue.pop()); assertSame(Operators.NOT_EQUAL, queue.pop()); assertVariable("m", queue.pop()); assertVariable("n", queue.pop()); assertSame(Operators.INSTANCEOF, queue.pop()); assertSame(Operators.LOGICAL_AND, queue.pop()); assertSame(Operators.LOGICAL_OR, queue.pop()); assertVariable("0", queue.pop()); assertSame(Operators.NOT, queue.pop()); assertSame(Operators.LOGICAL_OR, queue.pop()); } @Test public void testUnaryOperators1() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("-+a"); // a + - assertNotNull(queue); assertEquals(3, queue.size()); assertVariable("a", queue.pop()); assertSame(Operators.POS, queue.pop()); assertSame(Operators.NEG, queue.pop()); } @Test public void testUnaryOperators2() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a+-b"); // a b - + assertNotNull(queue); assertEquals(4, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.NEG, queue.pop()); assertSame(Operators.ADD, queue.pop()); } @Test public void testUnaryOperators3() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a-+-b"); // a b - + - assertNotNull(queue); assertEquals(5, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.NEG, queue.pop()); assertSame(Operators.POS, queue.pop()); assertSame(Operators.SUB, queue.pop()); } @Test public void testUnaryOperators4() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("-+a-+-b"); // a + - b - + - assertNotNull(queue); assertEquals(7, queue.size()); assertVariable("a", queue.pop()); assertSame(Operators.POS, queue.pop()); assertSame(Operators.NEG, queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.NEG, queue.pop()); assertSame(Operators.POS, queue.pop()); assertSame(Operators.SUB, queue.pop()); } @Test public void testNullaryGroup() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("()"); // (0) assertNotNull(queue); assertEquals(1, queue.size()); assertGroup(Operators.PARENS, 0, queue.pop()); } @Test public void testUnaryGroup() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("(a)"); // a (1) assertNotNull(queue); assertEquals(2, queue.size()); assertVariable("a", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); } @Test public void testNestedGroups() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("(())"); // (0) (1) assertNotNull(queue); assertEquals(2, queue.size()); assertGroup(Operators.PARENS, 0, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); } @Test public void testNullaryFunction() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("f()"); // f (0) assertNotNull(queue); assertEquals(3, queue.size()); assertVariable("f", queue.pop()); assertGroup(Operators.PARENS, 0, queue.pop()); assertFunction(queue.pop()); } @Test public void testUnaryFunction() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("f(a)"); // f a (1) assertNotNull(queue); assertVariable("f", queue.pop()); assertVariable("a", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertFunction(queue.pop()); } @Test public void testBinaryFunction() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("f(a,b)"); // f a b (2) assertNotNull(queue); assertEquals(5, queue.size()); assertVariable("f", queue.pop()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertGroup(Operators.PARENS, 2, queue.pop()); assertFunction(queue.pop()); } @Test public void testTernaryFunction() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("f(a,b,c)"); // f a b c (3) assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("f", queue.pop()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.PARENS, 3, queue.pop()); assertFunction(queue.pop()); } @Test public void testNestedFunctions() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("f(g(),a,h(b),i(c,d))"); // f g (0) a h b (1) i c d (2) (4) assertNotNull(queue); assertEquals(16, queue.size()); assertVariable("f", queue.pop()); assertVariable("g", queue.pop()); assertGroup(Operators.PARENS, 0, queue.pop()); assertFunction(queue.pop()); assertVariable("a", queue.pop()); assertVariable("h", queue.pop()); assertVariable("b", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("i", queue.pop()); assertVariable("c", queue.pop()); assertVariable("d", queue.pop()); assertGroup(Operators.PARENS, 2, queue.pop()); assertFunction(queue.pop()); assertGroup(Operators.PARENS, 4, queue.pop()); assertFunction(queue.pop()); } @Test public void testFunctionParens1() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a.b(c)"); // a b . c (1) assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DOT, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertFunction(queue.pop()); } @Test public void testFunctionParens2() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a(b).c"); // a b (1) c . assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("c", queue.pop()); assertSame(Operators.DOT, queue.pop()); } @Test public void testFunctionParens3() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a.b(c).d"); // a b . c (1) d . assertNotNull(queue); assertEquals(8, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DOT, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("d", queue.pop()); assertSame(Operators.DOT, queue.pop()); } @Test public void testFunctionParens4() { final List operators = Operators.standardList(); final Operator atOp = new Operator("@", 2, Associativity.LEFT, 100); operators.add(atOp); final ExpressionParser parser = new ExpressionParser(operators); final LinkedList queue = parser.parsePostfix("a@b(c)@d"); // a b @ c (1) d @ assertNotNull(queue); assertEquals(8, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(atOp, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("d", queue.pop()); assertSame(atOp, queue.pop()); assertFunction(queue.pop()); } @Test public void testFunctionBrackets1() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a.b[c]"); // a b . c [1] assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DOT, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.BRACKETS, 1, queue.pop()); assertFunction(queue.pop()); } @Test public void testFunctionBrackets2() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a[b].c"); // a b [1] c . assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertGroup(Operators.BRACKETS, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("c", queue.pop()); assertSame(Operators.DOT, queue.pop()); } @Test public void testFunctionBrackets3() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a.b[c].d"); // a b . c [1] d . assertNotNull(queue); assertEquals(8, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DOT, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.BRACKETS, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("d", queue.pop()); assertSame(Operators.DOT, queue.pop()); } @Test public void testFunctionBrackets4() { final List operators = Operators.standardList(); final Operator atOp = new Operator("@", 2, Associativity.LEFT, 100); operators.add(atOp); final ExpressionParser parser = new ExpressionParser(operators); final LinkedList queue = parser.parsePostfix("a@b[c]@d"); // a b @ c [1] d @ assertNotNull(queue); assertEquals(8, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(atOp, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.BRACKETS, 1, queue.pop()); assertVariable("d", queue.pop()); assertSame(atOp, queue.pop()); assertFunction(queue.pop()); } @Test public void testFunctionBraces1() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a.b{c}"); // a b . c {1} assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DOT, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.BRACES, 1, queue.pop()); assertFunction(queue.pop()); } @Test public void testFunctionBraces2() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a{b}.c"); // a b {1} c . assertNotNull(queue); assertEquals(6, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertGroup(Operators.BRACES, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("c", queue.pop()); assertSame(Operators.DOT, queue.pop()); } @Test public void testFunctionBraces3() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a.b{c}.d"); // a b . c {1} d . assertNotNull(queue); assertEquals(8, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DOT, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.BRACES, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("d", queue.pop()); assertSame(Operators.DOT, queue.pop()); } @Test public void testFunctionBraces4() { final List operators = Operators.standardList(); final Operator atOp = new Operator("@", 2, Associativity.LEFT, 100); operators.add(atOp); final ExpressionParser parser = new ExpressionParser(operators); final LinkedList queue = parser.parsePostfix("a@b{c}@d"); // a b @ c {1} d @ assertNotNull(queue); assertEquals(8, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(atOp, queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.BRACES, 1, queue.pop()); assertVariable("d", queue.pop()); assertSame(atOp, queue.pop()); assertFunction(queue.pop()); } @Test public void testMixedFunctions() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a[b(c), d{3:4}, 2]"); // a b c (1) d 3 4 : {1} 2 [3] assertNotNull(queue); assertEquals(14, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertVariable("c", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertFunction(queue.pop()); assertVariable("d", queue.pop()); assertNumber(3, queue.pop()); assertNumber(4, queue.pop()); assertSame(Operators.COLON, queue.pop()); assertGroup(Operators.BRACES, 1, queue.pop()); assertFunction(queue.pop()); assertNumber(2, queue.pop()); assertGroup(Operators.BRACKETS, 3, queue.pop()); assertFunction(queue.pop()); } @Test public void testParameterSyntax() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix( "(choices={'quick brown fox', 'lazy dog'}, persist=false, value=5)"); // choices 'quick brown fox' 'lazy dog' {2} = persist false = value 5 = (3) assertNotNull(queue); assertVariable("choices", queue.pop()); assertString("quick brown fox", queue.pop()); assertString("lazy dog", queue.pop()); assertGroup(Operators.BRACES, 2, queue.pop()); assertSame(Operators.ASSIGN, queue.pop()); assertVariable("persist", queue.pop()); assertSame(false, queue.pop()); assertSame(Operators.ASSIGN, queue.pop()); assertVariable("value", queue.pop()); assertNumber(5, queue.pop()); assertSame(Operators.ASSIGN, queue.pop()); assertGroup(Operators.PARENS, 3, queue.pop()); } @Test public void testNonFunctionGroup() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("f+(g)"); // f g (1) + assertNotNull(queue); assertEquals(4, queue.size()); assertVariable("f", queue.pop()); assertVariable("g", queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.ADD, queue.pop()); } @Test public void testOperatorPrecedence() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a+b*c^d.^e'"); // a b c d e .^ ^ ' * + assertNotNull(queue); assertEquals(10, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertVariable("c", queue.pop()); assertVariable("d", queue.pop()); assertVariable("e", queue.pop()); assertSame(Operators.DOT_POW, queue.pop()); assertSame(Operators.POW, queue.pop()); assertSame(Operators.TRANSPOSE, queue.pop()); assertSame(Operators.MUL, queue.pop()); assertSame(Operators.ADD, queue.pop()); } @Test public void testOperatorAssociativity() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("(a/b/c)+(a^b^c)"); // a b / c / (1) a b c ^ ^ (1) + assertNotNull(queue); assertEquals(13, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.DIV, queue.pop()); assertVariable("c", queue.pop()); assertSame(Operators.DIV, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertVariable("c", queue.pop()); assertSame(Operators.POW, queue.pop()); assertSame(Operators.POW, queue.pop()); assertGroup(Operators.PARENS, 1, queue.pop()); assertSame(Operators.ADD, queue.pop()); } /** Tests multiple semicolon-separated statements. */ @Test public void testMultipleStatements() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a=1 ; b=2 ; c=3"); // a 1 = b 2 = c 3 = assertNotNull(queue); assertEquals(9, queue.size()); assertVariable("a", queue.pop()); assertNumber(1, queue.pop()); assertSame(Operators.ASSIGN, queue.pop()); assertVariable("b", queue.pop()); assertNumber(2, queue.pop()); assertSame(Operators.ASSIGN, queue.pop()); assertVariable("c", queue.pop()); assertNumber(3, queue.pop()); assertSame(Operators.ASSIGN, queue.pop()); } /** A more complex test of {@link ExpressionParser#parsePostfix}. */ @Test public void testParsePostfix() { final ExpressionParser parser = new ExpressionParser(); final LinkedList queue = parser.parsePostfix("a*b-c*a/func(quick,brown,fox)^foo^bar"); // a b * c a * func quick brown fox (3) foo bar ^ ^ / - assertNotNull(queue); assertEquals(18, queue.size()); assertVariable("a", queue.pop()); assertVariable("b", queue.pop()); assertSame(Operators.MUL, queue.pop()); assertVariable("c", queue.pop()); assertVariable("a", queue.pop()); assertSame(Operators.MUL, queue.pop()); assertVariable("func", queue.pop()); assertVariable("quick", queue.pop()); assertVariable("brown", queue.pop()); assertVariable("fox", queue.pop()); assertGroup(Operators.PARENS, 3, queue.pop()); assertFunction(queue.pop()); assertVariable("foo", queue.pop()); assertVariable("bar", queue.pop()); assertSame(Operators.POW, queue.pop()); assertSame(Operators.POW, queue.pop()); assertSame(Operators.DIV, queue.pop()); assertSame(Operators.SUB, queue.pop()); } /** Tests empty expressions. */ @Test public void testEmpty() { final ExpressionParser parser = new ExpressionParser(); assertEquals(0, parser.parsePostfix("").size()); } /** Tests that parsing an invalid expression fails appropriately. */ @Test public void testInvalid() { final ExpressionParser parser = new ExpressionParser(); assertInvalid(parser, "a a", "Invalid character at index 2"); assertInvalid(parser, "func(,)", "Invalid character at index 5"); assertInvalid(parser, "foo,bar", "Misplaced separator or mismatched groups at index 4"); assertInvalid(parser, "(", "Mismatched groups at index 1"); assertInvalid(parser, ")", "Mismatched group terminator ')' at index 1"); assertInvalid(parser, ")(", "Mismatched group terminator ')' at index 1"); assertInvalid(parser, "(()", "Mismatched groups at index 3"); } // -- Helper private void assertUnary(final ExpressionParser parser, final String var, final Operator op, final String expression) { final LinkedList queue = parser.parsePostfix(expression); assertNotNull(queue); assertEquals(2, queue.size()); assertVariable(var, queue.pop()); assertSame(op, queue.pop()); } private void assertBinary(final ExpressionParser parser, final String var1, final Operator op, final String var2, final String expression) { final LinkedList queue = parser.parsePostfix(expression); assertNotNull(queue); assertEquals(3, queue.size()); assertVariable(var1, queue.pop()); assertVariable(var2, queue.pop()); assertSame(op, queue.pop()); } private void assertInvalid(final ExpressionParser parser, final String expression, final String message) { try { parser.parsePostfix(expression); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException exc) { assertEquals(message, exc.getMessage()); } } } parsington-parsington-1.0.1/src/test/java/org/scijava/parse/LiteralsTest.java000066400000000000000000000233111310313773000273470ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import java.math.BigInteger; import org.junit.Test; /** * Tests {@link Literals}. * * @author Curtis Rueden */ public class LiteralsTest extends AbstractTest { @Test public void testParseBoolean() { assertSame(Boolean.FALSE, Literals.parseBoolean("false")); assertSame(Boolean.TRUE, Literals.parseBoolean("true")); assertNull(Literals.parseBoolean("zfalse")); assertNull(Literals.parseBoolean("zfalsez")); assertNull(Literals.parseBoolean("ztrue")); assertNull(Literals.parseBoolean("ztruez")); final Position pos = new Position(); pos.set(0); assertSame(Boolean.FALSE, Literals.parseBoolean("false-", pos)); assertEquals(5, pos.get()); pos.set(0); assertSame(Boolean.TRUE, Literals.parseBoolean("true-", pos)); assertEquals(4, pos.get()); } @Test public void testParseString() { assertEquals("hello world", Literals.parseString("'hello world'")); // Test escape sequences. assertEquals("a\b\t\n\f\r\"\\z", Literals .parseString("\"a\\b\\t\\n\\f\\r\\\"\\\\z\"")); assertEquals("\t\\\t\\\\\t", Literals .parseString("\"\\t\\\\\\t\\\\\\\\\\t\"")); // Test Unicode escape sequences. assertEquals("\u9654", Literals.parseString("\"\u9654\"")); assertEquals("xyz\u9654abc", Literals.parseString("\"xyz\\u9654abc\"")); // Test octal escape sequences. assertEquals("\0", Literals.parseString("\"\\0\"")); assertEquals("\00", Literals.parseString("\"\\00\"")); assertEquals("\000", Literals.parseString("\"\\000\"")); assertEquals("\12", Literals.parseString("\"\\12\"")); assertEquals("\123", Literals.parseString("\"\\123\"")); assertEquals("\377", Literals.parseString("\"\\377\"")); assertEquals("\1234", Literals.parseString("\"\\1234\"")); // Test position final Position pos = new Position(); pos.set(2); assertEquals("cde", Literals.parseString("ab'cde'fg", pos)); assertEquals(7, pos.get()); } @Test public void testParseStringInvalid() { // Test non-string tokens. assertNull(Literals.parseString("")); assertNull(Literals.parseString("1234")); assertNull(Literals.parseString("foo")); assertNull(Literals.parseString("a'b'c")); // Test malformed string literals. try { Literals.parseString("'"); fail("IllegalArgumentException expected"); } catch (final IllegalArgumentException exc) { assertEquals("Unclosed string literal at index 0", exc.getMessage()); } } @Test public void testParseHex() { assertNumber(0x123, Literals.parseHex("0x123")); // Test explicit long. assertNumber(0x123L, Literals.parseHex("0x123L")); // Test implicit long. assertNumber(0x123456789abcdefL, Literals.parseHex("0x123456789abcdef")); // Test BigInteger. final String big = "123456789abcdeffedcba987654321"; final Number bigNum = Literals.parseHex("0x" + big); assertNumber(new BigInteger(big, 16), bigNum); } @Test public void testParseHexNegative() { assertNumber(-0x123, Literals.parseHex("-0x123")); // Test explicit long. assertNumber(-0x123L, Literals.parseHex("-0x123L")); // Test implicit long. assertNumber(-0x123456789abcdefL, Literals.parseHex("-0x123456789abcdef")); // Test BigInteger. final String big = "123456789abcdeffedcba987654321"; final Number bigNum = Literals.parseHex("-0x" + big); assertNumber(new BigInteger("-" + big, 16), bigNum); } @Test public void testParseBinary() { // NB: "0b..." syntax is only supported starting with Java 7. assertNumber(33, Literals.parseBinary("0b100001")); // Test explicit long. assertNumber(33L, Literals.parseBinary("0b100001L")); // Test implicit long. assertNumber(194588677707L, Literals.parseBinary( "0b10110101001110011000111001011001001011")); // Test BigInteger. final String big = "10110011100011110000111110000011111100000011111110000000" + "111111110000000011111111100000000011111111110000000000"; final Number bigNum = Literals.parseBinary("0b" + big); assertNumber(new BigInteger(big, 2), bigNum); } @Test public void testParseBinaryNegative() { // NB: "0b..." syntax is only supported starting with Java 7. assertNumber(-33, Literals.parseBinary("-0b100001")); // Test explicit long. assertNumber(-33L, Literals.parseBinary("-0b100001L")); // Test implicit long. assertNumber(-194588677707L, Literals.parseBinary( "-0b10110101001110011000111001011001001011")); // Test BigInteger. final String big = "10110011100011110000111110000011111100000011111110000000" + "111111110000000011111111100000000011111111110000000000"; final Number bigNum = Literals.parseBinary("-0b" + big); assertNumber(new BigInteger("-" + big, 2), bigNum); } @Test public void testParseOctal() { assertNumber(01234567, Literals.parseOctal("01234567")); // Test explicit long. assertNumber(01234567L, Literals.parseOctal("01234567L")); // Test implicit long. assertNumber(012345677654321L, Literals.parseOctal("012345677654321")); // Test BigInteger. final String big = "1234567765432112345677654321"; final Number bigNum = Literals.parseOctal("0" + big); assertNumber(new BigInteger(big, 8), bigNum); } @Test public void testParseOctalNegative() { assertNumber(-01234567, Literals.parseOctal("-01234567")); // Test explicit long. assertNumber(-01234567L, Literals.parseOctal("-01234567L")); // Test implicit long. assertNumber(-012345677654321L, Literals.parseOctal("-012345677654321")); // Test BigInteger. final String big = "1234567765432112345677654321"; final Number bigNum = Literals.parseOctal("-0" + big); assertNumber(new BigInteger("-" + big, 8), bigNum); } @Test public void testParseDecimal() { assertNumber(123456789, Literals.parseDecimal("123456789")); // Test explicit long. assertNumber(123456789L, Literals.parseDecimal("123456789L")); // Test implicit long. assertNumber(123456787654321L, Literals.parseDecimal("123456787654321")); // Test BigInteger. final String bigI = "1234567898765432123456789"; final Number bigInt = Literals.parseDecimal(bigI); assertNumber(new BigInteger(bigI), bigInt); // Test explicit float. assertNumber(1f, Literals.parseDecimal("1f")); // Test explicit double. assertNumber(1d, Literals.parseDecimal("1d")); // Test implicit double. assertNumber(1.0, Literals.parseDecimal("1.0")); assertNumber(1., Literals.parseDecimal("1.")); // Test scientific notation. assertNumber(1e2, Literals.parseDecimal("1e2")); assertNumber(1.2e3, Literals.parseDecimal("1.2e3")); assertNumber(1.2e3f, Literals.parseDecimal("1.2e3f")); } @Test public void testParseDecimalNegative() { assertNumber(-123456789, Literals.parseDecimal("-123456789")); // Test explicit long. assertNumber(-123456789L, Literals.parseDecimal("-123456789L")); // Test implicit long. assertNumber(-123456787654321L, Literals.parseDecimal("-123456787654321")); // Test BigInteger. final String bigI = "-1234567898765432123456789"; final Number bigInt = Literals.parseDecimal(bigI); assertNumber(new BigInteger(bigI), bigInt); // Test explicit float. assertNumber(-1f, Literals.parseDecimal("-1f")); // Test explicit double. assertNumber(-1d, Literals.parseDecimal("-1d")); // Test implicit double. assertNumber(-1.0, Literals.parseDecimal("-1.0")); assertNumber(-1., Literals.parseDecimal("-1.")); // Test scientific notation. assertNumber(-1e2, Literals.parseDecimal("-1e2")); assertNumber(-1.2e3, Literals.parseDecimal("-1.2e3")); assertNumber(-1.2e3f, Literals.parseDecimal("-1.2e3f")); } @Test public void testParseNumber() { final Position pos = new Position(); assertNumber(0, Literals.parseNumber("0", pos)); assertEquals(1, pos.get()); pos.set(1); assertNumber(5.7, Literals.parseNumber("a5.7a", pos)); assertEquals(4, pos.get()); pos.set(2); assertNumber(-11, Literals.parseNumber("bb-11bb", pos)); assertEquals(5, pos.get()); pos.set(3); assertNumber(0x123L, Literals.parseNumber("ccc0x123Lccc", pos)); assertEquals(9, pos.get()); } @Test public void testParseLiteral() { assertSame(Boolean.FALSE, Literals.parseLiteral("false")); assertSame(Boolean.TRUE, Literals.parseLiteral("true")); assertEquals("fubar", Literals.parseLiteral("'fubar'")); assertNumber(0, Literals.parseLiteral("0")); } } parsington-parsington-1.0.1/src/test/java/org/scijava/parse/SubSequenceTest.java000066400000000000000000000076341310313773000300240ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import org.junit.Test; /** * Test {@link SubSequence}. * * @author Curtis Rueden */ public class SubSequenceTest { private static String PHRASE = "The quick brown fox jumped over the lazy dogs."; @Test public void testLength() { for (int off = 0; off < PHRASE.length(); off++) { for (int len = 0; len < PHRASE.length() - off; len++) { assertEquals(len, sub(off, len).length()); } } } @Test public void testCharAt() { for (int off = 0; off < PHRASE.length(); off++) { for (int len = 0; len < PHRASE.length() - off; len++) { final SubSequence sub = sub(off, len); for (int c = 0; c < sub.length(); c++) { assertEquals(PHRASE.charAt(off + c), sub.charAt(c)); } } } } @Test public void testSubSequence() { final SubSequence sub = sub(4, 15); // quick brown fox final SubSequence subSub = sub.subSequence(6, 11); // brown assertEquals(5, subSub.length()); assertEquals('b', subSub.charAt(0)); assertEquals('r', subSub.charAt(1)); assertEquals('o', subSub.charAt(2)); assertEquals('w', subSub.charAt(3)); assertEquals('n', subSub.charAt(4)); } @Test public void testToString() { assertEquals("quick brown fox", sub(4, 15).toString()); } @Test public void testNegativeOffset() { try { sub(-1, 1); fail("Expected IndexOutOfBoundsException"); } catch (final IndexOutOfBoundsException exc) { assertEquals("Offset -1 < 0", exc.getMessage()); } } @Test public void testNegativeLength() { try { sub(1, -1); fail("Expected IndexOutOfBoundsException"); } catch (final IndexOutOfBoundsException exc) { assertEquals("Length -1 < 0", exc.getMessage()); } } @Test public void testOffsetTooLarge() { final int len = PHRASE.length(); final int off = len + 1; try { sub(off, 1); fail("Expected IndexOutOfBoundsException"); } catch (final IndexOutOfBoundsException exc) { assertEquals("Offset " + off + " > " + len, exc.getMessage()); } } @Test public void testLengthTooLong() { final int len = PHRASE.length(); try { sub(1, len); fail("Expected IndexOutOfBoundsException"); } catch (final IndexOutOfBoundsException exc) { assertEquals("Offset 1 + length " + len + " > " + len, exc.getMessage()); } } // -- Helper methods -- private SubSequence sub(final int offset, final int length) { return new SubSequence(PHRASE, offset, length); } } parsington-parsington-1.0.1/src/test/java/org/scijava/parse/SyntaxTreeTest.java000066400000000000000000000114651310313773000277050ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import java.util.Arrays; import java.util.LinkedList; import org.junit.Test; /** * Tests {@link SyntaxTree}. * * @author Curtis Rueden */ public class SyntaxTreeTest extends AbstractTest { @Test public void testSimple() { // infix: a + b * c // postfix: a b c * + final LinkedList queue = queue(var("a"), var("b"), var("c"), Operators.MUL, Operators.ADD); // NB: SyntaxTree constructor consumes the queue, so save a copy. final LinkedList expected = new LinkedList(queue); final SyntaxTree tree = new SyntaxTree(queue); assertNotNull(tree); assertSame(Operators.ADD, tree.token()); assertCount(2, tree); assertVariable("a", tree.child(0).token()); assertSame(Operators.MUL, tree.child(1).token()); assertCount(2, tree.child(1)); assertVariable("b", tree.child(1).child(0).token()); assertVariable("c", tree.child(1).child(1).token()); assertSameLists(expected, tree.postfix()); } @Test public void testComplicated() { // infix: a / b * c + a ^ b ^ c - f(d, c, b, a) // postfix: a b / c * a b c ^ ^ + f d c b a (4) - final LinkedList queue = queue(var("a"), var("b"), Operators.DIV, var("c"), Operators.MUL, var("a"), var("b"), var("c"), Operators.POW, Operators.POW, Operators.ADD, var("f"), var("d"), var("c"), var("b"), var("a"), group(Operators.PARENS, 4), func(), Operators.SUB); // NB: SyntaxTree constructor consumes the queue, so save a copy. final LinkedList expected = new LinkedList(queue); final SyntaxTree tree = new SyntaxTree(queue); assertNotNull(tree); assertSame(Operators.SUB, token(tree)); assertSame(Operators.ADD, token(tree, 0)); assertSame(Operators.MUL, token(tree, 0, 0)); assertSame(Operators.DIV, token(tree, 0, 0, 0)); assertVariable("a", token(tree, 0, 0, 0, 0)); assertVariable("b", token(tree, 0, 0, 0, 1)); assertVariable("c", token(tree, 0, 0, 1)); assertSame(Operators.POW, token(tree, 0, 1)); assertVariable("a", token(tree, 0, 1, 0)); assertSame(Operators.POW, token(tree, 0, 1, 1)); assertVariable("b", token(tree, 0, 1, 1, 0)); assertVariable("c", token(tree, 0, 1, 1, 1)); assertFunction(token(tree, 1)); assertVariable("f", token(tree, 1, 0)); assertGroup(Operators.PARENS, 4, token(tree, 1, 1)); assertVariable("d", token(tree, 1, 1, 0)); assertVariable("c", token(tree, 1, 1, 1)); assertVariable("b", token(tree, 1, 1, 2)); assertVariable("a", token(tree, 1, 1, 3)); assertSameLists(expected, tree.postfix()); } // -- Helper methods -- private LinkedList queue(final Object... args) { return new LinkedList(Arrays.asList(args)); } private Variable var(final String token) { return new Variable(token); } private Group group(final Group g, final int arity) { final Group group = g.instance(); for (int a = 0; a < arity; a++) group.incArity(); return group; } private Function func() { return new Function(0); // NB: Precedence does not matter here. } private Object token(final SyntaxTree tree, final int... indices) { if (indices.length == 0) return tree.token(); final int[] subIndices = new int[indices.length - 1]; System.arraycopy(indices, 1, subIndices, 0, subIndices.length); return token(tree.child(indices[0]), subIndices); } } parsington-parsington-1.0.1/src/test/java/org/scijava/parse/eval/000077500000000000000000000000001310313773000250145ustar00rootroot00000000000000parsington-parsington-1.0.1/src/test/java/org/scijava/parse/eval/DefaultEvaluatorTest.java000066400000000000000000000722331310313773000317750ustar00rootroot00000000000000/* * #%L * Parsington: the SciJava mathematical expression parser. * %% * Copyright (C) 2015 - 2016 Board of Regents of the University of * Wisconsin-Madison. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package org.scijava.parse.eval; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.junit.Before; import org.junit.Test; import org.scijava.parse.AbstractTest; import org.scijava.parse.Operators; import org.scijava.parse.SyntaxTree; import org.scijava.parse.Variable; /** Tests {@link DefaultEvaluator}. */ public class DefaultEvaluatorTest extends AbstractTest { private DefaultEvaluator e; @Before public void setUp() { e = new DefaultEvaluator(); } /** Tests {@link DefaultEvaluator#evaluate(String)}. */ @Test public void testEvaluate() { assertEquals(26, e.evaluate("(2*3)+(4*5)")); } /** Tests strict mode; see {@link Evaluator#setStrict(boolean)}. */ @Test(expected = IllegalArgumentException.class) public void testStrict() { e.evaluate("foo=bar"); } /** Tests non-strict mode; see {@link Evaluator#setStrict(boolean)}. */ @Test public void testNonStrict() { e.setStrict(false); e.evaluate("foo=bar"); final Object bar = e.get(new Variable("foo")); assertTrue(bar instanceof Unresolved); assertEquals("bar", ((Unresolved) bar).getToken()); } /** * Tests that {@link DefaultEvaluator#evaluate(String)} handles the built-in * {@code postfix} and {@code tree} functions as expected. */ @Test public void testBuiltIns() { final Object[] o = {1, 2, 3, Operators.MUL, Operators.ADD}; final List postfix = Arrays.asList(o); assertEquals(postfix, e.evaluate("postfix('1+2*3')")); final SyntaxTree tree = new SyntaxTree(new LinkedList(postfix)); assertEquals(tree, e.evaluate("tree('1+2*3')")); } // -- function -- /** Tests {@link DefaultEvaluator#function(Object, Object)}. */ @Test public void testFunction() { // test list access final Variable v = new Variable("v"); e.set(v, Arrays.asList("a", "b", "c")); assertEquals("a", e.function(v, Arrays.asList(0))); assertEquals("b", e.function(v, Arrays.asList(1))); assertEquals("c", e.function(v, Arrays.asList(2))); assertNull(e.function(o(0), o(1))); } // -- dot -- /** Tests {@link DefaultEvaluator#dot(Object, Object)}. */ @Test public void testDot() { assertNull(e.dot(o(0), o(1))); } // -- groups -- /** Tests {@link DefaultEvaluator#parens(Object[])}. */ @Test public void testParens() { final Object[] o = {1, 2, 3}; assertEquals(Arrays.asList(o), e.parens(o)); // test empty parentheses assertEquals(Collections.emptyList(), e.parens(new Object[0])); // test collapse of single elements assertEquals(4, e.parens(new Object[] {4})); } /** Tests {@link DefaultEvaluator#brackets(Object[])}. */ @Test public void testBrackets() { final Object[] o = {1, 2, 3}; assertEquals(Arrays.asList(o), e.brackets(o)); // test empty brackets assertEquals(Collections.emptyList(), e.brackets(new Object[0])); } /** Tests {@link DefaultEvaluator#braces(Object[])}. */ @Test public void testBraces() { final Object[] o = {1, 2, 3}; assertEquals(Arrays.asList(o), e.braces(o)); // test empty braces assertEquals(Collections.emptyList(), e.braces(new Object[0])); } // -- transpose, power -- /** Tests {@link DefaultEvaluator#transpose(Object)}. */ @Test public void testTranspose() { assertNull(e.transpose(o(0))); } /** Tests {@link DefaultEvaluator#dotTranspose(Object)}. */ @Test public void testDotTranspose() { assertNull(e.dotTranspose(o(0))); } /** Tests {@link DefaultEvaluator#pow(Object, Object)}. */ @Test public void testPow() { assertNumber(15.625d, e.pow(o(2.5d), o(3d))); assertNumber(bi(15625), e.pow(o(bi(5)), o(6))); assertNumber(bd(15.625), e.pow(o(bd(2.5d)), o(3))); } /** Tests {@link DefaultEvaluator#dotPow(Object, Object)}. */ @Test public void testDotPow() { assertNull(e.dotPow(o(0), o(0))); } // -- postfix -- /** Tests {@link DefaultEvaluator#postInc(Object)}. */ @Test public void testPostInc() { final Variable v = new Variable("v"); e.set(v, 2); assertEquals(2, e.postInc(v)); assertEquals(3, e.get(v)); e.set(v, 4L); assertEquals(4L, e.postInc(v)); assertEquals(5L, e.get(v)); e.set(v, 6f); assertEquals(6f, e.postInc(v)); assertEquals(7f, e.get(v)); e.set(v, 8d); assertEquals(8d, e.postInc(v)); assertEquals(9d, e.get(v)); e.set(v, bi(10)); assertEquals(bi(10), e.postInc(v)); assertEquals(bi(11), e.get(v)); e.set(v, bd(12)); assertEquals(bd(12), e.postInc(v)); assertEquals(bd(13), e.get(v)); } /** Tests {@link DefaultEvaluator#postDec(Object)}. */ @Test public void testPostDec() { final Variable v = new Variable("v"); e.set(v, 2); assertEquals(2, e.postDec(v)); assertEquals(1, e.get(v)); e.set(v, 4L); assertEquals(4L, e.postDec(v)); assertEquals(3L, e.get(v)); e.set(v, 6f); assertEquals(6f, e.postDec(v)); assertEquals(5f, e.get(v)); e.set(v, 8d); assertEquals(8d, e.postDec(v)); assertEquals(7d, e.get(v)); e.set(v, bi(10)); assertEquals(bi(10), e.postDec(v)); assertEquals(bi(9), e.get(v)); e.set(v, bd(12)); assertEquals(bd(12), e.postDec(v)); assertEquals(bd(11), e.get(v)); } // -- unary -- /** Tests {@link DefaultEvaluator#preInc(Object)}. */ @Test public void testPreInc() { final Variable v = new Variable("v"); e.set(v, 2); assertEquals(3, e.preInc(v)); assertEquals(3, e.get(v)); e.set(v, 4L); assertEquals(5L, e.preInc(v)); assertEquals(5L, e.get(v)); e.set(v, 6f); assertEquals(7f, e.preInc(v)); assertEquals(7f, e.get(v)); e.set(v, 8d); assertEquals(9d, e.preInc(v)); assertEquals(9d, e.get(v)); e.set(v, bi(10)); assertEquals(bi(11), e.preInc(v)); assertEquals(bi(11), e.get(v)); e.set(v, bd(12)); assertEquals(bd(13), e.preInc(v)); assertEquals(bd(13), e.get(v)); } /** Tests {@link DefaultEvaluator#preDec(Object)}. */ @Test public void testPreDec() { final Variable v = new Variable("v"); e.set(v, 2); assertEquals(1, e.preDec(v)); assertEquals(1, e.get(v)); e.set(v, 4L); assertEquals(3L, e.preDec(v)); assertEquals(3L, e.get(v)); e.set(v, 6f); assertEquals(5f, e.preDec(v)); assertEquals(5f, e.get(v)); e.set(v, 8d); assertEquals(7d, e.preDec(v)); assertEquals(7d, e.get(v)); e.set(v, bi(10)); assertEquals(bi(9), e.preDec(v)); assertEquals(bi(9), e.get(v)); e.set(v, bd(12)); assertEquals(bd(11), e.preDec(v)); assertEquals(bd(11), e.get(v)); } /** Tests {@link DefaultEvaluator#pos(Object)}. */ @Test public void testPos() { assertNumber(7, e.pos(o(7))); assertNumber(7L, e.pos(o(7L))); assertNumber(7.8d, e.pos(o(7.8d))); assertNumber(7.8f, e.pos(o(7.8f))); assertSame(BigInteger.ZERO, e.pos(BigInteger.ZERO)); assertSame(BigInteger.ONE, e.pos(BigInteger.ONE)); assertSame(BigInteger.TEN, e.pos(BigInteger.TEN)); assertSame(BigDecimal.ZERO, e.pos(BigDecimal.ZERO)); assertSame(BigDecimal.ONE, e.pos(BigDecimal.ONE)); assertSame(BigDecimal.TEN, e.pos(BigDecimal.TEN)); } /** Tests {@link DefaultEvaluator#neg(Object)}. */ @Test public void testNeg() { assertNumber(7, e.neg(o(-7))); assertNumber(7L, e.neg(o(-7L))); assertNumber(7.8f, e.neg(o(-7.8f))); assertNumber(7.8d, e.neg(o(-7.8d))); assertNumber(bi(7), e.neg(o(bi(-7)))); assertNumber(bd(7.8), e.neg(o(bd(-7.8)))); assertNumber(-7, e.neg(o(7))); assertNumber(-7L, e.neg(o(7L))); assertNumber(-7.8f, e.neg(o(7.8f))); assertNumber(-7.8d, e.neg(o(7.8d))); assertNumber(bi(-7), e.neg(o(bi(7)))); assertNumber(bd(-7.8), e.neg(o(bd(7.8)))); } /** Tests {@link DefaultEvaluator#complement(Object)}. */ @Test public void testComplement() { assertNumber(0x35014541, e.complement(o(0xcafebabe))); assertNumber(0x2152350141104541L, e.complement(o(0xdeadcafebeefbabeL))); } /** Tests {@link DefaultEvaluator#not(Object)}. */ @Test public void testNot() { assertSame(false, e.not(o(true))); assertSame(true, e.not(o(false))); } // -- multiplicative -- /** Tests {@link DefaultEvaluator#mul(Object, Object)}. */ @Test public void testMul() { assertNumber(24, e.mul(o(4), o(6))); assertNumber(24L, e.mul(o(4L), o(6L))); assertNumber(8.75f, e.mul(o(2.5f), o(3.5f))); assertNumber(8.75d, e.mul(o(2.5d), o(3.5d))); assertNumber(bi(24), e.mul(o(bi(4)), o(bi(6)))); assertNumber(bd(8.75), e.mul(o(bd(2.5)), o(bd(3.5)))); } /** Tests {@link DefaultEvaluator#div(Object, Object)}. */ @Test public void testDiv() { assertNumber(4, e.div(o(27), o(6))); assertNumber(4L, e.div(o(27L), o(6L))); assertNumber(2.5f, e.div(o(8.75f), o(3.5f))); assertNumber(2.5d, e.div(o(8.75d), o(3.5d))); assertNumber(bi(4), e.div(o(bi(27)), o(bi(6)))); assertNumber(bd(2.5), e.div(o(bd(8.75)), o(bd(3.5)))); } /** Tests {@link DefaultEvaluator#mod(Object, Object)}. */ @Test public void testMod() { assertNumber(3, e.mod(o(27), o(6))); assertNumber(3L, e.mod(o(27L), o(6L))); assertNumber(1.75f, e.mod(o(8.75f), o(3.5f))); assertNumber(1.75d, e.mod(o(8.75d), o(3.5d))); assertNumber(bi(3), e.mod(o(bi(27)), o(bi(6)))); assertNumber(bd(1.75), e.mod(o(bd(8.75)), o(bd(3.5)))); } /** Tests {@link DefaultEvaluator#rightDiv(Object, Object)}. */ @Test public void testRightDiv() { assertNull(e.rightDiv(o(0), o(0))); } /** Tests {@link DefaultEvaluator#dotDiv(Object, Object)}. */ @Test public void testDotDiv() { assertNull(e.dotDiv(o(0), o(0))); } /** Tests {@link DefaultEvaluator#dotRightDiv(Object, Object)}. */ @Test public void testDotRightDiv() { assertNull(e.dotRightDiv(o(0), o(0))); } // -- additive -- /** Tests {@link DefaultEvaluator#add(Object, Object)}. */ @Test public void testAdd() { assertEquals("Hello, world", e.add(o("Hello,"), o(" world"))); assertNumber(10, e.add(o(4), o(6))); assertNumber(10L, e.add(o(4L), o(6L))); assertNumber(3.6f, e.add(o(1.5f), o(2.1f))); assertNumber(3.6d, e.add(o(1.5d), o(2.1d))); assertNumber(bi(10), e.add(o(bi(4)), o(bi(6)))); assertNumber(bd(3.6), e.add(o(bd(1.5)), o(bd(2.1)))); } /** Tests {@link DefaultEvaluator#sub(Object, Object)}. */ @Test public void testSub() { assertNumber(4, e.sub(o(10), o(6))); assertNumber(4L, e.sub(o(10L), o(6L))); assertNumber(1.5f, e.sub(o(3.6f), o(2.1f))); assertNumber(1.5d, e.sub(o(3.6d), o(2.1d))); assertNumber(bi(4), e.sub(o(bi(10)), o(bi(6)))); assertNumber(bd(1.5), e.sub(o(bd(3.6)), o(bd(2.1)))); } // -- shift -- /** Tests {@link DefaultEvaluator#leftShift(Object, Object)}. */ @Test public void testLeftShift() { assertNumber(0xafebabe0, e.leftShift(o(0xcafebabe), o(4))); assertNumber(0xdcafebeefbabe000L, e.leftShift(o(0xdeadcafebeefbabeL), o(12))); assertNumber(bi(7296), e.leftShift(o(bi(57)), o(7))); } /** Tests {@link DefaultEvaluator#rightShift(Object, Object)}. */ @Test public void testRightShift() { assertNumber(0xfcafebab, e.rightShift(o(0xcafebabe), o(4))); assertNumber(0xfffdeadcafebeefbL, e.rightShift(o(0xdeadcafebeefbabeL), o(12))); assertNumber(bi(278), e.rightShift(o(bi(8920)), o(5))); } /** Tests {@link DefaultEvaluator#unsignedRightShift(Object, Object)}. */ @Test public void testUnsignedRightShift() { assertNumber(0x0cafebab, e.unsignedRightShift(o(0xcafebabe), o(4))); assertNumber(0x000deadcafebeefbL, e.unsignedRightShift(o(0xdeadcafebeefbabeL), o(12))); } // -- colon -- /** Tests {@link DefaultEvaluator#colon(Object, Object)}. */ @Test public void testColon() { assertNull(e.colon(o(0), o(0))); } // -- relational -- /** Tests {@link DefaultEvaluator#lessThan(Object, Object)}. */ @Test public void testLessThan() { assertSame(true, e.lessThan(o(false), o(true))); assertSame(false, e.lessThan(o(false), o(false))); assertSame(false, e.lessThan(o(true), o(false))); assertSame(true, e.lessThan(o("hello"), o("world"))); assertSame(false, e.lessThan(o("hello"), o("hello"))); assertSame(false, e.lessThan(o("young"), o("world"))); assertSame(true, e.lessThan(o(2), o(3))); assertSame(false, e.lessThan(o(2), o(2))); assertSame(false, e.lessThan(o(2), o(1))); assertSame(true, e.lessThan(o(5L), o(6L))); assertSame(false, e.lessThan(o(5L), o(5L))); assertSame(false, e.lessThan(o(5L), o(4L))); assertSame(true, e.lessThan(o(8f), o(9f))); assertSame(false, e.lessThan(o(8f), o(8f))); assertSame(false, e.lessThan(o(8f), o(7f))); assertSame(true, e.lessThan(o(11d), o(12d))); assertSame(false, e.lessThan(o(11d), o(11d))); assertSame(false, e.lessThan(o(11d), o(10d))); assertSame(true, e.lessThan(o(bi(14)), o(bi(15)))); assertSame(false, e.lessThan(o(bi(14)), o(bi(14)))); assertSame(false, e.lessThan(o(bi(14)), o(bi(13)))); assertSame(true, e.lessThan(o(bd(17)), o(bd(18)))); assertSame(false, e.lessThan(o(bd(17)), o(bd(17)))); assertSame(false, e.lessThan(o(bd(17)), o(bd(16)))); } /** Tests {@link DefaultEvaluator#greaterThan(Object, Object)}. */ @Test public void testGreaterThan() { assertSame(false, e.greaterThan(o(false), o(true))); assertSame(false, e.greaterThan(o(false), o(false))); assertSame(true, e.greaterThan(o(true), o(false))); assertSame(false, e.greaterThan(o("hello"), o("world"))); assertSame(false, e.greaterThan(o("hello"), o("hello"))); assertSame(true, e.greaterThan(o("young"), o("world"))); assertSame(false, e.greaterThan(o(2), o(3))); assertSame(false, e.greaterThan(o(2), o(2))); assertSame(true, e.greaterThan(o(2), o(1))); assertSame(false, e.greaterThan(o(5L), o(6L))); assertSame(false, e.greaterThan(o(5L), o(5L))); assertSame(true, e.greaterThan(o(5L), o(4L))); assertSame(false, e.greaterThan(o(8f), o(9f))); assertSame(false, e.greaterThan(o(8f), o(8f))); assertSame(true, e.greaterThan(o(8f), o(7f))); assertSame(false, e.greaterThan(o(11d), o(12d))); assertSame(false, e.greaterThan(o(11d), o(11d))); assertSame(true, e.greaterThan(o(11d), o(10d))); assertSame(false, e.greaterThan(o(bi(14)), o(bi(15)))); assertSame(false, e.greaterThan(o(bi(14)), o(bi(14)))); assertSame(true, e.greaterThan(o(bi(14)), o(bi(13)))); assertSame(false, e.greaterThan(o(bd(17)), o(bd(18)))); assertSame(false, e.greaterThan(o(bd(17)), o(bd(17)))); assertSame(true, e.greaterThan(o(bd(17)), o(bd(16)))); } /** Tests {@link DefaultEvaluator#lessThanOrEqual(Object, Object)}. */ @Test public void testLessThanOrEqual() { assertSame(true, e.lessThanOrEqual(o(false), o(true))); assertSame(true, e.lessThanOrEqual(o(false), o(false))); assertSame(false, e.lessThanOrEqual(o(true), o(false))); assertSame(true, e.lessThanOrEqual(o("hello"), o("world"))); assertSame(true, e.lessThanOrEqual(o("hello"), o("hello"))); assertSame(false, e.lessThanOrEqual(o("young"), o("world"))); assertSame(true, e.lessThanOrEqual(o(2), o(3))); assertSame(true, e.lessThanOrEqual(o(2), o(2))); assertSame(false, e.lessThanOrEqual(o(2), o(1))); assertSame(true, e.lessThanOrEqual(o(5L), o(6L))); assertSame(true, e.lessThanOrEqual(o(5L), o(5L))); assertSame(false, e.lessThanOrEqual(o(5L), o(4L))); assertSame(true, e.lessThanOrEqual(o(8f), o(9f))); assertSame(true, e.lessThanOrEqual(o(8f), o(8f))); assertSame(false, e.lessThanOrEqual(o(8f), o(7f))); assertSame(true, e.lessThanOrEqual(o(11d), o(12d))); assertSame(true, e.lessThanOrEqual(o(11d), o(11d))); assertSame(false, e.lessThanOrEqual(o(11d), o(10d))); assertSame(true, e.lessThanOrEqual(o(bi(14)), o(bi(15)))); assertSame(true, e.lessThanOrEqual(o(bi(14)), o(bi(14)))); assertSame(false, e.lessThanOrEqual(o(bi(14)), o(bi(13)))); assertSame(true, e.lessThanOrEqual(o(bd(17)), o(bd(18)))); assertSame(true, e.lessThanOrEqual(o(bd(17)), o(bd(17)))); assertSame(false, e.lessThanOrEqual(o(bd(17)), o(bd(16)))); } /** Tests {@link DefaultEvaluator#greaterThanOrEqual(Object, Object)}. */ @Test public void testGreaterThanOrEqual() { assertSame(false, e.greaterThanOrEqual(o(false), o(true))); assertSame(true, e.greaterThanOrEqual(o(false), o(false))); assertSame(true, e.greaterThanOrEqual(o(true), o(false))); assertSame(false, e.greaterThanOrEqual(o("hello"), o("world"))); assertSame(true, e.greaterThanOrEqual(o("hello"), o("hello"))); assertSame(true, e.greaterThanOrEqual(o("young"), o("world"))); assertSame(false, e.greaterThanOrEqual(o(2), o(3))); assertSame(true, e.greaterThanOrEqual(o(2), o(2))); assertSame(true, e.greaterThanOrEqual(o(2), o(1))); assertSame(false, e.greaterThanOrEqual(o(5L), o(6L))); assertSame(true, e.greaterThanOrEqual(o(5L), o(5L))); assertSame(true, e.greaterThanOrEqual(o(5L), o(4L))); assertSame(false, e.greaterThanOrEqual(o(8f), o(9f))); assertSame(true, e.greaterThanOrEqual(o(8f), o(8f))); assertSame(true, e.greaterThanOrEqual(o(8f), o(7f))); assertSame(false, e.greaterThanOrEqual(o(11d), o(12d))); assertSame(true, e.greaterThanOrEqual(o(11d), o(11d))); assertSame(true, e.greaterThanOrEqual(o(11d), o(10d))); assertSame(false, e.greaterThanOrEqual(o(bi(14)), o(bi(15)))); assertSame(true, e.greaterThanOrEqual(o(bi(14)), o(bi(14)))); assertSame(true, e.greaterThanOrEqual(o(bi(14)), o(bi(13)))); assertSame(false, e.greaterThanOrEqual(o(bd(17)), o(bd(18)))); assertSame(true, e.greaterThanOrEqual(o(bd(17)), o(bd(17)))); assertSame(true, e.greaterThanOrEqual(o(bd(17)), o(bd(16)))); } /** Tests {@link DefaultEvaluator#instanceOf(Object, Object)}. */ @Test public void testInstanceOf() { assertNull(e.instanceOf(o(0), o(0))); } // -- equality -- /** Tests {@link DefaultEvaluator#equal(Object, Object)}. */ @Test public void testEqual() { assertSame(false, e.equal(o(false), o(true))); assertSame(true, e.equal(o(false), o(false))); assertSame(false, e.equal(o(true), o(false))); assertSame(false, e.equal(o("hello"), o("world"))); assertSame(true, e.equal(o("hello"), o("hello"))); assertSame(false, e.equal(o("young"), o("world"))); assertSame(false, e.equal(o(2), o(3))); assertSame(true, e.equal(o(2), o(2))); assertSame(false, e.equal(o(2), o(1))); assertSame(false, e.equal(o(5L), o(6L))); assertSame(true, e.equal(o(5L), o(5L))); assertSame(false, e.equal(o(5L), o(4L))); assertSame(false, e.equal(o(8f), o(9f))); assertSame(true, e.equal(o(8f), o(8f))); assertSame(false, e.equal(o(8f), o(7f))); assertSame(false, e.equal(o(11d), o(12d))); assertSame(true, e.equal(o(11d), o(11d))); assertSame(false, e.equal(o(11d), o(10d))); assertSame(false, e.equal(o(bi(14)), o(bi(15)))); assertSame(true, e.equal(o(bi(14)), o(bi(14)))); assertSame(false, e.equal(o(bi(14)), o(bi(13)))); assertSame(false, e.equal(o(bd(17)), o(bd(18)))); assertSame(true, e.equal(o(bd(17)), o(bd(17)))); assertSame(false, e.equal(o(bd(17)), o(bd(16)))); } /** Tests {@link DefaultEvaluator#notEqual(Object, Object)}. */ @Test public void testNotEqual() { assertSame(true, e.notEqual(o(false), o(true))); assertSame(false, e.notEqual(o(false), o(false))); assertSame(true, e.notEqual(o(true), o(false))); assertSame(true, e.notEqual(o("hello"), o("world"))); assertSame(false, e.notEqual(o("hello"), o("hello"))); assertSame(true, e.notEqual(o("young"), o("world"))); assertSame(true, e.notEqual(o(2), o(3))); assertSame(false, e.notEqual(o(2), o(2))); assertSame(true, e.notEqual(o(2), o(1))); assertSame(true, e.notEqual(o(5L), o(6L))); assertSame(false, e.notEqual(o(5L), o(5L))); assertSame(true, e.notEqual(o(5L), o(4L))); assertSame(true, e.notEqual(o(8f), o(9f))); assertSame(false, e.notEqual(o(8f), o(8f))); assertSame(true, e.notEqual(o(8f), o(7f))); assertSame(true, e.notEqual(o(11d), o(12d))); assertSame(false, e.notEqual(o(11d), o(11d))); assertSame(true, e.notEqual(o(11d), o(10d))); assertSame(true, e.notEqual(o(bi(14)), o(bi(15)))); assertSame(false, e.notEqual(o(bi(14)), o(bi(14)))); assertSame(true, e.notEqual(o(bi(14)), o(bi(13)))); assertSame(true, e.notEqual(o(bd(17)), o(bd(18)))); assertSame(false, e.notEqual(o(bd(17)), o(bd(17)))); assertSame(true, e.notEqual(o(bd(17)), o(bd(16)))); } // -- bitwise -- /** Tests {@link DefaultEvaluator#bitwiseAnd(Object, Object)}. */ @Test public void testBitwiseAnd() { assertNumber(0xcaacbaae, e.bitwiseAnd(o(0xcafebabe), o(0xdeadbeef))); assertNumber(0L, e.bitwiseAnd(o(0x0d0e0a0d0c0a0f0eL), o(0xb0e0e0f0b0a0b0e0L))); assertNumber(bi(0xcaacbaae), e.bitwiseAnd(o(bi(0xcafebabe)), o(bi(0xdeadbeef)))); } /** Tests {@link DefaultEvaluator#bitwiseOr(Object, Object)}. */ @Test public void testBitwiseOr() { assertNumber(0xdeffbeff, e.bitwiseOr(o(0xcafebabe), o(0xdeadbeef))); assertNumber(0xbdeeeafdbcaabfeeL, e.bitwiseOr(o(0x0d0e0a0d0c0a0f0eL), o(0xb0e0e0f0b0a0b0e0L))); assertNumber(bi(0xdeffbeff), e.bitwiseOr(o(bi(0xcafebabe)), o(bi(0xdeadbeef)))); } // -- logical -- /** Tests {@link DefaultEvaluator#logicalAnd(Object, Object)}. */ @Test public void testLogicalAnd() { assertSame(false, e.logicalAnd(o(false), o(false))); assertSame(false, e.logicalAnd(o(false), o(true))); assertSame(false, e.logicalAnd(o(true), o(false))); assertSame(true, e.logicalAnd(o(true), o(true))); } /** Tests {@link DefaultEvaluator#logicalOr(Object, Object)}. */ @Test public void testLogicalOr() { assertSame(false, e.logicalOr(o(false), o(false))); assertSame(true, e.logicalOr(o(false), o(true))); assertSame(true, e.logicalOr(o(true), o(false))); assertSame(true, e.logicalOr(o(true), o(true))); } // -- assignment -- /** Tests {@link DefaultEvaluator#assign(Object, Object)}. */ @Test public void testAssign() { final Variable v = new Variable("v"); assertAssigned(true, v, e.assign(v, true)); assertAssigned("hello", v, e.assign(v, "hello")); assertAssigned(1, v, e.assign(v, 1)); assertAssigned(2L, v, e.assign(v, 2L)); assertAssigned(3f, v, e.assign(v, 3f)); assertAssigned(4d, v, e.assign(v, 4d)); assertAssigned(bi(5), v, e.assign(v, bi(5))); assertAssigned(bd(6), v, e.assign(v, bd(6))); } /** Tests {@link DefaultEvaluator#powAssign(Object, Object)}. */ @Test public void testPowAssign() { final Variable v = new Variable("v"); e.set(v, 2.5d); assertAssigned(15.625d, v, e.powAssign(v, 3)); e.set(v, bi(5)); assertAssigned(bi(15625), v, e.powAssign(v, 6)); e.set(v, bd(2.5)); assertAssigned(bd(15.625), v, e.powAssign(v, 3)); } /** Tests {@link DefaultEvaluator#dotPowAssign(Object, Object)}. */ @Test public void testDotPowAssign() { // NB: Nothing to test; dotPow is unimplemented. } /** Tests {@link DefaultEvaluator#mulAssign(Object, Object)}. */ @Test public void testMulAssign() { final Variable v = new Variable("v"); e.set(v, 4); assertAssigned(24, v, e.mulAssign(v, 6)); e.set(v, 4L); assertAssigned(24L, v, e.mulAssign(v, 6L)); e.set(v, 2.5f); assertAssigned(8.75f, v, e.mulAssign(v, 3.5f)); e.set(v, 2.5d); assertAssigned(8.75d, v, e.mulAssign(v, 3.5d)); e.set(v, bi(4)); assertAssigned(bi(24), v, e.mulAssign(v, bi(6))); e.set(v, bd(2.5)); assertAssigned(bd(8.75), v, e.mulAssign(v, bd(3.5))); } /** Tests {@link DefaultEvaluator#divAssign(Object, Object)}. */ @Test public void testDivAssign() { final Variable v = new Variable("v"); e.set(v, 27); assertAssigned(4, v, e.divAssign(v, 6)); e.set(v, 27L); assertAssigned(4L, v, e.divAssign(v, 6L)); e.set(v, 8.75f); assertAssigned(2.5f, v, e.divAssign(v, 3.5f)); e.set(v, 8.75d); assertAssigned(2.5d, v, e.divAssign(v, 3.5d)); e.set(v, bi(27)); assertAssigned(bi(4), v, e.divAssign(v, bi(6))); e.set(v, bd(8.75)); assertAssigned(bd(2.5), v, e.divAssign(v, bd(3.5))); } /** Tests {@link DefaultEvaluator#modAssign(Object, Object)}. */ @Test public void testModAssign() { final Variable v = new Variable("v"); e.set(v, 27); assertAssigned(3, v, e.modAssign(v, 6)); e.set(v, 27L); assertAssigned(3L, v, e.modAssign(v, 6L)); e.set(v, 8.75f); assertAssigned(1.75f, v, e.modAssign(v, 3.5f)); e.set(v, 8.75d); assertAssigned(1.75d, v, e.modAssign(v, 3.5d)); e.set(v, bi(27)); assertAssigned(bi(3), v, e.modAssign(v, bi(6))); e.set(v, bd(8.75)); assertAssigned(bd(1.75), v, e.modAssign(v, bd(3.5))); } /** Tests {@link DefaultEvaluator#rightDivAssign(Object, Object)}. */ @Test public void testRightDivAssign() { // NB: Nothing to test; rightDiv is unimplemented. } /** Tests {@link DefaultEvaluator#dotDivAssign(Object, Object)}. */ @Test public void testDotDivAssign() { // NB: Nothing to test; dotDiv is unimplemented. } /** Tests {@link DefaultEvaluator#dotRightDivAssign(Object, Object)}. */ @Test public void testDotRightDivAssign() { // NB: Nothing to test; dotRightDiv is unimplemented. } /** Tests {@link DefaultEvaluator#addAssign(Object, Object)}. */ @Test public void testAddAssign() { final Variable v = new Variable("v"); e.set(v, "Hello,"); assertAssigned("Hello, world", v, e.addAssign(v, " world")); e.set(v, 4); assertAssigned(10, v, e.addAssign(v, 6)); e.set(v, 4L); assertAssigned(10L, v, e.addAssign(v, 6L)); e.set(v, 1.5f); assertAssigned(3.6f, v, e.addAssign(v, 2.1f)); e.set(v, 1.5d); assertAssigned(3.6d, v, e.addAssign(v, 2.1d)); e.set(v, bi(4)); assertAssigned(bi(10), v, e.addAssign(v, bi(6))); e.set(v, bd(1.5)); assertAssigned(bd(3.6), v, e.addAssign(v, bd(2.1))); } /** Tests {@link DefaultEvaluator#subAssign(Object, Object)}. */ @Test public void testSubAssign() { final Variable v = new Variable("v"); e.set(v, 10); assertAssigned(4, v, e.subAssign(v, 6)); e.set(v, 10L); assertAssigned(4L, v, e.subAssign(v, 6L)); e.set(v, 3.6f); assertAssigned(1.5f, v, e.subAssign(v, 2.1f)); e.set(v, 3.6d); assertAssigned(1.5d, v, e.subAssign(v, 2.1d)); e.set(v, bi(10)); assertAssigned(bi(4), v, e.subAssign(v, bi(6))); e.set(v, bd(3.6)); assertAssigned(bd(1.5), v, e.subAssign(v, bd(2.1))); } /** Tests {@link DefaultEvaluator#andAssign(Object, Object)}. */ @Test public void testAndAssign() { final Variable v = new Variable("v"); e.set(v, 0xcafebabe); assertAssigned(0xcaacbaae, v, e.andAssign(v, 0xdeadbeef)); e.set(v, 0x0d0e0a0d0c0a0f0eL); assertAssigned(0L, v, e.andAssign(v, 0xb0e0e0f0b0a0b0e0L)); e.set(v, bi(0xcafebabeL)); assertAssigned(bi(0xcaacbaaeL), v, e.andAssign(v, bi(0xdeadbeefL))); } /** Tests {@link DefaultEvaluator#orAssign(Object, Object)}. */ @Test public void testOrAssign() { final Variable v = new Variable("v"); e.set(v, 0xcafebabe); assertAssigned(0xdeffbeff, v, e.orAssign(v, 0xdeadbeef)); e.set(v, 0x0d0e0a0d0c0a0f0eL); assertAssigned(0xbdeeeafdbcaabfeeL, v, e.orAssign(v, 0xb0e0e0f0b0a0b0e0L)); e.set(v, bi(0xcafebabeL)); assertAssigned(bi(0xdeffbeffL), v, e.orAssign(v, bi(0xdeadbeefL))); } /** Tests {@link DefaultEvaluator#leftShiftAssign(Object, Object)}. */ @Test public void testLeftShiftAssign() { final Variable v = new Variable("v"); e.set(v, 0xcafebabe); assertAssigned(0xafebabe0, v, e.leftShiftAssign(v, 4)); e.set(v, 0xdeadcafebeefbabeL); assertAssigned(0xdcafebeefbabe000L, v, e.leftShiftAssign(v, 12)); e.set(v, bi(57)); assertAssigned(bi(7296), v, e.leftShiftAssign(v, 7)); } /** Tests {@link DefaultEvaluator#rightShiftAssign(Object, Object)}. */ @Test public void testRightShiftAssign() { final Variable v = new Variable("v"); e.set(v, 0xcafebabe); assertAssigned(0xfcafebab, v, e.rightShiftAssign(v, 4)); e.set(v, 0xdeadcafebeefbabeL); assertAssigned(0xfffdeadcafebeefbL, v, e.rightShiftAssign(v, 12)); e.set(v, bi(8920)); assertAssigned(bi(278), v, e.rightShiftAssign(v, 5)); } /** Tests {@link DefaultEvaluator#unsignedRightShiftAssign(Object, Object)}. */ @Test public void testUnsignedRightShiftAssign() { final Variable v = new Variable("v"); e.set(v, 0xcafebabe); assertAssigned(0x0cafebab, v, e.unsignedRightShiftAssign(v, 4)); e.set(v, 0xdeadcafebeefbabeL); assertAssigned(0x000deadcafebeefbL, v, e.unsignedRightShiftAssign(v, 12)); } // -- Helper methods -- /** Widens the given object to an {@link Object}. */ private Object o(final Object o) { return o; } private BigInteger bi(final Object o) { return new BigInteger(o.toString()); } private BigDecimal bd(final Object o) { return new BigDecimal(o.toString()); } private void assertAssigned(final Object expected, final Variable v, final Object result) { assertSame(v, result); assertEquals(expected, e.get(v)); } }