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:06:03 -0400 |
| commit | fc7ec6de2c83ff6c074092534a644d1da129edd3 (patch) | |
| tree | 5b13f2ead5b1a0271dc61fbd207c3438ddb006b2 /BJC-Utils2/src/main/java/bjc/utils | |
| parent | 31bdd87d4d3330bd2da8ada79e213e1c232f4183 (diff) | |
Fix subgroups.
This fixes subgroups, by mostly rewriting the way the delimiter works.
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils')
| -rw-r--r-- | BJC-Utils2/src/main/java/bjc/utils/parserutils/SequenceDelimiter.java | 432 | ||||
| -rw-r--r-- | BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDelimiter.java | 2 |
2 files changed, 56 insertions, 378 deletions
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); } } |
