excel_to_code 0.0.1

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 (123) hide show
  1. data/README +41 -0
  2. data/bin/excel_to_c +63 -0
  3. data/bin/excel_to_ruby +9 -0
  4. data/src/commands.rb +2 -0
  5. data/src/commands/excel_to_c.rb +858 -0
  6. data/src/commands/excel_to_ruby.rb +620 -0
  7. data/src/compile.rb +2 -0
  8. data/src/compile/c.rb +5 -0
  9. data/src/compile/c/compile_to_c.rb +62 -0
  10. data/src/compile/c/compile_to_c_header.rb +26 -0
  11. data/src/compile/c/compile_to_c_unit_test.rb +42 -0
  12. data/src/compile/c/excel_to_c_runtime.c +2029 -0
  13. data/src/compile/c/map_formulae_to_c.rb +184 -0
  14. data/src/compile/c/map_sheet_names_to_c_names.rb +19 -0
  15. data/src/compile/c/map_values_to_c.rb +85 -0
  16. data/src/compile/c/map_values_to_c_structs.rb +37 -0
  17. data/src/compile/ruby.rb +3 -0
  18. data/src/compile/ruby/compile_to_ruby.rb +33 -0
  19. data/src/compile/ruby/compile_to_ruby_unit_test.rb +28 -0
  20. data/src/compile/ruby/excel_to_ruby_runtime.rb +1 -0
  21. data/src/compile/ruby/map_formulae_to_ruby.rb +95 -0
  22. data/src/compile/ruby/map_sheet_names_to_ruby_names.rb +19 -0
  23. data/src/compile/ruby/map_values_to_ruby.rb +65 -0
  24. data/src/excel.rb +5 -0
  25. data/src/excel/area.rb +93 -0
  26. data/src/excel/excel_functions.rb +84 -0
  27. data/src/excel/excel_functions/abs.rb +14 -0
  28. data/src/excel/excel_functions/add.rb +18 -0
  29. data/src/excel/excel_functions/and.rb +30 -0
  30. data/src/excel/excel_functions/apply_to_range.rb +17 -0
  31. data/src/excel/excel_functions/average.rb +12 -0
  32. data/src/excel/excel_functions/choose.rb +18 -0
  33. data/src/excel/excel_functions/cosh.rb +9 -0
  34. data/src/excel/excel_functions/count.rb +9 -0
  35. data/src/excel/excel_functions/counta.rb +8 -0
  36. data/src/excel/excel_functions/divide.rb +23 -0
  37. data/src/excel/excel_functions/excel_equal.rb +20 -0
  38. data/src/excel/excel_functions/excel_if.rb +8 -0
  39. data/src/excel/excel_functions/excel_match.rb +51 -0
  40. data/src/excel/excel_functions/find.rb +39 -0
  41. data/src/excel/excel_functions/iferror.rb +10 -0
  42. data/src/excel/excel_functions/index.rb +48 -0
  43. data/src/excel/excel_functions/left.rb +12 -0
  44. data/src/excel/excel_functions/less_than.rb +26 -0
  45. data/src/excel/excel_functions/less_than_or_equal.rb +26 -0
  46. data/src/excel/excel_functions/max.rb +12 -0
  47. data/src/excel/excel_functions/min.rb +12 -0
  48. data/src/excel/excel_functions/mod.rb +15 -0
  49. data/src/excel/excel_functions/more_than.rb +26 -0
  50. data/src/excel/excel_functions/more_than_or_equal.rb +26 -0
  51. data/src/excel/excel_functions/multiply.rb +24 -0
  52. data/src/excel/excel_functions/negative.rb +12 -0
  53. data/src/excel/excel_functions/not_equal.rb +19 -0
  54. data/src/excel/excel_functions/number_argument.rb +30 -0
  55. data/src/excel/excel_functions/pi.rb +7 -0
  56. data/src/excel/excel_functions/pmt.rb +16 -0
  57. data/src/excel/excel_functions/power.rb +18 -0
  58. data/src/excel/excel_functions/round.rb +13 -0
  59. data/src/excel/excel_functions/rounddown.rb +14 -0
  60. data/src/excel/excel_functions/roundup.rb +17 -0
  61. data/src/excel/excel_functions/string_join.rb +19 -0
  62. data/src/excel/excel_functions/subtotal.rb +13 -0
  63. data/src/excel/excel_functions/subtract.rb +18 -0
  64. data/src/excel/excel_functions/sum.rb +8 -0
  65. data/src/excel/excel_functions/sumif.rb +7 -0
  66. data/src/excel/excel_functions/sumifs.rb +74 -0
  67. data/src/excel/excel_functions/sumproduct.rb +32 -0
  68. data/src/excel/excel_functions/vlookup.rb +49 -0
  69. data/src/excel/formula_peg.rb +238 -0
  70. data/src/excel/formula_peg.txt +45 -0
  71. data/src/excel/reference.rb +56 -0
  72. data/src/excel/table.rb +108 -0
  73. data/src/excel_to_code.rb +7 -0
  74. data/src/extract.rb +13 -0
  75. data/src/extract/check_for_unknown_functions.rb +20 -0
  76. data/src/extract/extract_array_formulae.rb +23 -0
  77. data/src/extract/extract_formulae.rb +36 -0
  78. data/src/extract/extract_named_references.rb +38 -0
  79. data/src/extract/extract_relationships.rb +10 -0
  80. data/src/extract/extract_shared_formulae.rb +23 -0
  81. data/src/extract/extract_shared_strings.rb +20 -0
  82. data/src/extract/extract_simple_formulae.rb +18 -0
  83. data/src/extract/extract_table.rb +24 -0
  84. data/src/extract/extract_values.rb +29 -0
  85. data/src/extract/extract_worksheet_dimensions.rb +11 -0
  86. data/src/extract/extract_worksheet_names.rb +10 -0
  87. data/src/extract/extract_worksheet_table_relationships.rb +10 -0
  88. data/src/extract/simple_extract_from_xml.rb +19 -0
  89. data/src/rewrite.rb +10 -0
  90. data/src/rewrite/ast_copy_formula.rb +42 -0
  91. data/src/rewrite/ast_expand_array_formulae.rb +180 -0
  92. data/src/rewrite/rewrite_array_formulae.rb +71 -0
  93. data/src/rewrite/rewrite_array_formulae_to_arrays.rb +18 -0
  94. data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +56 -0
  95. data/src/rewrite/rewrite_formulae_to_ast.rb +24 -0
  96. data/src/rewrite/rewrite_merge_formulae_and_values.rb +18 -0
  97. data/src/rewrite/rewrite_relationship_id_to_filename.rb +22 -0
  98. data/src/rewrite/rewrite_shared_formulae.rb +38 -0
  99. data/src/rewrite/rewrite_values_to_ast.rb +28 -0
  100. data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +90 -0
  101. data/src/rewrite/rewrite_worksheet_names.rb +20 -0
  102. data/src/simplify.rb +16 -0
  103. data/src/simplify/count_formula_references.rb +58 -0
  104. data/src/simplify/identify_dependencies.rb +56 -0
  105. data/src/simplify/identify_repeated_formula_elements.rb +37 -0
  106. data/src/simplify/inline_formulae.rb +77 -0
  107. data/src/simplify/map_formulae_to_values.rb +157 -0
  108. data/src/simplify/remove_cells.rb +18 -0
  109. data/src/simplify/replace_arrays_with_single_cells.rb +27 -0
  110. data/src/simplify/replace_blanks.rb +58 -0
  111. data/src/simplify/replace_common_elements_in_formulae.rb +19 -0
  112. data/src/simplify/replace_formulae_with_calculated_values.rb +21 -0
  113. data/src/simplify/replace_indirects_with_references.rb +44 -0
  114. data/src/simplify/replace_named_references.rb +82 -0
  115. data/src/simplify/replace_ranges_with_array_literals.rb +54 -0
  116. data/src/simplify/replace_shared_strings.rb +49 -0
  117. data/src/simplify/replace_table_references.rb +71 -0
  118. data/src/simplify/replace_values_with_constants.rb +47 -0
  119. data/src/simplify/simplify_arithmetic.rb +54 -0
  120. data/src/util.rb +2 -0
  121. data/src/util/not_supported_exception.rb +2 -0
  122. data/src/util/try.rb +9 -0
  123. metadata +207 -0
