diff --git a/nfa.go b/nfa.go index 233a5cb..a3977f5 100644 --- a/nfa.go +++ b/nfa.go @@ -3,7 +3,7 @@ package main const EPSILON int = 0 type State struct { - content int // Contents of current state + content stateContents // Contents of current state isEmpty bool // If it is empty - Union operator and Kleene star states will be empty isLast bool // If it is the last state (acept state) output []*State // The outputs of the current state ie. the 'outward arrows'. A union operator state will have more than one of these. @@ -22,17 +22,25 @@ func verifyLastStatesHelper(state *State, visited map[*State]bool) { state.isLast = true return } - if len(state.transitions) == 1 && len(state.transitions[state.content]) == 1 && state.transitions[state.content][0] == state { // Eg. a* - state.isLast = true - return + // if len(state.transitions) == 1 && len(state.transitions[state.content]) == 1 && state.transitions[state.content][0] == state { // Eg. a* + if len(state.transitions) == 1 { // Eg. a* + var moreThanOneTrans bool // Dummy variable, check if all the transitions for the current's state's contents have a length of one + for _, c := range state.content { + if len(state.transitions[c]) != 1 || state.transitions[c][0] != state { + moreThanOneTrans = true + } + } + state.isLast = !moreThanOneTrans } - if len(state.transitions) == 1 && state.isKleene { // A State representing a Kleene Star has a transition going out, which loops back to it. If that is the only transition (and it contains only one state), then it must be a last-state - for _, v := range state.transitions { // Should only loop once - if len(v) == 1 { - state.isLast = true - return - } + if state.isKleene { // A State representing a Kleene Star has transitions going out, which loop back to it. If all those transitions point to the same (single) state, then it must be a last state + transitionDests := make([]*State, 0) + for _, v := range state.transitions { + transitionDests = append(transitionDests, v...) + } + if allEqual(transitionDests...) { + state.isLast = true + return } } if visited[state] == true { @@ -55,7 +63,9 @@ func verifyLastStates(start []*State) { func concatenate(s1 *State, s2 *State) *State { for i := range s1.output { - s1.output[i].transitions[s2.content], _ = unique_append(s1.output[i].transitions[s2.content], s2) + for _, c := range s2.content { // Create transitions for every element in s2's content to s2' + s1.output[i].transitions[c], _ = unique_append(s1.output[i].transitions[c], s2) + } } s1.output = s2.output return s1 @@ -64,14 +74,18 @@ func concatenate(s1 *State, s2 *State) *State { func kleene(s1 State) *State { toReturn := &State{} toReturn.transitions = make(map[int][]*State) - toReturn.content = EPSILON + toReturn.content = newContents(EPSILON) toReturn.isEmpty = true toReturn.isKleene = true toReturn.output = append(toReturn.output, toReturn) for i := range s1.output { - s1.output[i].transitions[toReturn.content], _ = unique_append(s1.output[i].transitions[toReturn.content], toReturn) + for _, c := range toReturn.content { + s1.output[i].transitions[c], _ = unique_append(s1.output[i].transitions[c], toReturn) + } + } + for _, c := range s1.content { + toReturn.transitions[c], _ = unique_append(toReturn.transitions[c], &s1) } - toReturn.transitions[s1.content], _ = unique_append(toReturn.transitions[s1.content], &s1) return toReturn } @@ -85,9 +99,13 @@ func alternate(s1 *State, s2 *State) *State { // For example, given the transition 'a', the state 's1' can only be mentioned once. // This would lead to multiple instances of the same set of match indices, since both // 's1' states would be considered to match. - toReturn.transitions[s1.content], _ = unique_append(toReturn.transitions[s1.content], s1) - toReturn.transitions[s2.content], _ = unique_append(toReturn.transitions[s2.content], s2) - toReturn.content = EPSILON + for _, c := range s1.content { + toReturn.transitions[c], _ = unique_append(toReturn.transitions[c], s1) + } + for _, c := range s2.content { + toReturn.transitions[c], _ = unique_append(toReturn.transitions[c], s2) + } + toReturn.content = newContents(EPSILON) toReturn.isEmpty = true return toReturn