cauldron 0.1.5 → 0.1.6

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 (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.md +26 -0
  7. data/Rakefile +24 -99
  8. data/build_sandbox.rb +41 -0
  9. data/cucumber.yml +8 -0
  10. data/features/chop.feature +23 -0
  11. data/features/create_dynamic_statements.feature +14 -0
  12. data/features/generate_known_solution.feature +42 -0
  13. data/features/generate_new_statement.feature +20 -0
  14. data/features/step_definitions/cauldron_steps.rb +47 -0
  15. data/features/support/env.rb +1 -1
  16. data/features/use_existing_statements.feature +23 -0
  17. data/lib/cauldron.rb +41 -5
  18. data/lib/cauldron/actualized_composite.rb +35 -0
  19. data/lib/cauldron/array_collect_template/default.rb +95 -0
  20. data/lib/cauldron/array_collect_template/template.rb +57 -0
  21. data/lib/cauldron/builder.rb +60 -0
  22. data/lib/cauldron/caret.rb +50 -0
  23. data/lib/cauldron/dynamic_operator.rb +90 -0
  24. data/lib/cauldron/dynamic_operator_module.rb +140 -0
  25. data/lib/cauldron/example.rb +18 -0
  26. data/lib/cauldron/example_set.rb +36 -0
  27. data/lib/cauldron/histories.rb +53 -0
  28. data/lib/cauldron/history.rb +34 -0
  29. data/lib/cauldron/if_relationship.rb +4 -6
  30. data/lib/cauldron/number_addition_template/add_five.rb +71 -0
  31. data/lib/cauldron/number_addition_template/template.rb +56 -0
  32. data/lib/cauldron/operator.rb +62 -0
  33. data/lib/cauldron/operator/array_reverse_operator.rb +160 -0
  34. data/lib/cauldron/operator/concat_operator.rb +72 -0
  35. data/lib/cauldron/operator/hash_key_value_operator.rb +74 -0
  36. data/lib/cauldron/operator/numeric_operator.rb +115 -0
  37. data/lib/cauldron/operator/string_asterisk_operator.rb +131 -0
  38. data/lib/cauldron/operator/to_s_operator.rb +18 -0
  39. data/lib/cauldron/operator/var_collect_operator.rb +29 -0
  40. data/lib/cauldron/pot.rb +136 -26
  41. data/lib/cauldron/scope.rb +24 -0
  42. data/lib/cauldron/solution/composite.rb +236 -0
  43. data/lib/cauldron/solution/one.rb +49 -0
  44. data/lib/cauldron/statement_generator.rb +298 -0
  45. data/lib/cauldron/template_base.rb +14 -0
  46. data/lib/cauldron/tracer.rb +55 -0
  47. data/lib/cauldron/version.rb +1 -1
  48. data/lib/pry_tester.rb +76 -0
  49. data/ruby_to_sexp.rb +74 -0
  50. data/sandbox.rb +7 -0
  51. data/sexp_to_ruby.rb +150 -0
  52. data/spec/cauldron/actualized_composite_spec.rb +140 -0
  53. data/spec/cauldron/array_collect_template/default_spec.rb +41 -0
  54. data/spec/cauldron/builder_spec.rb +186 -0
  55. data/spec/cauldron/dynamic/add_number_template_spec.rb +30 -0
  56. data/spec/cauldron/dynamic_operator_spec.rb +416 -0
  57. data/spec/cauldron/example_set_spec.rb +49 -0
  58. data/spec/cauldron/example_spec.rb +33 -0
  59. data/spec/cauldron/histories_spec.rb +135 -0
  60. data/spec/cauldron/history_spec.rb +118 -0
  61. data/spec/cauldron/if_relationship_spec.rb +1 -1
  62. data/spec/cauldron/operator/array_reverse_operator_spec.rb +73 -0
  63. data/spec/cauldron/{concat_operator_spec.rb → operator/concat_operator_spec.rb} +30 -12
  64. data/spec/cauldron/operator/hash_key_value_operator_spec.rb +98 -0
  65. data/spec/cauldron/operator/numeric_operator_spec.rb +110 -0
  66. data/spec/cauldron/operator/string_asterisk_operator_spec.rb +196 -0
  67. data/spec/cauldron/operator/var_collect_operator_spec.rb +38 -0
  68. data/spec/cauldron/pot_spec.rb +176 -14
  69. data/spec/cauldron/solution/composite_spec.rb +421 -0
  70. data/spec/cauldron/solution/one_spec.rb +24 -0
  71. data/spec/cauldron/statement_generator_spec.rb +211 -0
  72. data/spec/cauldron/terminal_spec.rb +2 -2
  73. data/spec/spec_helper.rb +5 -1
  74. data/spec/support/code_matcher.rb +55 -0
  75. data/spec/support/include_instance_of_matcher.rb +9 -0
  76. data/spec/support/shared_examples_for_leaf_operators.rb +22 -0
  77. data/spec/support/shared_examples_for_operators.rb +23 -0
  78. data/syntax_spec.txt +2 -0
  79. metadata +104 -41
  80. data/README +0 -1
  81. data/README.rdoc +0 -19
  82. data/VERSION +0 -1
  83. data/features/cauldron_new_approach.feature +0 -46
  84. data/lib/cauldron/array_reverse_operator.rb +0 -39
  85. data/lib/cauldron/concat_operator.rb +0 -34
  86. data/lib/cauldron/numeric_operator.rb +0 -45
  87. data/lib/cauldron/relationship.rb +0 -5
  88. data/spec/cauldron/array_reverse_operator_spec.rb +0 -59
  89. data/spec/cauldron/numeric_operator_spec.rb +0 -70
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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