stamina-core 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/CHANGELOG.md +78 -0
  2. data/LICENCE.md +22 -0
  3. data/lib/stamina-core/stamina-core.rb +1 -0
  4. data/lib/stamina-core/stamina/adl.rb +298 -0
  5. data/lib/stamina-core/stamina/automaton.rb +1300 -0
  6. data/lib/stamina-core/stamina/automaton/complement.rb +26 -0
  7. data/lib/stamina-core/stamina/automaton/complete.rb +36 -0
  8. data/lib/stamina-core/stamina/automaton/compose.rb +111 -0
  9. data/lib/stamina-core/stamina/automaton/determinize.rb +104 -0
  10. data/lib/stamina-core/stamina/automaton/equivalence.rb +57 -0
  11. data/lib/stamina-core/stamina/automaton/hide.rb +41 -0
  12. data/lib/stamina-core/stamina/automaton/metrics.rb +77 -0
  13. data/lib/stamina-core/stamina/automaton/minimize.rb +23 -0
  14. data/lib/stamina-core/stamina/automaton/minimize/hopcroft.rb +118 -0
  15. data/lib/stamina-core/stamina/automaton/minimize/pitchies.rb +130 -0
  16. data/lib/stamina-core/stamina/automaton/strip.rb +16 -0
  17. data/lib/stamina-core/stamina/automaton/walking.rb +361 -0
  18. data/lib/stamina-core/stamina/command.rb +38 -0
  19. data/lib/stamina-core/stamina/command/adl2dot.rb +82 -0
  20. data/lib/stamina-core/stamina/command/help.rb +23 -0
  21. data/lib/stamina-core/stamina/command/robustness.rb +21 -0
  22. data/lib/stamina-core/stamina/command/run.rb +84 -0
  23. data/lib/stamina-core/stamina/core.rb +11 -0
  24. data/lib/stamina-core/stamina/dsl.rb +6 -0
  25. data/lib/stamina-core/stamina/dsl/automata.rb +23 -0
  26. data/lib/stamina-core/stamina/dsl/core.rb +14 -0
  27. data/lib/stamina-core/stamina/engine.rb +32 -0
  28. data/lib/stamina-core/stamina/engine/context.rb +35 -0
  29. data/lib/stamina-core/stamina/errors.rb +26 -0
  30. data/lib/stamina-core/stamina/ext/math.rb +19 -0
  31. data/lib/stamina-core/stamina/loader.rb +3 -0
  32. data/lib/stamina-core/stamina/markable.rb +42 -0
  33. data/lib/stamina-core/stamina/utils.rb +1 -0
  34. data/lib/stamina-core/stamina/utils/decorate.rb +81 -0
  35. data/lib/stamina-core/stamina/version.rb +14 -0
  36. metadata +93 -0
