excel_to_code 0.1.23 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/src/commands/excel_to_c.rb +39 -92
  3. data/src/commands/excel_to_ruby.rb +9 -35
  4. data/src/commands/excel_to_x.rb +515 -536
  5. data/src/compile/c/a.out +0 -0
  6. data/src/compile/c/compile_named_reference_setters.rb +4 -6
  7. data/src/compile/c/compile_to_c.rb +34 -21
  8. data/src/compile/c/compile_to_c_header.rb +7 -7
  9. data/src/compile/c/excel_to_c_runtime.c +8 -4
  10. data/src/compile/c/map_formulae_to_c.rb +85 -86
  11. data/src/compile/c/map_values_to_c.rb +7 -1
  12. data/src/compile/c/map_values_to_c_structs.rb +1 -1
  13. data/src/compile/ruby/compile_to_ruby.rb +14 -11
  14. data/src/compile/ruby/compile_to_ruby_unit_test.rb +17 -10
  15. data/src/compile/ruby/map_formulae_to_ruby.rb +56 -56
  16. data/src/compile/ruby/map_values_to_ruby.rb +14 -2
  17. data/src/excel/area.rb +6 -8
  18. data/src/excel/excel_functions/hlookup.rb +1 -1
  19. data/src/excel/excel_functions/vlookup.rb +1 -1
  20. data/src/excel/formula_peg.rb +1 -1
  21. data/src/excel/formula_peg.txt +1 -1
  22. data/src/excel/reference.rb +4 -3
  23. data/src/excel/table.rb +4 -4
  24. data/src/extract.rb +1 -0
  25. data/src/extract/check_for_unknown_functions.rb +2 -2
  26. data/src/extract/extract_array_formulae.rb +9 -9
  27. data/src/extract/extract_everything.rb +140 -0
  28. data/src/extract/extract_formulae.rb +30 -20
  29. data/src/extract/extract_named_references.rb +37 -22
  30. data/src/extract/extract_relationships.rb +16 -3
  31. data/src/extract/extract_shared_formulae.rb +8 -11
  32. data/src/extract/extract_shared_formulae_targets.rb +1 -6
  33. data/src/extract/extract_shared_strings.rb +21 -8
  34. data/src/extract/extract_simple_formulae.rb +11 -6
  35. data/src/extract/extract_table.rb +26 -13
  36. data/src/extract/extract_values.rb +35 -11
  37. data/src/extract/extract_worksheet_dimensions.rb +13 -3
  38. data/src/extract/extract_worksheet_names.rb +16 -3
  39. data/src/extract/extract_worksheet_table_relationships.rb +16 -4
  40. data/src/extract/simple_extract_from_xml.rb +9 -11
  41. data/src/rewrite.rb +3 -0
  42. data/src/rewrite/ast_copy_formula.rb +5 -1
  43. data/src/rewrite/ast_expand_array_formulae.rb +71 -59
  44. data/src/rewrite/caching_formula_parser.rb +110 -0
  45. data/src/rewrite/rewrite_array_formulae.rb +21 -14
  46. data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +41 -13
  47. data/src/rewrite/rewrite_shared_formulae.rb +17 -18
  48. data/src/rewrite/rewrite_values_to_ast.rb +2 -0
  49. data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +28 -25
  50. data/src/simplify.rb +1 -0
  51. data/src/simplify/count_formula_references.rb +22 -23
  52. data/src/simplify/emergency_array_formula_replace_indirect_bodge.rb +44 -0
  53. data/src/simplify/identify_dependencies.rb +7 -8
  54. data/src/simplify/identify_repeated_formula_elements.rb +5 -6
  55. data/src/simplify/inline_formulae.rb +48 -48
  56. data/src/simplify/map_formulae_to_values.rb +197 -79
  57. data/src/simplify/remove_cells.rb +13 -6
  58. data/src/simplify/replace_arithmetic_on_ranges.rb +42 -28
  59. data/src/simplify/replace_arrays_with_single_cells.rb +11 -5
  60. data/src/simplify/replace_column_with_column_number.rb +31 -23
  61. data/src/simplify/replace_common_elements_in_formulae.rb +16 -17
  62. data/src/simplify/replace_indirects_with_references.rb +26 -21
  63. data/src/simplify/replace_named_references.rb +26 -31
  64. data/src/simplify/replace_offsets_with_references.rb +33 -34
  65. data/src/simplify/replace_ranges_with_array_literals.rb +48 -20
  66. data/src/simplify/replace_shared_strings.rb +15 -13
  67. data/src/simplify/replace_string_join_on_ranges.rb +7 -9
  68. data/src/simplify/replace_table_references.rb +16 -11
  69. data/src/simplify/replace_values_with_constants.rb +6 -4
  70. data/src/simplify/simplify_arithmetic.rb +33 -19
  71. data/src/simplify/sort_into_calculation_order.rb +13 -13
  72. data/src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb +21 -13
  73. metadata +19 -2
