diff --git a/src/org/iguana/grammar/GrammarGraphBuilder.java b/src/org/iguana/grammar/GrammarGraphBuilder.java index eadf909b4..b909a7275 100644 --- a/src/org/iguana/grammar/GrammarGraphBuilder.java +++ b/src/org/iguana/grammar/GrammarGraphBuilder.java @@ -371,13 +371,13 @@ private void visitSymbol(Symbol symbol) { Conditions preconditions = i == 0 ? ConditionsFactory.DEFAULT : getConditions(symbol.getPreConditions()); if (symbol.getLabel() != null) { - BodyGrammarSlot declared = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + BodyGrammarSlot declared = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i), null, null, null); EpsilonTransition transition = new EpsilonTransition(Type.DECLARE_LABEL, symbol.getLabel(), preconditions, currentSlot, declared); setTransition(transition); currentSlot = declared; } else { - BodyGrammarSlot checked = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i + 1), null, null, null); + BodyGrammarSlot checked = getBodyGrammarSlot(rule, i + 1, rule.getPosition(i), null, null, null); EpsilonTransition transition = new EpsilonTransition(preconditions, currentSlot, checked); setTransition(transition); currentSlot = checked; diff --git a/src/org/iguana/grammar/runtime/Position.java b/src/org/iguana/grammar/runtime/Position.java index 62ce7cfe3..25fe3eb51 100644 --- a/src/org/iguana/grammar/runtime/Position.java +++ b/src/org/iguana/grammar/runtime/Position.java @@ -37,7 +37,6 @@ public class Position { private final RuntimeRule rule; - private final int posInRule; private final int posInSymbol; @@ -69,15 +68,12 @@ public boolean isLast() { @Override public boolean equals(Object obj) { - if (this == obj) - return true; - - if (!(obj instanceof Position)) - return false; - + if (this == obj) return true; + if (!(obj instanceof Position)) return false; Position other = (Position) obj; - - return rule.equals(other.rule) && posInRule == other.posInRule && posInSymbol == other.posInSymbol; + return rule.equals(other.rule) + && posInRule == other.posInRule + && posInSymbol == other.posInSymbol; } @Override diff --git a/src/org/iguana/grammar/slot/AbstractTransition.java b/src/org/iguana/grammar/slot/AbstractTransition.java index 3be041f7f..b3260726b 100644 --- a/src/org/iguana/grammar/slot/AbstractTransition.java +++ b/src/org/iguana/grammar/slot/AbstractTransition.java @@ -35,11 +35,11 @@ public abstract class AbstractTransition implements Transition { protected final BodyGrammarSlot origin; public AbstractTransition(BodyGrammarSlot origin, BodyGrammarSlot dest) { - if (origin.equals(dest)) { - throw new IllegalArgumentException("Origin " + origin + " and Destination " + dest + " are equal"); - } this.origin = origin; this.dest = dest; + if (origin == dest) { + throw new IllegalArgumentException("Origin " + origin + " and Destination " + dest + " are equal"); + } } @Override diff --git a/src/org/iguana/grammar/slot/BodyGrammarSlot.java b/src/org/iguana/grammar/slot/BodyGrammarSlot.java index 69c755032..0ee4c47bd 100644 --- a/src/org/iguana/grammar/slot/BodyGrammarSlot.java +++ b/src/org/iguana/grammar/slot/BodyGrammarSlot.java @@ -41,7 +41,6 @@ import org.iguana.utils.input.Input; import java.util.Map; -import java.util.Objects; import java.util.Set; public class BodyGrammarSlot implements GrammarSlot { @@ -113,8 +112,7 @@ public T getIntermediateNode( T rightResult, Environment env, IguanaRuntime runtime) { - if (isFirst()) - return rightResult; + if (isFirst()) return rightResult; Key key = Keys.from(destinationIndex, rightResult.getRightExtent(), env); @@ -268,24 +266,4 @@ public boolean isEnd() { public FollowTest getFollowTest() { return followTest; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BodyGrammarSlot that = (BodyGrammarSlot) o; - return i1 == that.i1 - && i2 == that.i2 - && Objects.equals(position, that.position) - && Objects.equals(conditions, that.conditions) - && Objects.equals(label, that.label) - && Objects.equals(variable, that.variable) - && Objects.equals(state, that.state) - && Objects.equals(followTest, that.followTest); - } - - @Override - public int hashCode() { - return Objects.hash(position, conditions, label, i1, variable, i2, state, followTest); - } } diff --git a/src/org/iguana/grammar/slot/EpsilonTransition.java b/src/org/iguana/grammar/slot/EpsilonTransition.java index c1c53cb44..c679cb889 100644 --- a/src/org/iguana/grammar/slot/EpsilonTransition.java +++ b/src/org/iguana/grammar/slot/EpsilonTransition.java @@ -39,6 +39,8 @@ import org.iguana.util.Tuple; import org.iguana.utils.input.Input; +import java.util.Objects; + public class EpsilonTransition extends AbstractTransition { private final Type type; @@ -63,7 +65,10 @@ public EpsilonTransition( BodyGrammarSlot origin, BodyGrammarSlot dest) { super(origin, dest); - + if (Objects.equals(origin.position, dest.position)) { + throw new IllegalArgumentException("Origin and destination positions must be the same for epsilon " + + "transitions: " + origin.position + " is not equal to " + dest.position); + } assert label != null && (type == Type.DECLARE_LABEL || type == Type.STORE_LABEL); this.type = type; diff --git a/src/org/iguana/grammar/slot/NonterminalGrammarSlot.java b/src/org/iguana/grammar/slot/NonterminalGrammarSlot.java index 0fb26d65a..091ef328f 100644 --- a/src/org/iguana/grammar/slot/NonterminalGrammarSlot.java +++ b/src/org/iguana/grammar/slot/NonterminalGrammarSlot.java @@ -38,11 +38,11 @@ import org.iguana.result.Result; import org.iguana.util.Configuration.EnvironmentImpl; import org.iguana.util.ParserLogger; -import org.iguana.utils.collections.IntHashMap; import org.iguana.utils.collections.Keys; import org.iguana.utils.collections.OpenAddressingHashMap; -import org.iguana.utils.collections.OpenAddressingIntHashMap; import org.iguana.utils.collections.key.Key; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; import org.iguana.utils.collections.rangemap.RangeMap; import org.iguana.utils.input.Input; @@ -206,7 +206,7 @@ public void create( int inputIndex = result.isDummy() ? gssNode.getInputIndex() : result.getRightExtent(); if (!slot.getConditions().execute(input, returnSlot, gssNode, inputIndex, runtime.getEvaluatorContext(), - runtime)) { + runtime)) { runtime.scheduleDescriptor(slot, gssNode, runtime.getResultOps().dummy(), runtime.getEnvironment()); } diff --git a/src/org/iguana/grammar/slot/TerminalGrammarSlot.java b/src/org/iguana/grammar/slot/TerminalGrammarSlot.java index 0c48d2820..e96e19ccb 100644 --- a/src/org/iguana/grammar/slot/TerminalGrammarSlot.java +++ b/src/org/iguana/grammar/slot/TerminalGrammarSlot.java @@ -32,8 +32,8 @@ import org.iguana.regex.matcher.Matcher; import org.iguana.regex.matcher.MatcherFactory; import org.iguana.result.Result; -import org.iguana.utils.collections.IntHashMap; -import org.iguana.utils.collections.OpenAddressingIntHashMap; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; import org.iguana.utils.input.Input; public class TerminalGrammarSlot implements GrammarSlot { @@ -55,9 +55,7 @@ public T getResult(Input input, int i, IguanaRuntime runti terminalNodes = new OpenAddressingIntHashMap<>(); } Object node = terminalNodes.get(i); - if (node == failure) { - return null; - } + if (node == failure) return null; @SuppressWarnings("unchecked") T terminalNode = (T) node; diff --git a/src/org/iguana/grammar/slot/TerminalTransition.java b/src/org/iguana/grammar/slot/TerminalTransition.java index 93c60836b..afe4fe543 100644 --- a/src/org/iguana/grammar/slot/TerminalTransition.java +++ b/src/org/iguana/grammar/slot/TerminalTransition.java @@ -91,8 +91,9 @@ public void execute( return; } - if (dest.getLabel() != null) + if (dest.getLabel() != null) { runtime.getEvaluatorContext().declareVariable(dest.getLabel(), cr); + } if (postConditions.execute(input, origin, u, cr.getLeftExtent(), cr.getRightExtent(), runtime.getEvaluatorContext(), runtime)) { diff --git a/src/org/iguana/gss/StartGSSNode.java b/src/org/iguana/gss/StartGSSNode.java index b6c72308b..98f70801c 100644 --- a/src/org/iguana/gss/StartGSSNode.java +++ b/src/org/iguana/gss/StartGSSNode.java @@ -7,8 +7,8 @@ import org.iguana.parser.IguanaRuntime; import org.iguana.result.Result; import org.iguana.util.ParserLogger; -import org.iguana.utils.collections.IntHashMap; -import org.iguana.utils.collections.OpenAddressingIntHashMap; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; import org.iguana.utils.input.Input; import java.util.Collections; diff --git a/src/org/iguana/regex/matcher/DFAMatcherFactory.java b/src/org/iguana/regex/matcher/DFAMatcherFactory.java index c2ea87bb6..61ea13e6e 100644 --- a/src/org/iguana/regex/matcher/DFAMatcherFactory.java +++ b/src/org/iguana/regex/matcher/DFAMatcherFactory.java @@ -40,21 +40,14 @@ public class DFAMatcherFactory implements MatcherFactory { private final Map matcherCache = new HashMap<>(); private final Map backwardsMatcherCache = new HashMap<>(); - public org.iguana.regex.matcher.Matcher getMatcher(RegularExpression regex) { - - if (regex == Epsilon.getInstance()) - return epsilonMatcher(); - - if (regex instanceof Char) - return characterMatcher((Char) regex); - - if (regex instanceof CharRange) - return characterRangeMatcher((CharRange) regex); - + public Matcher getMatcher(RegularExpression regex) { + if (regex == Epsilon.getInstance()) return epsilonMatcher(); + if (regex instanceof Char) return characterMatcher((Char) regex); + if (regex instanceof CharRange) return characterRangeMatcher((CharRange) regex); return matcherCache.computeIfAbsent(regex, DFAMatcher::new); } - public org.iguana.regex.matcher.Matcher getBackwardsMatcher(RegularExpression regex) { + public Matcher getBackwardsMatcher(RegularExpression regex) { if (regex instanceof Char) return characterBackwardsMatcher((Char) regex); @@ -65,19 +58,19 @@ public org.iguana.regex.matcher.Matcher getBackwardsMatcher(RegularExpression re return backwardsMatcherCache.computeIfAbsent(regex, DFABackwardsMatcher::new); } - public static org.iguana.regex.matcher.Matcher characterMatcher(Char c) { + public static Matcher characterMatcher(Char c) { return (input, i) -> input.charAt(i) == c.getValue() ? 1 : -1; } - public static org.iguana.regex.matcher.Matcher characterBackwardsMatcher(Char c) { + public static Matcher characterBackwardsMatcher(Char c) { return (input, i) -> i == 0 ? -1 : (input.charAt(i - 1) == c.getValue() ? 1 : -1); } - public static org.iguana.regex.matcher.Matcher characterRangeMatcher(CharRange range) { + public static Matcher characterRangeMatcher(CharRange range) { return (input, i) -> input.charAt(i) >= range.getStart() && input.charAt(i) <= range.getEnd() ? 1 : -1; } - public static org.iguana.regex.matcher.Matcher characterRangeBackwardsMatcher(CharRange range) { + public static Matcher characterRangeBackwardsMatcher(CharRange range) { return (input, i) -> i == 0 ? -1 : (input.charAt(i - 1) >= range.getStart() && input.charAt(i - 1) <= range.getEnd() ? 1 : -1); } diff --git a/src/org/iguana/traversal/idea/GenerateJFlex.java b/src/org/iguana/traversal/idea/GenerateJFlex.java index 13dde93b5..6ccf7c16c 100644 --- a/src/org/iguana/traversal/idea/GenerateJFlex.java +++ b/src/org/iguana/traversal/idea/GenerateJFlex.java @@ -260,10 +260,10 @@ public String visit(org.iguana.regex.Opt o) { @Override public String visit(org.iguana.regex.Alt symbol) { - Map> parition = symbol.getSymbols().stream().collect( + Map> partition = symbol.getSymbols().stream().collect( Collectors.partitioningBy(s -> isCharClass(s))); - List charClasses = parition.get(true); - List other = parition.get(false); + List charClasses = partition.get(true); + List other = partition.get(false); StringBuilder sb = new StringBuilder(); diff --git a/src/org/iguana/util/visualization/GrammarGraphToDot.java b/src/org/iguana/util/visualization/GrammarGraphToDot.java index 6df4c9b50..e8d242509 100644 --- a/src/org/iguana/util/visualization/GrammarGraphToDot.java +++ b/src/org/iguana/util/visualization/GrammarGraphToDot.java @@ -88,14 +88,14 @@ private static void toDot(NonterminalGrammarSlot slot, DotGraph dotGraph, Set dotGraph.addEdge(newEdge(slotId, getId(s), ""))); - slot.getFirstSlots().forEach(s -> toDot(s, dotGraph, visited)); + slot.getFirstSlots().forEach(s -> toDot(s, dotGraph)); } - private static void toDot(BodyGrammarSlot slot, DotGraph dotGraph, Set visited) { + private static void toDot(BodyGrammarSlot slot, DotGraph dotGraph) { if (slot instanceof EndGrammarSlot) { - dotGraph.addNode(newNode(getId(slot)).setShape(DotGraph.Shape.DOUBLE_CIRCLE)); + dotGraph.addNode(newNode(getId(slot), slot.toString()).setShape(DotGraph.Shape.ROUNDED_RECTANGLE)); } else { - dotGraph.addNode(newNode(getId(slot), slot.toString()).setShape(DotGraph.Shape.CIRCLE)); + dotGraph.addNode(newNode(getId(slot), slot.toString()).setShape(DotGraph.Shape.ROUNDED_RECTANGLE)); // TODO: improve this code Transition t = slot.getOutTransition(); @@ -110,7 +110,7 @@ private static void toDot(BodyGrammarSlot slot, DotGraph dotGraph, Set dotGraph.addEdge(newEdge(getId(slot), getId(t.destination()), t.getLabel())); } - toDot(t.destination(), dotGraph, visited); + toDot(t.destination(), dotGraph); } } diff --git a/src/org/iguana/utils/collections/CollectionsUtil.java b/src/org/iguana/utils/collections/CollectionsUtil.java index b0f7520b3..a88525f44 100644 --- a/src/org/iguana/utils/collections/CollectionsUtil.java +++ b/src/org/iguana/utils/collections/CollectionsUtil.java @@ -28,7 +28,6 @@ package org.iguana.utils.collections; import org.iguana.util.Tuple; -import org.iguana.utils.collections.primitive.IntIterable; import org.iguana.utils.collections.primitive.IntIterator; import java.util.ArrayList; @@ -129,11 +128,7 @@ public static T last(List list) { return list.get(list.size() - 1); } - public static boolean isEqual( - org.iguana.utils.collections.primitive.IntIterable iterables1, - IntIterable iterables2) { - org.iguana.utils.collections.primitive.IntIterator it1 = iterables1.iterator(); - IntIterator it2 = iterables2.iterator(); + public static boolean isEqual(IntIterator it1, IntIterator it2) { while (it1.hasNext()) { if (!it2.hasNext()) return false; int t1 = it1.next(); diff --git a/src/org/iguana/utils/collections/ChainingIntHashMap.java b/src/org/iguana/utils/collections/primitive/ChainingIntHashMap.java similarity index 81% rename from src/org/iguana/utils/collections/ChainingIntHashMap.java rename to src/org/iguana/utils/collections/primitive/ChainingIntHashMap.java index 8c045b909..ca2f18860 100644 --- a/src/org/iguana/utils/collections/ChainingIntHashMap.java +++ b/src/org/iguana/utils/collections/primitive/ChainingIntHashMap.java @@ -1,4 +1,6 @@ -package org.iguana.utils.collections; +package org.iguana.utils.collections.primitive; + +import org.iguana.utils.collections.IntKeyMapper; import java.util.Arrays; import java.util.Iterator; @@ -31,7 +33,7 @@ public class ChainingIntHashMap implements IntHashMap { */ private int bitMask; - private Entry[] table; + private IntKeyEntry[] table; public ChainingIntHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); @@ -56,7 +58,7 @@ public ChainingIntHashMap(int initialCapacity, float loadFactor) { bitMask = capacity - 1; threshold = (int) (loadFactor * capacity); - table = new Entry[capacity]; + table = new IntKeyEntry[capacity]; } private int hash(int key) { @@ -72,10 +74,10 @@ public boolean containsKey(int key) { public T computeIfAbsent(int key, IntFunction f) { int index = hash(key); - Entry entry = table[index]; + IntKeyEntry entry = table[index]; if (entry == null) { - entry = new Entry<>(key, f.apply(key)); + entry = new IntKeyEntry<>(key, f.apply(key)); table[index] = entry; size++; if (size >= threshold) rehash(); @@ -90,7 +92,7 @@ else if (entry.key == key) { while (entry.next != null) entry = entry.next; - Entry newEntry = new Entry<>(key, f.apply(key)); + IntKeyEntry newEntry = new IntKeyEntry<>(key, f.apply(key)); entry.next = newEntry; size++; return null; @@ -101,10 +103,10 @@ else if (entry.key == key) { public T compute(int key, IntKeyMapper f) { int index = hash(key); - Entry entry = table[index]; + IntKeyEntry entry = table[index]; if (entry == null) { - entry = new Entry<>(key, f.apply(key, null)); + entry = new IntKeyEntry<>(key, f.apply(key, null)); table[index] = entry; size++; if (size >= threshold) rehash(); @@ -119,7 +121,7 @@ else if (entry.key == key) { while (entry.next != null) entry = entry.next; - Entry newEntry = new Entry<>(key, f.apply(key, null)); + IntKeyEntry newEntry = new IntKeyEntry<>(key, f.apply(key, null)); entry.next = newEntry; size++; return null; @@ -130,10 +132,10 @@ else if (entry.key == key) { public T put(int key, T value) { int index = hash(key); - Entry entry = table[index]; + IntKeyEntry entry = table[index]; if (entry == null) { - entry = new Entry<>(key, value); + entry = new IntKeyEntry<>(key, value); table[index] = entry; size++; if (size >= threshold) rehash(); @@ -148,7 +150,7 @@ else if (entry.key == key) { while (entry.next != null) entry = entry.next; - Entry newEntry = new Entry<>(key, value); + IntKeyEntry newEntry = new IntKeyEntry<>(key, value); entry.next = newEntry; size++; return null; @@ -167,19 +169,19 @@ private void rehash() { bitMask = capacity - 1; @SuppressWarnings({"unchecked", "rawtypes"}) - Entry[] newTable = new Entry[capacity]; + IntKeyEntry[] newTable = new IntKeyEntry[capacity]; - for (Entry e : this) { + for (IntKeyEntry e : this) { int index = hash(e.key); - Entry entry = newTable[index]; + IntKeyEntry entry = newTable[index]; if (entry == null) { - newTable[index] = new Entry<>(e.key, e.val); + newTable[index] = new IntKeyEntry<>(e.key, e.val); continue; } else { while (entry.next != null) entry = entry.next; - Entry newEntry = new Entry<>(e.key, e.val); + IntKeyEntry newEntry = new IntKeyEntry<>(e.key, e.val); entry.next = newEntry; entry = newEntry; } @@ -194,7 +196,7 @@ private void rehash() { @Override public T get(int key) { int index = hash(key); - Entry entry = table[index]; + IntKeyEntry entry = table[index]; while (true) { if (entry.val == null) @@ -211,11 +213,6 @@ public int size() { return size; } - @Override - public int getInitialCapacity() { - return initialCapacity; - } - @Override public boolean isEmpty() { return size == 0; @@ -239,11 +236,11 @@ public String toString() { } @Override - public Iterator> iterator() { - return new Iterator>() { + public Iterator> iterator() { + return new Iterator>() { int i = 0; - Entry current; + IntKeyEntry current; @Override public boolean hasNext() { @@ -262,12 +259,12 @@ public boolean hasNext() { return true; } - private Entry nextInChain() { + private IntKeyEntry nextInChain() { if (current.next == null) return null; return current = current.next; } - private Entry nextInTable() { + private IntKeyEntry nextInTable() { if (i == table.length) return null; while ((current = table[i]) == null) { i++; @@ -279,7 +276,7 @@ private Entry nextInTable() { } @Override - public Entry next() { + public IntKeyEntry next() { return current; } }; diff --git a/src/org/iguana/utils/collections/IntHashMap.java b/src/org/iguana/utils/collections/primitive/IntHashMap.java similarity index 74% rename from src/org/iguana/utils/collections/IntHashMap.java rename to src/org/iguana/utils/collections/primitive/IntHashMap.java index bdc71d455..3d07416a8 100644 --- a/src/org/iguana/utils/collections/IntHashMap.java +++ b/src/org/iguana/utils/collections/primitive/IntHashMap.java @@ -1,6 +1,8 @@ -package org.iguana.utils.collections; +package org.iguana.utils.collections.primitive; +import org.iguana.utils.collections.IntKeyMapper; + import java.util.function.IntFunction; /** @@ -8,7 +10,7 @@ * @author Ali Afroozeh * */ -public interface IntHashMap extends Iterable> { +public interface IntHashMap extends Iterable> { boolean containsKey(int key); @@ -27,8 +29,6 @@ public interface IntHashMap extends Iterable> { int size(); - int getInitialCapacity(); - boolean isEmpty(); void clear(); diff --git a/src/org/iguana/utils/collections/Entry.java b/src/org/iguana/utils/collections/primitive/IntKeyEntry.java similarity index 55% rename from src/org/iguana/utils/collections/Entry.java rename to src/org/iguana/utils/collections/primitive/IntKeyEntry.java index 6c2c3ad12..1d306fa43 100644 --- a/src/org/iguana/utils/collections/Entry.java +++ b/src/org/iguana/utils/collections/primitive/IntKeyEntry.java @@ -1,12 +1,12 @@ -package org.iguana.utils.collections; +package org.iguana.utils.collections.primitive; -public class Entry { +public class IntKeyEntry { int key; T val; - Entry next; + IntKeyEntry next; - public Entry(int key, T val) { + public IntKeyEntry(int key, T val) { this.key = key; this.val = val; } diff --git a/src/org/iguana/utils/collections/primitive/IntSet.java b/src/org/iguana/utils/collections/primitive/IntSet.java new file mode 100644 index 000000000..7bfd11c05 --- /dev/null +++ b/src/org/iguana/utils/collections/primitive/IntSet.java @@ -0,0 +1,34 @@ +package org.iguana.utils.collections.primitive; + +/** + * A specialized set interface for non-negative int values. Operations on this set work with primitive values and + * therefore avoid (un)boxing. + */ +public interface IntSet extends IntIterable { + + /** + * Returns true if this set contains the given element. + */ + boolean contains(int element); + + /** + * Adds the given element to the set. Only accepts non-negative integer values. + * @return true if the set does not contain the given element + * @throws IllegalArgumentException when the given element is negative. + */ + boolean add(int element); + + /** + * Returns the number of elements in this set + */ + int size(); + + /** + * Clears the set by removing all the elements + */ + void clear(); + + default boolean isEmpty() { + return size() == 0; + } +} diff --git a/src/org/iguana/utils/collections/primitive/IntSetFactory.java b/src/org/iguana/utils/collections/primitive/IntSetFactory.java new file mode 100644 index 000000000..c04943c5d --- /dev/null +++ b/src/org/iguana/utils/collections/primitive/IntSetFactory.java @@ -0,0 +1,150 @@ +package org.iguana.utils.collections.primitive; + +public class IntSetFactory { + + public static IntSet create(int... elements) { + if (elements == null || elements.length == 0) + throw new IllegalArgumentException("cannot be a null or empty array"); + + if (elements.length == 1) { + return new ImmutableSingleElementIntSet(elements[0]); + } + if (elements.length == 2) { + return new ImmutableTwoElementIntSet(elements[0], elements[1]); + } + + return OpenAddressingIntHashSet.from(elements); + } + + public static class ImmutableSingleElementIntSet implements IntSet { + + private final int value; + + public ImmutableSingleElementIntSet(int value) { + this.value = value; + } + + @Override + public boolean contains(int element) { + return value == element; + } + + @Override + public boolean add(int element) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return 1; + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IntSet)) return false; + IntSet other = (IntSet) obj; + if (other.size() != 1) return false; + return other.contains(value); + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + private int count = 0; + + @Override + public boolean hasNext() { + if (count < 1) { + count++; + return true; + } + return false; + } + + @Override + public int next() { + return value; + } + }; + } + } + + public static class ImmutableTwoElementIntSet implements IntSet { + + private final int first; + private final int second; + + public ImmutableTwoElementIntSet(int first, int second) { + this.first = first; + this.second = second; + } + + @Override + public boolean contains(int element) { + return first == element || second == element; + } + + @Override + public boolean add(int element) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return 2; + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return first + 31 * second; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IntSet)) return false; + IntSet other = (IntSet) obj; + if (other.size() != 2) return false; + return other.contains(first) && other.contains(second); + } + + @Override + public IntIterator iterator() { + return new IntIterator() { + private int count = 0; + + @Override + public boolean hasNext() { + return count < 2; + } + + @Override + public int next() { + if (count == 0) { + count++; + return first; + } + if (count == 1) { + count++; + return second; + } + throw new IllegalStateException("There is no more element"); + } + }; + } + } +} diff --git a/src/org/iguana/utils/collections/OpenAddressingIntHashMap.java b/src/org/iguana/utils/collections/primitive/OpenAddressingIntHashMap.java similarity index 95% rename from src/org/iguana/utils/collections/OpenAddressingIntHashMap.java rename to src/org/iguana/utils/collections/primitive/OpenAddressingIntHashMap.java index 9238c819f..565cd1377 100644 --- a/src/org/iguana/utils/collections/OpenAddressingIntHashMap.java +++ b/src/org/iguana/utils/collections/primitive/OpenAddressingIntHashMap.java @@ -1,4 +1,6 @@ -package org.iguana.utils.collections; +package org.iguana.utils.collections.primitive; + +import org.iguana.utils.collections.IntKeyMapper; import java.util.Arrays; import java.util.Iterator; @@ -206,11 +208,6 @@ public int size() { return size; } - @Override - public int getInitialCapacity() { - return initialCapacity; - } - @Override public boolean isEmpty() { return size == 0; @@ -227,10 +224,10 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{"); - Iterator> it = iterator(); + Iterator> it = iterator(); while (true) { - Entry next = it.next(); + IntKeyEntry next = it.next(); sb.append(next); if (!it.hasNext()) { sb.append("}"); @@ -258,7 +255,7 @@ public Iterable values() { } @Override - public Iterator> iterator() { + public Iterator> iterator() { return new Iterator<>() { int it = 0; int i = 0; @@ -269,12 +266,12 @@ public boolean hasNext() { } @Override - public Entry next() { + public IntKeyEntry next() { while (true) { if (values[i++] != null) break; } it++; - return new Entry<>(keys[i - 1], values[i - 1]); + return new IntKeyEntry<>(keys[i - 1], values[i - 1]); } }; } diff --git a/src/org/iguana/utils/collections/IntHashSet.java b/src/org/iguana/utils/collections/primitive/OpenAddressingIntHashSet.java similarity index 68% rename from src/org/iguana/utils/collections/IntHashSet.java rename to src/org/iguana/utils/collections/primitive/OpenAddressingIntHashSet.java index 796c77deb..280ebbd7d 100644 --- a/src/org/iguana/utils/collections/IntHashSet.java +++ b/src/org/iguana/utils/collections/primitive/OpenAddressingIntHashSet.java @@ -1,4 +1,4 @@ -package org.iguana.utils.collections; +package org.iguana.utils.collections.primitive; import java.util.Arrays; @@ -8,7 +8,7 @@ * @author Ali Afroozeh * */ -public class IntHashSet { +public class OpenAddressingIntHashSet implements IntSet { private static final int DEFAULT_INITIAL_CAPACITY = 16; private static final float DEFAULT_LOAD_FACTOR = 0.7f; @@ -35,15 +35,15 @@ public class IntHashSet { private int[] table; - public IntHashSet() { + public OpenAddressingIntHashSet() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } - public IntHashSet(int initialCapacity) { + public OpenAddressingIntHashSet(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } - public IntHashSet(int initialCapacity, float loadFactor) { + public OpenAddressingIntHashSet(int initialCapacity, float loadFactor) { this.initialCapacity = initialCapacity; @@ -61,47 +61,44 @@ public IntHashSet(int initialCapacity, float loadFactor) { Arrays.fill(table, -1); } - public static IntHashSet from(int... elements) { - IntHashSet set = new IntHashSet(); + public static OpenAddressingIntHashSet from(int... elements) { + OpenAddressingIntHashSet set = new OpenAddressingIntHashSet(elements.length); for (int e : elements) { set.add(e); } return set; } + @Override public boolean contains(int key) { return get(key) != -1; } - public int add(int key) { + @Override + public boolean add(int element) { + if (element < 0) throw new IllegalArgumentException("Element " + element + " is negative"); - int index = hash(key); + int index = hash(element); do { if (table[index] == -1) { - table[index] = key; + table[index] = element; size++; if (size >= threshold) { rehash(); } - return -1; + return true; } - - else if (table[index] == key) { - return table[index]; + if (table[index] == element) { + return false; } - collisionsCount++; - index = (index + 1) & bitMask; - } while (true); } private void rehash() { - capacity <<= 1; - bitMask = capacity - 1; int[] newTable = new int[capacity]; @@ -110,17 +107,13 @@ private void rehash() { label: for (int key : table) { if (key != -1) { - int index = hash(key); - do { if (newTable[index] == -1) { newTable[index] = key; continue label; } - index = (index + 1) & bitMask; - } while (true); } } @@ -139,6 +132,7 @@ public int get(int key) { return table[hash(key)]; } + @Override public int size() { return size; } @@ -155,14 +149,9 @@ public int getEnlargeCount() { return rehashCount; } - public boolean isEmpty() { - return size == 0; - } - + @Override public void clear() { - for (int i = 0; i < table.length; i++) { - table[i] = -1; - } + Arrays.fill(table, -1); size = 0; } @@ -183,4 +172,26 @@ public String toString() { sb.append("}"); return sb.toString(); } + + @Override + public IntIterator iterator() { + return new IntIterator() { + int i = 0; // index to the next element in the table to check + int count = 0; // the number of iterated elements + + @Override + public boolean hasNext() { + return count < size; + } + + @Override + public int next() { + while (true) { + if (table[i++] != -1) break; + } + count++; + return table[i - 1]; + } + }; + } } diff --git a/src/resources/Iguana.iggy b/src/resources/Iguana.iggy index 5dfa26d72..c4c113dc5 100644 --- a/src/resources/Iguana.iggy +++ b/src/resources/Iguana.iggy @@ -6,7 +6,7 @@ start Grammar = "grammar"? name:Identifier? defs:(Rule | TopLevelVar)+ TopLevelVar - = "global"? "var" id:Identifier "=" exp:Expression env=put(env,id.yield) + = "global"? "var" id:Identifier "=" exp:Expression env=put(env,id.yield) Rule = modifier: ("start" | "layout" | "lexical")? name:Name params:Parameters? "=" body:Body %ContextFree diff --git a/test/org/iguana/parser/firstfollow/FirstFollowTest.java b/test/org/iguana/parser/firstfollow/FirstFollowTest.java index ee55f9472..d4a17eb2f 100644 --- a/test/org/iguana/parser/firstfollow/FirstFollowTest.java +++ b/test/org/iguana/parser/firstfollow/FirstFollowTest.java @@ -6,6 +6,7 @@ import org.iguana.grammar.transformation.GrammarTransformer; import org.iguana.regex.CharRange; import org.iguana.regex.EOF; +import org.iguana.regex.Epsilon; import org.junit.jupiter.api.Test; import static org.iguana.iggy.IggyParserUtils.fromIggyGrammar; @@ -30,9 +31,37 @@ public void test() { FirstFollowSets firstFollowSets = new FirstFollowSets(grammar); assertEquals(set(a, b), firstFollowSets.getFirstSet(A)); - assertEquals(set(a, b), firstFollowSets.getFirstSet(B)); - assertEquals(set(c), firstFollowSets.getFirstSet(C)); - assertEquals(set(c, EOF.asCharRange()), firstFollowSets.getFollowSet(A)); - assertEquals(set(c, EOF.asCharRange()), firstFollowSets.getFollowSet(B)); + assertEquals(set(a, b), firstFollowSets.getFirstSet(B)); + assertEquals(set(c), firstFollowSets.getFirstSet(C)); + assertEquals(set(c, EOF.asCharRange()), firstFollowSets.getFollowSet(A)); + assertEquals(set(c, EOF.asCharRange()), firstFollowSets.getFollowSet(B)); + } + + @Test + public void testWithLayout() { + RuntimeGrammar grammar = GrammarTransformer.transform(fromIggyGrammar( + "A = B C 'd'\n" + + "B = 'b' | \n" + + "C = 'c'\n" + + "layout regex Layout = [\\ \\n]*").toRuntimeGrammar()); + + Nonterminal A = Nonterminal.withName("A"); + Nonterminal B = Nonterminal.withName("B"); + Nonterminal C = Nonterminal.withName("C"); + CharRange b = CharRange.from('b'); + CharRange c = CharRange.from('c'); + CharRange d = CharRange.from('d'); + CharRange newLine = CharRange.from('\n'); + CharRange space = CharRange.from(' '); + + FirstFollowSets firstFollowSets = new FirstFollowSets(grammar); + assertEquals(set(space, newLine, b, c), firstFollowSets.getFirstSet(A)); + assertEquals(set(EOF.asCharRange()), firstFollowSets.getFollowSet(A)); + + assertEquals(set(b, Epsilon.asCharRange()), firstFollowSets.getFirstSet(B)); + assertEquals(set(space, newLine, c, EOF.asCharRange()), firstFollowSets.getFollowSet(B)); + + assertEquals(set(c), firstFollowSets.getFirstSet(C)); + assertEquals(set(d, space, newLine, EOF.asCharRange()), firstFollowSets.getFollowSet(C)); } } diff --git a/test/org/iguana/utils/collections/TestIntHashMap.java b/test/org/iguana/utils/collections/TestIntHashMap.java index 90d8d8570..5f1e7a02d 100644 --- a/test/org/iguana/utils/collections/TestIntHashMap.java +++ b/test/org/iguana/utils/collections/TestIntHashMap.java @@ -1,5 +1,7 @@ package org.iguana.utils.collections; +import org.iguana.utils.collections.primitive.IntHashMap; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,7 +17,7 @@ public class TestIntHashMap { @BeforeEach public void init() { - map = new org.iguana.utils.collections.OpenAddressingIntHashMap<>(); + map = new OpenAddressingIntHashMap<>(); map.put(1, "a"); map.put(2, "b"); map.put(3, "c"); diff --git a/test/org/iguana/utils/collections/TestIntHashSet.java b/test/org/iguana/utils/collections/TestIntHashSet.java index 3f7b5a09f..04b229faa 100644 --- a/test/org/iguana/utils/collections/TestIntHashSet.java +++ b/test/org/iguana/utils/collections/TestIntHashSet.java @@ -1,23 +1,81 @@ package org.iguana.utils.collections; -import org.iguana.utils.collections.IntHashSet; +import org.iguana.utils.collections.primitive.IntIterator; +import org.iguana.utils.collections.primitive.IntList; +import org.iguana.utils.collections.primitive.IntSet; +import org.iguana.utils.collections.primitive.IntSetFactory; +import org.iguana.utils.collections.primitive.OpenAddressingIntHashSet; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class TestIntHashSet { @Test - public void test() { - org.iguana.utils.collections.IntHashSet set = new IntHashSet(); - set.add(1); - set.add(2); - set.add(3); + public void testOneElement() { + IntSet set = IntSetFactory.create(1); + assertInstanceOf(IntSetFactory.ImmutableSingleElementIntSet.class, set); + + assertFalse(set.isEmpty()); + assertTrue(set.contains(1)); + + IntIterator it = set.iterator(); + Set actual = new HashSet<>(); + while (it.hasNext()) { + actual.add(it.next()); + } + assertEquals(Set.of(1), actual); + + assertThrows(UnsupportedOperationException.class, () -> set.add(2)); + } + + @Test + public void testTwoElement() { + IntSet set = IntSetFactory.create(1, 2); + assertInstanceOf(IntSetFactory.ImmutableTwoElementIntSet.class, set); + + assertFalse(set.isEmpty()); + assertTrue(set.contains(1)); + assertTrue(set.contains(2)); + + IntIterator it = set.iterator(); + Set actual = new HashSet<>(); + while (it.hasNext()) { + actual.add(it.next()); + } + assertEquals(Set.of(1, 2), actual); + + assertThrows(UnsupportedOperationException.class, () -> set.add(3)); + } + + @Test + public void testMoreThanTwoElements() { + IntSet set = IntSetFactory.create(1, 2, 3); + assertInstanceOf(OpenAddressingIntHashSet.class, set); assertFalse(set.isEmpty()); assertTrue(set.contains(1)); assertTrue(set.contains(2)); assertTrue(set.contains(3)); + + IntIterator it = set.iterator(); + Set actual = new HashSet<>(); + while (it.hasNext()) { + actual.add(it.next()); + } + assertEquals(Set.of(1, 2, 3), actual); + + assertThrows(IllegalArgumentException.class, () -> set.add(-1)); + assertTrue(set.add(4)); + assertFalse(set.add(1)); } }