excel_to_code 0.1.23 → 0.2.0
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.
- 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
|