org.vesalainen.grammar.state.NFAState Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lpg Show documentation
Show all versions of lpg Show documentation
Java Lookahead Parser Generator. Generator produces LALR(k) parsers. Grammar
rules are entered using annotations. Rule annotation can be attached to reducer
method, which keeps rule and it's action together.
The newest version!
/*
* Copyright (C) 2012 Timo Vesalainen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.vesalainen.grammar.state;
import org.vesalainen.regex.Range;
import org.vesalainen.regex.RangeSet;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* This class represent a state in nondeterministic finite automaton (NFA)
* Note that epsilon transition is implemented being a null range
* @author tkv
* @param
*/
public final class NFAState extends State implements Vertex>, Iterable>
{
private final Map>>> transitions = new HashMap<>();
private boolean endStop;
private int fixedEndLength;
private boolean acceptImmediately; // if true the string is accepted without trying to read more input
// edges and inStates are initially constructed from transitions. However during
// dfa distribution these are changed while transitions are not!
private final Set> edges = new HashSet<>();
private final Set> inStates = new HashSet<>();
/**
* Construct a nfa state which has no transitions.
* @param scope
*/
public NFAState(Scope> scope)
{
super(scope);
}
/**
* Construct a new nfa state by cloning other state as well as all connected
* states.
* @param other
* @param map
*/
NFAState(Scope> scope, NFAState other, Map,NFAState> map)
{
super(scope, other);
map.put(other, this);
for (Range r : other.transitions.keySet())
{
Set>> s = other.transitions.get(r);
for (Transition> t : s)
{
NFAState to = map.get(t.getTo());
if (to == null)
{
to = new NFAState<>(scope, t.getTo(), map);
}
addTransition(r, to);
}
}
}
public boolean isAcceptImmediately()
{
return acceptImmediately;
}
public void setAcceptImmediately(boolean acceptImmediately)
{
this.acceptImmediately = acceptImmediately;
}
/**
* If FIXED_ENDER option is used this method returns the length of fixed end.
* For example fixed end length for .*end is 3.
* @return
*/
public int getFixedEndLength()
{
return fixedEndLength;
}
public void setFixedEndLength(int fixedEndLength)
{
this.fixedEndLength = fixedEndLength;
}
public boolean hasTransitions()
{
return !transitions.isEmpty();
}
public int getTransitionCount()
{
int count = 0;
for (Range range : transitions.keySet())
{
Set>> set = transitions.get(range);
count += set.size();
}
return count;
}
/**
* Returns true if transition with condition to state exists
* @param condition
* @param state
* @return
*/
public boolean hasTransitionTo(Range condition, NFAState state)
{
Set>> set = transitions.get(condition);
if (set != null)
{
for (Transition> tr : set)
{
if (state.equals(tr.getTo()))
{
return true;
}
}
}
return false;
}
public RangeSet getNonEpsilonConditions()
{
RangeSet rs = new RangeSet();
for (Range range : transitions.keySet())
{
if (range != null)
{
Set>> set2 = transitions.get(range);
for (Transition> tr : set2)
{
rs.add(range);
}
}
}
return rs;
}
public RangeSet getNonEpsilonConditionsTo(NFAState state)
{
RangeSet rs = new RangeSet();
for (Range range : transitions.keySet())
{
if (range != null)
{
Set>> set2 = transitions.get(range);
for (Transition> tr : set2)
{
if (state.equals(tr.getTo()))
{
rs.add(range);
}
}
}
}
return rs;
}
/**
* Returns a set of NFAStates to where a non epsilon transition exists
* @return
*/
public Set> getNonEpsilonDestinations()
{
Set> set = new HashSet<>();
for (Range range : transitions.keySet())
{
if (range != null)
{
Set>> set2 = transitions.get(range);
for (Transition> tr : set2)
{
set.add(tr.getTo());
}
}
}
return set;
}
public void removeAllNonEpsilonConditionsTo(NFAState state)
{
for (Range range : transitions.keySet())
{
if (range != null)
{
Set>> set2 = transitions.get(range);
for (Transition> tr : set2)
{
if (state.equals(tr.getTo()))
{
set2.remove(tr);
}
}
}
}
}
/**
* Returns non epsilon confitions to transit to given state
* @param state
* @return
*/
public RangeSet getConditionsTo(NFAState state)
{
RangeSet rs = new RangeSet();
for (Range range : transitions.keySet())
{
if (range != null)
{
Set>> set2 = transitions.get(range);
for (Transition> tr : set2)
{
if (state.equals(tr.getTo()))
{
rs.add(range);
}
}
}
}
return rs;
}
/**
* Returns Transition set instance if NFAState has only transitions to given
* state and no transitions to anywhere else. Otherwise returns null.
* @param state
* @return
*/
public Set>> getSingleTransitionTo(NFAState state)
{
Set>> set = new HashSet<>();
if (transitions.size() != 1)
{
return null;
}
for (Range range : transitions.keySet())
{
Set>> s = transitions.get(range);
for (Transition> tr : s)
{
if (state.equals(tr.getTo()))
{
set.add(tr);
}
else
{
return null; // to somewhere else
}
}
}
if (set.isEmpty())
{
return null;
}
else
{
return set;
}
}
public NFAState prev()
{
if (inStates.size() == 1)
{
for (NFAState p : inStates)
{
return p;
}
}
return null;
}
public Set> inStates()
{
return inStates;
}
/**
* Returns true if set only has one epsilon transition.
* @param set
* @return
*/
public static boolean isSingleEpsilonOnly(Set> set)
{
if (set.size() != 1)
{
return false;
}
for (Transition tr : set)
{
if (tr.isEpsilon())
{
return true;
}
}
return false;
}
/**
* Returns true if this state is included in accepting dfa state
* @return
*/
public boolean isEndStop()
{
return endStop;
}
void setEndStop(boolean endStop)
{
this.endStop = endStop;
}
/**
* Returns true if this state is not accepting and doesn't have any outbound
* transitions.
* @param nfaSet
* @return
*/
boolean isDeadEnd(Set> nfaSet)
{
for (Set>> set : transitions.values())
{
for (Transition> t : set)
{
if (!nfaSet.contains(t.getTo()))
{
return false;
}
}
}
return true;
}
/**
* Construct a dfa by using this state as starting state.
* @param dfaScope
* @return
*/
public DFAState constructDFA(Scope> dfaScope)
{
Map>,DFAState> all = new HashMap<>();
Deque> unmarked = new ArrayDeque<>();
Set> startSet = epsilonClosure(dfaScope);
DFAState startDfa = new DFAState<>(dfaScope, startSet);
all.put(startSet, startDfa);
unmarked.add(startDfa);
while (!unmarked.isEmpty())
{
DFAState dfa = unmarked.pop();
for (Range c : dfa.possibleMoves())
{
Set> moveSet = dfa.nfaTransitsFor(c);
if (!moveSet.isEmpty())
{
Set> newSet = epsilonClosure(dfaScope, moveSet);
DFAState ndfa = all.get(newSet);
if (ndfa == null)
{
ndfa = new DFAState<>(dfaScope, newSet);
all.put(newSet, ndfa);
unmarked.add(ndfa);
}
dfa.addTransition(c, ndfa);
}
}
}
// optimize
for (DFAState dfa : all.values())
{
dfa.removeTransitionsFromAcceptImmediatelyStates();
}
for (DFAState dfa : all.values())
{
dfa.removeDeadEndTransitions();
}
for (DFAState dfa : startDfa)
{
dfa.optimizeTransitions();
}
return startDfa;
}
/**
* Returns all ranges from all transitions
* @return
*/
RangeSet getConditions()
{
RangeSet is = new RangeSet();
for (Range ic : transitions.keySet())
{
if (ic != null)
{
is.add(ic);
}
}
return is;
}
/**
* Returns a set of states that can be reached by epsilon transitions.
* @param marked
* @return
*/
private Set> epsilonTransitions(StateVisitSet> marked)
{
marked.add(this);
Set> set = new HashSet<>();
for (NFAState nfa : epsilonTransit())
{
if (!marked.contains(nfa))
{
set.add(nfa);
set.addAll(nfa.epsilonTransitions(marked));
}
}
return set;
}
/**
* Creates a dfa state from all nfa states that can be reached from this state
* with epsilon move.
* @param scope
* @return
*/
public Set> epsilonClosure(Scope> scope)
{
Set> set = new HashSet<>();
set.add(this);
return epsilonClosure(scope, set);
}
/**
*
* @param set
* @return
*/
private Set> epsilonClosure(Scope> scope, Set> set)
{
StateVisitSet> marked = new StateVisitSet<>();
Set> result = new HashSet<>();
result.addAll(set);
for (NFAState nfa : set)
{
result.addAll(nfa.epsilonTransitions(marked));
}
return result;
}
private Set> epsilonTransit()
{
return transit(null);
}
Set> transit(Range condition)
{
Set> set = new HashSet<>();
for (Range r : transitions.keySet())
{
if (
(condition == null && r == null) ||
(r != null && r.contains(condition))
)
{
Set>> s = transitions.get(r);
for (Transition> t : s)
{
set.add(t.getTo());
}
}
}
return set;
}
/**
* Adds a set of transitions
* @param rs
* @param to
*/
public void addTransition(RangeSet rs, NFAState to)
{
for (Range c : rs)
{
addTransition(c, to);
}
edges.add(to);
to.inStates.add(this);
}
/**
* Adds a transition
* @param condition
* @param to
* @return
*/
public Transition> addTransition(Range condition, NFAState to)
{
Transition> t = new Transition<>(condition, this, to);
Set>> set = transitions.get(t.getCondition());
if (set == null)
{
set = new HashSet<>();
transitions.put(t.getCondition(), set);
}
set.add(t);
edges.add(to);
to.inStates.add(this);
return t;
}
/**
* Adds a epsilon transition
* @param to
*/
public void addEpsilon(NFAState to)
{
Transition> t = new Transition<>(this, to);
Set>> set = transitions.get(null);
if (set == null)
{
set = new HashSet<>();
transitions.put(null, set);
}
set.add(t);
edges.add(to);
to.inStates.add(this);
}
/**
* Returns all transitions
* @return
*/
public Collection>>> getTransitions()
{
return transitions.values();
}
public void dump(int position, StringBuilder sb, Map,Integer> map, Deque> deque)
{
if (!transitions.isEmpty())
{
boolean first = true;
for (Range range : transitions.keySet())
{
for (Transition> t : transitions.get(range))
{
if (first)
{
if (map.containsKey(t.getTo()))
{
sb.append(this);
}
else
{
int p = sb.length();
sb.append("-"+range+">"+t.getTo());
p = sb.length()-p;
t.getTo().dump(p, sb, map, deque);
}
first = false;
}
else
{
if (!map.containsKey(t.getTo()))
{
map.put(t.getTo(), position);
deque.addLast(t.getTo());
}
}
}
}
}
else
{
sb.append(this);
}
}
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final NFAState other = (NFAState) obj;
if (this.scope != other.scope)
{
throw new IllegalArgumentException("testing equality of NFAStates from different scope");
}
if (this.transitions != other.transitions && (this.transitions == null || !this.transitions.equals(other.transitions)))
{
return false;
}
if (this.number != other.number)
{
return false;
}
return true;
}
@Override
public Collection> edges()
{
return edges;
}
@Override
public Iterator> iterator()
{
return new DiGraphIterator<>(this);
}
}