@@ -0,0 +1,184 @@
1
+ require_relative 'map_values_to_c'
2
+
3
+ class MapFormulaeToC < MapValuesToC
4
+
5
+ attr_accessor :sheet_names
6
+ attr_accessor :worksheet
7
+ attr_reader :initializers
8
+ attr_reader :counter
9
+
10
+ def initialize
11
+ reset
12
+ end
13
+
14
+ def reset
15
+ @initializers = []
16
+ @counter = 0
17
+ end
18
+
19
+ FUNCTIONS = {
20
+ '*' => 'multiply',
21
+ '+' => 'add',
22
+ '-' => 'subtract',
23
+ '/' => 'divide',
24
+ '<' => 'less_than',
25
+ '<=' => 'less_than_or_equal',
26
+ '<>' => 'not_equal',
27
+ '=' => 'excel_equal',
28
+ '>' => 'more_than',
29
+ '>=' => 'more_than_or_equal',
30
+ 'ABS' => 'excel_abs',
31
+ 'AND' => 'excel_and',
32
+ 'AVERAGE' => 'average',
33
+ 'CHOOSE' => 'choose',
34
+ 'COSH' => 'cosh',
35
+ 'COUNT' => 'count',
36
+ 'COUNTA' => 'counta',
37
+ 'FIND2' => 'find_2',
38
+ 'FIND3' => 'find',
39
+ 'IF2' => 'excel_if_2',
40
+ 'IF3' => 'excel_if',
41
+ 'IFERROR' => 'iferror',
42
+ 'INDEX2' => 'excel_index_2',
43
+ 'INDEX3' => 'excel_index',
44
+ 'LEFT1' => 'left_1',
45
+ 'LEFT2' => 'left',
46
+ 'MATCH2' => 'excel_match_2',
47
+ 'MATCH3' => 'excel_match',
48
+ 'MAX' => 'max',
49
+ 'MIN' => 'min',
50
+ 'MOD' => 'mod',
51
+ 'PMT' => 'pmt',
52
+ 'ROUND' => 'excel_round',
53
+ 'ROUNDDOWN' => 'rounddown',
54
+ 'ROUNDUP' => 'roundup',
55
+ 'string_join' => 'string_join',
56
+ 'SUBTOTAL' => 'subtotal',
57
+ 'SUM' => 'sum',
58
+ 'SUMIF2' => 'sumif_2',
59
+ 'SUMIF3' => 'sumif',
60
+ 'SUMIFS' => 'sumifs',
61
+ 'SUMPRODUCT' => 'sumproduct',
62
+ 'VLOOKUP3' => 'vlookup_3',
63
+ 'VLOOKUP4' => 'vlookup',
64
+ '^' => 'power'
65
+ }
66
+
67
+ def prefix(symbol,ast)
68
+ return map(ast) if symbol == "+"
69
+ return "negative(#{map(ast)})"
70
+ end
71
+
72
+ def brackets(*contents)
73
+ "(#{contents.map { |a| map(a) }.join(',')})"
74
+ end
75
+
76
+ def arithmetic(left,operator,right)
77
+ "#{FUNCTIONS[operator.last]}(#{map(left)},#{map(right)})"
78
+ end
79
+
80
+ def string_join(*strings)
81
+ any_number_of_argument_function('string_join',strings)
82
+ end
83
+
84
+ def comparison(left,operator,right)
85
+ "#{FUNCTIONS[operator.last]}(#{map(left)},#{map(right)})"
86
+ end
87
+
88
+ def function(function_name,*arguments)
89
+ # Some functions are special cases
90
+ if self.respond_to?("function_#{function_name.downcase}")
91
+ send("function_#{function_name.downcase}",*arguments)
92
+ # Some arguments can take any number of arguments, which we need to treat separately
93
+ elsif FUNCTIONS_WITH_ANY_NUMBER_OF_ARGUMENTS.include?(function_name)
94
+ any_number_of_argument_function(function_name,arguments)
95
+
96
+ # Check for whether this function has variants based on the number of arguments
97
+ elsif FUNCTIONS.has_key?("#{function_name}#{arguments.size}")
98
+ "#{FUNCTIONS["#{function_name}#{arguments.size}"]}(#{arguments.map { |a| map(a) }.join(",")})"
99
+
100
+ # Then check for whether it is just a standard type
101
+ elsif FUNCTIONS.has_key?(function_name)
102
+ "#{FUNCTIONS[function_name]}(#{arguments.map { |a| map(a) }.join(",")})"
103
+
104
+ else
105
+ raise NotSupportedException.new("Function #{function_name} with #{arguments.size} arguments not supported")
106
+ end
107
+ end
108
+
109
+ FUNCTIONS_WITH_ANY_NUMBER_OF_ARGUMENTS = %w{SUM AND AVERAGE COUNT COUNTA MAX MIN SUMPRODUCT}
110
+
111
+ def function_pi()
112
+ "M_PI"
113
+ end
114
+
115
+ def function_choose(index,*arguments)
116
+ "#{FUNCTIONS["CHOOSE"]}(#{map(index)}, #{map_arguments_to_array(arguments)})"
117
+ end
118
+
119
+ def function_subtotal(type,*arguments)
120
+ "#{FUNCTIONS["SUBTOTAL"]}(#{map(type)}, #{map_arguments_to_array(arguments)})"
121
+ end
122
+
123
+ def function_sumifs(sum_range,*criteria)
124
+ "#{FUNCTIONS["SUMIFS"]}(#{map(sum_range)}, #{map_arguments_to_array(criteria)})"
125
+ end
126
+
127
+
128
+ def any_number_of_argument_function(function_name,arguments)
129
+ "#{FUNCTIONS[function_name]}(#{map_arguments_to_array(arguments)})"
130
+ end
131
+
132
+ def map_arguments_to_array(arguments)
133
+ # First we have to create an excel array
134
+ array_name = "array#{@counter}"
135
+ @counter +=1
136
+ arguments_size = arguments.size
137
+ arguments = arguments.map { |a| map(a) }.join(',')
138
+ initializers << "ExcelValue #{array_name}[] = {#{arguments}};"
139
+ "#{arguments_size}, #{array_name}"
140
+ end
141
+
142
+ def cell(reference)
143
+ # FIXME: What a cludge.
144
+ if reference =~ /common\d+/
145
+ "_#{reference}()"
146
+ else
147
+ reference.downcase.gsub('$','')
148
+ end
149
+ end
150
+
151
+ def sheet_reference(sheet,reference)
152
+ "#{sheet_names[sheet]}_#{map(reference).downcase}()"
153
+ end
154
+
155
+ def array(*rows)
156
+ # Make sure we get the right dimensions
157
+ number_of_rows = rows.size
158
+ number_of_columns = rows.max { |r| r.size }.size - 1
159
+
160
+ # First we have to create an excel array
161
+ array_name = "array#{@counter}"
162
+ @counter +=1
163
+
164
+ cells = rows.map do |r|
165
+ r.shift if r.first == :row
166
+ r.map do |c|
167
+ map(c)
168
+ end
169
+ end.flatten
170
+
171
+ initializers << "static ExcelValue #{array_name}[#{cells.size}];"
172
+ cells.each_with_index do |c,i|
173
+ initializers << "#{array_name}[#{i}] = #{c};"
174
+ end
175
+
176
+ # Then we need to assign it to an excel value
177
+ range_name = array_name+"_ev"
178
+ initializers << "ExcelValue #{range_name} = new_excel_range(#{array_name},#{number_of_rows},#{number_of_columns});"
179
+
180
+ range_name
181
+ end
182
+
183
+
184
+ end
@@ -0,0 +1,19 @@
1
+ class MapSheetNamesToCNames
2
+
3
+ def self.rewrite(*args)
4
+ self.new.rewrite(*args)
5
+ end
6
+
7
+ def rewrite(input,output)
8
+ c_names_assigned = {}
9
+ input.lines do |line|
10
+ excel_worksheet_name = line.split("\t").first
11
+ c_name = excel_worksheet_name.downcase.gsub(/[^a-z0-9]+/,'_')
12
+ c_name = "s"+c_name if c_name[0] !~ /[a-z]/
13
+ c_name = ruby_name + "2" if c_names_assigned.has_key?(c_name)
14
+ c_name.succ! while c_names_assigned.has_key?(c_name)
15
+ output.puts "#{excel_worksheet_name}\t#{c_name}"
16
+ c_names_assigned[c_name] = excel_worksheet_name
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,85 @@
1
+ require_relative '../../util/not_supported_exception'
2
+
3
+ class MapValuesToC
4
+
5
+ def map(ast)
6
+ if ast.is_a?(Array)
7
+ operator = ast[0]
8
+ if respond_to?(operator)
9
+ send(operator,*ast[1..-1])
10
+ else
11
+ raise NotSupportedException.new("#{operator} in #{ast.inspect} not supported")
12
+ end
13
+ else
14
+ raise NotSupportedException.new("#{ast} not supported")
15
+ end
16
+ end
17
+
18
+ def blank
19
+ "BLANK"
20
+ end
21
+
22
+ def constant(name)
23
+ name
24
+ end
25
+
26
+ alias :null :blank
27
+
28
+ def number(text)
29
+ n = case text
30
+ when /\./
31
+ text.to_f.to_s
32
+ when /e/i
33
+ text.to_f.to_s
34
+ else
35
+ text.to_i.to_s
36
+ end
37
+
38
+ case n
39
+ when 0; "ZERO"
40
+ when 1; "ONE"
41
+ when 2; "TWO"
42
+ when 3; "THREE"
43
+ when 4; "FOUR"
44
+ when 5; "FIVE"
45
+ when 6; "SIX"
46
+ when 7; "SEVEN"
47
+ when 8; "EIGHT"
48
+ when 9; "NINE"
49
+ when 10; "TEN"
50
+ else
51
+ "new_excel_number(#{n})"
52
+ end
53
+ end
54
+
55
+ def percentage(text)
56
+ "new_excel_number(#{(text.to_f / 100.0).to_s})"
57
+ end
58
+
59
+ def string(text)
60
+ "new_excel_string(#{text.inspect})"
61
+ end
62
+
63
+ ERRORS = {
64
+ "#NAME?" => "NAME",
65
+ "#VALUE!" => "VALUE",
66
+ "#DIV/0!" => "DIV0",
67
+ "#REF!" => "REF",
68
+ "#N/A" => "NA"
69
+ }
70
+
71
+ REVERSE_ERRORS = ERRORS.invert
72
+
73
+ def error(text)
74
+ ERRORS[text] || (raise NotSupportedException.new("#{text.inspect} error not recognised"))
75
+ end
76
+
77
+ def boolean_true
78
+ "TRUE"
79
+ end
80
+
81
+ def boolean_false
82
+ "FALSE"
83
+ end
84
+
85
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../../util/not_supported_exception'
2
+
3
+ class MapValuesToCStructs
4
+
5
+ def map(ast)
6
+ if ast.is_a?(Array)
7
+ operator = ast[0]
8
+ if respond_to?(operator)
9
+ send(operator,*ast[1..-1])
10
+ else
11
+ raise NotSupportedException.new("#{operator} in #{ast.inspect} not supported")
12
+ end
13
+ else
14
+ raise NotSupportedException.new("#{ast} not supported")
15
+ end
16
+ end
17
+
18
+ def number(text)
19
+ n = case text
20
+ when /\./
21
+ text.to_f.to_s
22
+ when /e/i
23
+ text.to_f.to_s
24
+ else
25
+ text.to_i.to_s
26
+ end
27
+ "{.type = ExcelNumber, .number = #{n}}"
28
+ end
29
+
30
+ def percentage(text)
31
+ "{.type = ExcelNumber, .number = #{(text.to_f / 100.0).to_s}}"
32
+ end
33
+
34
+ def string(text)
35
+ "{.type = ExcelString, .string = #{text.inspect}}"
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'ruby/compile_to_ruby'
2
+ require_relative 'ruby/compile_to_ruby_unit_test'
3
+ require_relative 'ruby/map_sheet_names_to_ruby_names'
@@ -0,0 +1,33 @@
1
+ require_relative 'map_formulae_to_ruby'
2
+
3
+ class CompileToRuby
4
+
5
+ attr_accessor :settable
6
+ attr_accessor :worksheet
7
+
8
+ def self.rewrite(*args)
9
+ self.new.rewrite(*args)
10
+ end
11
+
12
+ def rewrite(input,sheet_names_file,output,defaults = nil)
13
+ self.settable ||= lambda { |ref| false }
14
+ mapper = MapFormulaeToRuby.new
15
+ mapper.worksheet = worksheet
16
+ mapper.sheet_names = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}]
17
+ input.lines do |line|
18
+ begin
19
+ ref, formula = line.split("\t")
20
+ if settable.call(ref)
21
+ output.puts " attr_accessor :#{ref.downcase} # Default: #{mapper.map(eval(formula))}"
22
+ defaults.puts " @#{ref.downcase} = #{mapper.map(eval(formula))}" if defaults
23
+ else
24
+ output.puts " def #{ref.downcase}; @#{ref.downcase} ||= #{mapper.map(eval(formula))}; end"
25
+ end
26
+ rescue Exception => e
27
+ puts "Exception at line #{line}"
28
+ raise
29
+ end
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,28 @@
1
+ require_relative "map_values_to_ruby"
2
+
3
+ class CompileToRubyUnitTest
4
+
5
+ def self.rewrite(*args)
6
+ self.new.rewrite(*args)
7
+ end
8
+
9
+ def rewrite(input,output)
10
+ mapper = MapValuesToRuby.new
11
+ input.lines do |line|
12
+ ref, formula = line.split("\t")
13
+ ast = eval(formula)
14
+ value = mapper.map(ast)
15
+ full_reference = "worksheet.#{ref.downcase}"
16
+ if ast.first == :number
17
+ if value == "0" # Need to do a slightly different test, because needs to pass if nil returned, as well as zero
18
+ output.puts " def test_#{ref.downcase}; assert_in_epsilon(#{value},#{full_reference} || 0); end"
19
+ else
20
+ output.puts " def test_#{ref.downcase}; assert_in_epsilon(#{value},#{full_reference}); end"
21
+ end
22
+ else
23
+ output.puts " def test_#{ref.downcase}; assert_equal(#{value},#{full_reference}); end"
24
+ end
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1 @@
1
+ require_relative '../../excel/excel_functions'
@@ -0,0 +1,95 @@
1
+ require_relative 'map_values_to_ruby'
2
+
3
+ class MapFormulaeToRuby < MapValuesToRuby
4
+
5
+ attr_accessor :sheet_names
6
+ attr_accessor :worksheet
7
+
8
+ FUNCTIONS = {
9
+ '*' => 'multiply',
10
+ '+' => 'add',
11
+ '-' => 'subtract',
12
+ '/' => 'divide',
13
+ '<' => 'less_than?',
14
+ '<=' => 'less_than_or_equal?',
15
+ '<>' => 'not_equal?',
16
+ '=' => 'excel_equal?',
17
+ '>' => 'more_than?',
18
+ '>=' => 'more_than_or_equal?',
19
+ 'ABS' => 'abs',
20
+ 'AND' => 'excel_and',
21
+ 'AVERAGE' => 'average',
22
+ 'CHOOSE' => 'choose',
23
+ 'COSH' => 'cosh',
24
+ 'COUNT' => 'count',
25
+ 'COUNTA' => 'counta',
26
+ 'FIND' => 'find',
27
+ 'IF' => 'excel_if',
28
+ 'IFERROR' => 'iferror',
29
+ 'INDEX' => 'index',
30
+ 'LEFT' => 'left',
31
+ 'MATCH' => 'excel_match',
32
+ 'MAX' => 'max',
33
+ 'MIN' => 'min',
34
+ 'MOD' => 'mod',
35
+ 'PI' => 'pi',
36
+ 'PMT' => 'pmt',
37
+ 'ROUND' => 'round',
38
+ 'ROUNDDOWN' => 'rounddown',
39
+ 'ROUNDUP' => 'roundup',
40
+ 'SUBTOTAL' => 'subtotal',
41
+ 'SUM' => 'sum',
42
+ 'SUMIF' => 'sumif',
43
+ 'SUMIFS' => 'sumifs',
44
+ 'SUMPRODUCT' => 'sumproduct',
45
+ 'VLOOKUP' => 'vlookup',
46
+ '^' => 'power'
47
+ }
48
+
49
+ def prefix(symbol,ast)
50
+ return map(ast) if symbol == "+"
51
+ return "negative(#{map(ast)})"
52
+ end
53
+
54
+ def brackets(*contents)
55
+ "(#{contents.map { |a| map(a) }.join(',')})"
56
+ end
57
+
58
+ def arithmetic(left,operator,right)
59
+ "#{FUNCTIONS[operator.last]}(#{map(left)},#{map(right)})"
60
+ end
61
+
62
+ def string_join(*strings)
63
+ "string_join(#{strings.map {|a| map(a)}.join(',')})"
64
+ end
65
+
66
+ def comparison(left,operator,right)
67
+ "#{FUNCTIONS[operator.last]}(#{map(left)},#{map(right)})"
68
+ end
69
+
70
+ def function(function_name,*arguments)
71
+ if FUNCTIONS.has_key?(function_name)
72
+ "#{FUNCTIONS[function_name]}(#{arguments.map { |a| map(a) }.join(",")})"
73
+ else
74
+ raise NotSupportedException.new("Function #{function_name} not supported")
75
+ end
76
+ end
77
+
78
+ def cell(reference)
79
+ reference.downcase.gsub('$','')
80
+ end
81
+
82
+ def sheet_reference(sheet,reference)
83
+ return map(reference) if worksheet && worksheet == sheet
84
+ "#{sheet_names[sheet]}.#{map(reference)}"
85
+ end
86
+
87
+ def array(*rows)
88
+ "[#{rows.map {|r| map(r)}.join(",")}]"
89
+ end
90
+
91
+ def row(*cells)
92
+ "[#{cells.map {|r| map(r)}.join(",")}]"
93
+ end
94
+
95
+ end