Class: ToknInternal::State
- Inherits:
-
Object
- Object
- ToknInternal::State
- Defined in:
- lib/tokn/state.rb
Overview
A state within a state machine (NFA or DFA); also, various utility functions for manipulating state machines. Observe that a state machine can be referred to by its start state.
Each state has a set of directed edges to other states, where each edge is labelled with a CodeSet.
It also has a unique id (unique within a particular state machine), and a (boolean) final state flag.
For debug purposes, both the state and its edges can be labelled.
Instance Attribute Summary collapse
-
#edges ⇒ Object
readonly
Edges are a list of [label:CharSetRange, dest:State] pairs.
-
#finalState ⇒ Object
(also: #finalState?)
Returns the value of attribute finalState.
-
#id ⇒ Object
Returns the value of attribute id.
-
#label ⇒ Object
Returns the value of attribute label.
Class Method Summary collapse
-
.dumpNFA(st) ⇒ Object
Produce a readable description of an NFA, for debug purposes.
-
.normalizeStates(startState) ⇒ Object
Normalize a state machine.
Instance Method Summary collapse
-
#addEdge(codeSet, destState) ⇒ Object
Add an edge codeSet : the character codes to label it with destState : destination state.
-
#addEps(destState) ⇒ Object
Add a e-transition edge destState : destination state.
- #clearEdges ⇒ Object
-
#duplicateNFA(dupBaseId = nil) ⇒ Object
Duplicate the NFA reachable from this state, possibly with new ids.
- #eql?(other) ⇒ Boolean
-
#generatePDF(dir = nil, title = "nfa") ⇒ Object
Generate a PDF of the state machine; Makes a system call to the dot utility to convert a .dot file to a .pdf.
- #hash ⇒ Object
-
#initialize(id) ⇒ State
constructor
A new instance of State.
- #inspect ⇒ Object
- #name ⇒ Object
-
#normalize ⇒ Object
Normalize a state .
-
#reachableStates ⇒ Object
Build set of states reachable from this state.
-
#reverseNFA ⇒ Object
Construct the reverse of the NFA starting at this state < start state of reversed NFA.
Constructor Details
#initialize(id) ⇒ State
Returns a new instance of State.
51 52 53 54 |
# File 'lib/tokn/state.rb', line 51 def initialize(id) @edges = [] @id = id end |
Instance Attribute Details
#edges ⇒ Object (readonly)
Edges are a list of [label:CharSetRange, dest:State] pairs
26 27 28 |
# File 'lib/tokn/state.rb', line 26 def edges @edges end |
#finalState ⇒ Object Also known as: finalState?
Returns the value of attribute finalState.
21 22 23 |
# File 'lib/tokn/state.rb', line 21 def finalState @finalState end |
#id ⇒ Object
Returns the value of attribute id.
20 21 22 |
# File 'lib/tokn/state.rb', line 20 def id @id end |
#label ⇒ Object
Returns the value of attribute label.
23 24 25 |
# File 'lib/tokn/state.rb', line 23 def label @label end |
Class Method Details
.dumpNFA(st) ⇒ Object
Produce a readable description of an NFA, for debug purposes
> st start state
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/tokn/state.rb', line 32 def self.dumpNFA(st) str = "NFA:\n" map,_,_ = st.reachableStates map.each do |s| str += " "+d(s)+"\n" str += " edges= "+d(s.edges)+"\n" s.edges.each{ |lbl,dest| str += " "+d(lbl)+" ==> "+d(dest)+"\n"} end str end |
.normalizeStates(startState) ⇒ Object
Normalize a state machine.
For each state:
[] merge edges that go to a common state
[] delete edges that have empty labels
[] sort edges by destination state ids
> start state
96 97 98 99 |
# File 'lib/tokn/state.rb', line 96 def self.normalizeStates(startState) stateSet, _,_ = startState.reachableStates stateSet.map{|s| s.normalize} end |
Instance Method Details
#addEdge(codeSet, destState) ⇒ Object
Add an edge
codeSet : the character codes to label it with
destState : destination state
64 65 66 |
# File 'lib/tokn/state.rb', line 64 def addEdge(codeSet,destState) @edges.push([codeSet, destState]) end |
#addEps(destState) ⇒ Object
Add a e-transition edge
destState : destination state
71 72 73 |
# File 'lib/tokn/state.rb', line 71 def addEps(destState) addEdge(CodeSet.new(EPSILON), destState) end |
#clearEdges ⇒ Object
56 57 58 |
# File 'lib/tokn/state.rb', line 56 def clearEdges @edges.clear end |
#duplicateNFA(dupBaseId = nil) ⇒ Object
Duplicate the NFA reachable from this state, possibly with new ids
> dupBaseId : lowest id to use for duplicate; if nil, uses
next available id
< [ map of original states => duplicate states;
1 + highest id in new NFA ]
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/tokn/state.rb', line 198 def duplicateNFA(dupBaseId = nil) oldStates, oldMinId, oldMaxId = reachableStates() dupBaseId ||= oldMaxId oldToNewStateMap = {} oldStates.each do |s| s2 = State.new((s.id - oldMinId) + dupBaseId) s2.finalState = s.finalState? s2.label = s.label oldToNewStateMap[s] = s2 end oldStates.each do |s| s2 = oldToNewStateMap[s] s.edges.each{ |lbl,dest| s2.addEdge(lbl, oldToNewStateMap[dest])} end [oldToNewStateMap, (oldMaxId - oldMinId) + dupBaseId] end |
#eql?(other) ⇒ Boolean
47 48 49 |
# File 'lib/tokn/state.rb', line 47 def eql?(other) return id == other.id end |
#generatePDF(dir = nil, title = "nfa") ⇒ Object
Generate a PDF of the state machine; Makes a system call to the dot utility to convert a .dot file to a .pdf
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/tokn/state.rb', line 105 def generatePDF(dir = nil, title = "nfa") stateList = {} startState = self genAux( stateList, startState) g = "" g += "digraph "+title+" {\n" g += " '' [shape=none]\n" stateList.each_value do |s| g += " '" + s.name + "' [shape=" if s.finalState? g += "doubleoctagon" else g += "octagon" end g += "]\n" end g += "\n" g += " '' -> '" + startState.name + "'\n" stateList.each_value do |s| s.edges.each do |crs, s2| g += " '"+s.name+"' -> '" + s2.name + "' [label='" g += d(crs) g += "'][fontname=Courier][fontsize=12]\n" end end g += "\n}\n" g.gsub!( /'/, '"' ) dotToPDF(g,title,dir) end |
#hash ⇒ Object
43 44 45 |
# File 'lib/tokn/state.rb', line 43 def hash return @id end |
#inspect ⇒ Object
75 76 77 |
# File 'lib/tokn/state.rb', line 75 def inspect name end |
#name ⇒ Object
79 80 81 82 83 84 85 |
# File 'lib/tokn/state.rb', line 79 def name nm = 'S' + d(id) if label nm += ": "+label end nm end |
#normalize ⇒ Object
Normalize a state
[] merge edges that go to a common state
[] delete edges that have empty labels
[] sort edges by destination state ids
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/tokn/state.rb', line 148 def normalize() db = false !db || pr("\n\nnormalize state:\n %s\nedges=\n%s\n",d(self),d(@edges)) @edges.sort!{|x,y| label1,dest1 = x label2,dest2 = y dest1.id <=> dest2.id } !db || pr(" sorted edges: %s\n",d(@edges)) newEdges = [] prevLabel, prevDest = nil,nil edges.each do |label,dest| !db || pr(" processing edge %s, %s\n",d(label),d(dest)) if prevDest and prevDest.id == dest.id # changed = true !db || pr(" adding set %s to prevLabel %s...\n",d(label),d(prevLabel)) prevLabel.addSet(label) !db || pr(" ...now %s\n",d(prevLabel)) else if prevDest newEdges.push([prevLabel,prevDest]) end # Must start a fresh copy! Don't want to modify the original label. prevLabel = label.makeCopy() prevDest = dest !db || pr(" pushed onto new edges\n") end end if prevDest newEdges.push([prevLabel,prevDest]) end @edges = newEdges !db || pr("edges now: %s\n",d(@edges)) end |
#reachableStates ⇒ Object
Build set of states reachable from this state
> list of starting states < [ set, set of states reachable from those states
minId, lowest id in set
maxId 1 + highest id in set
]
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/tokn/state.rb', line 276 def reachableStates() set = Set.new stack = [] stack.push(self) maxId = nil minId = nil while !stack.empty? st = stack.pop set.add(st) if !minId || minId > st.id minId = st.id end if !maxId || maxId <= st.id maxId = 1 + st.id end st.edges.each do |lbl, dest| if set.add?(dest) stack.push(dest) end end end [set, minId, maxId] end |
#reverseNFA ⇒ Object
Construct the reverse of the NFA starting at this state < start state of reversed NFA
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/tokn/state.rb', line 226 def reverseNFA() stateSet, minId, maxId = reachableStates() edgeList = [] newStartStateList = [] newFinalStateList = [] newStateMap = {} stateSet.each do |s| u = State.new(s.id) newStateMap[u.id] = u if s.id == self.id newFinalStateList.push(u) u.finalState = true end if s.finalState? newStartStateList.push(u) end s.edges.each {|lbl, dest| edgeList.push([dest.id, s.id, lbl])} end edgeList.each do |srcId, destId, lbl| srcState = newStateMap[srcId] destState = newStateMap[destId] srcState.addEdge(lbl, destState) end # Create a distinguished start node that points to each of the start nodes w = State.new(maxId) newStartStateList.each {|s| w.addEps(s)} w end |