@@ -15,21 +15,20 @@ class IdentifyRepeatedFormulaElements
15
15
 
16
16
  def count(references)
17
17
  @references = references
18
- references.each do |sheet,cells|
19
- cells.each do |ref,ast|
20
- identify_repeated_formulae(ast)
21
- end
18
+ references.each do |ref,ast|
19
+ identify_repeated_formulae(ast)
22
20
  end
23
21
  return @counted_elements
24
22
  end
25
23
 
24
+ IGNORE_TYPES = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true, :row => true}
26
25
 
27
26
  def identify_repeated_formulae(ast)
28
27
  string = ast.to_s
29
28
  return unless ast.is_a?(Array)
30
- return if [:number,:string,:blank,:null,:error,:boolean_true,:boolean_false,:sheet_reference,:cell, :row].include?(ast.first)
29
+ return if IGNORE_TYPES.has_key?(ast.first)
31
30
  return if string.length < bothered_threshold
32
- @counted_elements[string] += 1
31
+ @counted_elements[ast] += 1
33
32
  ast.each do |a|
34
33
  identify_repeated_formulae(a)
35
34
  end
@@ -1,67 +1,67 @@
1
1
  class InlineFormulaeAst
2
+
3
+ BLANK = [:blank]
2
4
 
3
5
  attr_accessor :references, :current_sheet_name, :inline_ast
4
- attr_accessor :replacements_made_in_the_last_pass
6
+ attr_accessor :count_replaced
5
7
 
6
- def initialize(references, current_sheet_name, inline_ast = nil)
8
+ def initialize(references = nil, current_sheet_name = nil, inline_ast = nil)
7
9
  @references, @current_sheet_name, @inline_ast = references, [current_sheet_name], inline_ast
8
- @replacements_made_in_the_last_pass = 0
9
- @inline_ast ||= lambda { |sheet,reference,references| true }
10
+ @count_replaced = 0
11
+ @inline_ast ||= lambda { |sheet, ref, references| true } # Default is to always inline
10
12
  end
11
13
 
12
14
  def map(ast)
13
15
  return ast unless ast.is_a?(Array)
14
- operator = ast[0]
15
- if respond_to?(operator)
16
- send(operator,*ast[1..-1])
17
- else
18
- [operator,*ast[1..-1].map {|a| map(a) }]
16
+ if respond_to?(ast[0])
17
+ send(ast[0], ast)
18
+ else # In this case needs to be an else because don't want to map first argument in OFFSET(cell_to_offset_from_shouldn't_be_mapped, rows, columns)
19
+ ast.each { |a| map(a) }
19
20
  end
21
+ ast
20
22
  end
21
23
 
22
- def function(name,*args)
23
- case name
24
- when 'OFFSET'
25
- [:function, name, args.shift, *args.map { |a| map(a) }]
24
+ def function(ast)
25
+ if ast[1] == :OFFSET && (ast[2][0] == :cell || ast[2][0] == :sheet_reference)
26
+ # Don't map the second argument - it should be left as a cell refernce
27
+ ast[3..-1].each {|a| map(a) }
26
28
  else
27
- [:function, name, *args.map { |a| map(a) }]
29
+ # Otherwise good to map all the other arguments
30
+ ast.each { |a| map(a) }
28
31
  end
29
32
  end
30
33
 
