fae 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f0f89598b59524e013062bb117f47fcd1889716a
4
+ data.tar.gz: b9a9397367d130f47918553a63344e7e73fb4fa1
5
+ SHA512:
6
+ metadata.gz: ed9dff23f912d6156c0bd0d81e220ab95da5a44361883303ed56d1f5d009169b332ff3662b8b40b2285d45406302e65d7a6fba052a9249280d6b21f8c85e49b7
7
+ data.tar.gz: 2d20b2843097ee222bd03a73288fcba66f240a4b76324299efad7144979f8816fa77b14fa9c9efe2152b9c7bacb9d4299fc237ea69623b9b4525ab94917969af
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fae.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Casey Scarborough
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Fae
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'fae'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install fae
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/fae/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/fae ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'yaml'
4
+ require_relative '../lib/fae'
5
+
6
+ options = {}
7
+ OptionParser.new do |opt|
8
+ opt.banner = "Usage: fae [options]"
9
+
10
+ opt.on('-h', '--help', 'View this help menu') { puts opt; exit 0 }
11
+ opt.on('-f FILENAME', '--file FILENAME', 'Evaluate a data file') { |o| options[:file] = o }
12
+ opt.on('-v', '--version', 'Display the version number') { puts Fae::VERSION; exit 0 }
13
+ opt.on('-i', '--interactive', 'Start interactive check') { options[:interactive] = true }
14
+
15
+ begin
16
+ opt.parse!
17
+ rescue OptionParser::InvalidOption => e
18
+ puts e.message
19
+ puts opt
20
+ exit 1
21
+ rescue OptionParser::MissingArgument => e
22
+ puts e.message
23
+ puts opt
24
+ exit 1
25
+ end
26
+ end
27
+
28
+ if (options[:file])
29
+ Fae.load_file(options[:file])
30
+ else
31
+ Fae.interactive
32
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fae/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fae"
8
+ spec.version = Fae::VERSION
9
+ spec.authors = ["Casey Scarborough"]
10
+ spec.email = ["caseyscarborough@gmail.com"]
11
+ spec.summary = %q{Evaluates Finite Automata State Diagrams for correctness.}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_dependency "colorize"
24
+ end
@@ -0,0 +1,124 @@
1
+ require 'colorize'
2
+
3
+ # Fae classes
4
+ require_relative 'string'
5
+ require_relative 'fae/language'
6
+ require_relative 'fae/state'
7
+ require_relative 'fae/finite_automata'
8
+ require_relative 'fae/version'
9
+
10
+ module Fae
11
+
12
+ class << self
13
+
14
+ def load_file(filename)
15
+ diagrams = nil
16
+ begin
17
+ diagrams = YAML.load_file(filename)
18
+ rescue Exception => e
19
+ abort "No such file: #{filename}"
20
+ end
21
+
22
+ diagrams.each do |diagram|
23
+ language = diagram["language"].split(',')
24
+ language.map(&:strip!)
25
+ lang = Fae::Language.new(language)
26
+ fa = Fae::FiniteAutomata.new(lang, diagram["description"])
27
+
28
+ states = []
29
+ diagram["states"].keys.each do |state|
30
+ name = state
31
+ values = diagram["states"][name].split(",")
32
+ values.map(&:strip!)
33
+
34
+ paths = {}
35
+ values.each do |value|
36
+ match = value.split("->")
37
+ match.map(&:strip!)
38
+ if (match)
39
+ paths[match[0].to_sym] = match[1]
40
+ end
41
+ end
42
+ states << Fae::State.new(name, paths, values[-1] == "accepting")
43
+ end
44
+
45
+ strings = []
46
+ diagram["strings"].keys.each do |string|
47
+ value = string
48
+ valid = diagram["strings"][string] == "valid"
49
+ strings << String.new(value.dup, valid)
50
+ end
51
+
52
+ fa.add_states(states)
53
+ fa.add_strings(strings)
54
+ fa.evaluate!
55
+ end
56
+ end
57
+
58
+ def interactive
59
+ states = []
60
+ strings = []
61
+
62
+ print "~ Enter the letters of your language separated by a comma: ".colorize(:yellow)
63
+ letters = gets.chomp.split(",")
64
+ letters.map(&:strip!)
65
+
66
+ language = Language.new(letters)
67
+ print "~ Enter the description of your state diagram: ".colorize(:yellow)
68
+ description = gets.chomp
69
+
70
+ fa = FiniteAutomata.new(language, description)
71
+ print "~ Enter your state names separated by a comma: ".colorize(:yellow)
72
+ state_names = gets.chomp.split(",")
73
+ state_names.map(&:strip!)
74
+
75
+ state_names.each do |state|
76
+ paths = {}
77
+ puts "\nState #{state}:".colorize(:blue)
78
+
79
+ letters.each do |letter|
80
+ valid = false
81
+ while (!valid)
82
+ print "~ In state ".colorize(:yellow) + state.colorize(:blue) + " the letter ".colorize(:yellow) + letter.colorize(:blue) + " will take you to what state? ".colorize(:yellow)
83
+ next_state = gets.chomp
84
+ if (!state_names.include?(next_state))
85
+ puts "State #{next_state} is not one of your state names. Please choose from the following: #{state_names}".colorize(:red)
86
+ else
87
+ paths[letter.to_sym] = next_state
88
+ valid = true
89
+ end
90
+ end
91
+ end
92
+
93
+ print "~ Is state ".colorize(:yellow) + state.colorize(:blue) + " an accepting state? (y/n): ".colorize(:yellow)
94
+ accepting = gets.chomp.casecmp('y').zero?
95
+ states << State.new(state, paths, accepting)
96
+ end
97
+
98
+ finished = false
99
+ string_values = []
100
+ puts "~ Enter strings to test your state diagram with (type 'done' when finished):".colorize(:yellow)
101
+
102
+ while(!finished)
103
+ value = gets.chomp
104
+ if (value == 'done')
105
+ finished = true
106
+ else
107
+ string_values << value
108
+ end
109
+ end
110
+
111
+ string_values.each do |value|
112
+ print "~ Is ".colorize(:yellow) + value.colorize(:blue) + " a valid string for this state diagram? (y/n): ".colorize(:yellow)
113
+ valid = gets.chomp.casecmp('y').zero?
114
+ strings << String.new(value, valid)
115
+ end
116
+ puts
117
+
118
+ fa.add_states(states)
119
+ fa.add_strings(strings)
120
+ fa.evaluate!
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,95 @@
1
+ module Fae
2
+ class FiniteAutomata
3
+ def initialize(language, description)
4
+ @states = []
5
+ @strings = []
6
+ @invalids = []
7
+ @language = language
8
+ @description = description
9
+ end
10
+
11
+ def add_strings(strings)
12
+ strings.each do |string|
13
+ @strings << string
14
+ end
15
+ end
16
+
17
+ def add_states(states)
18
+ states.each do |state|
19
+ add_state(state)
20
+ end
21
+ end
22
+
23
+ def get_state(name)
24
+ retrieved_state = nil
25
+ @states.each do |state|
26
+ if (state.name == name)
27
+ retrieved_state = state
28
+ break
29
+ end
30
+ end
31
+ if (retrieved_state.nil?)
32
+ raise "State #{name} was not found in this Finite Automata. Ensure that all states have outputs for #{@language.characters}"
33
+ end
34
+ return retrieved_state
35
+ end
36
+
37
+ def evaluate!
38
+ @invalids = []
39
+ if (@states.length == 0)
40
+ raise "You must add some states to your Finite Automata before checking strings"
41
+ end
42
+
43
+ puts """Evaluating strings for #{@description} using language #{@language.characters}".colorize(:yellow)
44
+ @strings.each do |string|
45
+ valid = evaluate_string(string)
46
+ if (!valid)
47
+ @invalids << string
48
+ end
49
+ end
50
+
51
+ num_invalid = @invalids.length
52
+ if (num_invalid > 0)
53
+ puts "State diagram may be incorrect for #{@description}".colorize(:red)
54
+ puts "\nA total of #{num_invalid} string#{'s' if num_invalid != 1} did not meet your expectations:\n"
55
+
56
+ @invalids.each do |string|
57
+ expected_string = string.expected ? "valid".colorize(:green) : "invalid".colorize(:red)
58
+ puts "* You expected the string '#{string.colorize(:blue)}' to be #{expected_string}"
59
+ end
60
+ puts "\nIf these expectations are correct, then your state diagram needs revising. Otherwise, you've simply expected the wrong values."
61
+ else
62
+ puts "State diagram is correct.".colorize(:green)
63
+ end
64
+ puts
65
+ end
66
+
67
+ private
68
+ def add_state(new_state)
69
+ valid = true
70
+ @states.each do |state|
71
+ if (new_state.name == state.name)
72
+ raise 'Duplicate state added for Finite Automata'
73
+ end
74
+ end
75
+ @states << new_state
76
+ end
77
+
78
+ def evaluate_string(string)
79
+ print "#{string.colorize(:blue)}: "
80
+ if (@language.string_is_valid(string))
81
+ result = @states.first.evaluate(string, self)
82
+ if (result[:valid] != string.expected)
83
+ puts "\u2717".encode("utf-8").colorize(:red)
84
+ return false
85
+ else
86
+ puts "\u2713".encode("utf-8").colorize(:green)
87
+ return true
88
+ end
89
+ else
90
+ puts "not valid for language #{@characters}".colorize(:red)
91
+ return true
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,24 @@
1
+ module Fae
2
+ class Language
3
+ attr_accessor :characters
4
+
5
+ def initialize(characters)
6
+ @characters = []
7
+ @characters = characters.uniq
8
+ end
9
+
10
+ def string_is_valid(string)
11
+ # Use lookahead to check for valid string
12
+ regex = "^(?=.*\\D)[#{@characters.join('|')}]+$"
13
+
14
+ if (string.match /#{regex}/)
15
+ return true
16
+ end
17
+ return false
18
+ end
19
+
20
+ def add_character(char)
21
+ @characters << char
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Fae
2
+ class State
3
+ attr_accessor :name, :next_states, :valid
4
+
5
+ def initialize(name, next_states, valid)
6
+ @name = name
7
+ @next_states = next_states
8
+ @valid = valid
9
+ end
10
+
11
+ def evaluate(string, fa)
12
+ if (string.first.empty?)
13
+ output = valid ? "valid".colorize(:green) : "invalid".colorize(:red)
14
+ print "#{@name} (#{output}) "
15
+ return { :output => output, :valid => valid }
16
+ end
17
+ print "#{@name} #{'->'.colorize(:light_black)} "
18
+ fa.get_state(next_states[string.first.to_sym]).evaluate(string.shift_left, fa)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Fae
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,18 @@
1
+ # Used to add functionality to the Ruby String class.
2
+ class String
3
+ attr_accessor :expected
4
+
5
+ def self.new(value, expected)
6
+ string = value
7
+ string.expected = expected
8
+ string
9
+ end
10
+
11
+ def first
12
+ self[0, 1]
13
+ end
14
+
15
+ def shift_left(num = 1)
16
+ self[num..-1]
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fae
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Casey Scarborough
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: ''
56
+ email:
57
+ - caseyscarborough@gmail.com
58
+ executables:
59
+ - fae
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/fae
69
+ - fae.gemspec
70
+ - lib/fae.rb
71
+ - lib/fae/finite_automata.rb
72
+ - lib/fae/language.rb
73
+ - lib/fae/state.rb
74
+ - lib/fae/version.rb
75
+ - lib/string.rb
76
+ homepage: ''
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.2.2
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Evaluates Finite Automata State Diagrams for correctness.
100
+ test_files: []