cauldron 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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