31
- def sheet_reference(sheet,reference)
32
- return [:error, '#REF!'] unless references[sheet]
33
- if inline_ast.call(sheet,reference.last.upcase.gsub('$',''),references)
34
- @replacements_made_in_the_last_pass += 1
35
- ast = references[sheet][reference.last.upcase.gsub('$','')]
36
- if ast
37
- current_sheet_name.push(sheet)
38
- result = map(ast)
39
- current_sheet_name.pop
40
- else
41
- result = [:blank]
42
- end
43
- else
44
- result = [:sheet_reference,sheet,reference]
45
- end
46
- result
34
+ # Should be of the form [:sheet_reference, sheet_name, reference]
35
+ # FIXME: Can we rely on reference always being a [:cell, ref] at this stage?
36
+ def sheet_reference(ast)
37
+ return unless ast[2][0] == :cell
38
+ sheet = ast[1].to_sym
39
+ ref = ast[2][1].to_s.upcase.gsub('$','').to_sym
40
+ # FIXME: Need to check if valid worksheet and return [:error, "#REF!"] if not
41
+ # Now check user preference on this
42
+ return unless inline_ast.call(sheet,ref, references)
43
+ @count_replaced += 1
44
+ ast_to_inline = references[[sheet, ref]]
45
+ return ast.replace(BLANK) unless ast_to_inline
46
+ current_sheet_name.push(sheet)
47
+ map(ast_to_inline)
48
+ current_sheet_name.pop
49
+ ast.replace(ast_to_inline)
47
50
  end
48
51
 
49
- # TODO: Optimize by replacing contents of references hash with the inlined version
50
- def cell(reference)
51
- if inline_ast.call(current_sheet_name.last,reference.upcase.gsub('$',''),references)
52
- @replacements_made_in_the_last_pass += 1
53
- ast = references[current_sheet_name.last][reference.upcase.gsub('$','')]
54
- if ast
55
- map(ast)
56
- else
57
- [:blank]
58
- end
59
- else
60
- if current_sheet_name.size > 1
61
- [:sheet_reference,current_sheet_name.last,[:cell,reference]]
62
- else
63
- [:cell,reference]
64
- end
52
+ # Format [:cell, ref]
53
+ def cell(ast)
54
+ sheet = current_sheet_name.last
55
+ ref = ast[1].to_s.upcase.gsub('$', '').to_sym
56
+ if inline_ast.call(sheet, ref, references)
57
+ @count_replaced += 1
58
+ ast_to_inline = references[[sheet, ref]]
59
+ return ast.replace(BLANK) unless ast_to_inline
60
+ map(ast_to_inline)
61
+ ast.replace(ast_to_inline)
62
+ # FIXME: Check - is this right? does it work recursively enough?
63
+ elsif current_sheet_name.size > 1
64
+ ast.replace([:sheet_reference, sheet, ast.dup])
65
65
  end
66
66
  end
67
67
 
@@ -76,7 +76,7 @@ class InlineFormulae
76
76
  self.new.replace(*args)
77
77
  end
78
78
 
79
- attr_accessor :replacements_made_in_the_last_pass
79
+ attr_accessor :count_replaced
80
80
 
81
81
  def replace(input,output)
82
82
  rewriter = InlineFormulaeAst.new(references, default_sheet_name, inline_ast)
@@ -89,6 +89,6 @@ class InlineFormulae
89
89
  output.puts line
90
90
  end
91
91
  end
92
- @replacements_made_in_the_last_pass = rewriter.replacements_made_in_the_last_pass
92
+ @count_replaced = rewriter.count_replaced
93
93
  end
94
94
  end
@@ -16,115 +16,214 @@ class MapFormulaeToValues
16
16
  @value_for_ast = MapValuesToRuby.new
17
17
  @calculator = FormulaeCalculator.new
18
18
  @replacements_made_in_the_last_pass = 0
19
- @cache = {}
20
19
  end
21
20
 
22
- def reset
23
- @cache = {}
21
+ def original_excel_filename=(new_filename)
22
+ @original_excel_filename = new_filename
23
+ @calculator.original_excel_filename = new_filename
24
24
  end
25
25
 
26
- # FIXME: Caching works in the odd edge cases of long formula
27
- # but I really need to find the root cause of the problem
28
- def map(ast)
29
- @calculator.original_excel_filename = original_excel_filename
30
- @cache[ast] ||= do_map(ast)
26
+ def reset
27
+ # Not used any more
28
+ # FIXME: Remove references to this method
31
29
  end
32
30
 
33
- def do_map(ast)
34
- return ast unless ast.is_a?(Array)
35
- operator = ast[0]
36
- if respond_to?(operator)
37
- send(operator,*ast[1..-1])
38
- else
39
- [operator,*ast[1..-1].map {|a| map(a) }]
40
- end
31
+ DO_NOT_MAP = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true}
32
+
33
+ def map(ast)
34
+ ast[1..-1].each do |a|
35
+ next unless a.is_a?(Array)
36
+ next if DO_NOT_MAP[(a[0])]
37
+ map(a)
38
+ end # Depth first best in this case?
39
+ send(ast[0], ast) if respond_to?(ast[0])
40
+ ast
41
41
  end
