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.
- checksums.yaml +4 -4
- data/src/commands/excel_to_c.rb +39 -92
- data/src/commands/excel_to_ruby.rb +9 -35
- data/src/commands/excel_to_x.rb +515 -536
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/compile_named_reference_setters.rb +4 -6
- data/src/compile/c/compile_to_c.rb +34 -21
- data/src/compile/c/compile_to_c_header.rb +7 -7
- data/src/compile/c/excel_to_c_runtime.c +8 -4
- data/src/compile/c/map_formulae_to_c.rb +85 -86
- data/src/compile/c/map_values_to_c.rb +7 -1
- data/src/compile/c/map_values_to_c_structs.rb +1 -1
- data/src/compile/ruby/compile_to_ruby.rb +14 -11
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +17 -10
- data/src/compile/ruby/map_formulae_to_ruby.rb +56 -56
- data/src/compile/ruby/map_values_to_ruby.rb +14 -2
- data/src/excel/area.rb +6 -8
- data/src/excel/excel_functions/hlookup.rb +1 -1
- data/src/excel/excel_functions/vlookup.rb +1 -1
- data/src/excel/formula_peg.rb +1 -1
- data/src/excel/formula_peg.txt +1 -1
- data/src/excel/reference.rb +4 -3
- data/src/excel/table.rb +4 -4
- data/src/extract.rb +1 -0
- data/src/extract/check_for_unknown_functions.rb +2 -2
- data/src/extract/extract_array_formulae.rb +9 -9
- data/src/extract/extract_everything.rb +140 -0
- data/src/extract/extract_formulae.rb +30 -20
- data/src/extract/extract_named_references.rb +37 -22
- data/src/extract/extract_relationships.rb +16 -3
- data/src/extract/extract_shared_formulae.rb +8 -11
- data/src/extract/extract_shared_formulae_targets.rb +1 -6
- data/src/extract/extract_shared_strings.rb +21 -8
- data/src/extract/extract_simple_formulae.rb +11 -6
- data/src/extract/extract_table.rb +26 -13
- data/src/extract/extract_values.rb +35 -11
- data/src/extract/extract_worksheet_dimensions.rb +13 -3
- data/src/extract/extract_worksheet_names.rb +16 -3
- data/src/extract/extract_worksheet_table_relationships.rb +16 -4
- data/src/extract/simple_extract_from_xml.rb +9 -11
- data/src/rewrite.rb +3 -0
- data/src/rewrite/ast_copy_formula.rb +5 -1
- data/src/rewrite/ast_expand_array_formulae.rb +71 -59
- data/src/rewrite/caching_formula_parser.rb +110 -0
- data/src/rewrite/rewrite_array_formulae.rb +21 -14
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +41 -13
- data/src/rewrite/rewrite_shared_formulae.rb +17 -18
- data/src/rewrite/rewrite_values_to_ast.rb +2 -0
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +28 -25
- data/src/simplify.rb +1 -0
- data/src/simplify/count_formula_references.rb +22 -23
- data/src/simplify/emergency_array_formula_replace_indirect_bodge.rb +44 -0
- data/src/simplify/identify_dependencies.rb +7 -8
- data/src/simplify/identify_repeated_formula_elements.rb +5 -6
- data/src/simplify/inline_formulae.rb +48 -48
- data/src/simplify/map_formulae_to_values.rb +197 -79
- data/src/simplify/remove_cells.rb +13 -6
- data/src/simplify/replace_arithmetic_on_ranges.rb +42 -28
- data/src/simplify/replace_arrays_with_single_cells.rb +11 -5
- data/src/simplify/replace_column_with_column_number.rb +31 -23
- data/src/simplify/replace_common_elements_in_formulae.rb +16 -17
- data/src/simplify/replace_indirects_with_references.rb +26 -21
- data/src/simplify/replace_named_references.rb +26 -31
- data/src/simplify/replace_offsets_with_references.rb +33 -34
- data/src/simplify/replace_ranges_with_array_literals.rb +48 -20
- data/src/simplify/replace_shared_strings.rb +15 -13
- data/src/simplify/replace_string_join_on_ranges.rb +7 -9
- data/src/simplify/replace_table_references.rb +16 -11
- data/src/simplify/replace_values_with_constants.rb +6 -4
- data/src/simplify/simplify_arithmetic.rb +33 -19
- data/src/simplify/sort_into_calculation_order.rb +13 -13
- data/src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb +21 -13
- metadata +19 -2
@@ -15,21 +15,20 @@ class IdentifyRepeatedFormulaElements
|
|
15
15
|
|
16
16
|
def count(references)
|
17
17
|
@references = references
|
18
|
-
references.each do |
|
19
|
-
|
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
|
29
|
+
return if IGNORE_TYPES.has_key?(ast.first)
|
31
30
|
return if string.length < bothered_threshold
|
32
|
-
@counted_elements[
|
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 :
|
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
|
-
@
|
9
|
-
@inline_ast ||= lambda { |sheet,
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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(
|
23
|
-
|
24
|
-
|
25
|
-
[
|
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
|
-
|
29
|
+
# Otherwise good to map all the other arguments
|
30
|
+
ast.each { |a| map(a) }
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
#
|
50
|
-
def cell(
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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 :
|
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
|
-
@
|
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
|
23
|
-
@
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
63
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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 =
|
84
|
-
if values.any? { |a| a == :not_a_value }
|
85
|
-
|
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
|
-
|
93
|
-
|
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
|
-
|
99
|
-
|
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 =
|
102
|
-
row_as_number = value(
|
103
|
-
column_as_number = value(
|
143
|
+
array_mapped = ast[2]
|
144
|
+
row_as_number = value(ast[3])
|
145
|
+
column_as_number = value(ast[4])
|
104
146
|
|
105
|
-
return
|
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(
|
108
|
-
return
|
150
|
+
array_as_values = array_as_values(array_mapped)
|
151
|
+
return unless array_as_values
|
109
152
|
|
110
|
-
result = @calculator.send(MapFormulaeToRuby::FUNCTIONS[
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
146
|
-
|
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
|
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
|
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
|