@@ -0,0 +1,38 @@
1
+ module Stamina
2
+ #
3
+ # Stamina - A Ruby Automaton & Induction Toolkit
4
+ #
5
+ # SYNOPSIS
6
+ # #{program_name} [--version] [--help] COMMAND [cmd opts] ARGS...
7
+ #
8
+ # OPTIONS
9
+ # #{summarized_options}
10
+ #
11
+ # COMMANDS
12
+ # #{summarized_subcommands}
13
+ #
14
+ # See '#{program_name} help COMMAND' for more information on a specific command.
15
+ #
16
+ class Command < ::Quickl::Delegator(__FILE__, __LINE__)
17
+
18
+ # Install options
19
+ options do |opt|
20
+
21
+ # Show the help and exit
22
+ opt.on_tail("--help", "Show help") do
23
+ raise Quickl::Help
24
+ end
25
+
26
+ # Show version and exit
27
+ opt.on_tail("--version", "Show version") do
28
+ raise Quickl::Exit, "#{program_name} #{VERSION} (c) 2010-2011, Bernard Lambeau"
29
+ end
30
+
31
+ end
32
+
33
+ end # class Command
34
+ end # module Stamina
35
+ require_relative 'command/robustness'
36
+ require_relative 'command/help'
37
+ require_relative 'command/adl2dot'
38
+ require_relative 'command/run'
@@ -0,0 +1,82 @@
1
+ module Stamina
2
+ class Command
3
+ #
4
+ # Prints an automaton expressed in ADL in dot (or gif) format
5
+ #
6
+ # SYNOPSIS
7
+ # #{program_name} #{command_name} automaton.adl
8
+ #
9
+ # OPTIONS
10
+ # #{summarized_options}
11
+ #
12
+ class Adl2dot < Quickl::Command(__FILE__, __LINE__)
13
+ include Robustness
14
+
15
+ attr_reader :output_format
16
+
17
+ # Install options
18
+ options do |opt|
19
+
20
+ @output_file = nil
21
+ opt.on("-o", "--output=OUTPUT",
22
+ "Flush result output file") do |value|
23
+ @output_file = assert_writable_file(value)
24
+ end
25
+
26
+ @output_format = "dot"
27
+ opt.on("-g", "--gif",
28
+ "Generates a gif file instead of a dot one") do
29
+ @output_format = "gif"
30
+ end
31
+
32
+ opt.on("--png",
33
+ "Generates a png file instead of a dot one") do
34
+ @output_format = "png"
35
+ end
36
+
37
+ end # options
38
+
39
+ def output_file(infile)
40
+ @output_file || "#{File.basename(infile || 'stdin.adl', '.adl')}.#{output_format}"
41
+ end
42
+
43
+ # Command execution
44
+ def execute(args)
45
+ raise Quickl::Help unless args.size <= 1
46
+
47
+ # Loads the target automaton
48
+ input = if args.size == 1
49
+ File.read assert_readable_file(args.first)
50
+ else
51
+ $stdin.readlines.join("\n")
52
+ end
53
+
54
+ begin
55
+ automaton = Stamina::ADL::parse_automaton(input)
56
+ rescue ADL::ParseError
57
+ sample = Stamina::ADL::parse_sample(input)
58
+ automaton = sample.to_pta
59
+ end
60
+
61
+ # create a file for the dot output
62
+ if output_format == 'dot'
63
+ dotfile = output_file(args.first)
64
+ else
65
+ require 'tempfile'
66
+ dotfile = Tempfile.new("stamina").path
67
+ end
68
+
69
+ # Flush automaton inside it
70
+ File.open(dotfile, 'w') do |f|
71
+ f << automaton.to_dot
72
+ end
73
+
74
+ # if gif output, use dot to convert it
75
+ unless output_format == 'dot'
76
+ `dot -T#{output_format} -o #{output_file(args.first)} #{dotfile}`
77
+ end
78
+ end
79
+
80
+ end # class Adl2dot
81
+ end # class Command
82
+ end # module Stamina
@@ -0,0 +1,23 @@
1
+ module Stamina
2
+ class Command
3
+ #
4
+ # Show help about a specific command
5
+ #
6
+ # SYNOPSIS
7
+ # #{program_name} #{command_name} COMMAND
8
+ #
9
+ class Help < Quickl::Command(__FILE__, __LINE__)
10
+
11
+ # Let NoSuchCommandError be passed to higher stage
12
+ no_react_to Quickl::NoSuchCommand
13
+
14
+ # Command execution
15
+ def execute(args)
16
+ sup = Quickl.super_command(self)
17
+ sub = (args.size != 1) ? sup : Quickl.sub_command!(sup, args.first)
18
+ puts Quickl.help(sub)
19
+ end
20
+
21
+ end # class Help
22
+ end # class Command
23
+ end # module Stamina
@@ -0,0 +1,21 @@
1
+ module Stamina
2
+ class Command
3
+ module Robustness
4
+
5
+ # Checks that a given file is readable or raises a Quickl::IOAccessError
6
+ def assert_readable_file(file)
7
+ raise Quickl::IOAccessError, "File #{file} does not exists" unless File.exists?(file)
8
+ raise Quickl::IOAccessError, "File #{file} cannot be read" unless File.readable?(file)
9
+ file
10
+ end
11
+
12
+ # Checks that a given file is writable or raises a Quickl::IOAccessError
13
+ def assert_writable_file(file)
14
+ raise Quickl::IOAccessError, "File #{file} cannot be written" \
15
+ unless not(File.exists?(file)) or File.writable?(file)
16
+ file
17
+ end
18
+
19
+ end # module Robustness
20
+ end # class Command
21
+ end # module Stamina
@@ -0,0 +1,84 @@
1
+ module Stamina
2
+ class Command
3
+ #
4
+ # Run a file with the Stamina Engine
5
+ #
6
+ # SYNOPSIS
7
+ # stamina #{command_name} FILE
8
+ #
9
+ # OPTIONS
10
+ # #{summarized_options}
11
+ #
12
+ class Run < Quickl::Command(__FILE__, __LINE__)
13
+ include Robustness
14
+
15
+ attr_accessor :output
16
+ attr_accessor :images
17
+
18
+ # Install options
19
+ options do |opt|
20
+
21
+ @output = [:main]
22
+ opt.on("--output=x,y,z", Array,
23
+ "Output specified variables only") do |args|
24
+ self.output = args.collect{|v| v.to_sym}
25
+ end
26
+ opt.on("--all",
27
+ "Output all variables") do
28
+ self.output = nil
29
+ end
30
+
31
+ @images = []
32
+ opt.on('--gif') do
33
+ self.images << :gif
34
+ end
35
+ opt.on('--png') do
36
+ self.images << :png
37
+ end
38
+ opt.on('--dot') do
39
+ self.images << :dot
40
+ end
41
+
42
+ end # options
43
+
44
+ # Command execution
45
+ def execute(args)
46
+ raise Quickl::Help unless args.size == 1
47
+ assert_readable_file(file = args.first)
48
+ context = Stamina::Engine.execute(File.read(file), file)
49
+ do_output(context, File.dirname(file))
50
+ context
51
+ end
52
+
53
+ private
54
+
55
+ def do_output(context, dir)
56
+ self.output ||= context.vars
57
+ self.output.each do |varname|
58
+ varvalue = context[varname]
59
+
60
+ # textual output
61
+ puts "# #{varname} ###########################################"
62
+ puts varvalue.respond_to?(:to_adl) ? varvalue.to_adl : varvalue
63
+ puts
64
+
65
+ # image output
66
+ if varvalue.respond_to?(:to_dot)
67
+ output_images(varname, varvalue, dir)
68
+ end
69
+ end
70
+ end
71
+
72
+ def output_images(varname, varvalue, dir)
73
+ images.each do |format|
74
+ output_file = File.join(dir, "#{varname}.#{format}")
75
+ puts cmd = "dot -q -T#{format} -o#{output_file}"
76
+ IO::popen(cmd, "w") do |io|
77
+ io << varvalue.to_dot
78
+ end
79
+ end
80
+ end
81
+
82
+ end # class Run
83
+ end # class Command
84
+ end # module Stamina
@@ -0,0 +1,11 @@
1
+ require_relative 'version'
2
+ require_relative 'loader'
3
+ require_relative 'errors'
4
+ require_relative 'ext/math'
5
+ require_relative 'markable'
6
+ require_relative 'adl'
7
+ require_relative 'automaton'
8
+ require_relative 'utils'
9
+ require_relative 'dsl'
10
+ require_relative 'engine'
11
+ require_relative 'command'
@@ -0,0 +1,6 @@
1
+ module Stamina
2
+ module Dsl
3
+ require_relative 'dsl/core'
4
+ require_relative 'dsl/automata'
5
+ end # module Dsl
6
+ end # module Stamina
@@ -0,0 +1,23 @@
1
+ module Stamina
2
+ module Dsl
3
+ module Automata
4
+
5
+ #
6
+ # Coerces `arg` to an automaton
7
+ #
8
+ def automaton(arg)
9
+ Automaton.coerce(arg)
10
+ end
11
+
12
+ #
13
+ # Computes the synchronous composition of many automata
14
+ #
15
+ def compose(*args)
16
+ automata = args.collect{|a| automaton(a)}
17
+ Stamina::Automaton::Compose.execute(automata)
18
+ end
19
+
20
+ end # module Automata
21
+ include Automata
22
+ end # module Dsl
23
+ end # module Stamina
@@ -0,0 +1,14 @@
1
+ module Stamina
2
+ module Dsl
3
+ module Core
4
+
5
+ def assert(x, msg = nil)
6
+ raise Stamina::AssertionError,
7
+ "Assertion failed: #{msg || 'no message provided'}",
8
+ caller unless x
9
+ end
10
+
11
+ end # module Core
12
+ include Core
13
+ end # module Dsl
14
+ end # module Stamina
@@ -0,0 +1,32 @@
1
+ require_relative 'engine/context'
2
+ module Stamina
3
+ class Engine
4
+
5
+ def initialize
6
+ extend(Stamina::Dsl)
7
+ end
8
+
9
+ def execute_binding
10
+ binding
11
+ end
12
+
13
+ def execute(code, file = nil)
14
+ code = <<-EOF
15
+ main = begin
16
+ #{code}
17
+ end
18
+ Context.new(local_variables, binding)
19
+ EOF
20
+ if file
21
+ eval(code, execute_binding, file)
22
+ else
23
+ eval(code, execute_binding)
24
+ end
25
+ end
26
+
27
+ def self.execute(*args)
28
+ new.execute(*args)
29
+ end
30
+
31
+ end # class Engine
32
+ end # module Stamina
@@ -0,0 +1,35 @@
1
+ module Stamina
2
+ class Engine
3
+ class Context
4
+ include Enumerable
5
+
6
+ attr_reader :vars, :binding
7
+
8
+ def initialize(vars, binding)
9
+ @vars = vars.collect{|v| v.to_sym}
10
+ @binding = binding
11
+ end
12
+
13
+ def each
14
+ vars.each do |key|
15
+ yield(key, self[key])
16
+ end
17
+ end
18
+
19
+ def [](name)
20
+ binding.eval(name.to_s)
21
+ end
22
+
23
+ def to_h
24
+ Hash[collect{|k,v| [k,v]}]
25
+ end
26
+
27
+ def to_s
28
+ collect{|k,v|
29
+ "#{k}: #{v}"
30
+ }.join("\n")
31
+ end
32
+
33
+ end # class Context
34
+ end # class Engine
35
+ end # module Stamina
@@ -0,0 +1,26 @@
1
+ module Stamina
2
+
3
+ # Raised when an algorithm explicitely abords something
4
+ class Abord < StandardError; end
5
+
6
+ # Main class of all stamina errors.
7
+ class StaminaError < StandardError; end
8
+
9
+ # Raised when an assertion fails.
10
+ class AssertionError < StandardError; end
11
+
12
+ # Raised by samples implementations and other induction algorithms
13
+ # when a sample is inconsistent (same string labeled as being both
14
+ # positive and negative)
15
+ class InconsistencyError < StaminaError; end
16
+
17
+ # Specific errors of the ADL module.
18
+ module ADL
19
+
20
+ # Raised by the ADL module when an automaton, string or sample
21
+ # format is violated at parsing time.
22
+ class ParseError < StaminaError; end
23
+
24
+ end
25
+
26
+ end # module Stamina
@@ -0,0 +1,19 @@
1
+ if RUBY_VERSION < "1.9"
2
+
3
+ def Math.log2( x )
4
+ Math.log( x ) / Math.log( 2 )
5
+ end
6
+
7
+ def Math.logn( x, n )
8
+ Math.log( x ) / Math.log( n )
9
+ end
10
+
11
+ end
12
+
13
+ def Math.max(i, j)
14
+ i > j ? i : j
15
+ end
16
+
17
+ def Math.min(i, j)
18
+ i < j ? i : j
19
+ end
@@ -0,0 +1,3 @@
1
+ require "quickl"
2
+ require "citrus"
3
+ require "sinatra"
@@ -0,0 +1,42 @@
1
+ module Stamina
2
+ #
3
+ # Allows any object to be markable with user-data.
4
+ #
5
+ # This module is expected to be included by classes that want to implement the
6
+ # Markable design pattern. Moreover, if the instances of the including class
7
+ # respond to <tt>state_changed</tt>, this method is automatically invoked when
8
+ # marks change. This method is used by <tt>automaton</tt> in order to make it
9
+ # possible to track changes and check modified automata for consistency.
10
+ #
11
+ # == Detailed API
12
+ module Markable
13
+
14
+ #
15
+ # Returns user-value associated to _key_, nil if no such key in user-data.
16
+ #
17
+ def [](key) @data[key] end
18
+
19
+ #
20
+ # Associates _value_ to _key_ in user-data. Overrides previous value if
21
+ # present.
22
+ #
23
+ def []=(key,value)
24
+ oldvalue = @data[key]
25
+ @data[key] = value
26
+ state_changed(:loaded_pair, [key,oldvalue,value]) if self.respond_to? :state_changed
27
+ end
28
+
29
+ # Removes a mark
30
+ def remove_mark(key)
31
+ oldvalue = @data[key]
32
+ @data.delete(key)
33
+ state_changed(:loaded_pair, [key,oldvalue,nil]) if self.respond_to? :state_changed
34
+ end
35
+
36
+ # Extracts the copy of attributes which can subsequently be modified.
37
+ def data
38
+ @data.nil? ? {} : @data.dup
39
+ end
40
+
41
+ end # module Markable
42
+ end # module Stamina