42
42
 
43
- def prefix(operator,argument)
44
- argument_value = value(map(argument))
45
- return [:prefix, operator, map(argument)] if argument_value == :not_a_value
46
- return ast_for_value(argument_value || 0) if operator == "+"
47
- ast_for_value(@calculator.negative(argument_value))
43
+ # [:prefix, operator, argument]
44
+ def prefix(ast)
45
+ operator, argument = ast[1], ast[2]
46
+ argument_value = value(argument)
47
+ return if argument_value == :not_a_value
48
+ return ast.replace(ast_for_value(argument_value || 0)) if operator == "+"
49
+ ast.replace(ast_for_value(@calculator.negative(argument_value)))
48
50
  end
49
51
 
50
- def arithmetic(left,operator,right)
51
- l = value(map(left))
52
- r = value(map(right))
53
- if (l != :not_a_value) && (r != :not_a_value)
54
- formula_value(operator.last,l,r)
55
- else
56
- [:arithmetic,map(left),operator,map(right)]
52
+ # [:arithmetic, left, operator, right]
53
+ def arithmetic(ast)
54
+ left, operator, right = ast[1], ast[2], ast[3]
55
+ l = @calculator.number_argument(value(left))
56
+ r = @calculator.number_argument(value(right))
57
+ if (l == :not_a_value) && (r == :not_a_value)
58
+ return ast
59
+ elsif (l != :not_a_value) && (r != :not_a_value)
60
+ ast.replace(formula_value(operator.last,l,r))
61
+ # SPECIAL CASES
62
+ elsif l == 0
63
+ case operator.last
64
+ when :+
65
+ ast.replace(right)
66
+ when :*, :/, :^
67
+ ast.replace([:number, 0])
68
+ end
69
+ elsif r == 0
70
+ case operator.last
71
+ when :+, :-
72
+ ast.replace(left)
73
+ when :*
74
+ ast.replace([:number, 0])
75
+ when :/
76
+ ast.replace([:error, :'#DIV/0!'])
77
+ when :^
78
+ ast.replace([:number, 1])
79
+ end
80
+ elsif l == 1
81
+ case operator.last
82
+ when :*
83
+ ast.replace(right)
84
+ when :^
85
+ ast.replace([:number, 1])
86
+ end
87
+ elsif r == 1
88
+ case operator.last
89
+ when :*, :/, :^
90
+ ast.replace(left)
91
+ end
57
92
  end
93
+ ast
58
94
  end
59
-
60
- alias :comparison :arithmetic
61
95
 
62
- def percentage(number)
63
- ast_for_value(value([:percentage, number]))
96
+ def comparison(ast)
97
+ left, operator, right = ast[1], ast[2], ast[3]
98
+ l = value(left)
99
+ r = value(right)
100
+ return ast if (l == :not_a_value) || (r == :not_a_value)
101
+ ast.replace(formula_value(operator.last,l,r))
64
102
  end
65
-
66
- def string_join(*args)
67
- values = args.map { |a| value(map(a)) } # FIXME: These eval statements are really bugging me. Must find a better solution
68
- if values.any? { |a| a == :not_a_value }
69
- [:string_join,*args.map { |a| map(a) }]
70
- else
71
- ast_for_value(@calculator.string_join(*values))
72
- end
103
+
104
+ # [:percentage, number]
105
+ def percentage(ast)
106
+ ast.replace(ast_for_value(value([:percentage, ast[1]])))
73
107
  end
74
108
 
75
- FUNCTIONS_THAT_SHOULD_NOT_BE_CONVERTED = %w{TODAY RAND RANDBETWEEN INDIRECT}
109
+ # [:string_join, stringA, stringB, ...]
110
+ def string_join(ast)
111
+ values = ast[1..-1].map { |a| value(a) }
112
+ return if values.any? { |a| a == :not_a_value }
113
+ ast.replace(ast_for_value(@calculator.string_join(*values)))
114
+ end
76
115
 
77
- def function(name,*args)
78
- if FUNCTIONS_THAT_SHOULD_NOT_BE_CONVERTED.include?(name)
79
- [:function,name,*args.map { |a| map(a) }]
80
- elsif respond_to?("map_#{name.downcase}")
81
- send("map_#{name.downcase}",*args)
116
+ # [:function, function_name, arg1, arg2, ...]
117
+ def function(ast)
118
+ name = ast[1]
119
+ return if name == :INDIRECT
120
+ return if name == :OFFSET
121
+ return if name == :COLUMN
122
+ if respond_to?("map_#{name.to_s.downcase}")
123
+ send("map_#{name.to_s.downcase}",ast)
82
124
  else
