stamina-core 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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