summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BJC-Utils2/src/examples/java/bjc/utils/examples/TreeConstructTest.java4
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/data/IPair.java17
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java236
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java28
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java2
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java80
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java44
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java522
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java57
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java3
10 files changed, 542 insertions, 451 deletions
diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/TreeConstructTest.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/TreeConstructTest.java
index 8c945e9..a8f8134 100644
--- a/BJC-Utils2/src/examples/java/bjc/utils/examples/TreeConstructTest.java
+++ b/BJC-Utils2/src/examples/java/bjc/utils/examples/TreeConstructTest.java
@@ -12,11 +12,11 @@ import bjc.utils.funcutils.ListUtils;
import bjc.utils.funcutils.StringUtils;
import bjc.utils.parserutils.ShuntingYard;
import bjc.utils.parserutils.TreeConstructor;
+import bjc.utils.parserutils.TreeConstructor.QueueFlattener;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
-import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -92,7 +92,7 @@ public class TreeConstructTest {
return false;
};
- IMap<String, Function<Deque<ITree<String>>, ITree<String>>> operators
+ IMap<String, QueueFlattener<String>> operators
= new FunctionalMap<>();
operators.put("[", (queuedTrees) -> {
diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java b/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java
index 24e0381..2dbd141 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java
@@ -68,8 +68,7 @@ public interface IPair<LeftType, RightType> extends Bifunctor<LeftType, RightTyp
*/
public default <OtherLeft, OtherRight> IPair<IPair<LeftType, OtherLeft>, IPair<RightType, OtherRight>> combine(
IPair<OtherLeft, OtherRight> otherPair) {
- return combine(otherPair, (left, otherLeft) -> new Pair<>(left, otherLeft),
- (right, otherRight) -> new Pair<>(right, otherRight));
+ return combine(otherPair, Pair<LeftType, OtherLeft>::new, Pair<RightType, OtherRight>::new);
}
/**
@@ -113,8 +112,11 @@ public interface IPair<LeftType, RightType> extends Bifunctor<LeftType, RightTyp
default <OldLeft, OldRight, NewLeft> Function<Bifunctor<OldLeft, OldRight>, Bifunctor<NewLeft, OldRight>> fmapLeft(
Function<OldLeft, NewLeft> func) {
return (argumentPair) -> {
- if(!(argumentPair instanceof IPair<?, ?>)) throw new IllegalArgumentException(
- "This function can only be applied to instances of IPair");
+ if(!(argumentPair instanceof IPair<?, ?>)) {
+ String msg = "This function can only be applied to instances of IPair";
+
+ throw new IllegalArgumentException(msg);
+ }
IPair<OldLeft, OldRight> argPair = (IPair<OldLeft, OldRight>) argumentPair;
@@ -127,8 +129,11 @@ public interface IPair<LeftType, RightType> extends Bifunctor<LeftType, RightTyp
fmapRight(Function<OldRight, NewRight> func) {
return (argumentPair) -> {
- if(!(argumentPair instanceof IPair<?, ?>)) throw new IllegalArgumentException(
- "This function can only be applied to instances of IPair");
+ if(!(argumentPair instanceof IPair<?, ?>)) {
+ String msg = "This function can only be applied to instances of IPair";
+
+ throw new IllegalArgumentException(msg);
+ }
IPair<OldLeft, OldRight> argPair = (IPair<OldLeft, OldRight>) argumentPair;
diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java
index 8e9bbd2..5380b24 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java
@@ -1,97 +1,139 @@
-package bjc.utils.funcdata.theory;
-
-import java.util.function.Function;
-
-/**
- * A functor over a pair of heterogeneous types
- *
- * @author ben
- * @param <LeftType>
- * The type stored on the 'left' of the pair
- * @param <RightType>
- * The type stored on the 'right' of the pair
- *
- */
-public interface Bifunctor<LeftType, RightType> {
- /**
- * Lift a pair of functions to a single function that maps over both
- * parts of a pair
- *
- * @param <OldLeft>
- * The old left type of the pair
- * @param <OldRight>
- * The old right type of the pair
- * @param <NewLeft>
- * The new left type of the pair
- * @param <NewRight>
- * The new right type of the pair
- * @param leftFunc
- * The function that maps over the left of the pair
- * @param rightFunc
- * The function that maps over the right of the pair
- * @return A function that maps over both parts of the pair
- */
- public default <OldLeft, OldRight, NewLeft, NewRight> Function<Bifunctor<OldLeft, OldRight>, Bifunctor<NewLeft, NewRight>> bimap(
- Function<OldLeft, NewLeft> leftFunc, Function<OldRight, NewRight> rightFunc) {
- Function<Bifunctor<OldLeft, OldRight>, Bifunctor<NewLeft, NewRight>> bimappedFunc = (argPair) -> {
- Function<Bifunctor<OldLeft, OldRight>, Bifunctor<NewLeft, OldRight>> leftMapper = argPair
- .<OldLeft, OldRight, NewLeft>fmapLeft(leftFunc);
-
- Bifunctor<NewLeft, OldRight> leftMappedFunctor = leftMapper.apply(argPair);
- Function<Bifunctor<NewLeft, OldRight>, Bifunctor<NewLeft, NewRight>> rightMapper = leftMappedFunctor
- .<NewLeft, OldRight, NewRight>fmapRight(rightFunc);
-
- return rightMapper.apply(leftMappedFunctor);
- };
-
- return bimappedFunc;
- }
-
- /**
- * Lift a function to operate over the left part of this pair
- *
- * @param <OldLeft>
- * The old left type of the pair
- * @param <OldRight>
- * The old right type of the pair
- * @param <NewLeft>
- * The new left type of the pair
- * @param func
- * The function to lift to work over the left side of the
- * pair
- * @return The function lifted to work over the left side of bifunctors
- */
- public <OldLeft, OldRight, NewLeft> Function<Bifunctor<OldLeft, OldRight>, Bifunctor<NewLeft, OldRight>> fmapLeft(
- Function<OldLeft, NewLeft> func);
-
- /**
- * Lift a function to operate over the right part of this pair
- *
- * @param <OldLeft>
- * The old left type of the pair
- * @param <OldRight>
- * The old right type of the pair
- * @param <NewRight>
- * The new right type of the pair
- * @param func
- * The function to lift to work over the right side of
- * the pair
- * @return The function lifted to work over the right side of bifunctors
- */
- public <OldLeft, OldRight, NewRight> Function<Bifunctor<OldLeft, OldRight>, Bifunctor<OldLeft, NewRight>> fmapRight(
- Function<OldRight, NewRight> func);
-
- /**
- * Get the value contained on the left of this bifunctor
- *
- * @return The value on the left side of this bifunctor
- */
- public LeftType getLeft();
-
- /**
- * Get the value contained on the right of this bifunctor
- *
- * @return The value on the right of this bifunctor
- */
- public RightType getRight();
-}
+package bjc.utils.funcdata.theory;
+
+import java.util.function.Function;
+
+/**
+ * A functor over a pair of heterogeneous types
+ *
+ * @author ben
+ * @param <LeftType>
+ * The type stored on the 'left' of the pair
+ * @param <RightType>
+ * The type stored on the 'right' of the pair
+ *
+ */
+public interface Bifunctor<LeftType, RightType> {
+ /**
+ * Alias for functor mapping.
+ *
+ * @author EVE
+ *
+ * @param <OldLeft>
+ * @param <OldRight>
+ * @param <NewLeft>
+ * @param <NewRight>
+ */
+ public interface BifunctorMap<OldLeft, OldRight, NewLeft, NewRight>
+ extends Function<Bifunctor<OldLeft, OldRight>, Bifunctor<NewLeft, NewRight>> {
+
+ }
+
+ /**
+ * Alias for left functor mapping.
+ *
+ * @author EVE
+ *
+ * @param <OldLeft>
+ * @param <OldRight>
+ * @param <NewLeft>
+ */
+ public interface LeftBifunctorMap<OldLeft, OldRight, NewLeft>
+ extends BifunctorMap<OldLeft, OldRight, NewLeft, OldRight> {
+
+ }
+
+ /**
+ * Alias for right functor mapping.
+ *
+ * @author EVE
+ *
+ * @param <OldLeft>
+ * @param <OldRight>
+ * @param <NewRight>
+ */
+ public interface RightBifunctorMap<OldLeft, OldRight, NewRight>
+ extends BifunctorMap<OldLeft, OldRight, OldLeft, NewRight> {
+
+ }
+
+ /**
+ * Lift a pair of functions to a single function that maps over both
+ * parts of a pair
+ *
+ * @param <OldLeft>
+ * The old left type of the pair
+ * @param <OldRight>
+ * The old right type of the pair
+ * @param <NewLeft>
+ * The new left type of the pair
+ * @param <NewRight>
+ * The new right type of the pair
+ * @param leftFunc
+ * The function that maps over the left of the pair
+ * @param rightFunc
+ * The function that maps over the right of the pair
+ * @return A function that maps over both parts of the pair
+ */
+ public default <OldLeft, OldRight, NewLeft, NewRight> BifunctorMap<OldLeft, OldRight, NewLeft, NewRight> bimap(
+ Function<OldLeft, NewLeft> leftFunc, Function<OldRight, NewRight> rightFunc) {
+ BifunctorMap<OldLeft, OldRight, NewLeft, NewRight> bimappedFunc = (argPair) -> {
+ LeftBifunctorMap<OldLeft, OldRight, NewLeft> leftMapper = argPair.fmapLeft(leftFunc);
+
+ Bifunctor<NewLeft, OldRight> leftMappedFunctor = leftMapper.apply(argPair);
+ RightBifunctorMap<NewLeft, OldRight, NewRight> rightMapper = leftMappedFunctor
+ .fmapRight(rightFunc);
+
+ return rightMapper.apply(leftMappedFunctor);
+ };
+
+ return bimappedFunc;
+ }
+
+ /**
+ * Lift a function to operate over the left part of this pair
+ *
+ * @param <OldLeft>
+ * The old left type of the pair
+ * @param <OldRight>
+ * The old right type of the pair
+ * @param <NewLeft>
+ * The new left type of the pair
+ * @param func
+ * The function to lift to work over the left side of the
+ * pair
+ * @return The function lifted to work over the left side of bifunctors
+ */
+ public <OldLeft, OldRight, NewLeft> LeftBifunctorMap<OldLeft, OldRight, NewLeft> fmapLeft(
+ Function<OldLeft, NewLeft> func);
+
+ /**
+ * Lift a function to operate over the right part of this pair
+ *
+ * @param <OldLeft>
+ * The old left type of the pair
+ * @param <OldRight>
+ * The old right type of the pair
+ * @param <NewRight>
+ * The new right type of the pair
+ * @param func
+ * The function to lift to work over the right side of
+ * the pair
+ * @return The function lifted to work over the right side of bifunctors
+ */
+ public <OldLeft, OldRight, NewRight> RightBifunctorMap<OldLeft, OldRight, NewRight> fmapRight(
+ Function<OldRight, NewRight> func);
+
+ /**
+ * Get the value contained on the left of this bifunctor
+ *
+ * @return The value on the left side of this bifunctor
+ */
+ public LeftType getLeft();
+
+ /**
+ * Get the value contained on the right of this bifunctor
+ *
+ * @return The value on the right of this bifunctor
+ */
+ public RightType getRight();
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java
index 83d2d2a..a91bf2a 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java
@@ -13,31 +13,31 @@ class DoubleMatcher {
/*
* Unit pieces.
*/
- private static final String DecDigits = getRegex("fpDigits");
- private static final String HexDigits = getRegex("fpHexDigits");
- private static final String Exponent = applyFormat("fpExponent", getRegex("fpExponent"), DecDigits);
+ private static final String rDecDigits = getRegex("fpDigits");
+ private static final String rHexDigits = getRegex("fpHexDigits");
+ private static final String rExponent = applyFormat("fpExponent", getRegex("fpExponent"), rDecDigits);
/*
* Decimal floating point numbers.
*/
- private static final String SIMPLE_DEC = applyFormat("fpDecimalDecimal", DecDigits, Exponent);
- private static final String SIMPLE_INTDEC = applyFormat("fpDecimalInteger", DecDigits, Exponent);
+ private static final String rSimpleDec = applyFormat("fpDecimalDecimal", rDecDigits, rExponent);
+ private static final String rSimpleIntDec = applyFormat("fpDecimalInteger", rDecDigits, rExponent);
/*
* Hex floating point numbers.
*/
- private static final String HEX_INT = applyFormat("fpHexInteger", HexDigits);
- private static final String HEX_DEC = applyFormat("fpHexDecimal", HexDigits);
- private static final String HEX_LEAD = applyFormat("fpHexLeader", HEX_INT, HEX_DEC);
- private static final String HEX_STRING = applyFormat("fpHexString", HEX_LEAD, DecDigits);
+ private static final String rHexInt = applyFormat("fpHexInteger", rHexDigits);
+ private static final String rHexDec = applyFormat("fpHexDecimal", rHexDigits);
+ private static final String rHexLead = applyFormat("fpHexLeader", rHexInt, rHexDec);
+ private static final String rHexString = applyFormat("fpHexString", rHexLead, rDecDigits);
/*
* Floating point components.
*/
- private static final String FP_LEADER = getRegex("fpLeader");
- private static final String FP_NUM = applyFormat("fpNumber", SIMPLE_INTDEC, SIMPLE_DEC,
- HEX_STRING);
+ private static final String rFPLeader = getRegex("fpLeader");
+ private static final String rFPNum = applyFormat("fpNumber", rSimpleIntDec, rSimpleDec,
+ rHexString);
- private static final String fpRegex = applyFormat("fpDouble", FP_LEADER, FP_NUM);
- public static final Pattern floatingLiteral = Pattern.compile("\\A" + fpRegex + "\\Z");
+ private static final String rDouble = applyFormat("fpDouble", rFPLeader, rFPNum);
+ public static final Pattern doubleLiteral = Pattern.compile("\\A" + rDouble + "\\Z");
} \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java
index 67812de..2a41009 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java
@@ -25,4 +25,4 @@ public class ParserException extends Exception {
public ParserException(String msg, Exception cause) {
super(msg, cause);
}
-}
+} \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java
index 7fc3688..b30a69c 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java
@@ -12,36 +12,36 @@ import java.util.function.Consumer;
import java.util.function.Function;
/**
- * Utility to run the shunting yard algorithm on a bunch of tokens
+ * Utility to run the shunting yard algorithm on a bunch of tokens.
*
* @author ben
*
* @param <TokenType>
- * The type of tokens being shunted
+ * The type of tokens being shunted.
*/
public class ShuntingYard<TokenType> {
/**
- * A enum representing the fundamental operator types
+ * A enum representing the fundamental operator types.
*
* @author ben
*
*/
public static enum Operator implements IPrecedent {
/**
- * Represents addition
+ * Represents addition.
*/
ADD(1),
/**
- * Represents subtraction
+ * Represents subtraction.
*/
SUBTRACT(2),
/**
- * Represents multiplication
+ * Represents multiplication.
*/
MULTIPLY(3),
/**
- * Represents division
+ * Represents division.
*/
DIVIDE(4);
@@ -72,26 +72,26 @@ public class ShuntingYard<TokenType> {
@Override
public void accept(String token) {
// Handle operators
- if (operators.containsKey(token)) {
+ if(operators.containsKey(token)) {
// Pop operators while there isn't a higher
// precedence one
- while (!stack.isEmpty() && isHigherPrec(token, stack.peek())) {
+ while(!stack.isEmpty() && isHigherPrec(token, stack.peek())) {
output.add(transformer.apply(stack.pop()));
}
// Put this operator onto the stack
stack.push(token);
- } else if (StringUtils.containsOnly(token, "\\(")) {
+ } else if(StringUtils.containsOnly(token, "\\(")) {
// Handle groups of parenthesis for multiple
// nesting levels
stack.push(token);
- } else if (StringUtils.containsOnly(token, "\\)")) {
+ } else if(StringUtils.containsOnly(token, "\\)")) {
// Handle groups of parenthesis for multiple
// nesting levels
String swappedToken = token.replace(')', '(');
// Remove tokens up to a matching parenthesis
- while (!stack.peek().equals(swappedToken)) {
+ while(!stack.peek().equals(swappedToken)) {
output.add(transformer.apply(stack.pop()));
}
@@ -105,21 +105,22 @@ public class ShuntingYard<TokenType> {
}
/*
- * Holds all the shuntable operations
+ * Holds all the shuntable operations.
*/
private IMap<String, IPrecedent> operators;
/**
- * Create a new shunting yard with a default set of operators
+ * Create a new shunting yard with a default set of operators.
*
* @param configureBasics
- * Whether or not basic math operators should be provided
+ * Whether or not basic math operators should be
+ * provided.
*/
public ShuntingYard(boolean configureBasics) {
operators = new FunctionalMap<>();
// Add basic operators if we're configured to do so
- if (configureBasics) {
+ if(configureBasics) {
operators.put("+", Operator.ADD);
operators.put("-", Operator.SUBTRACT);
operators.put("*", Operator.MULTIPLY);
@@ -128,12 +129,13 @@ public class ShuntingYard<TokenType> {
}
/**
- * Add an operator to the list of shuntable operators
+ * Add an operator to the list of shuntable operators.
*
* @param operator
- * The token representing the operator
+ * The token representing the operator.
+ *
* @param precedence
- * The precedence of the operator to add
+ * The precedence of the operator to add.
*/
public void addOp(String operator, int precedence) {
/*
@@ -145,24 +147,24 @@ public class ShuntingYard<TokenType> {
}
/**
- * Add an operator to the list of shuntable operators
+ * Add an operator to the list of shuntable operators.
*
* @param operator
- * The token representing the operator
+ * The token representing the operator.
*
* @param precedence
- * The precedence of the operator
+ * The precedence of the operator.
*/
public void addOp(String operator, IPrecedent precedence) {
/*
- * Complain about trying to add an incorrect operator
+ * Complain about trying to add an incorrect operator
*/
- if (operator == null)
+ if(operator == null)
throw new NullPointerException("Operator must not be null");
- else if (precedence == null) throw new NullPointerException("Precedence must not be null");
+ else if(precedence == null) throw new NullPointerException("Precedence must not be null");
/*
- * Add the operator to the ones we handle
+ * Add the operator to the ones we handle
*/
operators.put(operator, precedence);
}
@@ -172,7 +174,7 @@ public class ShuntingYard<TokenType> {
boolean exists = operators.containsKey(right);
// If it doesn't, the left is higher precedence.
- if (!exists) return false;
+ if(!exists) return false;
// Get the precedence of operators
int rightPrecedence = operators.get(right).getPrecedence();
@@ -183,19 +185,21 @@ public class ShuntingYard<TokenType> {
}
/**
- * Transform a string of tokens from infix notation to postfix
+ * Transform a string of tokens from infix notation to postfix.
*
* @param input
- * The string to transform
+ * The string to transform.
+ *
* @param transformer
- * The function to use to transform strings to tokens
- * @return A list of tokens in postfix notation
+ * The function to use to transform strings to tokens.
+ *
+ * @return A list of tokens in postfix notation.
*/
public IList<TokenType> postfix(IList<String> input, Function<String, TokenType> transformer) {
// Check our input
- if (input == null)
+ if(input == null)
throw new NullPointerException("Input must not be null");
- else if (transformer == null) throw new NullPointerException("Transformer must not be null");
+ else if(transformer == null) throw new NullPointerException("Transformer must not be null");
// Here's what we're handing back
IList<TokenType> output = new FunctionalList<>();
@@ -207,7 +211,7 @@ public class ShuntingYard<TokenType> {
input.forEach(new TokenShunter(output, stack, transformer));
// Transform any resulting tokens
- stack.forEach((token) -> {
+ stack.forEach(token -> {
output.add(transformer.apply(token));
});
@@ -215,18 +219,18 @@ public class ShuntingYard<TokenType> {
}
/**
- * Remove an operator from the list of shuntable operators
+ * Remove an operator from the list of shuntable operators.
*
* @param operator
* The token representing the operator. If null, remove
- * all operators
+ * all operators.
*/
public void removeOp(String operator) {
// Check if we want to remove all operators
- if (operator == null) {
+ if(operator == null) {
operators = new FunctionalMap<>();
} else {
operators.remove(operator);
}
}
-}
+} \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
index 5f843a2..c441dff 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
@@ -1,10 +1,11 @@
package bjc.utils.parserutils;
import bjc.utils.data.IHolder;
-import bjc.utils.data.IPair;
import bjc.utils.data.ITree;
import bjc.utils.data.Pair;
import bjc.utils.data.Tree;
+import bjc.utils.parserutils.TreeConstructor.ConstructorState;
+import bjc.utils.parserutils.TreeConstructor.QueueFlattener;
import java.util.Deque;
import java.util.function.Consumer;
@@ -14,7 +15,7 @@ import java.util.function.UnaryOperator;
final class TokenTransformer<TokenType> implements Consumer<TokenType> {
// Handle operators
- private final class OperatorHandler implements UnaryOperator<IPair<Deque<ITree<TokenType>>, ITree<TokenType>>> {
+ private final class OperatorHandler implements UnaryOperator<ConstructorState<TokenType>> {
private TokenType element;
public OperatorHandler(TokenType element) {
@@ -22,17 +23,15 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
}
@Override
- public IPair<Deque<ITree<TokenType>>, ITree<TokenType>> apply(
- IPair<Deque<ITree<TokenType>>, ITree<TokenType>> pair) {
+ public ConstructorState<TokenType> apply(ConstructorState<TokenType> pair) {
// Replace the current AST with the result of handling
// an operator
- return pair.bindLeft((queuedASTs) -> {
+ return new ConstructorState<>(pair.bindLeft(queuedASTs -> {
return handleOperator(queuedASTs);
- });
+ }));
}
- private IPair<Deque<ITree<TokenType>>, ITree<TokenType>> handleOperator(
- Deque<ITree<TokenType>> queuedASTs) {
+ private ConstructorState<TokenType> handleOperator(Deque<ITree<TokenType>> queuedASTs) {
// The AST we're going to hand back
ITree<TokenType> newAST;
@@ -42,10 +41,13 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
} else {
// Error if we don't have enough for a binary
// operator
- if(queuedASTs.size() < 2) throw new IllegalStateException(
- "Attempted to parse binary operator without enough operands.\n"
- + "Problem operator is " + element
- + "\nPossible operand is: \n\t" + queuedASTs.peek());
+ if(queuedASTs.size() < 2) {
+ String msg = String.format(
+ "Attempted to parse binary operator without enough operands\n\tProblem operator is: %s\n\tPossible operand is: %s",
+ element.toString(), queuedASTs.peek().toString());
+
+ throw new IllegalStateException(msg);
+ }
// Grab the two operands
ITree<TokenType> right = queuedASTs.pop();
@@ -59,21 +61,21 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
queuedASTs.push(newAST);
// Hand back the state
- return new Pair<>(queuedASTs, newAST);
+ return new ConstructorState<>(queuedASTs, newAST);
}
}
- private IHolder<IPair<Deque<ITree<TokenType>>, ITree<TokenType>>> initialState;
+ private IHolder<ConstructorState<TokenType>> initialState;
private Predicate<TokenType> operatorPredicate;
- private Predicate<TokenType> isSpecialOperator;
- private Function<TokenType, Function<Deque<ITree<TokenType>>, ITree<TokenType>>> handleSpecialOperator;
+ private Predicate<TokenType> isSpecialOperator;
+ private Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator;
// Create a new transformer
- public TokenTransformer(IHolder<IPair<Deque<ITree<TokenType>>, ITree<TokenType>>> initialState,
+ public TokenTransformer(IHolder<ConstructorState<TokenType>> initialState,
Predicate<TokenType> operatorPredicate, Predicate<TokenType> isSpecialOperator,
- Function<TokenType, Function<Deque<ITree<TokenType>>, ITree<TokenType>>> handleSpecialOperator) {
+ Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator) {
this.initialState = initialState;
this.operatorPredicate = operatorPredicate;
this.isSpecialOperator = isSpecialOperator;
@@ -89,15 +91,15 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
ITree<TokenType> newAST = new Tree<>(element);
// Insert the new tree into the AST
- initialState.transform((pair) -> {
+ initialState.transform(pair -> {
// Transform the pair, ignoring the current AST
// in favor of the
// one consisting of the current element
- return pair.bindLeft((queue) -> {
+ return new ConstructorState<>(pair.bindLeft(queue -> {
queue.push(newAST);
return new Pair<>(queue, newAST);
- });
+ }));
});
}
}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java
index 0ec00ee..52eba1d 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java
@@ -1,257 +1,267 @@
-package bjc.utils.parserutils;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static bjc.utils.PropertyDB.getRegex;
-import static bjc.utils.PropertyDB.getCompiledRegex;
-import static bjc.utils.PropertyDB.applyFormat;
-
-/**
- * Utilities useful for operating on PL tokens.
- *
- * @author EVE
- *
- */
-public class TokenUtils {
- private static String possibleEscapeString = getRegex("possibleStringEscape");
-
- private static Pattern possibleEscapePatt = Pattern.compile(possibleEscapeString);
-
- private static String shortEscape = getRegex("shortFormStringEscape");
- private static String octalEscape = getRegex("octalStringEscape");
- private static String unicodeEscape = getRegex("unicodeStringEscape");
-
- private static String escapeString = applyFormat("stringEscape", shortEscape, octalEscape, unicodeEscape);
-
- private static Pattern escapePatt = Pattern.compile(escapeString);
-
- private static String doubleQuoteString = applyFormat("doubleQuotes", getRegex("nonEscape"),
- possibleEscapeString);
-
- private static Pattern doubleQuotePatt = Pattern.compile(doubleQuoteString);
-
- private static Pattern quotePatt = getCompiledRegex("unescapedQuote");
-
- /**
- * Remove double quoted strings from a string.
- *
- * Splits a string around instances of java-style double-quoted strings.
- *
- * @param inp
- * The string to split.
- *
- * @return An list containing alternating bits of the string and the
- * embedded double-quoted strings that separated them.
- */
- public static List<String> removeDQuotedStrings(String inp) {
- if(inp == null) {
- throw new NullPointerException("inp must not be null");
- }
-
- /*
- * What we need for piece-by-piece string building
- */
- StringBuffer work = new StringBuffer();
- List<String> res = new LinkedList<>();
-
- /*
- * Matcher for proper strings and single quotes.
- */
- Matcher mt = doubleQuotePatt.matcher(inp);
- Matcher corr = quotePatt.matcher(inp);
-
- if(corr.find() && !corr.find()) {
- /*
- * There's a unmatched opening quote with no strings.
- */
- throw new IllegalArgumentException(
- String.format("Unclosed string literal '%s'. Opening quote was at position %d",
- inp, inp.indexOf("\"")));
- }
-
- while(mt.find()) {
- /*
- * Remove the string until the quoted string.
- */
- mt.appendReplacement(work, "");
-
- /*
- * Add the string preceding the double-quoted string and
- * the double-quoted string to the list.
- */
- res.add(work.toString());
- res.add(mt.group(1));
-
- /*
- * Renew the buffer.
- */
- work = new StringBuffer();
- }
-
- /*
- * Grab the remainder of the string.
- */
- mt.appendTail(work);
- String tail = work.toString();
-
- if(tail.contains("\"")) {
- /*
- * There's a unmatched opening quote with at least one
- * string.
- */
- throw new IllegalArgumentException(
- String.format("Unclosed string literal '%s'. Opening quote was at position %d",
- inp, inp.lastIndexOf("\"")));
- }
-
- /*
- * Only add an empty tail if the string was empty.
- */
- if(!tail.equals("") || res.isEmpty()) {
- res.add(tail);
- }
-
- return res;
- }
-
- /**
- * Replace escape characters with their actual equivalents.
- *
- * @param inp
- * The string to replace escape sequences in.
- *
- * @return The string with escape sequences replaced by their equivalent
- * characters.
- */
- public static String descapeString(String inp) {
- if(inp == null) {
- throw new NullPointerException("inp must not be null");
- }
-
- StringBuffer work = new StringBuffer();
-
- Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp);
- Matcher escapeFinder = escapePatt.matcher(inp);
-
- while(possibleEscapeFinder.find()) {
- if(!escapeFinder.find()) {
- throw new IllegalArgumentException(String.format(
- "Illegal escape sequence '%s' at position %d",
- possibleEscapeFinder.group(), possibleEscapeFinder.start()));
- }
-
- String escapeSeq = escapeFinder.group();
-
- String escapeRep = "";
- switch(escapeSeq) {
- case "\\b":
- escapeRep = "\b";
- break;
- case "\\t":
- escapeRep = "\t";
- break;
- case "\\n":
- escapeRep = "\n";
- break;
- case "\\f":
- escapeRep = "\f";
- break;
- case "\\r":
- escapeRep = "\r";
- break;
- case "\\\"":
- escapeRep = "\"";
- break;
- case "\\'":
- escapeRep = "'";
- break;
- case "\\\\":
- /*
- * Skip past the second slash.
- */
- possibleEscapeFinder.find();
- escapeRep = "\\";
- break;
- default:
- if(escapeSeq.startsWith("u")) {
- escapeRep = handleUnicodeEscape(escapeSeq.substring(1));
- } else {
- escapeRep = handleOctalEscape(escapeSeq);
- }
- }
-
- escapeFinder.appendReplacement(work, escapeRep);
- }
-
- escapeFinder.appendTail(work);
-
- return work.toString();
- }
-
- private static String handleUnicodeEscape(String seq) {
- try {
- int codepoint = Integer.parseInt(seq, 16);
-
- return new String(Character.toChars(codepoint));
- } catch(IllegalArgumentException iaex) {
- IllegalArgumentException reiaex = new IllegalArgumentException(
- String.format("'%s' is not a valid Unicode escape sequence'", seq));
-
- reiaex.initCause(iaex);
-
- throw reiaex;
- }
- }
-
- private static String handleOctalEscape(String seq) {
- try {
- int codepoint = Integer.parseInt(seq, 8);
-
- if(codepoint > 255) {
- throw new IllegalArgumentException(String
- .format("'%d' is outside the range of octal escapes', codepoint"));
- }
-
- return new String(Character.toChars(codepoint));
- } catch(IllegalArgumentException iaex) {
- IllegalArgumentException reiaex = new IllegalArgumentException(
- String.format("'%s' is not a valid octal escape sequence'", seq));
-
- reiaex.initCause(iaex);
-
- throw reiaex;
- }
- }
-
- /**
- * Check if a given string would be successfully converted to a double
- * by {@link Double#parseDouble(String)}.
- *
- * @param inp
- * The string to check.
- * @return Whether the string is a valid double or not.
- */
- public static boolean isDouble(String inp) {
- return DoubleMatcher.floatingLiteral.matcher(inp).matches();
- }
-
- private static Pattern intLitPattern = getCompiledRegex("intLiteral");
-
- /**
- * Check if a given string would be successfully converted to a integer
- * by {@link Integer#parseInt(String)}.
- *
- * NOTE: This only checks syntax. Using values out of the range of
- * integers will still cause errors.
- *
- * @param inp
- * The input to check.
- * @return Whether the string is a valid double or not.
- */
- public static boolean isInt(String inp) {
- return intLitPattern.matcher(inp).matches();
- }
+package bjc.utils.parserutils;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static bjc.utils.PropertyDB.getRegex;
+import static bjc.utils.PropertyDB.getCompiledRegex;
+import static bjc.utils.PropertyDB.applyFormat;
+
+/**
+ * Utilities useful for operating on PL tokens.
+ *
+ * @author EVE
+ *
+ * TODO add support for user defined escapes.
+ */
+public class TokenUtils {
+ /*
+ * Patterns and pattern parts.
+ */
+ private static String rPossibleEscapeString = getRegex("possibleStringEscape");
+
+ private static Pattern possibleEscapePatt = Pattern.compile(rPossibleEscapeString);
+
+ private static String rShortEscape = getRegex("shortFormStringEscape");
+ private static String rOctalEscape = getRegex("octalStringEscape");
+ private static String rUnicodeEscape = getRegex("unicodeStringEscape");
+
+ private static String rEscapeString = applyFormat("stringEscape", rShortEscape, rOctalEscape, rUnicodeEscape);
+
+ private static Pattern escapePatt = Pattern.compile(rEscapeString);
+
+ private static String rDoubleQuoteString = applyFormat("doubleQuotes", getRegex("nonEscape"),
+ rPossibleEscapeString);
+
+ private static Pattern doubleQuotePatt = Pattern.compile(rDoubleQuoteString);
+
+ private static Pattern quotePatt = getCompiledRegex("unescapedQuote");
+
+ private static Pattern intLitPattern = getCompiledRegex("intLiteral");
+
+ /**
+ * Remove double quoted strings from a string.
+ *
+ * Splits a string around instances of java-style double-quoted strings.
+ *
+ * @param inp
+ * The string to split.
+ *
+ * @return An list containing alternating bits of the string and the
+ * embedded double-quoted strings that separated them.
+ */
+ public static List<String> removeDQuotedStrings(String inp) {
+ if(inp == null) {
+ throw new NullPointerException("inp must not be null");
+ }
+
+ /*
+ * What we need for piece-by-piece string building
+ */
+ StringBuffer work = new StringBuffer();
+ List<String> res = new LinkedList<>();
+
+ /*
+ * Matcher for proper strings and single quotes.
+ */
+ Matcher mt = doubleQuotePatt.matcher(inp);
+ Matcher corr = quotePatt.matcher(inp);
+
+ if(corr.find() && !corr.find()) {
+ /*
+ * There's a unmatched opening quote with no strings.
+ */
+ String msg = String.format("Unclosed string literal '%s'. Opening quote was at position %d",
+ inp, inp.indexOf("\""));
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ while(mt.find()) {
+ /*
+ * Remove the string until the quoted string.
+ */
+ mt.appendReplacement(work, "");
+
+ /*
+ * Add the string preceding the double-quoted string and
+ * the double-quoted string to the list.
+ */
+ res.add(work.toString());
+ res.add(mt.group(1));
+
+ /*
+ * Renew the buffer.
+ */
+ work = new StringBuffer();
+ }
+
+ /*
+ * Grab the remainder of the string.
+ */
+ mt.appendTail(work);
+ String tail = work.toString();
+
+ if(tail.contains("\"")) {
+ /*
+ * There's a unmatched opening quote with at least one
+ * string.
+ */
+ String msg = String.format("Unclosed string literal '%s'. Opening quote was at position %d",
+ inp, inp.lastIndexOf("\""));
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ /*
+ * Only add an empty tail if the string was empty.
+ */
+ if(!tail.equals("") || res.isEmpty()) {
+ res.add(tail);
+ }
+
+ return res;
+ }
+
+ /**
+ * Replace escape characters with their actual equivalents.
+ *
+ * @param inp
+ * The string to replace escape sequences in.
+ *
+ * @return The string with escape sequences replaced by their equivalent
+ * characters.
+ */
+ public static String descapeString(String inp) {
+ if(inp == null) {
+ throw new NullPointerException("inp must not be null");
+ }
+
+ StringBuffer work = new StringBuffer();
+
+ Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp);
+ Matcher escapeFinder = escapePatt.matcher(inp);
+
+ while(possibleEscapeFinder.find()) {
+ if(!escapeFinder.find()) {
+ String msg = String.format("Illegal escape sequence '%s' at position %d",
+ possibleEscapeFinder.group(), possibleEscapeFinder.start());
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ String escapeSeq = escapeFinder.group();
+
+ String escapeRep = "";
+ switch(escapeSeq) {
+ case "\\b":
+ escapeRep = "\b";
+ break;
+ case "\\t":
+ escapeRep = "\t";
+ break;
+ case "\\n":
+ escapeRep = "\n";
+ break;
+ case "\\f":
+ escapeRep = "\f";
+ break;
+ case "\\r":
+ escapeRep = "\r";
+ break;
+ case "\\\"":
+ escapeRep = "\"";
+ break;
+ case "\\'":
+ escapeRep = "'";
+ break;
+ case "\\\\":
+ /*
+ * Skip past the second slash.
+ */
+ possibleEscapeFinder.find();
+ escapeRep = "\\";
+ break;
+ default:
+ if(escapeSeq.startsWith("u")) {
+ escapeRep = handleUnicodeEscape(escapeSeq.substring(1));
+ } else {
+ escapeRep = handleOctalEscape(escapeSeq);
+ }
+ }
+
+ escapeFinder.appendReplacement(work, escapeRep);
+ }
+
+ escapeFinder.appendTail(work);
+
+ return work.toString();
+ }
+
+ private static String handleUnicodeEscape(String seq) {
+ try {
+ int codepoint = Integer.parseInt(seq, 16);
+
+ return new String(Character.toChars(codepoint));
+ } catch(IllegalArgumentException iaex) {
+ String msg = String.format("'%s' is not a valid Unicode escape sequence'", seq);
+
+ IllegalArgumentException reiaex = new IllegalArgumentException(msg);
+
+ reiaex.initCause(iaex);
+
+ throw reiaex;
+ }
+ }
+
+ private static String handleOctalEscape(String seq) {
+ try {
+ int codepoint = Integer.parseInt(seq, 8);
+
+ if(codepoint > 255) {
+ String msg = String.format("'%d' is outside the range of octal escapes', codepoint");
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ return new String(Character.toChars(codepoint));
+ } catch(IllegalArgumentException iaex) {
+ String msg = String.format("'%s' is not a valid octal escape sequence'", seq);
+
+ IllegalArgumentException reiaex = new IllegalArgumentException(msg);
+
+ reiaex.initCause(iaex);
+
+ throw reiaex;
+ }
+ }
+
+ /**
+ * Check if a given string would be successfully converted to a double
+ * by {@link Double#parseDouble(String)}.
+ *
+ * @param inp
+ * The string to check.
+ * @return Whether the string is a valid double or not.
+ */
+ public static boolean isDouble(String inp) {
+ return DoubleMatcher.doubleLiteral.matcher(inp).matches();
+ }
+
+ /**
+ * Check if a given string would be successfully converted to a integer
+ * by {@link Integer#parseInt(String)}.
+ *
+ * NOTE: This only checks syntax. Using values out of the range of
+ * integers will still cause errors.
+ *
+ * @param inp
+ * The input to check.
+ * @return Whether the string is a valid double or not.
+ */
+ public static boolean isInt(String inp) {
+ return intLitPattern.matcher(inp).matches();
+ }
} \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
index 82ded42..bd0ab97 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
@@ -20,6 +20,29 @@ import java.util.function.Predicate;
*/
public class TreeConstructor {
/**
+ * Alias interface for special operator types.
+ *
+ * @param <TokenType>
+ * The token type of the tree.
+ */
+ public interface QueueFlattener<TokenType> extends Function<Deque<ITree<TokenType>>, ITree<TokenType>> {
+
+ }
+
+ /*
+ * Alias for constructor state.
+ */
+ static final class ConstructorState<TokenType> extends Pair<Deque<ITree<TokenType>>, ITree<TokenType>> {
+ public ConstructorState(Deque<ITree<TokenType>> left, ITree<TokenType> right) {
+ super(left, right);
+ }
+
+ public ConstructorState(IPair<Deque<ITree<TokenType>>, ITree<TokenType>> par) {
+ super(par.getLeft(), par.getRight());
+ }
+ }
+
+ /**
* Construct a tree from a list of tokens in postfix notation
*
* Only binary operators are accepted.
@@ -36,36 +59,38 @@ public class TreeConstructor {
public static <TokenType> ITree<TokenType> constructTree(IList<TokenType> tokens,
Predicate<TokenType> isOperator) {
// Construct a tree with no special operators
- return constructTree(tokens, isOperator, (op) -> false, null);
+ return constructTree(tokens, isOperator, op -> false, null);
}
/**
- * Construct a tree from a list of tokens in postfix notation
+ * Construct a tree from a list of tokens in postfix notation.
*
* Only binary operators are accepted by default. Use the last two
- * parameters to handle non-binary operators
+ * parameters to handle non-binary operators.
*
* @param <TokenType>
- * The elements of the parse tree
+ * The elements of the parse tree.
+ *
* @param tokens
- * The list of tokens to build a tree from
+ * The list of tokens to build a tree from.
+ *
* @param isOperator
* The predicate to use to determine if something is a
- * operator
+ * operator.
+ *
* @param isSpecialOperator
* The predicate to use to determine if an operator needs
- * special handling
+ * special handling.
+ *
* @param handleSpecialOperator
- * The function to use to handle special case operators
+ * The function to use to handle special case operators.
+ *
* @return A AST from the expression
*
- * FIXME The handleSpecialOp function seems like an ugly
- * interface. Maybe there's a better way to express how that
- * works.
*/
public static <TokenType> ITree<TokenType> constructTree(IList<TokenType> tokens,
Predicate<TokenType> isOperator, Predicate<TokenType> isSpecialOperator,
- Function<TokenType, Function<Deque<ITree<TokenType>>, ITree<TokenType>>> handleSpecialOperator) {
+ Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator) {
// Make sure our parameters are valid
if(tokens == null)
throw new NullPointerException("Tokens must not be null");
@@ -75,16 +100,16 @@ public class TreeConstructor {
throw new NullPointerException("Special operator determiner must not be null");
// Here is the state for the tree construction
- IHolder<IPair<Deque<ITree<TokenType>>, ITree<TokenType>>> initialState = new Identity<>(
- new Pair<>(new LinkedList<>(), null));
+ IHolder<ConstructorState<TokenType>> initialState = new Identity<>(
+ new ConstructorState<>(new LinkedList<>(), null));
// Transform each of the tokens
tokens.forEach(new TokenTransformer<>(initialState, isOperator, isSpecialOperator,
handleSpecialOperator));
// Grab the tree from the state
- return initialState.unwrap((pair) -> {
+ return initialState.unwrap(pair -> {
return pair.getRight();
});
}
-}
+} \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java
index 8b078a9..170c619 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java
@@ -1,5 +1,7 @@
package bjc.utils.parserutils.splitter;
+import bjc.utils.ioutils.RegexStringEditor;
+
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
@@ -9,6 +11,7 @@ import java.util.regex.Pattern;
*
* @author EVE
*
+ * TODO rewrite using {@link RegexStringEditor}
*/
public class SimpleTokenSplitter implements TokenSplitter {
/*