Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/Gemfile +16 -9
  5. data/Gemfile.lock +134 -64
  6. data/README +0 -1
  7. data/README.md +26 -0
  8. data/README.rdoc +0 -19
  9. data/Rakefile +24 -99
  10. data/VERSION +0 -1
  11. data/build_sandbox.rb +41 -0
  12. data/cucumber.yml +8 -0
  13. data/features/cauldron_new_approach.feature +0 -46
  14. data/features/chop.feature +23 -0
  15. data/features/create_dynamic_statements.feature +14 -0
  16. data/features/generate_known_solution.feature +42 -0
  17. data/features/generate_new_statement.feature +20 -0
  18. data/features/step_definitions/cauldron_steps.rb +47 -0
  19. data/features/support/env.rb +1 -1
  20. data/features/use_existing_statements.feature +23 -0
  21. data/lib/cauldron.rb +41 -5
  22. data/lib/cauldron/actualized_composite.rb +35 -0
  23. data/lib/cauldron/array_collect_template/default.rb +95 -0
  24. data/lib/cauldron/array_collect_template/template.rb +57 -0
  25. data/lib/cauldron/array_reverse_operator.rb +0 -39
  26. data/lib/cauldron/builder.rb +60 -0
  27. data/lib/cauldron/caret.rb +50 -0
  28. data/lib/cauldron/concat_operator.rb +0 -34
  29. data/lib/cauldron/dynamic_operator.rb +90 -0
  30. data/lib/cauldron/dynamic_operator_module.rb +140 -0
  31. data/lib/cauldron/example.rb +18 -0
  32. data/lib/cauldron/example_set.rb +36 -0
  33. data/lib/cauldron/histories.rb +53 -0
  34. data/lib/cauldron/history.rb +34 -0
  35. data/lib/cauldron/if_relationship.rb +4 -6
  36. data/lib/cauldron/number_addition_template/add_five.rb +71 -0
  37. data/lib/cauldron/number_addition_template/template.rb +56 -0
  38. data/lib/cauldron/numeric_operator.rb +0 -45
  39. data/lib/cauldron/operator.rb +62 -0
  40. data/lib/cauldron/operator/array_reverse_operator.rb +160 -0
  41. data/lib/cauldron/operator/concat_operator.rb +72 -0
  42. data/lib/cauldron/operator/hash_key_value_operator.rb +74 -0
  43. data/lib/cauldron/operator/numeric_operator.rb +115 -0
  44. data/lib/cauldron/operator/string_asterisk_operator.rb +131 -0
  45. data/lib/cauldron/operator/to_s_operator.rb +18 -0
  46. data/lib/cauldron/operator/var_collect_operator.rb +29 -0
  47. data/lib/cauldron/pot.rb +136 -26
  48. data/lib/cauldron/relationship.rb +0 -5
  49. data/lib/cauldron/scope.rb +24 -0
  50. data/lib/cauldron/solution/composite.rb +236 -0
  51. data/lib/cauldron/solution/one.rb +49 -0
  52. data/lib/cauldron/statement_generator.rb +298 -0
  53. data/lib/cauldron/template_base.rb +14 -0
  54. data/lib/cauldron/tracer.rb +55 -0
  55. data/lib/cauldron/version.rb +1 -1
  56. data/lib/pry_tester.rb +76 -0
  57. data/ruby_to_sexp.rb +74 -0
  58. data/sandbox.rb +7 -0
  59. data/sexp_to_ruby.rb +150 -0
  60. data/spec/cauldron/actualized_composite_spec.rb +140 -0
  61. data/spec/cauldron/array_collect_template/default_spec.rb +41 -0
  62. data/spec/cauldron/array_reverse_operator_spec.rb +0 -59
  63. data/spec/cauldron/builder_spec.rb +186 -0
  64. data/spec/cauldron/dynamic/add_number_template_spec.rb +30 -0
  65. data/spec/cauldron/dynamic_operator_spec.rb +416 -0
  66. data/spec/cauldron/example_set_spec.rb +49 -0
  67. data/spec/cauldron/example_spec.rb +33 -0
  68. data/spec/cauldron/histories_spec.rb +135 -0
  69. data/spec/cauldron/history_spec.rb +118 -0
  70. data/spec/cauldron/if_relationship_spec.rb +1 -1
  71. data/spec/cauldron/numeric_operator_spec.rb +0 -70
  72. data/spec/cauldron/operator/array_reverse_operator_spec.rb +73 -0
  73. data/spec/cauldron/{concat_operator_spec.rb → operator/concat_operator_spec.rb} +30 -12
  74. data/spec/cauldron/operator/hash_key_value_operator_spec.rb +98 -0
  75. data/spec/cauldron/operator/numeric_operator_spec.rb +110 -0
  76. data/spec/cauldron/operator/string_asterisk_operator_spec.rb +196 -0
  77. data/spec/cauldron/operator/var_collect_operator_spec.rb +38 -0
  78. data/spec/cauldron/pot_spec.rb +176 -14
  79. data/spec/cauldron/solution/composite_spec.rb +421 -0
  80. data/spec/cauldron/solution/one_spec.rb +24 -0
  81. data/spec/cauldron/statement_generator_spec.rb +211 -0
  82. data/spec/cauldron/terminal_spec.rb +2 -2
  83. data/spec/spec_helper.rb +5 -1
  84. data/spec/support/code_matcher.rb +55 -0
  85. data/spec/support/include_instance_of_matcher.rb +9 -0
  86. data/spec/support/shared_examples_for_leaf_operators.rb +22 -0
  87. data/spec/support/shared_examples_for_operators.rb +23 -0
  88. data/syntax_spec.txt +2 -0
  89. metadata +104 -41
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dedfa4647815accc4eb122bd68da99c45587899f
4
+ data.tar.gz: de6686c30eb37dba7ee69436b92feb8dccbd4b28
5
+ SHA512:
6
+ metadata.gz: 6c768e91f010f80cca34b5fe4f5b09c50fc83c38326afabb34d696dcb3ab04210d15f2019e68759508f69c6ad54062caf8083a9f216a596b4a20d3a10a55c811
7
+ data.tar.gz: 4506c6030f20854024b7198b6a96be9d3625201022e664c77a3941ab1205d20d1375ba0cfee37717bb48374b6f8313966cdc4e33b2b0c35e75a81fc8eb5fbd04
data/.gitignore CHANGED
@@ -27,7 +27,7 @@ pkg
27
27
#
28
28
# For MacOS:
29
29
#
30
- #.DS_Store
30
+ .DS_Store
31
31
32
32
# For TextMate
33
33
#*.tmproj
@@ -49,3 +49,6 @@ pkg
49
49
50
50
# For Eclipse/Aptana
51
51
.project
52
+
53
+ tmp
54
+ NOTES.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
# Add dependencies required to use your gem here.
3
3
# Example:
4
4
# gem "activesupport", ">= 2.3.5"
@@ -7,19 +7,26 @@ source "http://rubygems.org"
7
7
#require 'rubygems'
8
8
#require 'lib/cauldron'
9
9
10
+ gem 'activesupport'
11
+
10
12
# Add dependencies to develop your gem here.
11
13
# Include everything needed to run rake, tests, features, etc.
12
14
group :development, :cucumber, :test do
13
15
#gem "shoulda", ">= 0"
14
16
#gem "bundler", "~> 1.0.15"
15
- gem "jeweler", "~> 1.6.2"
16
- gem "rspec", "~>2.6.0"
17
- gem "rcov", ">= 0"
18
- gem "ruby_parser", "~>2.0.6"
19
- gem "aruba", "~>0.4.5"
17
+ gem "jeweler"
18
+ gem "rspec"
19
+ gem 'simplecov', :require => false
20
+ gem "ruby_parser"
21
+ gem 'aruba', '~> 0.14.6'
20
22
#gem 'ruby-debug', '0.10.4'
21
- gem 'debugger'
23
+ #gem 'pry'
22
- gem 'pry'
23
24
end
24
25
25
- gem "ruby2ruby", "~>1.2.5"
26
+ # http://rubytree.anupamsg.me
27
+ gem 'tree'
28
+
29
+ gem 'pry', '~> 0.10.2'
30
+
31
+ gem 'ruby2ruby', '~> 2.3'
32
+ gem 'sorcerer', '~> 1.0.2'
data/Gemfile.lock CHANGED
@@ -1,76 +1,146 @@
1
1
GEM
2
- remote: http://rubygems.org/
2
+ remote: https://rubygems.org/
3
3
specs:
4
- aruba (0.4.5)
5
- bcat (>= 0.6.1)
6
- childprocess (>= 0.1.9)
7
- cucumber (>= 0.10.7)
8
- rdiscount (>= 1.6.8)
9
- rspec (>= 2.6.0)
10
- bcat (0.6.1)
11
- rack (~> 1.0)
12
- builder (3.0.0)
13
- childprocess (0.2.2)
14
- ffi (~> 1.0.6)
15
- coderay (1.1.0)
16
- columnize (0.3.6)
17
- cucumber (1.1.0)
4
+ activesupport (5.2.3)
5
+ concurrent-ruby (~> 1.0, >= 1.0.2)
6
+ i18n (>= 0.7, < 2)
7
+ minitest (~> 5.1)
8
+ tzinfo (~> 1.1)
9
+ addressable (2.4.0)
10
+ aruba (0.14.11)
11
+ childprocess (>= 0.6.3, < 3.0.0)
12
+ contracts (~> 0.9)
13
+ cucumber (>= 1.3.19)
14
+ ffi (~> 1.9)
15
+ rspec-expectations (>= 2.99)
16
+ thor (~> 0.19)
17
+ backports (3.15.0)
18
+ builder (3.2.3)
19
+ childprocess (2.0.0)
20
+ rake (< 13.0)
21
+ coderay (1.1.2)
22
+ concurrent-ruby (1.1.5)
23
+ contracts (0.16.0)
24
+ cucumber (3.1.2)
18
25
builder (>= 2.1.2)
19
- diff-lcs (>= 1.1.2)
20
- gherkin (~> 2.5.0)
21
- json (>= 1.4.6)
22
- term-ansicolor (>= 1.0.6)
23
- debugger (1.6.5)
24
- columnize (>= 0.3.1)
25
- debugger-linecache (~> 1.2.0)
26
- debugger-ruby_core_source (~> 1.3.1)
27
- debugger-linecache (1.2.0)
28
- debugger-ruby_core_source (1.3.2)
29
- diff-lcs (1.1.2)
30
- ffi (1.0.9)
31
- gherkin (2.5.1)
32
- json (>= 1.4.6)
33
- git (1.2.5)
34
- jeweler (1.6.2)
35
- bundler (~> 1.0)
26
+ cucumber-core (~> 3.2.0)
27
+ cucumber-expressions (~> 6.0.1)
28
+ cucumber-wire (~> 0.0.1)
29
+ diff-lcs (~> 1.3)
30
+ gherkin (~> 5.1.0)
31
+ multi_json (>= 1.7.5, < 2.0)
32
+ multi_test (>= 0.1.2)
33
+ cucumber-core (3.2.1)
34
+ backports (>= 3.8.0)
35
+ cucumber-tag_expressions (~> 1.1.0)
36
+ gherkin (~> 5.0)
37
+ cucumber-expressions (6.0.1)
38
+ cucumber-tag_expressions (1.1.1)
39
+ cucumber-wire (0.0.1)
40
+ descendants_tracker (0.0.4)
41
+ thread_safe (~> 0.3, >= 0.3.1)
42
+ diff-lcs (1.3)
43
+ docile (1.3.2)
44
+ faraday (0.9.2)
45
+ multipart-post (>= 1.2, < 3)
46
+ ffi (1.11.1)
47
+ gherkin (5.1.0)
48
+ git (1.5.0)
49
+ github_api (0.16.0)
50
+ addressable (~> 2.4.0)
51
+ descendants_tracker (~> 0.0.4)
52
+ faraday (~> 0.8, < 0.10)
53
+ hashie (>= 3.4)
54
+ mime-types (>= 1.16, < 3.0)
55
+ oauth2 (~> 1.0)
56
+ hashie (3.6.0)
57
+ highline (2.0.2)
58
+ i18n (1.6.0)
59
+ concurrent-ruby (~> 1.0)
60
+ jeweler (2.3.9)
61
+ builder
62
+ bundler
36
63
git (>= 1.2.5)
64
+ github_api (~> 0.16.0)
65
+ highline (>= 1.6.15)
66
+ nokogiri (>= 1.5.10)
67
+ psych
37
68
rake
38
- json (1.6.1)
69
+ rdoc
70
+ semver2
71
+ json (2.2.0)
72
+ jwt (2.2.1)
39
73
method_source (0.8.2)
40
- pry (0.9.12.6)
41
- coderay (~> 1.0)
42
- method_source (~> 0.8)
74
+ mime-types (2.99.3)
75
+ mini_portile2 (2.4.0)
76
+ minitest (5.11.3)
77
+ multi_json (1.13.1)
78
+ multi_test (0.1.2)
79
+ multi_xml (0.6.0)
80
+ multipart-post (2.1.1)
81
+ nokogiri (1.10.4)
82
+ mini_portile2 (~> 2.4.0)
83
+ oauth2 (1.4.1)
84
+ faraday (>= 0.8, < 0.16.0)
85
+ jwt (>= 1.0, < 3.0)
86
+ multi_json (~> 1.3)
87
+ multi_xml (~> 0.5)
88
+ rack (>= 1.2, < 3)
89
+ pry (0.10.4)
90
+ coderay (~> 1.1.0)
91
+ method_source (~> 0.8.1)
43
92
slop (~> 3.4)
44
- rack (1.3.4)
45
- rake (0.9.2)
46
- rcov (0.9.9)
47
- rdiscount (1.6.8)
48
- rspec (2.6.0)
49
- rspec-core (~> 2.6.0)
50
- rspec-expectations (~> 2.6.0)
51
- rspec-mocks (~> 2.6.0)
52
- rspec-core (2.6.3)
53
- rspec-expectations (2.6.0)
54
- diff-lcs (~> 1.1.2)
55
- rspec-mocks (2.6.0)
56
- ruby2ruby (1.2.5)
57
- ruby_parser (~> 2.0)
58
- sexp_processor (~> 3.0)
59
- ruby_parser (2.0.6)
60
- sexp_processor (~> 3.0)
61
- sexp_processor (3.0.5)
62
- slop (3.4.7)
63
- term-ansicolor (1.0.6)
93
+ psych (3.1.0)
94
+ rack (2.0.7)
95
+ rake (12.3.3)
96
+ rdoc (6.1.1)
97
+ rspec (3.8.0)
98
+ rspec-core (~> 3.8.0)
99
+ rspec-expectations (~> 3.8.0)
100
+ rspec-mocks (~> 3.8.0)
101
+ rspec-core (3.8.2)
102
+ rspec-support (~> 3.8.0)
103
+ rspec-expectations (3.8.4)
104
+ diff-lcs (>= 1.2.0, < 2.0)
105
+ rspec-support (~> 3.8.0)
106
+ rspec-mocks (3.8.1)
107
+ diff-lcs (>= 1.2.0, < 2.0)
108
+ rspec-support (~> 3.8.0)
109
+ rspec-support (3.8.2)
110
+ ruby2ruby (2.4.3)
111
+ ruby_parser (~> 3.1)
112
+ sexp_processor (~> 4.6)
113
+ ruby_parser (3.13.1)
114
+ sexp_processor (~> 4.9)
115
+ semver2 (3.4.2)
116
+ sexp_processor (4.12.1)
117
+ simplecov (0.17.0)
118
+ docile (~> 1.1)
119
+ json (>= 1.8, < 3)
120
+ simplecov-html (~> 0.10.0)
121
+ simplecov-html (0.10.2)
122
+ slop (3.6.0)
123
+ sorcerer (1.0.2)
124
+ thor (0.20.3)
125
+ thread_safe (0.3.6)
126
+ tree (0.2.1)
127
+ tzinfo (1.2.5)
128
+ thread_safe (~> 0.1)
64
129
65
130
PLATFORMS
66
131
ruby
67
132
68
133
DEPENDENCIES
69
- aruba (~> 0.4.5)
70
- debugger
71
- jeweler (~> 1.6.2)
72
- pry
73
- rcov
74
- rspec (~> 2.6.0)
75
- ruby2ruby (~> 1.2.5)
76
- ruby_parser (~> 2.0.6)
134
+ activesupport
135
+ aruba (~> 0.14.6)
136
+ jeweler
137
+ pry (~> 0.10.2)
138
+ rspec
139
+ ruby2ruby (~> 2.3)
140
+ ruby_parser
141
+ simplecov
142
+ sorcerer (~> 1.0.2)
143
+ tree
144
+
145
+ BUNDLED WITH
146
+ 1.16.1
data/README DELETED
@@ -1 +0,0 @@
1
- # Comming soon
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ [![Code Climate](https://codeclimate.com/github/theinbetweens/cauldron/badges/gpa.svg)](https://codeclimate.com/github/theinbetweens/cauldron)
2
+
3
+ [![Build Status](https://semaphoreci.com/api/v1/projects/68fafc64-3ef2-41f6-8936-d69d40e0fe2b/617362/badge.svg)](https://semaphoreci.com/theinbetweens/cauldron)
4
+
5
+ ## Usage
6
+
7
+ **Cauldron** can generate a very limited range of simple ruby functions to solve supplied problem. The following example will print the following ruby code.
8
+
9
+ ```ruby
10
+ def function(var0)
11
+ var0 + 1
12
+ end
13
+ ```
14
+
15
+ ```ruby
16
+ require 'cauldron'
17
+
18
+ pot = Cauldron::Pot.new
19
+ result = pot.solve(
20
+ [
21
+ {arguments: [7], response: 8},
22
+ {arguments: [10], response: 11}
23
+ ]
24
+ )
25
+ puts result
26
+ ```
data/README.rdoc DELETED
@@ -1,19 +0,0 @@
1
- = cauldron
2
-
3
- Description goes here.
4
-
5
- == Contributing to cauldron
6
-
7
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
- * Fork the project
10
- * Start a feature/bugfix branch
11
- * Commit and push until you are happy with your contribution
12
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2011 theinbetweens. See LICENSE.txt for
18
- further details.
19
-
data/Rakefile CHANGED
@@ -1,108 +1,33 @@
1
1
# encoding: utf-8
2
2
3
- require "bundler/gem_tasks"
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup
6
+ Bundler::GemHelper.install_tasks
4
7
5
- # require 'rubygems'
6
- # require 'bundler'
8
+ #require 'cucumber/rake/task'
9
+ #require "bundler/gem_tasks"
10
+ #require 'cucumber'
7
11
8
- # # => TODO This need to be above the bundler for some reason
9
- # require "rspec"
10
- # require "rspec/core/rake_task"
11
12
12
- # require 'lib/cauldron'
13
+ require 'rubygems'
14
+ require 'cucumber'
15
+ require 'cucumber/rake/task'
13
16
14
- # # => TODO Should check whether this needs to be here
15
- # begin
16
- # Bundler.setup(:default, :development)
17
- # rescue Bundler::BundlerError => e
18
- # $stderr.puts e.message
19
- # $stderr.puts "Run `bundle install` to install missing gems"
17
+ # Copied - https://github.com/cucumber/aruba/blob/master/Rakefile
18
+ Cucumber::Rake::Task.new do |t|
19
+ t.cucumber_opts = ""
20
+ # t.cucumber_opts = "--format Cucumber::Pro --out cucumber-pro.log" if ENV['CUCUMBER_PRO_TOKEN']
21
+ t.cucumber_opts << "--format pretty"
22
+ end
20
- # exit e.status_code
21
- # end
22
23
23
- # require 'rake'
24
+ # https://www.relishapp.com/rspec/rspec-core/docs/command-line/rake-task
25
+ begin
26
+ require 'rspec/core/rake_task'
24
27
25
- # $LOAD_PATH << File.expand_path('../lib',__FILE__)
28
+ RSpec::Core::RakeTask.new(:spec)
26
29
27
- # load File.join(File.dirname(__FILE__), 'tasks', 'theory_tasks.rake')
28
-
29
- # require 'jeweler'
30
- # Jeweler::Tasks.new do |gem|
30
+ task :default => :spec
31
+ rescue LoadError
32
+ # no rspec available
33
+ end
31
- # # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
32
- # gem.name = "cauldron"
33
- # gem.homepage = "http://github.com/theinbetweens/cauldron"
34
- # gem.license = "MIT"
35
- # gem.summary = %Q{Generate simple ruby methods from the input(s) and expected output}
36
- # gem.description = %Q{Cauldron generates a methd from a number of examples that describe the input and the expected output. It is still at a very early stage of development right now so you're unlikely to get much practical use out of it.}
37
- # gem.email = "warrensangster@yahoo.com"
38
- # gem.authors = ["Warren Sangster"]
39
- # gem.required_ruby_version = '>= 1.6.8'
40
- # # dependencies defined in Gemfile
41
- # end
42
- # Jeweler::RubygemsDotOrgTasks.new
43
-
44
- # # require 'rake/testtask'
45
- # # Rake::TestTask.new(:test) do |test|
46
- # # test.libs << 'lib' << 'test'
47
- # # test.pattern = 'test/**/test_*.rb'
48
- # # test.verbose = true
49
- # # end
50
-
51
- # # require 'rcov/rcovtask'
52
- # # Rcov::RcovTask.new do |test|
53
- # # #test.libs << 'test'
54
- # # #test.pattern = 'test/**/test_*.rb'
55
- # # test.libs << 'spec'
56
- # # test.pattern = 'spec/**/*.rb'
57
- # # test.verbose = true
58
- # # test.rcov_opts << '--exclude "gems/*"'
59
- # # end
60
-
61
- # #task :default => :test
62
-
63
- # require 'rake/rdoctask'
64
- # Rake::RDocTask.new do |rdoc|
65
- # version = File.exist?('VERSION') ? File.read('VERSION') : ""
66
-
67
- # rdoc.rdoc_dir = 'rdoc'
68
- # rdoc.title = "cauldron #{version}"
69
- # rdoc.rdoc_files.include('README*')
70
- # rdoc.rdoc_files.include('lib/**/*.rb')
71
- # end
72
-
73
- # # => http://pivotallabs.com/users/alex/blog/articles/1451-upgrading-your-rakefile-from-rspec-1-3-to-rspec-2
74
- # RSpec::Core::RakeTask.new(:core) do |spec|
75
- # spec.pattern = 'spec/cauldron/*_spec.rb'
76
- # spec.rspec_opts = ['--backtrace']
77
- # end
78
-
79
- # # RSpec::Core::RakeTask.new(:rcov) do |spec|
80
- # # spec.pattern = 'spec/cauldron/*_spec.rb'
81
- # # spec.rspec_opts = ['--backtrace']
82
- # # end
83
-
84
- # RSpec::Core::RakeTask.new(:coverage) do |t|
85
- # t.rcov = true
86
- # #t.rcov_opts = %q[--exclude "spec"]
87
- # t.verbose = true
88
- # end
89
-
90
- # # => =====================
91
-
92
- # # desc "Run all specs with rcov"
93
- # # RSpec::Core::RakeTask.new(:rcov => spec_prereq) do |t|
94
- # # t.rcov = true
95
- # # end
96
-
97
- # # => http://stackoverflow.com/questions/3058676/rcov-with-rspec-2
98
- # #require 'spec-core'
99
- # #require 'rubygems'
100
- # #require 'rspec'
101
- # #require 'rspec/core'
102
-
103
- # #
104
- # # desc "Run all specs with rcov"
105
- # # RSpec::Core::RakeTask.new(:rcov => spec_prereq) do |t|
106
- # # t.rcov = true
107
- # # t.rcov_opts = %w{--rails --exclude osx\/objc,gems\/,spec\/,features\/}
108
- # # end
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.4
data/build_sandbox.rb ADDED
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib',__FILE__)
2
+
3
+ require 'cauldron'
4
+
5
+ a = %q{
6
+ def function(var1)
7
+ var2 = var1.collect {|x| x + 2}
8
+ var3 = var2.collect {|x| x.to_s }
9
+ end
10
+ }
11
+
12
+ b = %q{
13
+ def function2(var1)
14
+ var2 = var1.collect do |x|
15
+ x + 2
16
+ end
17
+ var3 = var2.collect do |x|
18
+ x.to_s
19
+ end
20
+ end
21
+ }
22
+
23
+
24
+
25
+ sexp = Ripper::SexpBuilder.new(b).parse
26
+
27
+
28
+ [
29
+ [ArrayCollect.new, NumericOperator.new(2)]
30
+ [ArrayCollect.new, ToSOperator.new]
31
+ ]
32
+
33
+
34
+ a.build('var1')
35
+
36
+ build(name, next_element)
37
+ [:sdf, :dsfsd, next_element.build('x')]
38
+ end
39
+ a.build('var2')
40
+
41
+ build()
data/cucumber.yml ADDED
@@ -0,0 +1,8 @@
1
+ # config/cucumber.yml
2
+ ##YAML Template
3
+ # https://github.com/cucumber/cucumber/wiki/cucumber.yml
4
+ ---
5
+ default: --profile main
6
+ html_report: --format progress --format html --out=features_report.html
7
+ bvt: --tags @bvt
8
+ main: --tags ~@wip
data/features/cauldron_new_approach.feature DELETED
@@ -1,46 +0,0 @@
1
- Feature: Cauldron generates single parameter methods
2
-
3
- Cauldron can generate runtime methods that accepts one parameters
4
-
5
- NOTE: it creates the file in tmp/aruba/launch.rb - so that loading path needs to be changed
6
- - use @pause to see if it's working.
7
-
8
- #TODO Change the method name
9
-
10
- @announce @slow_process
11
- Scenario: Method returns the passed in value
12
- Given a theory named "example_1.yml" with:
13
- """
14
- dependents:
15
- -
16
- "if RUNTIME_METHOD.kind_of?(RuntimeMethod)
17
- return true
18
- end"
19
- -
20
- "if ARG_1 == OUTPUT
21
- return true
22
- end"
23
- action:
24
- statement: "return x"
25
- values:
26
- x: ARG_1
27
- position: RUNTIME_METHOD.first.statement_id
28
- results:
29
- -
30
- RUNTIME_METHOD.all_pass(ARG_1)
31
- """
32
- And a file named "launch.rb" with:
33
- """
34
- $LOAD_PATH.unshift File.expand_path( File.join('lib') )
35
- require 'cauldron'
36
- cauldron = Cauldron::Pot.new
37
- cauldron.load_theory File.join('theories','example_1.yml')
38
- cauldron.generate [["sparky","sparky"],["kel","kel"]]
39
- """
40
- When I run `ruby launch.rb` interactively
41
- Then the output should contain:
42
- """
43
- def extend_function_test_method(var1)
44
- return var1
45
- end
46
- """
data/features/chop.feature ADDED
@@ -0,0 +1,23 @@
1
+ Feature: Using String#chop
2
+
3
+ Scenario: Using chop example
4
+ Given I'm using the chop example
5
+ When I generate a solution
6
+ Then the solution should include:
7
+ """
8
+ def function(var0)
9
+ var1 = var0.collect do |var2|
10
+ var2.chop
11
+ end
12
+ end
13
+ """
14
+
15
+ Scenario: Using simple chop example
16
+ Given I'm using the simple chop example
17
+ When I generate a solution
18
+ Then the solution should include:
19
+ """
20
+ def function(var0)
21
+ var0.chop
22
+ end
23
+ """
data/features/create_dynamic_statements.feature ADDED
@@ -0,0 +1,14 @@
1
+ Feature: Using dynamic method
2
+
3
+ @wip
4
+ Scenario: Using the collect and + 7 example
5
+ Given I'm using the collect and plus 7 example
6
+ When I generate a solution
7
+ Then the solution should include:
8
+ """
9
+ def function(var0)
10
+ var1 = var0.collect do |var2|
11
+ var2 + 7
12
+ end
13
+ end
14
+ """
data/features/generate_known_solution.feature ADDED
@@ -0,0 +1,42 @@
1
+ Feature: Cauldron generates single parameter methods
2
+
3
+ Cauldron can generate runtime methods that accepts one parameters
4
+
5
+ @announce @slow_process
6
+ Scenario: Chop the last character off a string
7
+ Given a file named "launch.rb" with:
8
+ """
9
+ $LOAD_PATH.unshift File.expand_path( File.join(['..','..','lib']) )
10
+ require 'cauldron'
11
+ cauldron = Cauldron::Pot.new
12
+ puts cauldron.solve [{arguments: ['Sparky'], response: 'Spark'}, {arguments: ['Kel'], response: 'Ke'}]
13
+ """
14
+ When I run `ruby launch.rb` interactively
15
+ Then the output should contain:
16
+ """
17
+ def function(var0)
18
+ var0.chop
19
+ end
20
+ """
21
+
22
+ @announce @slow_process @wip
23
+ Scenario: Chop the last character off a string
24
+ Given a file named "launch.rb" with:
25
+ """
26
+ $LOAD_PATH.unshift File.expand_path( File.join('lib') )
27
+ require 'cauldron'
28
+ cauldron = Cauldron::Pot.new
29
+ puts cauldron.solve [
30
+ {arguments: [['Sparky', 'Kels']], response: ['Spark', 'Kel']},
31
+ {arguments: [['Pip','Rowe']], response: ['Pi','Row']}
32
+ ]
33
+ """
34
+ When I run `ruby launch.rb` interactively
35
+ Then the output should contain:
36
+ """
37
+ def function(var0)
38
+ var2 = var0.collect do |var1|
39
+ var1.chop
40
+ end
41
+ end
42
+ """
data/features/generate_new_statement.feature ADDED
@@ -0,0 +1,20 @@
1
+ Feature: Cauldron generates single parameter methods
2
+
3
+ Cauldron can generate runtime methods that accepts one parameters
4
+
5
+ @announce @slow_process @wip
6
+ Scenario: Using statements that require a constant
7
+ Given a file named "launch.rb" with:
8
+ """
9
+ $LOAD_PATH.unshift File.expand_path( File.join('lib') )
10
+ require 'cauldron'
11
+ cauldron = Cauldron::Pot.new
12
+ puts cauldron.solve [{arguments: [8], response: 4}, {arguments: [12], response: 8}]
13
+ """
14
+ When I run `ruby launch.rb` interactively
15
+ Then the output should contain:
16
+ """
17
+ def function(var0)
18
+ var0 - 4
19
+ end
20
+ """
data/features/step_definitions/cauldron_steps.rb CHANGED
@@ -28,4 +28,51 @@ Then /^I should receive a runtime method like this "([^"]*)"#x2F; do |runtime_metho
28
28
output.messages.should include(runtime_method_statement)
29
29
end
30
30
31
+ Given(/^I'm using the chop example#x2F;) do
32
+ #pending # Write code here that turns the phrase above into concrete actions
33
+ @pot = Cauldron::Pot.new
34
+ @examples = [
35
+ {arguments: [['Sparky', 'Kels']], response: ['Spark', 'Kel']},
36
+ {arguments: [['Pip','Rowe']], response: ['Pi','Row']}
37
+ ]
38
+ end
39
+
40
+ Given(/^I'm using the simple chop example#x2F;) do
41
+ @pot = Cauldron::Pot.new
42
+ @examples = [
43
+ {arguments: ['Andy'], response: 'And'},
44
+ {arguments: ['Kels'], response: 'Kel'}
45
+ ]
46
+ end
47
+
48
+ Given(/^I'm using the reverse example#x2F;) do
49
+ @pot = Cauldron::Pot.new
50
+ @examples = [
51
+ {arguments: [['Sparky', 'Kels']], response: ['Kels', 'Sparky']}
52
+ ]
53
+ end
54
+
55
+ Given(/^I'm using the collect and \+ (\d+) example#x2F;) do |arg1|
56
+ @pot = Cauldron::Pot.new
57
+ @examples = [
58
+ {arguments: [[5,7]], response: [10, 12]},
59
+ {arguments: [[9,15]], response: [14, 20]}
60
+ ]
61
+ end
62
+
63
+ Given(/^I'm using the collect and plus (\d+) example#x2F;) do |arg1|
64
+ @pot = Cauldron::Pot.new
65
+ @examples = [
66
+ {arguments: [[5,7]], response: [12, 14]},
67
+ {arguments: [[9,15]], response: [16, 22]}
68
+ ]
69
+ end
70
+
71
+ When(/^I generate a solution#x2F;) do
72
+ @solution = @pot.solve @examples
73
+ end
74
+
75
+ Then(/^the solution should include:#x2F;) do |string|
76
+ @solution.should include(string)
77
+ end
31
78
data/features/support/env.rb CHANGED
@@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path('../../../lib',__FILE__)
2
2
3
3
#require 'ruby-debug'
4
4
require 'cauldron'
5
-
5
+ require 'pry'
6
6
require 'aruba/cucumber'
7
7
# See https://github.com/cucumber/aruba/blob/master/lib/aruba/cucumber.rb for the available steps
8
8
data/features/use_existing_statements.feature ADDED
@@ -0,0 +1,23 @@
1
+ Feature: Using Array#reverse
2
+
3
+ Scenario: Using chop example
4
+ Given I'm using the reverse example
5
+ When I generate a solution
6
+ Then the solution should include:
7
+ """
8
+ def function(var0)
9
+ var0.reverse
10
+ end
11
+ """
12
+
13
+ Scenario: Using the collect and + 5 example
14
+ Given I'm using the collect and + 5 example
15
+ When I generate a solution
16
+ Then the solution should include:
17
+ """
18
+ def function(var0)
19
+ var1 = var0.collect do |var2|
20
+ var2 + 5
21
+ end
22
+ end
23
+ """
data/lib/cauldron.rb CHANGED
@@ -5,14 +5,50 @@ require 'logger'
5
5
require 'yaml'
6
6
require 'ruby2ruby'
7
7
require 'ruby_parser'
8
- #require 'ruby-debug'
8
+ require 'sorcerer'
9
+
10
+ require 'pry_tester'
11
+ require 'pry'
12
+
13
+ # http://stackoverflow.com/questions/18732338/trying-to-require-active-support-in-gem
14
+ require "active_support/all"
15
+
16
+ require 'tree'
9
17
10
18
require 'core/string'
11
19
12
20
require 'cauldron/pot'
21
+ require 'cauldron/caret'
22
+ require 'cauldron/example'
23
+ require 'cauldron/example_set'
13
24
require 'cauldron/terminal'
14
- require 'cauldron/relationship'
25
+ require 'cauldron/scope'
26
+ require 'cauldron/histories'
27
+ require 'cauldron/history'
28
+ require 'cauldron/tracer'
29
+ require 'cauldron/operator'
30
+ require 'cauldron/statement_generator'
31
+ require 'cauldron/dynamic_operator'
32
+ require 'cauldron/operator/numeric_operator'
33
+ require 'cauldron/operator/concat_operator'
34
+ require 'cauldron/operator/array_reverse_operator'
35
+ require 'cauldron/operator/hash_key_value_operator'
36
+ require 'cauldron/operator/string_asterisk_operator'
37
+ require 'cauldron/operator/to_s_operator'
38
+ require 'cauldron/operator/var_collect_operator'
15
39
require 'cauldron/if_relationship'
16
- require 'cauldron/numeric_operator'
17
- require 'cauldron/concat_operator'
18
- require 'cauldron/array_reverse_operator'
40
+
41
+ require 'cauldron/template_base'
42
+
43
+ require 'cauldron/array_collect_template/default'
44
+ require 'cauldron/array_collect_template/template'
45
+
46
+ require 'cauldron/number_addition_template/template'
47
+ require 'cauldron/number_addition_template/add_five'
48
+
49
+ require 'cauldron/dynamic_operator_module'
50
+
51
+ require 'cauldron/solution/one'
52
+ require 'cauldron/actualized_composite'
53
+ require 'cauldron/solution/composite'
54
+ require 'cauldron/builder'
data/lib/cauldron/actualized_composite.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Cauldron
2
+
3
+ class ActualizedComposite
4
+
5
+ attr_reader :examples, :composite
6
+
7
+ # Might include the insert point here too
8
+ def initialize(composite,examples)
9
+ @composite, @examples = composite, examples
10
+ end
11
+
12
+ def histories
13
+ results = @examples.collect do |example|
14
+ @composite.record(example)
15
+ end
16
+ Cauldron::Histories.new(results)
17
+ end
18
+
19
+ def extend_solution
20
+ builder = Builder.new(composite)
21
+ builder.insertable_operators(examples)
22
+ end
23
+
24
+ def solution?(problems)
25
+ # TODO Should not need to
26
+ composite.solution?(examples)
27
+ end
28
+
29
+ def to_ruby
30
+ composite.to_ruby(examples.scope)
31
+ end
32
+
33
+ end
34
+
35
+ end
data/lib/cauldron/array_collect_template/default.rb ADDED
@@ -0,0 +1,95 @@
1
+ module Cauldron
2
+
3
+ module ArrayCollectTemplate
4
+
5
+ class Default
6
+
7
+ attr_reader :indexes
8
+
9
+ def initialize(indexes)
10
+ @indexes = indexes
11
+ end
12
+
13
+ def context_realizable?(context)
14
+
15
+ vars = context.keys.select {|x| x.match(/var\d/) }
16
+ var_names = vars.collect(&:to_s)
17
+
18
+ first_variable = 'var'+@indexes[0].to_s
19
+
20
+ a = %Q{
21
+ def function(#{first_variable})
22
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
23
+ end
24
+ }
25
+
26
+ o = Object.new
27
+ o.instance_eval(a)
28
+
29
+ begin
30
+ #o.function(*vars.collect {|x| context[x] })
31
+ o.function context[first_variable.to_sym]
32
+ rescue NoMethodError => e
33
+ return false
34
+ rescue StandardError => e
35
+ puts e
36
+ return false
37
+ end
38
+ return true
39
+
40
+ end
41
+
42
+ def to_sexp(scope, children)
43
+ scope_var = scope.new_variable!
44
+ first_variable = 'var'+@indexes[0].to_s
45
+ [:method_add_block,
46
+ [:call,
47
+ [:vcall,
48
+ # [:@ident, scope[@indexes[0]] ]],
49
+ [:@ident, first_variable ]],
50
+ :".",
51
+ [:@ident, "collect"]
52
+ ],
53
+ unless children.empty?
54
+ [:brace_block,
55
+ [:block_var,
56
+ [:params, [[:@ident, scope_var]]]],
57
+ [
58
+ :stmts_add,
59
+ [:stmts_new],
60
+ # TODO Shouild probably be passing the children through here
61
+ children.first.content.to_sexp(scope, [])
62
+ ]
63
+ ]
64
+ else
65
+ [:brace_block,
66
+ [:block_var,
67
+ [:params, [[:@ident, scope_var]]],
68
+ [:stmts_add, [:stmts_new]]
69
+ ]
70
+ ]
71
+ end
72
+ ]
73
+ end
74
+
75
+ def to_ruby(scope, operators)
76
+ Sorcerer.source to_sexp(scope, operators)
77
+ end
78
+
79
+ def clone_statement
80
+ # TODO Need to clone the sexp methods
81
+ # o = DynamicOperator.new(@information, @sexp_methods)
82
+ # o.instance_eval(Sorcerer.source(@sexp_methods, indent: true))
83
+ # o
84
+ self.class.new(@indexes.clone)
85
+ end
86
+
87
+ def branch?
88
+ true
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
data/lib/cauldron/array_collect_template/template.rb ADDED
@@ -0,0 +1,57 @@
1
+ module Cauldron
2
+
3
+ module ArrayCollectTemplate
4
+
5
+ class Template < Cauldron::TemplateBase
6
+
7
+ def self.instances(histories, composite, examples, insert_points)
8
+
9
+ # TEMP
10
+ unless examples.class == ExampleSet
11
+ raise StandardError.new('Examples should be an example')
12
+ end
13
+
14
+ # Print out each insertable statements
15
+ scope = examples.scope
16
+
17
+ # self.init([0]).to_ruby(scope)
18
+ # - this will print out "var0.chop"
19
+
20
+ # Get the variables available at each point
21
+ results = []
22
+
23
+ insert_points.each do |point|
24
+
25
+ # Find the variables at a particular point
26
+ # TODO Change to test
27
+ contexts = histories.contexts_at(point)
28
+ composites = context_instances(contexts)
29
+
30
+ composites.each do |x|
31
+ if contexts.all? { |context| x.context_realizable?(context) }
32
+ #binding.pry
33
+ results << extend_actualized_composite(x, composite, examples, point)
34
+ end
35
+ #results << extend_actualized_composite(x, composite, examples, point)
36
+ end
37
+ end
38
+
39
+ results
40
+ end
41
+
42
+ def self.context_instances(contexts)
43
+ temp = []
44
+ contexts.each do |context|
45
+ temp << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
46
+ end
47
+ results = temp.flatten.uniq
48
+
49
+ variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
50
+ variable_numbers.collect { |x| Cauldron::ArrayCollectTemplate::Default.new([x.to_i])}
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
data/lib/cauldron/array_reverse_operator.rb DELETED
@@ -1,39 +0,0 @@
1
- class ArrayReverseOperator
2
-
3
- #Arra
4
-
5
- def initialize
6
- end
7
-
8
- # Matching in
9
- def successful?(input,output)
10
- # NOTE - for the future - like the idea of not actually calling the method
11
- # input.length.each do |i|
12
- # does input[0] == output[input.length-0]
13
- # does input[1] == output[input.length-1]
14
- # does input[3] == output[input.length-3]
15
- # end
16
-
17
- # in this case x.reverse will work
18
- return true if input.first.reverse == output
19
- false
20
- end
21
-
22
- # 1. Only has one argument value
23
- # 2. Argument is an array value
24
- # 3. Response is an array
25
- def self.viable?(arguments,output)
26
- return false unless arguments.length == 1
27
- return false unless arguments.all? { |x| x.kind_of?(Array) }
28
- return false unless output.kind_of?(Array)
29
- true
30
- end
31
-
32
- def self.find_constants(problems)
33
- []
34
- end
35
-
36
- # def describe -
37
- # should be able to describe the x.object_id moving to the different locations
38
-
39
- end
data/lib/cauldron/builder.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Cauldron
2
+
3
+ class Builder
4
+
5
+ attr_reader :composite
6
+
7
+ def initialize(composite)
8
+ @composite = composite
9
+ end
10
+
11
+ def tree
12
+ root_node = Tree::TreeNode.new("ROOT", "Root Content")
13
+ line_count = 0
14
+ composite.operators.each do |x|
15
+ child_node = Tree::TreeNode.new('CHILD-'+line_count.to_s)
16
+ root_node << child_node
17
+ line_count += 1
18
+ end
19
+ end
20
+
21
+ # NOTE: returns an array of new actualized composites extending the current composite
22
+ def insertable_operators(examples)
23
+ actualized_composite = ActualizedComposite.new(composite.clone_solution, examples)
24
+ h = actualized_composite.histories
25
+ results = self.class.available_statement_types.inject([]) do |total,x|
26
+ total += x.instances(h, composite, examples, h.insert_points)
27
+ total
28
+ end
29
+ #binding.pry
30
+ results
31
+ end
32
+
33
+ def self.available_statement_types
34
+
35
+ # CURRENT
36
+ # StatementGenerator.new.build(
37
+ # ['A','B', 'AC'],
38
+ # [:collect]
39
+ # )+Cauldron::StatementGenerator.new.build('string',[:chop])
40
+
41
+ # TRYING
42
+ # StatementGenerator.new.build(
43
+ # ['A','B', 'AC'],
44
+ # [:collect]
45
+ # )+Cauldron::StatementGenerator.new.build('string',[:chop])+[Cauldron::ArrayCollectTemplate::Template]
46
+ [StatementGenerator.new.build_template(
47
+ ['A','B', 'AC'],
48
+ :collect
49
+ ).statement_classes.first]+[Cauldron::StatementGenerator.new.build_template('string',:chop).statement_classes.first]+[Cauldron::ArrayCollectTemplate::Template]+[Cauldron::NumberAdditionTemplate::Template]
50
+
51
+ # TODO Not very effient to regenerate the opperators
52
+ # StatementGenerator.new.build(
53
+ # ['A','B', 'AC'],
54
+ # [:collect]
55
+ # )+Cauldron::StatementGenerator.new.build('string',[:chop])+[ArrayReverseOperator]+[StatementGenerator.new.build(3,[:+])]
56
+ end
57
+
58
+ end
59
+
60
+ end
data/lib/cauldron/caret.rb ADDED
@@ -0,0 +1,50 @@
1
+ module Cauldron
2
+
3
+ class Caret
4
+
5
+ #attr_reader :line, :depth, :total_line
6
+ attr_reader :total_lines, :current_depth
7
+
8
+ def initialize
9
+ @total_lines, @current_depth = 0, 0 #,@line = 0, 0, 0
10
+ @lines = { 0 => 0}
11
+ end
12
+
13
+ # TODO This approach will need re-worked to support nesting - in and out
14
+ def add_line(depth)
15
+ unless @lines.has_key?(depth)
16
+ @lines[depth] = 0
17
+ end
18
+
19
+ @total_lines += 1
20
+ if @current_depth != depth
21
+ @current_depth = depth
22
+ else
23
+ @lines[depth] += 1
24
+ end
25
+
26
+ end
27
+
28
+ def point
29
+
30
+ end
31
+
32
+ def line
33
+ @lines[@current_depth]
34
+ end
35
+
36
+ def step_in
37
+ @current_depth += 1
38
+ unless @lines.has_key?(@current_depth)
39
+ @lines[@current_depth] = 0
40
+ end
41
+ #@line = 0
42
+ end
43
+
44
+ def return_depth(depth)
45
+ @current_depth = depth
46
+ end
47
+
48
+ end
49
+
50
+ end
data/lib/cauldron/concat_operator.rb DELETED
@@ -1,34 +0,0 @@
1
- class ConcatOperator
2
-
3
- def initialize(constant)
4
- @constant = constant
5
- end
6
-
7
- def self.viable?(arguments, response)
8
- return false unless arguments.all? { |x| x.kind_of?(String) }
9
- return false unless response.kind_of?(String)
10
- # TODO - Only accpets one argument
11
- true
12
- end
13
-
14
- def self.find_constants(problems)
15
- problems.inject([]) do |total, x|
16
- result = x[:response].gsub( Regexp.new('^'+x[:arguments].first),'')
17
- total << result unless result == x[:response]
18
- total
19
- end.uniq
20
- end
21
-
22
- # Operator for "x.concat("bar")"
23
- def successful?(problem)
24
- if (problem[:arguments].first + @constant) == problem[:response]
25
- return true
26
- end
27
- return false
28
- end
29
-
30
- def to_ruby
31
- ' var0.concat(\''+@constant.to_s+'\')'+"\n"
32
- end
33
-
34
- end
data/lib/cauldron/dynamic_operator.rb ADDED
@@ -0,0 +1,90 @@
1
+ module Cauldron
2
+
3
+ class DynamicOperator
4
+
5
+ include Cauldron::Operator
6
+
7
+ attr_reader :indexes
8
+ attr_accessor :failed_uses
9
+
10
+ def initialize(information, sexp_methods)
11
+ @information, @sexp_methods = information, sexp_methods
12
+ @failed_uses = []
13
+ @closed = false
14
+ end
15
+
16
+ def uses_constants?
17
+ @information[:constants]
18
+ end
19
+
20
+ def indexes=(value)
21
+ raise StandardError.new('') if @closed
22
+ @indexes = value
23
+ end
24
+
25
+ def close
26
+ @closed = true
27
+ end
28
+
29
+ def extend_actualized_composite(x, container, examples, point)
30
+ cloned_container = container.clone_solution
31
+ cloned_container.add_statement_at(x, point)
32
+ cloned_container
33
+ Cauldron::ActualizedComposite.new(cloned_container, examples)
34
+ end
35
+
36
+ def context_realizable?(context)
37
+
38
+ vars = context.keys.select {|x| x.match(/var\d/) }
39
+ var_names = vars.collect(&:to_s)
40
+
41
+ a = %Q{
42
+ def function(var0)
43
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
44
+ end
45
+ }
46
+
47
+ o = Object.new
48
+ o.instance_eval(a)
49
+
50
+ begin
51
+ o.function(vars.collect {|x| context[x] })
52
+ rescue NoMethodError => e
53
+ return false
54
+ rescue StandardError => e
55
+ puts e
56
+ end
57
+ return true
58
+
59
+ end
60
+
61
+ def write_to_file(filename)
62
+ FileUtils.mkdir_p File.join('tmp')
63
+ File.open( File.join('tmp',filename), 'w+') do |file|
64
+ file << "class DynamicOperator"+"\n"
65
+ file << Sorcerer.source(@sexp_methods, indent: true)
66
+ file << "\n"
67
+ file << "end"
68
+ end
69
+ end
70
+
71
+ def rip2
72
+ %Q{
73
+ def function(var0)
74
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(['var0'])), indent: true)}
75
+ end
76
+ }
77
+ end
78
+
79
+ def rip(composite,examples)
80
+ Ripper::SexpBuilder.new(
81
+ %Q{
82
+ def function(var0)
83
+ #{composite.to_ruby(examples.scope)}
84
+ end
85
+ }).parse
86
+ end
87
+
88
+ end
89
+
90
+ end
data/lib/cauldron/dynamic_operator_module.rb ADDED
@@ -0,0 +1,140 @@
1
+ module Cauldron
2
+
3
+ module DynamicOperatorModule
4
+
5
+ def uses_constants?
6
+ @information[:constants]
7
+ end
8
+
9
+ def indexes=(value)
10
+ raise StandardError.new('') if @closed
11
+ @indexes = value
12
+ end
13
+
14
+ def close
15
+ @closed = true
16
+ end
17
+
18
+ def clone_statement
19
+ # TODO Need to clone the sexp methods
20
+ # o = DynamicOperator.new(@information, @sexp_methods)
21
+ # o.instance_eval(Sorcerer.source(@sexp_methods, indent: true))
22
+ # o
23
+ self.class.new(@indexes.clone)
24
+ end
25
+
26
+ # def context_instances(contexts)
27
+ # results = []
28
+ # contexts.each do |context|
29
+ # results << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
30
+ # end
31
+ # results = results.flatten.uniq
32
+ # variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
33
+ # variable_numbers.collect { |x| init([x.to_i])}
34
+ # end
35
+
36
+ # def extend_actualized_composite(x, container, examples, point)
37
+ # cloned_container = container.clone_solution
38
+ # cloned_container.add_statement_at(x, point)
39
+ # cloned_container
40
+ # Cauldron::ActualizedComposite.new(cloned_container, examples)
41
+ # end
42
+
43
+ def context_realizable?(context)
44
+
45
+ vars = context.keys.select {|x| x.match(/var\d/) }
46
+ var_names = vars.collect(&:to_s)
47
+
48
+ first_variable = 'var'+@indexes[0].to_s
49
+
50
+ # a = %Q{
51
+ # def function(var0)
52
+ # #{Sorcerer.source(to_sexp(var_names), indent: true)}
53
+ # end
54
+ # }
55
+ # a = %Q{
56
+ # def function(var0)
57
+ # #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
58
+ # end
59
+ # }
60
+ a = %Q{
61
+ def function(#{first_variable})
62
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
63
+ end
64
+ }
65
+
66
+ o = Object.new
67
+ o.instance_eval(a)
68
+
69
+ begin
70
+ #o.function(*vars.collect {|x| context[x] })
71
+ o.function context[first_variable.to_sym]
72
+ rescue NoMethodError => e
73
+ return false
74
+ rescue StandardError => e
75
+ puts e
76
+ return false
77
+ end
78
+ return true
79
+
80
+
81
+
82
+ #o.function(*params.values)
83
+
84
+ # a = %Q{
85
+ # def function(var0)
86
+ # #{Sorcerer.source(to_sexp(Cauldron::Scope.new(['var0'])), indent: true)}
87
+ # end
88
+ # }
89
+ end
90
+
91
+ def write_to_file(filename)
92
+ File.open( File.join('tmp',filename), 'w+') do |file|
93
+ file << "class DynamicOperator"+"\n"
94
+ file << Sorcerer.source(@sexp_methods, indent: true)
95
+ file << "\n"
96
+ file << "end"
97
+ end
98
+ end
99
+
100
+ def rip(composite,examples)
101
+ Ripper::SexpBuilder.new(
102
+ %Q{
103
+ def function(var0)
104
+ #{composite.to_ruby(examples.scope)}
105
+ end
106
+ }).parse
107
+ end
108
+
109
+ # def to_tracking_sexp(operators, scope, caret)
110
+ # raise StandardError.new('statement has been instance closed') unless @closed
111
+ # to_sexp(scope)
112
+ # end
113
+
114
+ def realizable?(histories, point)
115
+ parameters = histories.variable_permutations(@indexes.length)
116
+ parameters.each do |params|
117
+ begin
118
+ realize(params)
119
+ rescue => e
120
+ puts e
121
+ failed_uses.push(histories)
122
+ return false
123
+ end
124
+ end
125
+ true
126
+ rescue => e
127
+ puts e
128
+ puts e.backtrace
129
+ # TODO GENERATE RSPEC TEST with arguments
130
+ end
131
+
132
+ def realize(params)
133
+ o = Object.new
134
+ o.instance_eval(rip2)
135
+ o.function(*params.values)
136
+ end
137
+
138
+ end
139
+
140
+ end
data/lib/cauldron/example.rb ADDED
@@ -0,0 +1,18 @@
1
+ module Cauldron
2
+
3
+ class Example
4
+
5
+ attr_reader :arguments, :response
6
+
7
+ def initialize(hash)
8
+ @arguments, @response = hash[:arguments], hash[:response]
9
+ @arguments.freeze
10
+ end
11
+
12
+ def params
13
+ (0...@arguments.length).collect {|x| 'var'+x.to_s}
14
+ end
15
+
16
+ end
17
+
18
+ end
data/lib/cauldron/example_set.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Cauldron
2
+
3
+ class ExampleSet
4
+
5
+ attr_reader :examples
6
+
7
+ def initialize(examples)
8
+ @examples = examples
9
+ end
10
+
11
+ def variables
12
+ args = examples.first.arguments
13
+ (0...args.length).collect {|x| 'var'+x.to_s}
14
+ end
15
+
16
+ def all?(&block)
17
+ examples.all?(&block)
18
+ end
19
+
20
+ def collect(&block)
21
+ examples.collect(&block)
22
+ end
23
+
24
+ # TODO Might drop - limit access
25
+ def each_with_index(&block)
26
+ examples.each_with_index(&block)
27
+ end
28
+
29
+ def scope
30
+ sexp = Ripper::SexpBuilder.new(examples.first.params.to_s).parse
31
+ Cauldron::Scope.new(eval(Sorcerer.source(sexp)))
32
+ end
33
+
34
+ end
35
+
36
+ end
data/lib/cauldron/histories.rb ADDED
@@ -0,0 +1,53 @@
1
+ module Cauldron
2
+
3
+ class Histories
4
+
5
+ def initialize(results)
6
+ @results = results
7
+ end
8
+
9
+ def variable_permutations(count)
10
+ variables = @results.first.logs.first.keys.select { |x| x.match /var/ }
11
+ v = Hash[*variables.collect {|x| [x,nil]}.flatten]
12
+
13
+ @results.collect do |history|
14
+ history.logs.collect do |a|
15
+ Hash[*v.keys.collect do |x|
16
+ [x, a[x] ]
17
+ end.flatten(1)]
18
+ end
19
+ end.flatten
20
+ end
21
+
22
+ def each(&block)
23
+ @results.each(&block)
24
+ end
25
+
26
+ def first
27
+ @results.first
28
+ end
29
+
30
+ def length
31
+ @results.length
32
+ end
33
+
34
+ def insert_points
35
+ @results.inject([]) { |total, x| total += x.insert_points; total }.uniq
36
+ end
37
+
38
+ def contexts_at(point)
39
+ a = []
40
+ @results.each do |history|
41
+ a += history.logs.inject([]) do |total,log|
42
+ if log[:point] == point
43
+ total << log
44
+ end
45
+ total
46
+ end
47
+ end
48
+ a
49
+ end
50
+
51
+ end
52
+
53
+ end
data/lib/cauldron/history.rb ADDED
@@ -0,0 +1,34 @@
1
+ module Cauldron
2
+
3
+ class History
4
+
5
+ attr_reader :logs
6
+
7
+ def initialize(logs)
8
+ @logs = logs
9
+ end
10
+
11
+ def variables
12
+ results = []
13
+ @logs.select do |line|
14
+ results += line.keys.select {|x| x.match(/var*/) }
15
+ end
16
+ results
17
+ end
18
+
19
+ def values(variable_name)
20
+ @logs.inject([]) do |total,line|
21
+ if line.has_key?(variable_name)
22
+ total << line[variable_name]
23
+ end
24
+ total
25
+ end
26
+ end
27
+
28
+ def insert_points
29
+ logs.collect {|x| x[:point] }.uniq
30
+ end
31
+
32
+ end
33
+
34
+ end
data/lib/cauldron/if_relationship.rb CHANGED
@@ -1,4 +1,4 @@
1
- class IfRelationship < Relationship
1
+ class IfRelationship #< Relationship
2
2
3
3
# TODO Need to change to IfSolution probably
4
4
@@ -6,15 +6,13 @@ class IfRelationship < Relationship
6
6
@problems = problems
7
7
end
8
8
9
- def to_ruby
9
+ def to_ruby(variables)
10
10
11
11
# Add the arguments
12
- args = @problems.first[:arguments]
13
- variables = (0...args.length).collect {|x| 'var'+x.to_s}
14
12
result = ''
15
13
@problems.each_with_index do |x,i|
16
- result += ' if '+variables[0].to_s+' == '+quote(x[:arguments][0])+"\n"
17
- result += ' return '+quote(x[:response])+"\n"
14
+ result += ' if '+variables[0].to_s+' == '+quote(x.arguments[0])+"\n"
15
+ result += ' return '+quote(x.response)+"\n"
18
16
result += ' end'+"\n"
19
17
end
20
18
result
data/lib/cauldron/number_addition_template/add_five.rb ADDED
@@ -0,0 +1,71 @@
1
+ module Cauldron
2
+
3
+ module NumberAdditionTemplate
4
+
5
+ class AddFive
6
+
7
+ attr_reader :indexes
8
+
9
+ def initialize(indexes)
10
+ @indexes = indexes
11
+ end
12
+
13
+ def context_realizable?(context)
14
+
15
+ vars = context.keys.select {|x| x.match(/var\d/) }
16
+ var_names = vars.collect(&:to_s)
17
+
18
+ first_variable = 'var'+@indexes[0].to_s
19
+
20
+ a = %Q{
21
+ def function(#{first_variable})
22
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
23
+ end
24
+ }
25
+
26
+ o = Object.new
27
+ o.instance_eval(a)
28
+
29
+ begin
30
+ #o.function(*vars.collect {|x| context[x] })
31
+ o.function context[first_variable.to_sym]
32
+ rescue NoMethodError => e
33
+ return false
34
+ rescue StandardError => e
35
+ puts e
36
+ return false
37
+ end
38
+ return true
39
+
40
+ end
41
+
42
+ def to_sexp(scope, children)
43
+ first_variable = 'var'+@indexes[0].to_s
44
+ #binding.pry
45
+ #'var0 + 5'
46
+ #''
47
+ puts %Q{#{first_variable} + 5}
48
+ Ripper::SexpBuilder.new(%Q{#{first_variable} + 5}).parse
49
+ end
50
+
51
+ def clone_statement
52
+ # TODO Need to clone the sexp methods
53
+ # o = DynamicOperator.new(@information, @sexp_methods)
54
+ # o.instance_eval(Sorcerer.source(@sexp_methods, indent: true))
55
+ # o
56
+ self.class.new(@indexes.clone)
57
+ end
58
+
59
+ def to_ruby(scope, operators)
60
+ Sorcerer.source to_sexp(scope, operators)
61
+ end
62
+
63
+ def branch?
64
+ false
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ end
data/lib/cauldron/number_addition_template/template.rb ADDED
@@ -0,0 +1,56 @@
1
+ module Cauldron
2
+
3
+ module NumberAdditionTemplate
4
+
5
+ class Template < Cauldron::TemplateBase
6
+
7
+ def self.instances(histories, composite, examples, insert_points)
8
+
9
+ # TEMP
10
+ unless examples.class == ExampleSet
11
+ raise StandardError.new('Examples should be an example')
12
+ end
13
+
14
+ # Print out each insertable statements
15
+ scope = examples.scope
16
+
17
+ # self.init([0]).to_ruby(scope)
18
+ # - this will print out "var0.chop"
19
+
20
+ # Get the variables available at each point
21
+ results = []
22
+
23
+ insert_points.each do |point|
24
+
25
+ # Find the variables at a particular point
26
+ # TODO Change to test
27
+ contexts = histories.contexts_at(point)
28
+ composites = context_instances(contexts)
29
+
30
+ composites.each do |x|
31
+ if contexts.all? { |context| x.context_realizable?(context) }
32
+ results << extend_actualized_composite(x, composite, examples, point)
33
+ end
34
+ #results << extend_actualized_composite(x, composite, examples, point)
35
+ end
36
+ end
37
+
38
+ results
39
+ end
40
+
41
+ def self.context_instances(contexts)
42
+ temp = []
43
+ contexts.each do |context|
44
+ temp << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
45
+ end
46
+ results = temp.flatten.uniq
47
+
48
+ variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
49
+ variable_numbers.collect { |x| Cauldron::NumberAdditionTemplate::AddFive.new([x.to_i])}
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
data/lib/cauldron/numeric_operator.rb DELETED
@@ -1,45 +0,0 @@
1
- class NumericOperator
2
-
3
- # Maybe NumericOperation
4
-
5
- def initialize(constant)
6
- @constant = constant
7
- end
8
-
9
- # Is the problem suitable for a numeric operatio?
10
- # e.g. can the .find_contants call be called without error
11
- def self.viable?(arguments,output)
12
-
13
- # 1. Only has one argument value
14
- # 2. Argument is a numeric value
15
- # 3. Response is numeric
16
-
17
- # TODO Need to save these viablility tests in shared (easily comparable) state.
18
- # e.g. so all viable operations can be found in one go.
19
-
20
- return false unless arguments.all? { |x| x.kind_of?(Numeric) }
21
- return false unless output.kind_of?(Numeric)
22
- true
23
-
24
- end
25
-
26
- def to_ruby
27
- ' var0 + '+@constant.to_s+"\n"
28
- end
29
-
30
- # Operator for "x + n" e.g. x + 1
31
- def successful?(problem)
32
- # Does the input match the answer
33
- #problem.collect {|x| x[:response] - x[:arguments].first }.uniq.length == 1
34
- if (problem[:arguments].first + @constant) == problem[:response]
35
- return true
36
- end
37
- return false
38
- end
39
-
40
- def self.find_constants(problems)
41
- problems.collect {|x| x[:response] - x[:arguments].first }.uniq
42
-
43
- end
44
-
45
- end
data/lib/cauldron/operator.rb ADDED
@@ -0,0 +1,62 @@
1
+ module Cauldron
2
+
3
+ module Operator
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def instances(histories, composite, examples, insert_points)
12
+
13
+ # TEMP
14
+ unless examples.class == ExampleSet
15
+ raise StandardError.new('Examples should be an example')
16
+ end
17
+
18
+ # Print out each insertable statements
19
+ scope = examples.scope
20
+
21
+ # self.init([0]).to_ruby(scope)
22
+ # - this will print out "var0.chop"
23
+
24
+ # Get the variables available at each point
25
+ results = []
26
+
27
+ insert_points.each do |point|
28
+
29
+ # Find the variables at a particular point
30
+ # TODO Change to test
31
+ contexts = histories.contexts_at(point)
32
+ composites = context_instances(contexts)
33
+
34
+ composites.each do |x|
35
+ if contexts.all? do |context|
36
+ x.context_realizable?(context)
37
+ end
38
+ results << extend_actualized_composite(x, composite, examples, point)
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ results
45
+ end
46
+
47
+ def context_instances(contexts)
48
+ temp = []
49
+ contexts.each do |context|
50
+ temp << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
51
+ end
52
+ results = temp.flatten.uniq
53
+
54
+ variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
55
+ variable_numbers.collect { |x| new([x.to_i])}
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
data/lib/cauldron/operator/array_reverse_operator.rb ADDED
@@ -0,0 +1,160 @@
1
+ class ArrayReverseOperator
2
+
3
+ def initialize(indexes)
4
+ raise StandardError.new('Need at least one item') if indexes.empty?
5
+ @indexes = indexes
6
+ end
7
+
8
+ # Matching in
9
+ def successful?(problem)
10
+ # NOTE - for the future - like the idea of not actually calling the method
11
+ # input.length.each do |i|
12
+ # does input[0] == output[input.length-0]
13
+ # does input[1] == output[input.length-1]
14
+ # does input[3] == output[input.length-3]
15
+ # end
16
+
17
+ # in this case x.reverse will work
18
+ return true if problem[:arguments].first.reverse == problem[:response]
19
+ false
20
+ end
21
+
22
+ # 1. Only has one argument value
23
+ # 2. Argument is an array value
24
+ # 3. Response is an array
25
+ def self.viable?(arguments,output)
26
+ return false unless arguments.length == 1
27
+ return false unless arguments.all? { |x| x.kind_of?(Array) }
28
+ return false unless output.kind_of?(Array)
29
+ true
30
+ end
31
+
32
+ def self.uses_constants?
33
+ false
34
+ end
35
+
36
+ def self.find_constants(problems)
37
+ []
38
+ end
39
+
40
+ def self.uses_block?
41
+ false
42
+ end
43
+
44
+ def self.process(arguments)
45
+ arguments.collect {|x| x.reverse }
46
+ end
47
+
48
+ def to_ruby(operators, scope)
49
+ Sorcerer.source build(operators, scope)
50
+ end
51
+
52
+ def build(operators, scope)
53
+ to_sexp(operators, scope)
54
+ end
55
+
56
+ def to_sexp(scope, operators)
57
+ [:call,
58
+ [:vcall,
59
+ [:@ident, scope[@indexes[0]] ]
60
+ ],
61
+ :".",
62
+ [:@ident, "reverse"]
63
+ ]
64
+ end
65
+
66
+ def branch?
67
+ false
68
+ end
69
+
70
+ def self.init(indexes)
71
+ self.new(indexes)
72
+ end
73
+
74
+ def self.instances(histories, composite, examples, insert_points)
75
+
76
+ # TEMP
77
+ unless examples.class == Cauldron::ExampleSet
78
+ raise StandardError.new('Examples should be an example')
79
+ end
80
+
81
+ # Print out each insertable statements
82
+ scope = examples.scope
83
+
84
+ # self.init([0]).to_ruby(scope)
85
+ # - this will print out "var0.chop"
86
+
87
+ # Get the variables available at each point
88
+ results = []
89
+
90
+ insert_points.each do |point|
91
+
92
+ # Find the variables at a particular point
93
+ # TODO Change to test
94
+ contexts = histories.contexts_at(point)
95
+
96
+ composites = context_instances(contexts)
97
+
98
+ # scopes = scopes_at_point(point)
99
+
100
+ composites.each do |x|
101
+ if contexts.all? do |context|
102
+ x.context_realizable?(context)
103
+ end
104
+
105
+ results << extend_actualized_composite(x, composite, examples, point)
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ results
112
+ end
113
+
114
+ def self.extend_actualized_composite(x, container, examples, point)
115
+ cloned_container = container.clone_solution
116
+ cloned_container.add_statement_at(x, point)
117
+ cloned_container
118
+ Cauldron::ActualizedComposite.new(cloned_container, examples)
119
+ end
120
+
121
+ def clone_statement
122
+ self.class.init(@indexes.clone)
123
+ end
124
+
125
+ def context_realizable?(context)
126
+
127
+ vars = context.keys.select {|x| x.match(/var\d/) }
128
+ var_names = vars.collect(&:to_s)
129
+
130
+ a = %Q{
131
+ def function(var0)
132
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
133
+ end
134
+ }
135
+
136
+ o = Object.new
137
+ o.instance_eval(a)
138
+
139
+ begin
140
+ o.function(vars.collect {|x| context[x] })
141
+ rescue NoMethodError => e
142
+ return false
143
+ rescue StandardError => e
144
+ puts e
145
+ end
146
+ return true
147
+
148
+ end
149
+
150
+ def self.context_instances(contexts)
151
+ results = []
152
+ contexts.each do |context|
153
+ results << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
154
+ end
155
+ results = results.flatten.uniq
156
+ variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
157
+ variable_numbers.collect { |x| init([x.to_i])}
158
+ end
159
+
160
+ end
data/lib/cauldron/operator/concat_operator.rb ADDED
@@ -0,0 +1,72 @@
1
+ class ConcatOperator
2
+
3
+ include Cauldron::Operator
4
+
5
+ def initialize(indexes)
6
+ @indexes = indexes
7
+ @constant = 'bar'
8
+ end
9
+
10
+ def self.viable?(arguments, response)
11
+ return false unless arguments.all? { |x| x.kind_of?(String) }
12
+ return false unless response.kind_of?(String)
13
+ # TODO - Only accpets one argument
14
+ true
15
+ end
16
+
17
+ def self.find_constants(problems)
18
+ problems.examples.inject([]) do |total, x|
19
+ result = x.response.gsub( Regexp.new('^'+x.arguments.first),'')
20
+ total << result unless result == x.response
21
+ total
22
+ end.uniq
23
+ end
24
+
25
+ def self.uses_constants?
26
+ true
27
+ end
28
+
29
+ def self.uses_block?
30
+ false
31
+ end
32
+
33
+ def branch?
34
+ false
35
+ end
36
+
37
+ # Operator for "x.concat("bar")"
38
+ def successful?(problem)
39
+ if (problem[:arguments].first + @constant) == problem[:response]
40
+ return true
41
+ end
42
+ return false
43
+ end
44
+
45
+ def to_ruby(scope, operators)
46
+ Sorcerer.source self.to_sexp(scope, operators)
47
+ end
48
+
49
+ def build(operators, scope)
50
+ to_sexp(scope)
51
+ end
52
+
53
+ def to_sexp(scope, operators)
54
+ first_variable = 'var'+@indexes[0].to_s
55
+ [:program,
56
+ [:stmts_add,
57
+ [:stmts_new],
58
+ [:method_add_arg,
59
+ [:call,
60
+ [:vcall, [:@ident, first_variable ]],
61
+ :".",
62
+ [:@ident, "concat"]],
63
+ [:arg_paren,
64
+ [:args_add_block,
65
+ [:args_add,
66
+ [:args_new],
67
+ [:string_literal,
68
+ [:string_add, [:string_content], [:@tstring_content, @constant]]]],
69
+ false]]]]]
70
+ end
71
+
72
+ end
data/lib/cauldron/operator/hash_key_value_operator.rb ADDED
@@ -0,0 +1,74 @@
1
+ #http://www.ruby-doc.org/core-2.1.1/Hash.html
2
+ #hsh[key] → value
3
+ class HashKeyValueOperator
4
+
5
+ # var0[:foo]
6
+
7
+ def initialize(indexes)
8
+ @indexes = indexes
9
+ @constant = :foo
10
+ #@constant, @indexes = constant, indexes
11
+ end
12
+
13
+ def self.viable?(arguments, response)
14
+ return false unless arguments.all? { |x| x.kind_of?(Hash) }
15
+ true
16
+ end
17
+
18
+ def self.uses_constants?
19
+ true
20
+ end
21
+
22
+ def self.find_constants(problems)
23
+ problems.collect {|x| x.arguments.first.keys }.flatten
24
+ end
25
+
26
+ def successful?(problem)
27
+ if problem.arguments.first[@constant] == problem.response
28
+ return true
29
+ end
30
+ return false
31
+ end
32
+
33
+ def to_ruby(scope, operators)
34
+ Sorcerer.source self.to_sexp(scope, operators)
35
+ end
36
+
37
+ def to_sexp(scope, operators)
38
+ [:aref,
39
+ [:vcall,
40
+ [:@ident, scope[0]]
41
+ ],
42
+ [:args_add_block,
43
+ [
44
+ :args_add,
45
+ [:args_new],
46
+ sexp_index
47
+ ]
48
+ ]
49
+ ]
50
+ end
51
+
52
+ def sexp_index
53
+ if @constant.kind_of?(Symbol)
54
+ a = [
55
+ :symbol_literal,
56
+ [:symbol, [:@ident, @constant]],
57
+ [:string_add, [:@ident, @constant]]
58
+ ]
59
+ return a
60
+ elsif @constant.kind_of?(String)
61
+ return [
62
+ :string_literal,
63
+ [
64
+ :string_add,
65
+ [:string_content],
66
+ [:@tstring_content, @constant ]
67
+ ]
68
+ ]
69
+ else
70
+ raise StandardError.new('Unknown index')
71
+ end
72
+ end
73
+
74
+ end
data/lib/cauldron/operator/numeric_operator.rb ADDED
@@ -0,0 +1,115 @@
1
+ class NumericOperator
2
+
3
+ include Cauldron::Operator
4
+
5
+ # Maybe NumericOperation
6
+ ADDITION = 4
7
+
8
+ def initialize(indexes)
9
+ @indexes = indexes
10
+ end
11
+
12
+ # Is the problem suitable for a numeric operatio?
13
+ # e.g. can the .find_contants call be called without error
14
+ def self.viable?(arguments,output)
15
+
16
+ # 1. Only has one argument value
17
+ # 2. Argument is a numeric value
18
+ # 3. Response is numeric
19
+
20
+ # TODO Need to save these viablility tests in shared (easily comparable) state.
21
+ # e.g. so all viable operations can be found in one go.
22
+
23
+ return false unless arguments.all? { |x| x.kind_of?(Numeric) }
24
+ return false unless output.kind_of?(Numeric)
25
+ true
26
+
27
+ end
28
+
29
+ def realizable?(histories)
30
+ parameters = histories.variable_permutations(@indexes.length)
31
+ parameters.each do |params|
32
+ begin
33
+ realize(params)
34
+ rescue TypeError
35
+ return false
36
+ end
37
+ end
38
+ true
39
+ end
40
+
41
+ def realize(params)
42
+ o = Object.new
43
+ a = %Q{
44
+ def function(var0)
45
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(['var0']),[]), indent: true)}
46
+ end
47
+ }
48
+ o.instance_eval(a)
49
+ o.function(*params.values)
50
+ end
51
+
52
+ def to_sexp(scope, operators)
53
+ [:binary, [:@ident, scope[@indexes[0]] ] , :+, [:@int, ADDITION.to_s]]
54
+ end
55
+
56
+ def to_ruby(scope, operators)
57
+ Sorcerer.source self.to_sexp(scope, operators)
58
+ end
59
+
60
+ def build(operators, scope)
61
+ to_sexp(scope)
62
+ end
63
+
64
+ # Operator for "x + n" e.g. x + 1
65
+ # Does the input match the answer
66
+ def successful?(problem)
67
+ if (problem[:arguments].first + ADDITION) == problem[:response]
68
+ return true
69
+ end
70
+ return false
71
+ end
72
+
73
+ def self.find_constants(problems)
74
+ problems.collect {|x| x.response - x.arguments.first }.uniq
75
+ end
76
+
77
+ def self.uses_constants?
78
+ true
79
+ end
80
+
81
+ def self.uses_block?
82
+ false
83
+ end
84
+
85
+ def branch?
86
+ false
87
+ end
88
+
89
+ def context_realizable?(context)
90
+ vars = context.keys.select {|x| x.match(/var\d/) }
91
+ var_names = vars.collect(&:to_s)
92
+
93
+ first_variable = 'var'+@indexes[0].to_s
94
+
95
+ a = %Q{
96
+ def function(#{first_variable})
97
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(var_names), []), indent: true)}
98
+ end
99
+ }
100
+
101
+ o = Object.new
102
+ o.instance_eval(a)
103
+
104
+ begin
105
+ #o.function(*vars.collect {|x| context[x] })
106
+ o.function context[first_variable.to_sym]
107
+ rescue NoMethodError => e
108
+ return false
109
+ rescue StandardError => e
110
+ puts e
111
+ end
112
+ return true
113
+ end
114
+
115
+ end
data/lib/cauldron/operator/string_asterisk_operator.rb ADDED
@@ -0,0 +1,131 @@
1
+ class StringAsteriskOperator
2
+
3
+ # var0 * 3
4
+
5
+ # TODO Possibly include the scope of the index
6
+ # a = 5
7
+ # ['sdsd'].each do |b|
8
+ # c = 5
9
+ # end
10
+ # (1...6).each do |d|
11
+ # g = d
12
+ # end
13
+
14
+ # [0] = ['a']
15
+ # [1] = ['b', 'c']
16
+ # [2] = ['g', 'd']
17
+
18
+ # Although it should probably be
19
+ # [0] = [ ['a'] ]
20
+ # [1] = [ ['b', 'c'], ['g', 'd'] ]
21
+ #
22
+ # Or the order it was added might be more useful - e.g. last variable, second last variable or first variable
23
+ # - variable at depth(1)[1] - stepUp(1).first
24
+
25
+ def initialize(indexes)
26
+ @indexes = indexes
27
+ @constant = 2
28
+ #@constant, @indexes = constant, indexes
29
+ end
30
+
31
+ def self.instances(histories, composite, examples, insert_points)
32
+
33
+ # TEMP
34
+ unless examples.class == ExampleSet
35
+ raise StandardError.new('Examples should be an example')
36
+ end
37
+
38
+ # Print out each insertable statements
39
+ scope = examples.scope
40
+
41
+ # self.init([0]).to_ruby(scope)
42
+ # - this will print out "var0.chop"
43
+
44
+ # Get the variables available at each point
45
+ results = []
46
+
47
+ insert_points.each do |point|
48
+
49
+ # Find the variables at a particular point
50
+ # TODO Change to test
51
+ contexts = histories.contexts_at(point)
52
+ composites = context_instances(contexts)
53
+
54
+ composites.each do |x|
55
+ if contexts.all? do |context|
56
+ x.context_realizable?(context)
57
+ end
58
+ results << extend_actualized_composite(x, composite, examples, point)
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ results
65
+ end
66
+
67
+ # def self.instances(context_history, target)
68
+ # res = history_goals(context_history, target)
69
+
70
+ # possible_constant = res.collect do |x|
71
+ # x[1].scan( x[0][:x] ).count
72
+ # end.uniq
73
+
74
+ # if possible_constant.length == 1
75
+ # #return [StringAsteriskOperator.new([1],possible_constant.first)]
76
+ # return [StringAsteriskOperator.new([1])]
77
+ # end
78
+
79
+ # end
80
+
81
+ def self.history_goals(context_history,target)
82
+ variables = context_history.first.keys
83
+ context_history.each {|x| x[variables.first] }.zip(target)
84
+ end
85
+
86
+ def self.find_constants(problems)
87
+ return [] unless problems.all? { |x| x.response.kind_of?(String) }
88
+ problems.collect {|x| x.response.scan(x.arguments.first).count }.reject {|x| x == 0}
89
+ end
90
+
91
+ def self.viable?(arguments,output)
92
+ return false unless output.kind_of?(String)
93
+ return false unless arguments.first.kind_of?(String)
94
+ true
95
+ end
96
+
97
+ def self.uses_constants?
98
+ true
99
+ end
100
+
101
+ def self.uses_block?
102
+ false
103
+ end
104
+
105
+ def branch?
106
+ false
107
+ end
108
+
109
+ def successful?(problem)
110
+ return true if problem[:arguments].first*@constant == problem[:response]
111
+ false
112
+ end
113
+
114
+ def to_ruby(scope, operators)
115
+ Sorcerer.source self.to_sexp([], scope)
116
+ end
117
+
118
+ # def to_sexp(operators, scope)
119
+ # [:binary, [:vcall, [:@ident, scope[@indexes[0]] ]], :*, [:@int, @constant]]
120
+ # end
121
+ def to_sexp(scope, children)
122
+ first_variable = 'var'+@indexes[0].to_s
123
+ Ripper::SexpBuilder.new(%Q{#{first_variable} * #{@constant}}).parse
124
+ end
125
+
126
+ # TODO Get rid of the defined names
127
+ def build(operators, scope)
128
+ to_sexp(operators, scope)
129
+ end
130
+
131
+ end
data/lib/cauldron/operator/to_s_operator.rb ADDED
@@ -0,0 +1,18 @@
1
+ class ToSOperator
2
+
3
+ def initialize(indexes)
4
+ @indexes = indexes
5
+ end
6
+
7
+ def build(operators, scope)
8
+ [
9
+ :call,
10
+ [:vcall,
11
+ [:@ident, scope[@indexes[0]]]
12
+ ],
13
+ :".",
14
+ [:@ident, "to_s"]
15
+ ]
16
+ end
17
+
18
+ end
data/lib/cauldron/operator/var_collect_operator.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Cauldron
2
+
3
+ class VarCollectOperator
4
+
5
+ def initialize(indexes)
6
+ @indexes = indexes
7
+ end
8
+
9
+ def to_ruby(contents, variables)
10
+ Sorcerer.source self.to_sexp( contents ,variables)
11
+ end
12
+
13
+ def to_sexp(scope, operators)
14
+ scope_var = scope.new_variable!
15
+ second_scope_var = scope.new_variable!
16
+ if operators.empty?
17
+ return [:stmts_add, [:stmts_new], [:assign, [:var_field, [:@ident, scope_var ]], [:method_add_block, [:call, [:vcall, [:@ident, scope[@indexes[0]]]], :".", [:@ident, "collect"]], [:do_block, [:block_var, [:params, [[:@ident, second_scope_var]], nil, nil, nil, nil, nil, nil], false], [:stmts_add, [:stmts_new], [:var_ref, [:@ident, second_scope_var]]]]]]]
18
+ else
19
+ return [:stmts_add, [:stmts_new], [:assign, [:var_field, [:@ident, scope_var ]], [:method_add_block, [:call, [:vcall, [:@ident, scope[@indexes[0]]]], :".", [:@ident, "collect"]], [:do_block, [:block_var, [:params, [[:@ident, second_scope_var]], nil, nil, nil, nil, nil, nil], false], [:stmts_add, [:stmts_new], operators.first.content.build([], scope) ]]]]]
20
+ end
21
+ end
22
+
23
+ def build(children, scope)
24
+ to_sexp(scope, children)
25
+ end
26
+
27
+ end
28
+
29
+ end
data/lib/cauldron/pot.rb CHANGED
@@ -4,20 +4,59 @@ module Cauldron
4
4
5
5
def solve(problems)
6
6
7
+ example_set = Cauldron::ExampleSet.new(problems.collect {|x| Cauldron::Example.new(x) })
8
+
7
9
# Identify the relationship
8
- relationship = find_relationship(problems)
10
+
11
+ # Pry::Code
12
+ # TODO Change term to solution
13
+ if find_saved_solution(example_set)
14
+ variables = example_set.variables
15
+ solution = find_saved_solution(example_set)
16
+ sexp = Ripper::SexpBuilder.new('def function('+variables.join(',')+');'+solution.to_ruby(variables)+"; end").parse
17
+ return Sorcerer.source(sexp, indent: true)
18
+ end
19
+ relationship = find_relationship(example_set)
9
20
10
21
# Generate if statements
11
22
result = ''
12
23
13
- # Add the arguments
14
- args = problems.first[:arguments]
15
- variables = (0...args.length).collect {|x| 'var'+x.to_s}
16
- result = 'def function('+variables.join(',')+')'+"\n"
17
- result << relationship.to_ruby
18
- result += 'end'
24
+ variables = example_set.variables
25
+ #sexp = Ripper::SexpBuilder.new('def function('+variables.join(',')+');'+relationship.to_ruby(variables)+"; end").parse
26
+ sexp = Ripper::SexpBuilder.new('def function('+variables.join(',')+');'+relationship.to_ruby(example_set.scope)+"; end").parse
27
+
28
+ Sorcerer.source(sexp, indent: true)
29
+
30
+ end
31
+
32
+ def chain_operators(problems,operators)
33
+ # TODO Presumes only two operators
34
+
35
+ operators[0].to_ruby( [
36
+ Tree::TreeNode.new("CHILD1", operators[1])
37
+ ], Cauldron::Scope.new(['var0']) )
38
+ end
39
+
40
+ def single_viable_operators(problems)
41
+
42
+ operations = [
43
+ NumericOperator, ArrayReverseOperator,
44
+ HashKeyValueOperator, StringAsteriskOperator,
45
+ ConcatOperator
46
+ ]
47
+
48
+ # Try each possible operation
49
+ viable_option_classes = []
50
+ operations.each do |operation_class|
51
+
52
+ # Are all the problems viable for this operation
53
+ if problems.all? {|x| operation_class.viable?(x.arguments,x.response) }
54
+ viable_option_classes << operation_class
55
+ end
56
+
57
+ end
19
58
20
- result
59
+ viable_option_classes
21
60
22
61
end
23
62
@@ -30,33 +69,104 @@ module Cauldron
30
69
value.to_s
31
70
end
32
71
33
- def find_relationship(problems)
72
+ def build_operators(operation_class,problems)
73
+ results = []
74
+ if operation_class.uses_constants?
75
+
76
+ possible_constants = operation_class.find_constants(problems)
34
77
35
- operations = [NumericOperator, ConcatOperator, ArrayReverseOperator]
78
+ possible_constants.each do |constant|
79
+ #operator = operation_class.new([0],constant)
80
+ operator = operation_class.new([0])
81
+ results << operator
82
+ end
83
+ else
36
84
37
- # Try each possible operation
38
- operations.each do |operation_class|
85
+ # Does the operator always result in the correct solution
86
+ operator = operation_class.new([0])
87
+ results << operator
39
88
40
- # Are all the problems viable for this operation
41
- if problems.all? {|x| operation_class.viable?(x[:arguments],x[:response]) }
42
- possible_constants = operation_class.find_constants(problems)
43
- possible_constants.each do |constant|
44
- operator = operation_class.new(constant)
45
-
46
- # Does the operator always result in the correct solution
47
- if problems.all? {|x| operator.successful?(x) }
48
- return operator
49
- end
50
- end
89
+ end
90
+ results
91
+ end
92
+
93
+ # BRUTE FORCE - Loop through all the solutions
94
+ def find_saved_solution examples
95
+ solutions = [
96
+ Cauldron::Solution::One.new
97
+ ]
98
+ successful_solutions = solutions.select do |solution|
99
+ examples.all? { |problem| solution.successful?(problem) }
100
+ end
101
+ return successful_solutions[0] unless successful_solutions.empty?
102
+ nil
103
+ end
104
+
105
+ def find_relationship(examples)
106
+
107
+ # ==== NEW APPROACH ====
108
+
109
+ new_composites = [
110
+ Cauldron::ActualizedComposite.new(
111
+ Cauldron::Solution::Composite.new([]),
112
+ examples
113
+ )
114
+ ]
115
+ itterations = 0
116
+ until itterations == 2
117
+
118
+ new_composites = extended_composites(new_composites)
119
+
120
+ if new_composites.any? {|x| x.solution?(examples) }
121
+ return new_composites.select {|x| x.solution?(examples) }.first.composite
122
+ end
123
+ itterations += 1
124
+ end
125
+
126
+ # 1. TRY TO FIND SOLUTION via the history
127
+ # 2. Desired history
128
+ # 3. Chaing matching history
129
+
130
+ # ================== END HERE ===============
131
+
132
+ solutions = []
133
+ single_viable_operators(examples).each do |operation_class|
134
+
135
+ operators = build_operators(operation_class,examples)
136
+ operators.each do |operator|
137
+ root = Tree::TreeNode.new("ROOT", "Root Content")
138
+ root << Tree::TreeNode.new("CHILD1", operator)
139
+ solutions << Cauldron::Solution::Composite.new(root.children)
51
140
end
141
+ end
52
142
143
+ solutions.each do |solution|
144
+ if solution.solution?(examples)
145
+ return solution
146
+ end
53
147
end
54
148
55
- if IfRelationship.match? problems
56
- return IfRelationship.new(problems)
149
+ # operator_chains = viable_double_operators(problems)
150
+
151
+ # operator_chains.each do |operators|
152
+
153
+ # code = build_chain_operator(operators,problems)
154
+ # if problems.all? {|x| code.successful?(x) }
155
+ # return code
156
+ # end
157
+ # end
158
+
159
+ if IfRelationship.match? examples
160
+ return IfRelationship.new(examples)
57
161
end
58
- IfRelationShip.new(problems)
162
+ IfRelationShip.new(examples)
59
163
end
164
+
165
+ def extended_composites(actualized_composites)
166
+ actualized_composites.inject([]) do |total, x|
167
+ total += x.extend_solution; total
168
+ end
169
+ end
60
170
61
171
end
62
172
data/lib/cauldron/relationship.rb DELETED
@@ -1,5 +0,0 @@
1
- class Relationship
2
-
3
-
4
-
5
- end
data/lib/cauldron/scope.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Cauldron
2
+
3
+ class Scope
4
+ include Enumerable
5
+
6
+ attr_reader :variables
7
+
8
+ def initialize(variables)
9
+ @variables = variables
10
+ end
11
+
12
+ def new_variable!
13
+ #@variables << "var#{variables.length+1}"
14
+ @variables << "var#{variables.length}"
15
+ @variables.last
16
+ end
17
+
18
+ def [](index)
19
+ variables[index]
20
+ end
21
+
22
+ end
23
+
24
+ end
data/lib/cauldron/solution/composite.rb ADDED
@@ -0,0 +1,236 @@
1
+ module Cauldron::Solution
2
+
3
+ class Composite
4
+
5
+ attr_reader :operators
6
+
7
+ def initialize(children)
8
+ raise StandardError.new('inital value should be an array') unless children.kind_of?(Array)
9
+ @operators = children
10
+ end
11
+
12
+ def record(example)
13
+ # TODO params passed twice - and example not used at all
14
+ insert_tracking(example.params).process(example)
15
+ end
16
+
17
+ def end_points
18
+ results = []
19
+ operators.each do |x|
20
+ if x.content.branch?
21
+ results << [0,x.children.length]
22
+ end
23
+ end
24
+ results << [operators.length]
25
+ end
26
+
27
+ def clone_solution
28
+ #self.clone
29
+ tree_operators = operators.collect do |node|
30
+ Tree::TreeNode.new('x', node.content.clone_statement)
31
+ end
32
+ Composite.new(tree_operators)
33
+ end
34
+
35
+ def add_statement_at(statement, point)
36
+ if point.length == 2
37
+ container = self.operators[0]
38
+ #return self if container.length > 1 # TODO: Quick hack to get it working
39
+ container << Tree::TreeNode.new('SASA', statement)
40
+ elsif point.length == 1
41