Class: Fae::FiniteAutomata

Inherits:
Object
  • Object
show all
Defined in:
lib/fae/finite_automata.rb

Overview

The main class that drives the Finite Automata evaluation.

Takes in a language, states, and strings, and checks them to validate a state diagram.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(language, description) ⇒ FiniteAutomata

Initializes a new instance of the FiniteAutomata.

Parameters:

  • language (Language)

    a language instance

  • description (String)

    the description of the finite automata



15
16
17
18
19
20
21
# File 'lib/fae/finite_automata.rb', line 15

def initialize(language, description)
  @states = []
  @strings = []
  @invalids = []
  @language = language
  @description = description
end

Instance Attribute Details

#descriptionObject (readonly)

Returns the value of attribute description.



8
9
10
# File 'lib/fae/finite_automata.rb', line 8

def description
  @description
end

#languageObject (readonly)

Returns the value of attribute language.



8
9
10
# File 'lib/fae/finite_automata.rb', line 8

def language
  @language
end

#statesObject (readonly)

Returns the value of attribute states.



8
9
10
# File 'lib/fae/finite_automata.rb', line 8

def states
  @states
end

#stringsObject (readonly)

Returns the value of attribute strings.



8
9
10
# File 'lib/fae/finite_automata.rb', line 8

def strings
  @strings
end

#valid_blockObject

Returns the value of attribute valid_block.



9
10
11
# File 'lib/fae/finite_automata.rb', line 9

def valid_block
  @valid_block
end

Instance Method Details

#add_state(new_state) ⇒ Object

Adds a single state to the states array.

Parameters:

  • new_state (State)

    the state to add.



49
50
51
52
53
54
55
# File 'lib/fae/finite_automata.rb', line 49

def add_state(new_state)
  valid = true
  @states.each do |state|
    raise Fae::DuplicateStateException, 'Duplicate state added for Finite Automata' if new_state.name == state.name
  end
  @states << new_state
end

#add_states(states) ⇒ Object

Adds strings to check against when evaluating.

Parameters:

  • states (Array)

    an array of states



41
42
43
44
# File 'lib/fae/finite_automata.rb', line 41

def add_states(states)
  states.each { |state| add_state(state) }
  self
end

#add_string(string) ⇒ Object

Adds a single string to the strings array.

Parameters:

  • string (String)

    the string to add



34
35
36
# File 'lib/fae/finite_automata.rb', line 34

def add_string(string)
  @strings << string
end

#add_strings(strings) ⇒ Object

Adds strings to check against when evaluating.

Parameters:

  • strings (Array)

    an array of strings



26
27
28
29
# File 'lib/fae/finite_automata.rb', line 26

def add_strings(strings)
  strings.each { |string| @strings << string }
  self
end

#difference(fa) ⇒ Object

Generates the finite automata for the difference of two different finite automatas.

Parameters:

  • fa (FiniteAutomata)

    the finite automata to difference this one with



94
95
96
# File 'lib/fae/finite_automata.rb', line 94

def difference(fa)
  perform_set_operation(:difference, fa)
end

#evaluate!(suppress_output = false) ⇒ Object

Runs the evaluation on the finite automata.



99
100
101
102
103
104
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
# File 'lib/fae/finite_automata.rb', line 99

def evaluate!(suppress_output=false)
  raise Fae::EmptyStatesException, 'You must add some states to your Finite Automata before checking strings' if @states.length == 0

  output = "Evaluating strings for #{@description} using language #{@language.characters}".colorize(:yellow)
  @invalids = []

  @strings.each do |string|
    result = evaluate_string(string)
    @invalids << string if !result[:valid]
    output    << result[:output]
  end

  num_invalid = @invalids.length
  valid = num_invalid == 0
  if !valid
    output << "\nState diagram may be incorrect for #{@description}\n".colorize(:red)
    output << "\nA total of #{num_invalid} string#{'s' if num_invalid != 1} did not meet your expectations:\n\n"

    @invalids.each do |string|
      expected_string = string.expected ? "valid".colorize(:green) : "invalid".colorize(:red)
      output << "* You expected the string '#{string.colorize(:blue)}' to be #{expected_string}\n"
    end
    output << "\nIf these expectations are correct, then your state diagram needs revising. Otherwise, you've simply expected the wrong values."
  else
    output << "\nState diagram is correct.\n".colorize(:green)
  end

  if (!suppress_output)
    puts output
  end
  valid
end

#generate_strings(number, length) ⇒ Object

Generates strings for the finite automata and adds them to the strings array.

Parameters:

  • number (Integer)

    number of strings

  • length (Integer)

    length of each string

Raises:



137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/fae/finite_automata.rb', line 137

def generate_strings(number, length)
  raise Fae::MissingValidBlockException, 'You must set the valid block for the Finite Automata to generate strings' if valid_block.nil?
  
  strings = []
  number.times do
    string = ""
    length.times { string << @language.characters.sample }
    strings << String.new(string, valid_block.call(string))
  end
  self.add_strings(strings)
  self
end

#get_state(name) ⇒ Object

Retrieves a state from this finite automata by name.

Parameters:

  • name (String)

    the name of the state to find



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/fae/finite_automata.rb', line 60

def get_state(name)
  retrieved_state = nil
  @states.each do |state|
    if (state.name == name)
      retrieved_state = state
      break
    end
  end
  if (retrieved_state.nil?)
    raise Fae::StateNotFoundException, "State #{name} was not found in this Finite Automata. Ensure that all states have outputs for #{@language.characters}"
  end
  retrieved_state
end

#intersection(fa) ⇒ Object

Generates the finite automata for the intersection of two different finite automatas.

Parameters:

  • fa (FiniteAutomata)

    the finite automata to intersect this one with



78
79
80
# File 'lib/fae/finite_automata.rb', line 78

def intersection(fa)
  perform_set_operation(:intersection, fa)
end

#to_sObject



150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/fae/finite_automata.rb', line 150

def to_s
  output =  "Description: ".colorize(:yellow) + @description
  output << "\nLanguage: ".colorize(:yellow) + language.characters.to_s
  output << "\nStates:".colorize(:yellow)
  @states.each do |state|
    output << "\n  State #{state.name}: ".colorize(:blue)
    state.paths.keys.each do |key|
      output << "\n    #{'~'.colorize(:yellow)} #{key} #{'->'.colorize(:light_black)} #{state.paths[key]}"
    end
    accepting = state.accepting ? "accepting".colorize(:green) : "not accepting".colorize(:red)
    output << "\n    #{'~'.colorize(:yellow)} #{accepting}"
  end
  output
end

#union(fa) ⇒ Object

Generates the finite automata for the union of two different finite automatas.

Parameters:



86
87
88
# File 'lib/fae/finite_automata.rb', line 86

def union(fa)
  perform_set_operation(:union, fa)
end