83
- values = args.map { |a| value(map(a)) }
84
- if values.any? { |a| a == :not_a_value }
85
- [:function,name,*args.map { |a| map(a) }]
86
- else
87
- formula_value(name,*values)
88
- end
125
+ values = ast[2..-1].map { |a| value(a) }
126
+ return if values.any? { |a| a == :not_a_value }
127
+ ast.replace(formula_value(name,*values))
89
128
  end
90
129
  end
91
130
 
92
- def map_count(range)
93
- return [:function, "COUNT", range] unless [:array, :cell, :sheet_reference].include?(range.first)
131
+ # [:function, "COUNT", range]
132
+ def map_count(ast)
133
+ range = ast[2]
134
+ return unless [:array, :cell, :sheet_reference].include?(range.first)
94
135
  range = array_as_values(range)
95
- ast_for_value(range.size * range.first.size)
136
+ ast.replace(ast_for_value(range.size * range.first.size))
96
137
  end
97
138
 
98
- def map_index(array,row_number,column_number = :not_specified)
99
- return map_index_with_only_two_arguments(array,row_number) if column_number == :not_specified
139
+ # [:function, "INDEX", array, row_number, column_number]
140
+ def map_index(ast)
141
+ return map_index_with_only_two_arguments(ast) if ast.length == 4
100
142
 
101
- array_mapped = map(array)
102
- row_as_number = value(map(row_number))
103
- column_as_number = value(map(column_number))
143
+ array_mapped = ast[2]
144
+ row_as_number = value(ast[3])
145
+ column_as_number = value(ast[4])
104
146
 
105
- return [:function, "INDEX", array_mapped, map(row_number), map(column_number)] if row_as_number == :not_a_value || column_as_number == :not_a_value
147
+ return if row_as_number == :not_a_value
148
+ return if column_as_number == :not_a_value
106
149
 
107
- array_as_values = array_as_values(array)
108
- return [:function, "INDEX", array_mapped, map(row_number), map(column_number)] unless array_as_values
150
+ array_as_values = array_as_values(array_mapped)
151
+ return unless array_as_values
109
152
 
110
- result = @calculator.send(MapFormulaeToRuby::FUNCTIONS["INDEX"],array_as_values,row_as_number,column_as_number)
153
+ result = @calculator.send(MapFormulaeToRuby::FUNCTIONS[:INDEX],array_as_values,row_as_number,column_as_number)
111
154
  result = [:number, 0] if result == [:blank]
112
155
  result = ast_for_value(result)
113
- result
156
+ ast.replace(result)
114
157
  end
115
158
 
116
- def map_index_with_only_two_arguments(array,row_number)
117
- array_mapped = map(array)
118
- row_as_number = value(map(row_number))
119
- return [:function, "INDEX", array_mapped, map(row_number)] if row_as_number == :not_a_value
120
- array_as_values = array_as_values(array)
121
- return [:function, "INDEX", array_mapped, map(row_number)] unless array_as_values
122
- result = @calculator.send(MapFormulaeToRuby::FUNCTIONS["INDEX"],array_as_values,row_as_number)
159
+ # [:function, "INDEX", array, row_number]
160
+ def map_index_with_only_two_arguments(ast)
161
+ array_mapped = ast[2]
162
+ row_as_number = value(ast[3])
163
+ return if row_as_number == :not_a_value
164
+ array_as_values = array_as_values(array_mapped)
165
+ return unless array_as_values
166
+ result = @calculator.send(MapFormulaeToRuby::FUNCTIONS[:INDEX],array_as_values,row_as_number)
123
167
  result = [:number, 0] if result == [:blank]
124
168
  result = ast_for_value(result)
125
- result
169
+ ast.replace(result)
126
170
  end
