diff options
| author | bjculkin <bjculkin@mix.wvu.edu> | 2017-03-19 19:06:03 -0400 |
|---|---|---|
| committer | bjculkin <bjculkin@mix.wvu.edu> | 2017-03-19 19:09:35 -0400 |
| commit | 3099ab89d28ffabdd67f5bcd74efb050e061691c (patch) | |
| tree | 3523135690ead64a01cc98ce73b46ec2cf5c1913 /BJC-Utils2/src/main | |
| parent | 31bdd87d4d3330bd2da8ada79e213e1c232f4183 (diff) | |
Fix subgroups.
This fixes subgroups, by mostly rewriting the way the delimiter works.
Diffstat (limited to 'BJC-Utils2/src/main')
7 files changed, 576 insertions, 382 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DelimiterException.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DelimiterException.java new file mode 100644 index 0000000..667e2fe --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DelimiterException.java @@ -0,0 +1,16 @@ +package bjc.utils.parserutils; + +/** + * The superclass for exceptions thrown during sequence delimitation. + */ +public class DelimiterException extends RuntimeException { + /** + * Create a new generic delimiter exception. + * + * @param res + * The reason for this exception. + */ + public DelimiterException(String res) { + super(res); + } +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DelimiterGroup.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DelimiterGroup.java new file mode 100644 index 0000000..37a8726 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DelimiterGroup.java @@ -0,0 +1,392 @@ +package bjc.utils.parserutils; + +import bjc.utils.data.ITree; +import bjc.utils.data.Tree; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +import java.util.Arrays; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Represents a possible delimiter group to match. + * + * @author EVE + * + * @param <T> + * The type of items in the sequence. + */ +public class DelimiterGroup<T> { + /** + * Represents an instance of a delimiter group. + * + * @author EVE + * + */ + public class OpenGroup { + private Deque<ITree<T>> contents; + + private IList<ITree<T>> currentGroup; + + private T opener; + + /** + * Create a new instance of a delimiter group. + * + * @param open + * The item that opened this group. + */ + public OpenGroup(T open) { + opener = open; + + contents = new LinkedList<>(); + + currentGroup = new FunctionalList<>(); + } + + /** + * Add an item to this group instance. + * + * @param itm + * The item to add to this group instance. + */ + public void addItem(ITree<T> itm) { + currentGroup.add(itm); + } + + /** + * Mark a subgroup. + * + * @param marker + * The item that indicated this subgroup. + * + * @param chars + * The characteristics for building the tree. + */ + public void markSubgroup(T marker, SequenceCharacteristics<T> chars) { + ITree<T> subgroupContents = new Tree<>(chars.contents); + for(ITree<T> itm : currentGroup) { + subgroupContents.addChild(itm); + } + + while(!contents.isEmpty()) { + ITree<T> possibleSubordinate = contents.peek(); + + if(possibleSubordinate.getHead().equals(chars.subgroup)) { + T otherMarker = possibleSubordinate.getChild(1).getHead(); + + if(subgroups.get(marker) > subgroups.get(otherMarker)) { + subgroupContents.prependChild(contents.pop()); + } else { + break; + } + } else { + subgroupContents.prependChild(contents.pop()); + } + } + + Tree<T> subgroup = new Tree<>(chars.subgroup, subgroupContents, new Tree<>(marker)); + + //System.out.println("\tTRACE: generated subgroup\n" + subgroup + "\n\n"); + contents.push(subgroup); + + currentGroup = new FunctionalList<>(); + } + + /** + * Convert this group into a tree. + * + * @param closer + * The item that closed this group. + * + * @param chars + * The characteristics for building the tree. + * + * @return This group as a tree. + */ + public ITree<T> toTree(T closer, SequenceCharacteristics<T> chars) { + ITree<T> res = new Tree<>(chars.contents); + + if(contents.isEmpty()) { + currentGroup.forEach(res::addChild); + } else { + while(!contents.isEmpty()) { + res.prependChild(contents.poll()); + } + + currentGroup.forEach(res::addChild); + } + + return new Tree<>(groupName, new Tree<>(opener), res, new Tree<>(closer)); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("OpenGroup [contents="); + builder.append(contents); + builder.append(", currentGroup="); + builder.append(currentGroup); + builder.append(", opener="); + builder.append(opener); + builder.append("]"); + + return builder.toString(); + } + + /** + * Check if a group is excluded at the top level of this group. + * + * @param groupName + * The group to check. + * + * @return Whether or not the provided group is excluded. + */ + public boolean excludes(T groupName) { + return topLevelExclusions.contains(groupName); + } + + /** + * Check if the provided delimiter would close this group. + * + * @param del + * The string to check as a closing delimiter. + * + * @return Whether or not the provided delimiter closes this + * group. + */ + public boolean isClosing(T del) { + return closingDelimiters.contains(del); + } + + /** + * Get the name of the group this is an instance of. + * + * @return The name of the group this is an instance of. + */ + public T getName() { + return groupName; + } + + /** + * Get the groups that aren't allowed at all in this group. + * + * @return The groups that aren't allowed at all in this group. + */ + public Set<T> getNestingExclusions() { + return groupExclusions; + } + + /** + * Checks if a given token marks a subgroup. + * + * @param tok + * The token to check. + * + * @return Whether or not the token marks a subgroup. + */ + public boolean marksSubgroup(T tok) { + return subgroups.containsKey(tok); + } + } + + /** + * The name of this delimiter group. + */ + public final T groupName; + + /* + * The delimiters that close this group. + */ + private Set<T> closingDelimiters; + + /* + * The groups that can't occur in the top level of this group. + */ + private Set<T> topLevelExclusions; + + /* + * The groups that can't occur anywhere inside this group. + */ + private Set<T> groupExclusions; + + /* + * Mapping from sub-group delimiters, to any sub-groups enclosed in + * them. + */ + private Map<T, Integer> subgroups; + + /** + * Create a new empty delimiter group. + * + * @param name + * The name of the delimiter group + */ + public DelimiterGroup(T name) { + if(name == null) throw new NullPointerException("Group name must not be null"); + + groupName = name; + + closingDelimiters = new HashSet<>(); + topLevelExclusions = new HashSet<>(); + groupExclusions = new HashSet<>(); + subgroups = new HashMap<>(); + } + + /** + * Adds one or more delimiters that close this group. + * + * @param closers + * Delimiters that close this group. + */ + @SafeVarargs + public final void addClosing(T... closers) { + List<T> closerList = Arrays.asList(closers); + + for(T closer : closerList) { + if(closer == null) { + throw new NullPointerException("Closing delimiter must not be null"); + } else if(closer.equals("")) { + /* + * We can do this because equals works on + * arbitrary objects, not just those of the same + * type. + */ + throw new IllegalArgumentException("Empty string is not a valid exclusion"); + } else { + closingDelimiters.add(closer); + } + } + } + + /** + * Adds one or more groups that cannot occur in the top level of this + * group. + * + * @param exclusions + * The groups forbidden in the top level of this group. + */ + @SafeVarargs + public final void addTopLevelForbid(T... exclusions) { + for(T exclusion : exclusions) { + if(exclusion == null) { + throw new NullPointerException("Exclusion must not be null"); + } else if(exclusion.equals("")) { + /* + * We can do this because equals works on + * arbitrary objects, not just those of the same + * type. + */ + throw new IllegalArgumentException("Empty string is not a valid exclusion"); + } else { + topLevelExclusions.add(exclusion); + } + } + } + + /** + * Adds one or more groups that cannot occur at all in this group. + * + * @param exclusions + * The groups forbidden inside this group. + */ + @SafeVarargs + public final void addGroupForbid(T... exclusions) { + for(T exclusion : exclusions) { + if(exclusion == null) { + throw new NullPointerException("Exclusion must not be null"); + } else if(exclusion.equals("")) { + /* + * We can do this because equals works on + * arbitrary objects, not just those of the same + * type. + */ + throw new IllegalArgumentException("Empty string is not a valid exclusion"); + } else { + groupExclusions.add(exclusion); + } + } + } + + /** + * Adds sub-group markers to this group. + * + * @param subgroup + * The token to mark a sub-group. + * + * @param priority + * The priority of this sub-group. + * + * @param contained + * Any sub-groups to enclose in this group. + */ + public void addSubgroup(T subgroup, int priority) { + if(subgroup == null) { + throw new NullPointerException("Subgroup marker must not be null"); + } + + subgroups.put(subgroup, priority); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("("); + + builder.append("groupName=["); + builder.append(groupName); + builder.append("], "); + + builder.append("closingDelimiters=["); + for(T closer : closingDelimiters) { + builder.append(closer + ","); + } + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + + if(topLevelExclusions != null && !topLevelExclusions.isEmpty()) { + builder.append(", "); + builder.append("topLevelExclusions=["); + for(T exclusion : topLevelExclusions) { + builder.append(exclusion + ","); + } + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + } + + if(groupExclusions != null && !groupExclusions.isEmpty()) { + builder.append(", "); + builder.append("groupExclusions=["); + for(T exclusion : groupExclusions) { + builder.append(exclusion + ","); + } + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + } + + builder.append(" )"); + + return builder.toString(); + } + + /** + * Open an instance of this group. + * + * @param opener + * The item that opened this group. + * + * @return An opened instance of this group. + */ + public OpenGroup open(T opener) { + return new OpenGroup(opener); + } + +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceCharacteristics.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceCharacteristics.java new file mode 100644 index 0000000..c28e42b --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceCharacteristics.java @@ -0,0 +1,101 @@ +package bjc.utils.parserutils; + +/** + * Marks the parameters for building a sequence tree. + * + * @author EVE + * + * @param <T> + * The type of item in the tree. + */ +public class SequenceCharacteristics<T> { + /** + * The item to mark the root of the tree. + */ + public final T root; + + /** + * The item to mark the contents of a group/subgroup. + */ + + public final T contents; + + /** + * The item to mark a subgroup. + */ + public final T subgroup; + + /** + * Create a new set of parameters for building a tree. + * + * @param root + * The root marker. + * @param contents + * The group/subgroup contents marker. + * @param subgroup + * The subgroup marker. + */ + public SequenceCharacteristics(T root, T contents, T subgroup) { + this.root = root; + this.contents = contents; + this.subgroup = subgroup; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((contents == null) ? 0 : contents.hashCode()); + result = prime * result + ((root == null) ? 0 : root.hashCode()); + result = prime * result + ((subgroup == null) ? 0 : subgroup.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof SequenceCharacteristics)) return false; + + SequenceCharacteristics<?> other = (SequenceCharacteristics<?>) obj; + + if(contents == null) { + if(other.contents != null) return false; + } else if(!contents.equals(other.contents)) return false; + + if(root == null) { + if(other.root != null) return false; + } else if(!root.equals(other.root)) return false; + + if(subgroup == null) { + if(other.subgroup != null) return false; + } else if(!subgroup.equals(other.subgroup)) return false; + + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("SequenceCharacteristics [root="); + builder.append(root == null ? "(null)" : root); + builder.append(", contents="); + builder.append(contents == null ? "(null)" : contents); + builder.append(", subgroup="); + builder.append(subgroup == null ? "(null)" : subgroup); + builder.append("]"); + + return builder.toString(); + } +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceDelimiter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceDelimiter.java index af6ba81..55e3dd1 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceDelimiter.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceDelimiter.java @@ -7,16 +7,13 @@ import bjc.utils.esodata.SimpleStack; import bjc.utils.esodata.Stack; import bjc.utils.funcdata.IMap; import bjc.utils.funcutils.StringUtils; +import bjc.utils.parserutils.DelimiterGroup.OpenGroup; import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; -import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Set; /** * Convert linear sequences into trees that represent group structure. @@ -27,234 +24,6 @@ import java.util.Set; * The type of items in the sequence. */ public class SequenceDelimiter<T> { - /** - * Represents a possible delimiter group to match. - * - * @author EVE - * - * @param <T2> - * The type of items in the sequence. - */ - public static class DelimiterGroup<T2> { - /** - * The name of this delimiter group. - */ - public final T2 groupName; - - /* - * The delimiters that close this group. - */ - private Set<T2> closingDelimiters; - - /* - * The groups that can't occur in the top level of this group. - */ - private Set<T2> topLevelExclusions; - - /* - * The groups that can't occur anywhere inside this group. - */ - private Set<T2> groupExclusions; - - /* - * Mapping from sub-group delimiters, to any sub-groups enclosed - * in them. - */ - private Map<T2, Set<T2>> subgroups; - - /** - * Create a new empty delimiter group. - * - * @param name - * The name of the delimiter group - */ - public DelimiterGroup(T2 name) { - if(name == null) throw new NullPointerException("Group name must not be null"); - - groupName = name; - - closingDelimiters = new HashSet<>(); - topLevelExclusions = new HashSet<>(); - groupExclusions = new HashSet<>(); - subgroups = new HashMap<>(); - } - - /** - * Check if the provided delimiter would close this group. - * - * @param del - * The string to check as a closing delimiter. - * - * @return Whether or not the provided delimiter closes this - * group. - */ - public boolean isClosing(T2 del) { - return closingDelimiters.contains(del); - } - - /** - * Adds one or more delimiters that close this group. - * - * @param closers - * Delimiters that close this group. - */ - @SafeVarargs - public final void addClosing(T2... closers) { - List<T2> closerList = Arrays.asList(closers); - - for(T2 closer : closerList) { - if(closer == null) { - throw new NullPointerException("Closing delimiter must not be null"); - } else if(closer.equals("")) { - /* - * We can do this because equals works - * on arbitrary objects, not just those - * of the same type. - */ - throw new IllegalArgumentException("Empty string is not a valid exclusion"); - } else { - closingDelimiters.add(closer); - } - } - } - - /** - * Adds one or more groups that cannot occur in the top level of - * this group. - * - * @param exclusions - * The groups forbidden in the top level of this - * group. - */ - @SafeVarargs - public final void addTopLevelForbid(T2... exclusions) { - for(T2 exclusion : exclusions) { - if(exclusion == null) { - throw new NullPointerException("Exclusion must not be null"); - } else if(exclusion.equals("")) { - /* - * We can do this because equals works - * on arbitrary objects, not just those - * of the same type. - */ - throw new IllegalArgumentException("Empty string is not a valid exclusion"); - } else { - topLevelExclusions.add(exclusion); - } - } - } - - /** - * Adds one or more groups that cannot occur at all in this - * group. - * - * @param exclusions - * The groups forbidden inside this group. - */ - @SafeVarargs - public final void addGroupForbid(T2... exclusions) { - for(T2 exclusion : exclusions) { - if(exclusion == null) { - throw new NullPointerException("Exclusion must not be null"); - } else if(exclusion.equals("")) { - /* - * We can do this because equals works - * on arbitrary objects, not just those - * of the same type. - */ - throw new IllegalArgumentException("Empty string is not a valid exclusion"); - } else { - groupExclusions.add(exclusion); - } - } - } - - /** - * Adds sub-group markers to this group. - * - * @param subgroup - * The token to mark a sub-group. - * - * @param contained - * Any sub-groups to enclose in this group. - */ - @SafeVarargs - public final void addSubgroup(T2 subgroup, T2... contained) { - if(subgroup == null) { - throw new NullPointerException("Subgroup marker must not be null"); - } - - Set<T2> contains = new HashSet<>(); - for(T2 contain : contained) { - if(contain == null) { - throw new NullPointerException("Contained group must not be null"); - } - - contains.add(contain); - } - - subgroups.put(subgroup, contains); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append("("); - - builder.append("groupName=["); - builder.append(groupName); - builder.append("], "); - - builder.append("closingDelimiters=["); - for(T2 closer : closingDelimiters) { - builder.append(closer + ","); - } - builder.deleteCharAt(builder.length() - 1); - builder.append("]"); - - if(topLevelExclusions != null && !topLevelExclusions.isEmpty()) { - builder.append(", "); - builder.append("topLevelExclusions=["); - for(T2 exclusion : topLevelExclusions) { - builder.append(exclusion + ","); - } - builder.deleteCharAt(builder.length() - 1); - builder.append("]"); - } - - if(groupExclusions != null && !groupExclusions.isEmpty()) { - builder.append(", "); - builder.append("groupExclusions=["); - for(T2 exclusion : topLevelExclusions) { - builder.append(exclusion + ","); - } - builder.deleteCharAt(builder.length() - 1); - builder.append("]"); - } - - builder.append(" )"); - - return builder.toString(); - } - - } - - /** - * The superclass for exceptions thrown during sequence delimitation. - */ - public static class DelimiterException extends RuntimeException { - /** - * Create a new generic delimiter exception. - * - * @param res - * The reason for this exception. - */ - public DelimiterException(String res) { - super(res); - } - } - /* * Mapping from opening delimiters to the names of the groups they open */ @@ -265,6 +34,8 @@ public class SequenceDelimiter<T> { */ private Map<T, DelimiterGroup<T>> groups; + private DelimiterGroup<T> initialGroup; + /** * Create a new sequence delimiter. */ @@ -292,18 +63,12 @@ public class SequenceDelimiter<T> { * <marker> -> STRING * </pre> * + * @param chars + * The parameters on how to mark certain portions of the + * tre. * @param seq * The sequence to delimit. * - * @param root - * The root of the returned tree. - * - * @param contents - * The item to use to mark the contents of a group - * - * @param subgroup - * The item to use to mark a sub-group. - * * @return The sequence as a tree that matches its group structure. Each * node in the tree is either a data node, a subgroup node, or a * group node. @@ -329,36 +94,34 @@ public class SequenceDelimiter<T> { * delimitation. * */ - public ITree<T> delimitSequence(T root, T contents, T subgroup, @SuppressWarnings("unchecked") T... seq) + public ITree<T> delimitSequence(SequenceCharacteristics<T> chars, @SuppressWarnings("unchecked") T... seq) throws DelimiterException { + if(initialGroup == null) { + throw new NullPointerException("Initial group must be specified."); + } + /* - * The root node of the tree to give back. + * The stack of opened and not yet closed groups. */ - ITree<T> res = new Tree<>(root); + Stack<DelimiterGroup<T>.OpenGroup> groupStack = new SimpleStack<>(); + + /* + * Open initial group. + */ + groupStack.push(initialGroup.open(chars.root)); /* * Handle the trivial case where there are no groups. */ if(openDelimiters.isEmpty()) { for(T tok : seq) { - res.addChild(new Tree<>(tok)); + groupStack.top().addItem(new Tree<>(tok)); } - return res; + return groupStack.pop().toTree(chars.root, chars); } /* - * The stack of trees that represent the sequence. - */ - Stack<ITree<T>> trees = new SimpleStack<>(); - trees.push(res); - - /* - * The stack of opened and not yet closed groups. - */ - Stack<DelimiterGroup<T>> groupStack = new SimpleStack<>(); - - /* * Groups that aren't allowed to be opened at the moment. */ Multiset<T> forbiddenDelimiters = HashMultiset.create(); @@ -394,7 +157,7 @@ public class SequenceDelimiter<T> { if(whoForbid.containsKey(tok)) { forbiddenBy = whoForbid.get(tok); } else { - forbiddenBy = groupStack.top().groupName; + forbiddenBy = groupStack.top().getName(); } String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then"); @@ -413,30 +176,13 @@ public class SequenceDelimiter<T> { /* * Add an open group. */ - groupStack.push(group); - - /* - * The tree that represents the opened group. - */ - ITree<T> groupTree = new Tree<>(groupName); - groupTree.addChild(new Tree<>(tok)); - - /* - * The tree that represents the contents of the - * opened group. - */ - ITree<T> groupContents = new Tree<>(contents); - - /* - * Add the trees to the open trees. - */ - trees.push(groupTree); - trees.push(groupContents); + DelimiterGroup<T>.OpenGroup open = group.open(tok); + groupStack.push(open); /* * Add the nested exclusions from this group */ - for(T exclusion : group.groupExclusions) { + for(T exclusion : open.getNestingExclusions()) { forbiddenDelimiters.add(exclusion); whoForbid.put(exclusion, groupName); @@ -445,111 +191,39 @@ public class SequenceDelimiter<T> { /* * Close the group. */ - DelimiterGroup<T> closed = groupStack.pop(); - - /* - * Remove the contents of the group and the - * group itself from the stack. - */ - ITree<T> contentTree = trees.pop(); - ITree<T> groupTree = trees.pop(); - - /* - * Fill in the group node. - */ - groupTree.addChild(contentTree); - groupTree.addChild(new Tree<>(tok)); + DelimiterGroup<T>.OpenGroup closed = groupStack.pop(); - /* - * Add the group node to the group that - * contained it. - */ - trees.top().addChild(groupTree); + groupStack.top().addItem(closed.toTree(tok, chars)); /* * Remove nested exclusions from this group. */ - for(T excludedGroup : closed.groupExclusions) { + for(T excludedGroup : closed.getNestingExclusions()) { forbiddenDelimiters.remove(excludedGroup); whoForbid.remove(excludedGroup); } - } else if(!groupStack.empty() && groupStack.top().subgroups.containsKey(tok)) { - /* - * Parse a sub-group. - */ - - /* - * The set of enclosed groups. - */ - Set<T> enclosed = groupStack.top().subgroups.get(tok); - - /* - * The current contents of this group. - */ - ITree<T> contentTree = trees.pop(); - - /* - * Find the first element to enclose in the - * subgroup. - */ - int ind = contentTree.revFind((chd) -> { - return checkChild(subgroup, enclosed, chd); - }); - - if(ind == -1) ind = 0; - - ITree<T> newContentTree = new Tree<>(contentTree.getHead()); - ITree<T> subgroupContents = new Tree<>(contents); - - /* - * Split content tree into an untouched tree, - * and the subgroup. - */ - for(int j = 0; j < contentTree.getChildrenCount(); j++) { - ITree<T> child = contentTree.getChild(j); - - if(j < ind) { - newContentTree.addChild(child); - } else { - subgroupContents.addChild(child); - } - } - - /* - * Construct the subgroup. - */ - ITree<T> subgroupTree = new Tree<>(subgroup); - subgroupTree.addChild(subgroupContents); - subgroupTree.addChild(new Tree<>(tok)); - - /* - * Add the subgroup to the group. - */ - newContentTree.addChild(subgroupTree); - - /* - * Add the group contents. - */ - trees.push(newContentTree); + } else if(!groupStack.empty() && groupStack.top().marksSubgroup(tok)) { + groupStack.top().markSubgroup(tok, chars); } else { - trees.top().addChild(new Tree<>(tok)); + groupStack.top().addItem(new Tree<>(tok)); } } /* * Error if not all groups were closed. */ - if(!groupStack.empty()) { - DelimiterGroup<T> group = groupStack.top(); + if(groupStack.size() > 1) { + DelimiterGroup<T>.OpenGroup group = groupStack.top(); + StringBuilder msgBuilder = new StringBuilder(); - String closingDelims = StringUtils.toEnglishList(group.closingDelimiters.toArray(), false); + String closingDelims = StringUtils.toEnglishList(group.getNestingExclusions().toArray(), false); String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then"); msgBuilder.append("Unclosed group '"); - msgBuilder.append(group.groupName); + msgBuilder.append(group.getName()); msgBuilder.append("'. Expected one of "); msgBuilder.append(closingDelims); msgBuilder.append(" to close it\nOpen groups: "); @@ -558,28 +232,16 @@ public class SequenceDelimiter<T> { throw new DelimiterException(msgBuilder.toString()); } - return res; - } - - private boolean checkChild(T subgroup, Set<T> enclosed, ITree<T> chd) { - System.out.println("Checking child '" + chd.getHead() + "' for subgroups."); - - if(chd.getHead().equals(subgroup)) { - System.out.println("Checking if '" + chd.getChild(1) + "' is a subordinate group."); - boolean contains = enclosed.contains(chd.getChild(1)); - System.out.println("It " + (contains ? "was" : "wasn't")); - return contains; - } else { - return false; - } + return groupStack.pop().toTree(chars.root, chars); } - private boolean isForbidden(Stack<DelimiterGroup<T>> groupStack, Multiset<T> forbiddenDelimiters, T groupName) { + private boolean isForbidden(Stack<DelimiterGroup<T>.OpenGroup> groupStack, Multiset<T> forbiddenDelimiters, + T groupName) { boolean localForbid; if(groupStack.empty()) localForbid = false; else - localForbid = groupStack.top().topLevelExclusions.contains(groupName); + localForbid = groupStack.top().excludes(groupName); return localForbid || forbiddenDelimiters.contains(groupName); } @@ -657,10 +319,26 @@ public class SequenceDelimiter<T> { if(groups != null) { builder.append("groups="); builder.append(groups); + builder.append(","); + } + + if(initialGroup != null) { + builder.append("initialGroup="); + builder.append(initialGroup); } builder.append("]"); return builder.toString(); } + + /** + * Set the initial group of this delimiter. + * + * @param initialGroup + * The initial group of this delimiter. + */ + public void setInitialGroup(DelimiterGroup<T> initialGroup) { + this.initialGroup = initialGroup; + } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDelimiter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDelimiter.java index 8b437aa..26d0c59 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDelimiter.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDelimiter.java @@ -26,6 +26,6 @@ public class StringDelimiter extends SequenceDelimiter<String> { * @see SequenceDelimiter */ public ITree<String> delimitSequence(String... seq) throws DelimiterException { - return super.delimitSequence("root", "contents", "subgroup", seq); + return super.delimitSequence(new SequenceCharacteristics("root", "contents", "subgroup"), seq); } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenSplitter.java index ec69ade..db2c288 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenSplitter.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenSplitter.java @@ -104,6 +104,8 @@ public class TokenSplitter { */ public void addDelimiter(String... delims) { for(String delim : delims) { + if(delim == null) throw new NullPointerException("Delim must not be null"); + String quoteDelim = Pattern.quote(delim); String delimPat = String.format(WITH_DELIM, quoteDelim); @@ -133,6 +135,8 @@ public class TokenSplitter { */ public void addMultiDelimiter(String... delims) { for(String delim : delims) { + if(delim == null) throw new NullPointerException("Delim must not be null"); + String delimPat = String.format(WITH_MULTI_DELIM, "(?:" + delim + ")"); if(currPatt == null) { @@ -154,11 +158,13 @@ public class TokenSplitter { /** * Marks strings matching the pattern delim as non-splittable. * - * @param delimSet + * @param delims * The regex to not splitting matching strings. */ public void addNonMatcher(String... delims) { for(String delim : delims) { + if(delim == null) throw new NullPointerException("Delim must not be null"); + if(currPatt == null) { currPatt = new StringBuilder(); currExclusionPatt = new StringBuilder(); 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 8224928..ce975f1 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java @@ -20,11 +20,12 @@ public class TokenUtils { * situation that indicates its use as an infix operator. * * @param expression - * The expression to check + * The expression to check. * @param operator - * The operator to see if it is contained + * The operator to see if it is contained. + * * @return Whether or not the given expression contains the specified - * operator as a infix operator + * operator as a infix operator. */ public static boolean containsInfixOperator(String expression, String operator) { return StringUtils.countMatches(expression, operator) == 1 && !expression.equalsIgnoreCase(operator) |
