Example #1
0
File: dfa.go Project: postfix/regex
// Returns true if there isn't a word that takes this state into a final ones
func (d *DFA) IsReverseUnreachable(state int) bool {
	if d.IsFinal(state) {
		return false
	}
	if len(d.Graph[state]) == 0 {
		return true
	}
	q := queue.New(d.NumStates)
	viz := make([]bool, d.NumStates+1)
	q.Push(state)
	for !q.Empty() {
		node, _ := q.Pop()
		for _, neighbours := range d.Graph[node] {
			if viz[neighbours[0]] {
				continue
			}
			if d.IsFinal(neighbours[0]) {
				return false
			}
			viz[neighbours[0]] = true
			q.Push(neighbours[0])
		}
	}
	return true
}
Example #2
0
func TestQ(t *testing.T) {
	q := queue.New()
	fmt.Println(q.Empty())
	fmt.Println(q.Size())
	fmt.Println("~~~~~~~~~~~")
	for i := 0; i < 10; i++ {
		q.Push(i)
		fmt.Println(q.Empty())
		fmt.Println(q.Size())
	}
	fmt.Println("~~~~~~~~~~~~~~")
	for !q.Empty() {
		fmt.Println(q.Front())
		q.Pop()
	}
}
Example #3
0
File: nfa.go Project: postfix/regex
// Transforms an NFA into a DFA that accepts the same language; some
// inaccessible states will be lost in the process
func (original_nfa *NFA) ToDFA() dfa.DFA {
	nfa := Copy(*original_nfa)
	n := &nfa
	// first follow along λ-transitions to make them obsolete
	is_final := make([]bool, n.NumStates+1)
	for _, node := range n.FinalStates {
		is_final[node] = true
	}
	for i := 1; i <= n.NumStates; i++ {
		q := queue.New(n.NumStates)
		added := make([]bool, n.NumStates+1)
		for _, node := range n.Graph[i]['λ'] {
			q.Push(node)
		}
		for !q.Empty() {
			node, _ := q.Pop()
			if is_final[node] && !is_final[i] {
				is_final[i] = true
				n.FinalStates = append(n.FinalStates, i)
			}
			for character, neighbours := range n.Graph[node] {
				for _, neighbour := range neighbours {
					if character == 'λ' && !added[neighbour] {
						q.Push(neighbour)
						added[neighbour] = true
					}
					n.Graph[i][character] = append(n.Graph[i][character], neighbour)
					n.NumTransitions++
				}
			}
		}
	}

	// then remove λ-transitions
	for i := 1; i <= n.NumStates; i++ {
		_, ok := n.Graph[i]['λ']
		if ok {
			n.NumTransitions -= len(n.Graph[i]['λ'])
			delete(n.Graph[i], 'λ')
		}
	}

	// then turn that into a DFA
	visited := make([]bool, 1<<uint(n.NumStates+1))
	dfa := make(map[int]map[rune]int)
	q := queue.New(1 << uint(n.NumStates))
	q.Push(1 << uint(n.EntryState-1))
	final_states := make([]int, 0)
	for !q.Empty() {
		node_code, _ := q.Pop()
		is_final_node := false
		for node := 1; node < (1 << uint(n.NumStates)); node++ {
			if 1<<uint(node-1) > node_code {
				break
			}
			if ((1 << uint(node-1)) & node_code) == 0 {
				continue
			}
			if is_final[node] {
				is_final_node = true
			}
			if _, ok := dfa[node_code]; !ok {
				dfa[node_code] = make(map[rune]int)
			}
			for character, neighbours := range n.Graph[node] {
				for _, neighbour := range neighbours {
					dfa[node_code][character] = dfa[node_code][character] | (1 << uint(neighbour-1))
				}
			}
			for _, neighbour := range dfa[node_code] {
				if !visited[neighbour] {
					q.Push(neighbour)
					visited[neighbour] = true
				}
			}
		}
		if is_final_node {
			final_states = append(final_states, node_code)
		}
	}

	// encode the newly created DFA into our standard form
	for node, _ := range n.Graph {
		delete(n.Graph, node)
	}
	nr := n.NumStates
	n.NumStates = 0
	n.NumTransitions = 0
	n.EntryState = 1 << uint(n.EntryState-1)

	mapping := make(map[int]int)
	current_node := 0
	for i := 1; i < (1 << uint(nr)); i++ {
		if _, ok := dfa[i]; !ok || len(dfa[i]) == 0 {
			continue
		}

		if _, ok := mapping[i]; !ok {
			current_node++
			mapping[i] = current_node
		}
		n.Graph[mapping[i]] = make(map[rune][]int)
		n.NumTransitions += len(dfa[i])
		for character, node := range dfa[i] {
			if _, ok := mapping[node]; !ok {
				current_node++
				mapping[node] = current_node
			}
			n.Graph[mapping[i]][character] = []int{mapping[node]}
		}
	}
	if _, ok := mapping[n.EntryState]; ok {
		n.EntryState = mapping[n.EntryState]
	} else {
		current_node++
		mapping[n.EntryState] = current_node
		n.EntryState = current_node
	}
	n.NumStates = current_node
	n.FinalStates = make([]int, 0)

	for _, node := range final_states {
		if _, ok := mapping[node]; ok {
			n.FinalStates = append(n.FinalStates, mapping[node])
		}
	}

	return n.DFA
}
Example #4
0
File: dfa.go Project: postfix/regex
// Minimize simplifies the DFA, by removing unnecessary states and merging
// states that can be merged
func (d *DFA) Minimize() {
	// find unreachable and reverse-unreachable states
	q := queue.New(d.NumStates)
	to_remove := make([]bool, d.NumStates+1)
	viz := make(map[int]bool)
	nodes_removed := 0
	transitions_removed := 0
	q.Push(d.EntryState)
	viz[d.EntryState] = true
	for !q.Empty() {
		node, _ := q.Pop()
		for _, neighbours := range d.Graph[node] {
			ok, _ := viz[neighbours[0]]
			if !ok {
				viz[neighbours[0]] = true
				q.Push(neighbours[0])
			}
		}
	}
	for i := 1; i <= d.NumStates; i++ {
		ok, _ := viz[i]
		if !ok || d.IsReverseUnreachable(i) {
			to_remove[i] = true
			nodes_removed++
		}
	}

	// Moore's Algorithm
	// See http://en.wikipedia.org/wiki/DFA_minimization#Moore.27s_algorithm
	is_final := make([]bool, d.NumStates+1)
	renames := make(map[int]int)
	for i := 1; i <= d.NumStates; i++ {
		renames[i] = i
		if d.IsFinal(i) {
			is_final[i] = true
		}
	}
	found_match := true
	for found_match {
		found_match = false
		old_graph := d.Graph
		for i := 1; i <= d.NumStates; i++ {
			if to_remove[renames[i]] {
				continue
			}
			for j := 1; j <= d.NumStates; j++ {
				if to_remove[renames[i]] || renames[i] >= renames[j] {
					continue
				}
				if is_final[renames[i]] && !is_final[renames[j]] ||
					is_final[renames[j]] && !is_final[renames[i]] {
					continue
				}
				match := nodes_match(old_graph, is_final, i, j)
				if match {
					// join states i and j
					obsolete := 0
					used := 0
					if renames[i] > renames[j] {
						obsolete, used = renames[i], renames[j]
						renames[i] = renames[j]
					} else {
						obsolete, used = renames[j], renames[i]
						renames[j] = renames[i]
					}
					if !to_remove[obsolete] {
						to_remove[obsolete] = true
						nodes_removed++
					}
					for character, nodes := range d.Graph[obsolete] {
						if _, ok := d.Graph[used]; !ok {
							d.Graph[used] = make(map[rune][]int)
						}
						if _, ok := d.Graph[used][character]; !ok {
							d.Graph[used][character] = make([]int, 1)
						} else {
							transitions_removed++
						}
						d.Graph[used][character][0] = renames[nodes[0]]
					}
					renames[obsolete] = renames[used]
					found_match = true
				}
			}
		}
	}

	// Apply remaining renames
	// TODO: improve the main algorithm so that it doesn't need this
	for i := 1; i <= d.NumStates; i++ {
		for character, _ := range d.Graph[i] {
			for d.Graph[i][character][0] != renames[d.Graph[i][character][0]] {
				d.Graph[i][character][0] = renames[d.Graph[i][character][0]]
			}
		}
	}
	for d.EntryState != renames[d.EntryState] {
		d.EntryState = renames[d.EntryState]
	}

	// rename states so that there are no gaps between them
	mapping := make(map[int]int)
	next_available := 0
	for node := 1; node <= d.NumStates; node++ {
		if !to_remove[node] {
			next_available++
			mapping[node] = next_available
		}
	}
	d.EntryState = mapping[d.EntryState]
	final_states := make([]int, 0, len(d.FinalStates))
	for id, _ := range d.FinalStates {
		if _, ok := mapping[d.FinalStates[id]]; ok {
			final_states = append(final_states, mapping[d.FinalStates[id]])
		}
	}
	d.FinalStates = final_states

	// rename the states found
	for node, _ := range d.Graph {
		for character, _ := range d.Graph[node] {
			d.Graph[node][character][0] = mapping[d.Graph[node][character][0]]
		}
	}
	for id := 1; id <= d.NumStates; id++ {
		_, ok := mapping[id]
		if !ok {
			delete(d.Graph, id)
			continue
		}
		d.Graph[mapping[id]] = d.Graph[id]
	}
	d.NumStates -= nodes_removed
	d.NumTransitions -= transitions_removed
}