127
-
171
+
172
+ # [:function, :SUM, a, b, c...]
173
+ def map_sum(ast)
174
+ values = ast[2..-1].map { |a| value(a) }
175
+ return partially_map_sum(ast) if values.any? { |a| a == :not_a_value }
176
+ ast.replace(formula_value(:SUM,*values))
177
+ end
178
+
179
+ def partially_map_sum(ast)
180
+ number_total = 0
181
+ not_number_array = []
182
+ ast[2..-1].each do |a|
183
+ result = filter_numbers_and_not(a)
184
+ number_total += result.first
185
+ not_number_array.concat(result.last)
186
+ end
187
+ if number_total == 0 && not_number_array.empty?
188
+ ast.replace([:number, number_total])
189
+ # FIXME: Will I be haunted by this? What if doing a sum of something that isn't a number
190
+ # and so what is expected is a VALUE error?. YES. This doesn't work well.
191
+ #elsif number_total == 0 && not_number_array.size == 1
192
+ # p not_number_array[0]
193
+ # ast.replace(not_number_array[0])
194
+ else
195
+ new_ast = [:function, :SUM].concat(not_number_array)
196
+ new_ast.push([:number, number_total]) unless number_total == 0
197
+ ast.replace(new_ast)
198
+ end
199
+ ast
200
+ end
201
+
202
+ def filter_numbers_and_not(ast)
203
+ number_total = 0
204
+ not_number_array = []
205
+ case ast.first
206
+ when :array
207
+ array_as_values(ast).each do |row|
208
+ row.each do |c|
209
+ result = filter_numbers_and_not(c)
210
+ number_total += result.first
211
+ not_number_array.concat(result.last)
212
+ end
213
+ end
214
+ when :blank, :number, :percentage, :string, :boolean_true, :boolean_false
215
+ number = @calculator.number_argument(value(ast))
216
+ if number.is_a?(Symbol)
217
+ not_number_array.push(ast)
218
+ else
219
+ number_total += number
220
+ end
221
+ else
222
+ not_number_array.push(ast)
223
+ end
224
+ [number_total, not_number_array]
225
+ end
226
+
128
227
  def array_as_values(array_mapped)
129
228
  case array_mapped.first
130
229
  when :array
@@ -139,11 +238,29 @@ class MapFormulaeToValues
139
238
  nil
140
239
  end
141
240
  end
241
+
242
+ ERRORS = {
243
+ :"#NAME?" => :name,
244
+ :"#VALUE!" => :value,
245
+ :"#DIV/0!" => :div0,
246
+ :"#REF!" => :ref,
247
+ :"#N/A" => :na,
248
+ :"#NUM!" => :num
249
+ }
142
250
 
143
251
  def value(ast)
144
252
  return extract_values_from_array(ast) if ast.first == :array
145
- return :not_a_value unless @value_for_ast.respond_to?(ast.first)
146
- eval(@value_for_ast.send(*ast))
253
+ case ast.first
254
+ when :blank; nil
255
+ when :null; nil
256
+ when :number; ast[1]
257
+ when :percentage; ast[1]/100.0
258
+ when :string; ast[1]
259
+ when :error; ERRORS[ast[1]]
260
+ when :boolean_true; true
261
+ when :boolean_false; false
262
+ else return :not_a_value
263
+ end
147
264
  end
148
265
 
149
266
  def extract_values_from_array(ast)
@@ -157,15 +274,15 @@ class MapFormulaeToValues
157
274
  end
158
275
 
159
276
  def formula_value(ast_name,*arguments)
160
- raise NotSupportedException.new("#{ast_name.inspect} function not recognised in #{MapFormulaeToRuby::FUNCTIONS.inspect}") unless MapFormulaeToRuby::FUNCTIONS.has_key?(ast_name)
277
+ raise NotSupportedException.new("#{ast_name} function not recognised in #{MapFormulaeToRuby::FUNCTIONS.inspect}") unless MapFormulaeToRuby::FUNCTIONS.has_key?(ast_name)
161
278
  ast_for_value(@calculator.send(MapFormulaeToRuby::FUNCTIONS[ast_name],*arguments))
162
279
  end
163
280
 
164
281
  def ast_for_value(value)
165
282
  return value if value.is_a?(Array) && value.first.is_a?(Symbol)
166
283
  @replacements_made_in_the_last_pass += 1
167
- case value
168
- when Numeric; [:number,value.inspect]
284
+ ast = case value
285
+ when Numeric; [:number,value]
169
286
  when true; [:boolean_true]
170
287
  when false; [:boolean_false]
171
288
  when Symbol;
@@ -177,6 +294,7 @@ class MapFormulaeToValues
177
294
  else
178
295
  raise NotSupportedException.new("Ast for #{value.inspect} of class #{value.class} not recognised")
179
296
  end
297
+ CachingFormulaParser.map(ast)
180
298
  end
181
299
 
182
300
  end