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.
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
@@ -16,7 +16,7 @@ class MapValuesToCStructs
16
16
  end
17
17
 
18
18
  def number(text)
19
- n = case text
19
+ n = case text.to_s
20
20
  when /\./
21
21
  text.to_f.to_s
22
22
  when /e/i
@@ -4,29 +4,32 @@ class CompileToRuby
4
4
 
5
5
  attr_accessor :settable
6
6
  attr_accessor :worksheet
7
+ attr_accessor :defaults
7
8
 
8
9
  def self.rewrite(*args)
9
10
  self.new.rewrite(*args)
10
11
  end
11
12
 
12
- def rewrite(input,sheet_names_file,output,defaults = nil)
13
+ def rewrite(input, sheet_names, output)
13
14
  self.settable ||= lambda { |ref| false }
15
+ self.defaults ||= []
14
16
  mapper = MapFormulaeToRuby.new
15
- mapper.worksheet = worksheet
16
- mapper.sheet_names = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}]
17
- c_name = mapper.sheet_names[worksheet]
18
- input.each_line do |line|
17
+ mapper.sheet_names = sheet_names
18
+ input.each do |ref, ast|
19
19
  begin
20
- ref, formula = line.split("\t")
21
- name = c_name ? "#{c_name}_#{ref.downcase}" : ref.downcase
20
+ worksheet = ref.first.to_s
21
+ cell = ref.last
22
+ mapper.worksheet = worksheet
23
+ worksheet_c_name = mapper.sheet_names[worksheet] || worksheet.to_s
24
+ name = worksheet_c_name.length > 0 ? "#{worksheet_c_name}_#{cell.downcase}" : cell.downcase
22
25
  if settable.call(ref)
23
- output.puts " attr_accessor :#{name} # Default: #{mapper.map(eval(formula))}"
24
- defaults.puts " @#{name} = #{mapper.map(eval(formula))}" if defaults
26
+ output.puts " attr_accessor :#{name} # Default: #{mapper.map(ast)}"
27
+ defaults << " @#{name} = #{mapper.map(ast)}"
25
28
  else
26
- output.puts " def #{name}; @#{name} ||= #{mapper.map(eval(formula))}; end"
29
+ output.puts " def #{name}; @#{name} ||= #{mapper.map(ast)}; end"
27
30
  end
28
31
  rescue Exception => e
29
- puts "Exception at line #{line}"
32
+ puts "Exception at #{ref} => #{ast}"
30
33
  raise
31
34
  end
32
35
  end
@@ -7,23 +7,30 @@ class CompileToRubyUnitTest
7
7
  attr_accessor :delta
8
8
 
9
9
  def initialize
10
- @epsilon = 0.001
11
- @delta = 0.001
10
+ @epsilon = 0.002
11
+ @delta = 0.002
12
12
  end
13
13
 
14
14
  def self.rewrite(*args)
15
15
  self.new.rewrite(*args)
16
16
  end
17
17
 
18
- def rewrite(input, sloppy, o)
18
+ def rewrite(input, sloppy, sheet_names, constants, o)
19
19
  mapper = MapValuesToRuby.new
20
- input.each_line do |line|
21
- c_name, ref, formula = line.split("\t")
22
- ast = eval(formula)
20
+ mapper.constants = constants
21
+ input.each do |ref, ast|
22
+ worksheet_c_name = sheet_names[ref.first.to_s] || ref.first.to_s #FIXME: Need to make it the actual c_name
23
+ cell = ref.last
23
24
  value = mapper.map(ast)
24
- full_reference = "worksheet.#{c_name}_#{ref.downcase}"
25
- test_name = "test_#{c_name}_#{ref.downcase}"
26
- case ast.first
25
+ full_reference = worksheet_c_name.length > 0 ? "worksheet.#{worksheet_c_name}_#{cell.downcase}" : "worksheet.#{cell.downcase}"
26
+ test_name = "test_#{worksheet_c_name}_#{cell.downcase}"
27
+ if ast.first == :constant
28
+ type = constants[ast[1]][0] || :constant
29
+ else
30
+ type = ast.first
31
+ end
32
+
33
+ case type
27
34
  when :blank
28
35
  if sloppy
29
36
  o.puts " def #{test_name}; assert_includes([nil, 0], #{full_reference}); end"
@@ -33,7 +40,7 @@ class CompileToRubyUnitTest
33
40
  when :number
34
41
  if sloppy
35
42
  if value.to_f.abs <= 1
36
- if value == "0"
43
+ if value.to_f == 0
37
44
  o.puts " def #{test_name}; assert_in_delta(#{value}, (#{full_reference}||0), #{delta}); end"
38
45
  else
39
46
  o.puts " def #{test_name}; assert_in_delta(#{value}, #{full_reference}, #{delta}); end"
@@ -6,61 +6,61 @@ class MapFormulaeToRuby < MapValuesToRuby
6
6
  attr_accessor :worksheet
7
7
 
8
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
- 'CELL' => 'cell',
23
- 'CHOOSE' => 'choose',
24
- 'CONCATENATE' => 'string_join',
25
- 'COSH' => 'cosh',
26
- 'COUNT' => 'count',
27
- 'COUNTA' => 'counta',
28
- 'FIND' => 'find',
29
- 'HLOOKUP' => 'hlookup',
30
- 'IF' => 'excel_if',
31
- 'IFERROR' => 'iferror',
32
- 'INDEX' => 'index',
33
- 'INT' => 'int',
34
- 'ISNUMBER' => 'isnumber',
35
- 'LARGE' => 'large',
36
- 'LEFT' => 'left',
37
- 'LEN' => 'len',
38
- 'LOG' => 'log',
39
- 'MATCH' => 'excel_match',
40
- 'MAX' => 'max',
41
- 'MID' => 'mid',
42
- 'MIN' => 'min',
43
- 'MMULT' => 'mmult',
44
- 'MOD' => 'mod',
45
- 'PI' => 'pi',
46
- 'PMT' => 'pmt',
47
- 'POWER' => 'power',
48
- 'PV' => 'pv',
49
- 'RANK' => 'rank',
50
- 'RIGHT' => 'right',
51
- 'ROUND' => 'round',
52
- 'ROUNDDOWN' => 'rounddown',
53
- 'ROUNDUP' => 'roundup',
54
- 'SUBSTITUTE' => 'substitute',
55
- 'SUBTOTAL' => 'subtotal',
56
- 'SUM' => 'sum',
57
- 'SUMIF' => 'sumif',
58
- 'SUMIFS' => 'sumifs',
59
- 'SUMPRODUCT' => 'sumproduct',
60
- 'TEXT' => 'text',
61
- 'TRIM' => 'trim',
62
- 'VLOOKUP' => 'vlookup',
63
- '^' => 'power'
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
+ :'CELL' => 'cell',
23
+ :'CHOOSE' => 'choose',
24
+ :'CONCATENATE' => 'string_join',
25
+ :'COSH' => 'cosh',
26
+ :'COUNT' => 'count',
27
+ :'COUNTA' => 'counta',
28
+ :'FIND' => 'find',
29
+ :'HLOOKUP' => 'hlookup',
30
+ :'IF' => 'excel_if',
31
+ :'IFERROR' => 'iferror',
32
+ :'INDEX' => 'index',
33
+ :'INT' => 'int',
34
+ :'ISNUMBER' => 'isnumber',
35
+ :'LARGE' => 'large',
36
+ :'LEFT' => 'left',
37
+ :'LEN' => 'len',
38
+ :'LOG' => 'log',
39
+ :'MATCH' => 'excel_match',
40
+ :'MAX' => 'max',
41
+ :'MID' => 'mid',
42
+ :'MIN' => 'min',
43
+ :'MMULT' => 'mmult',
44
+ :'MOD' => 'mod',
45
+ :'PI' => 'pi',
46
+ :'PMT' => 'pmt',
47
+ :'POWER' => 'power',
48
+ :'PV' => 'pv',
49
+ :'RANK' => 'rank',
50
+ :'RIGHT' => 'right',
51
+ :'ROUND' => 'round',
52
+ :'ROUNDDOWN' => 'rounddown',
53
+ :'ROUNDUP' => 'roundup',
54
+ :'SUBSTITUTE' => 'substitute',
55
+ :'SUBTOTAL' => 'subtotal',
56
+ :'SUM' => 'sum',
57
+ :'SUMIF' => 'sumif',
58
+ :'SUMIFS' => 'sumifs',
59
+ :'SUMPRODUCT' => 'sumproduct',
60
+ :'TEXT' => 'text',
61
+ :'TRIM' => 'trim',
62
+ :'VLOOKUP' => 'vlookup',
63
+ :'^' => 'power'
64
64
  }
65
65
 
66
66
  def prefix(symbol,ast)
@@ -93,7 +93,7 @@ class MapFormulaeToRuby < MapValuesToRuby
93
93
  end
94
94
 
95
95
  def cell(reference)
96
- reference.downcase.gsub('$','')
96
+ reference.to_s.downcase.gsub('$','')
97
97
  end
98
98
 
99
99
  def sheet_reference(sheet,reference)
@@ -2,6 +2,8 @@ require_relative '../../util/not_supported_exception'
2
2
 
3
3
  class MapValuesToRuby
4
4
 
5
+ attr_accessor :constants
6
+
5
7
  def map(ast)
6
8
  if ast.is_a?(Array)
7
9
  operator = ast[0]
@@ -19,10 +21,14 @@ class MapValuesToRuby
19
21
  "nil"
20
22
  end
21
23
 
24
+ def constant(constant)
25
+ map(constants[constant])
26
+ end
27
+
22
28
  alias :null :blank
23
29
 
24
30
  def number(text)
25
- case text
31
+ case text.to_s
26
32
  when /\./
27
33
  text.to_f.to_s
28
34
  when /e/i
@@ -46,7 +52,13 @@ class MapValuesToRuby
46
52
  "#DIV/0!" => ":div0",
47
53
  "#REF!" => ":ref",
48
54
  "#N/A" => ":na",
49
- "#NUM!" => ":num"
55
+ "#NUM!" => ":num",
56
+ :"#NAME?" => ":name",
57
+ :"#VALUE!" => ":value",
58
+ :"#DIV/0!" => ":div0",
59
+ :"#REF!" => ":ref",
60
+ :"#N/A" => ":na",
61
+ :"#NUM!" => ":num"
50
62
  }
51
63
 
52
64
  REVERSE_ERRORS = ERRORS.invert
data/src/excel/area.rb CHANGED
@@ -10,7 +10,7 @@ class Area < String
10
10
 
11
11
  # This is so that we only have one instance of a given reference specified by its variables
12
12
  def Area.for(text)
13
- @@areas_for_text[text]
13
+ @@areas_for_text[text.to_s]
14
14
  end
15
15
 
16
16
  attr_reader :excel_start, :excel_finish
@@ -59,17 +59,15 @@ class Area < String
59
59
  def to_array_literal(sheet = nil)
60
60
  calculate_excel_variables
61
61
  unfixed_start = @excel_start.unfix
62
+ fc = CachingFormulaParser.instance
62
63
  [:array,
63
64
  *(0.upto(height).map do |row|
64
65
  [:row,
65
66
  *(0.upto(width).map do |column|
66
67
  if sheet
67
- [:sheet_reference,
68
- sheet,
69
- [:cell,
70
- unfixed_start.offset(row,column)
71
- ]
72
- ]
68
+ fc.sheet_reference(
69
+ [:sheet_reference, sheet, [:cell, unfixed_start.offset(row,column)]]
70
+ )
73
71
  else
74
72
  [:cell,
75
73
  unfixed_start.offset(row,column)
@@ -90,4 +88,4 @@ class Area < String
90
88
  true
91
89
  end
92
90
 
93
- end
91
+ end
@@ -23,7 +23,7 @@ module ExcelFunctions
23
23
  next if lookup_value.is_a?(String) && !possible_match.is_a?(String)
24
24
  next if lookup_value.is_a?(Numeric) && !possible_match.is_a?(Numeric)
25
25
 
26
- possible_match.downcase! if lookup_value.is_a?(String)
26
+ possible_match = possible_match.downcase if lookup_value.is_a?(String)
27
27
 
28
28
  if lookup_value == possible_match
29
29
  return lookup_table[row_number-1][column_number]
@@ -21,7 +21,7 @@ module ExcelFunctions
21
21
  next if lookup_value.is_a?(String) && !possible_match.is_a?(String)
22
22
  next if lookup_value.is_a?(Numeric) && !possible_match.is_a?(Numeric)
23
23
 
24
- possible_match.downcase! if lookup_value.is_a?(String)
24
+ possible_match = possible_match.downcase if lookup_value.is_a?(String)
25
25
 
26
26
  if lookup_value == possible_match
27
27
  return :value unless column_number <= row.length
@@ -204,7 +204,7 @@ class Formula < RubyPeg
204
204
  end
205
205
 
206
206
  def reference
207
- terminal(/\$?[A-Za-z]{1,3}\$?[0-9]+(?![0-9A-Za-z_])/)
207
+ terminal(/\$?[A-Za-z]{1,3}\$?[0-9]+(?![0-9A-Za-z_.])/)
208
208
  end
209
209
 
210
210
  def boolean
@@ -36,7 +36,7 @@ area := reference `':' reference
36
36
  cell := reference
37
37
  row_number = /\$?\d+/
38
38
  column = /\$?[A-Za-z]{1,3}/
39
- reference = /\$?[A-Za-z]{1,3}\$?[0-9]+(?![0-9A-Za-z_])/
39
+ reference = /\$?[A-Za-z]{1,3}\$?[0-9]+(?![0-9A-Za-z_.])/
40
40
  boolean = boolean_true | boolean_false
41
41
  boolean_true := `'TRUE'
42
42
  boolean_false := `'FALSE'
@@ -1,3 +1,4 @@
1
+ # FIXME Make this a subclass of symbol
1
2
  class Reference < String
2
3
 
3
4
  # This is so that we only have one instance for a given reference
@@ -8,7 +9,7 @@ class Reference < String
8
9
 
9
10
  # This is so that we only have one instance of a given reference specified by its variables
10
11
  def Reference.for(text)
11
- @@references_for_text[text]
12
+ @@references_for_text[text.to_s]
12
13
  end
13
14
 
14
15
  # This caches the calculation for turning column letters (e.g., AAB) into column numbers (e.g., 127)
@@ -46,11 +47,11 @@ class Reference < String
46
47
  calculate_excel_variables
47
48
  new_column = @excel_fixed_column ? @excel_column : @@column_letters_for_column_number[@excel_column_number + columns]
48
49
  new_row = @excel_fixed_row ? @excel_row : @excel_row_number + rows
49
- Reference.new([@excel_fixed_column,new_column,@excel_fixed_row,new_row].join)
50
+ [@excel_fixed_column,new_column,@excel_fixed_row,new_row].join.to_sym
50
51
  end
51
52
 
52
53
  def unfix
53
54
  gsub("$","")
54
55
  end
55
56
 
56
- end
57
+ end
data/src/excel/table.rb CHANGED
@@ -6,7 +6,7 @@ class Table
6
6
  attr_accessor :name
7
7
 
8
8
  def initialize(name,worksheet,reference,number_of_total_rows,*column_name_array)
9
- @name, @worksheet, @area, @number_of_total_rows, @column_name_array = name, worksheet, Area.for(reference), number_of_total_rows.to_i, column_name_array.map { |c| c.strip.downcase }
9
+ @name, @worksheet, @area, @number_of_total_rows, @column_name_array = name, worksheet.to_sym, Area.for(reference), number_of_total_rows.to_i, column_name_array.map { |c| c.strip.downcase }
10
10
  @area.calculate_excel_variables
11
11
  @data_area = Area.for("#{@area.excel_start.offset(1,0)}:#{@area.excel_finish.offset(-@number_of_total_rows,0)}")
12
12
  @data_area.calculate_excel_variables
@@ -82,7 +82,7 @@ class Table
82
82
  when /#Data/io, ""
83
83
  ast_for_area @data_area.excel_start, @data_area.excel_finish
84
84
 
85
- when /#All/io, ""
85
+ when /#All/io
86
86
  ast_for_area @area.excel_start, @area.excel_finish
87
87
 
88
88
  when /#This Row/io
@@ -108,11 +108,11 @@ class Table
108
108
  end
109
109
 
110
110
  def ast_for_area(start,finish)
111
- [:sheet_reference,@worksheet,[:area,start,finish]]
111
+ [:sheet_reference,@worksheet,[:area,start.to_sym,finish.to_sym]]
112
112
  end
113
113
 
114
114
  def ast_for_cell(ref)
115
- [:sheet_reference,@worksheet,[:cell,ref]]
115
+ [:sheet_reference,@worksheet,[:cell,ref.to_sym]]
116
116
  end
117
117
 
118
118
  def ref_error
data/src/extract.rb CHANGED
@@ -12,3 +12,4 @@ require_relative "extract/extract_worksheet_names"
12
12
  require_relative "extract/extract_named_references"
13
13
  require_relative "extract/extract_worksheet_table_relationships"
14
14
  require_relative "extract/extract_table"
15
+ require_relative "extract/extract_everything"
@@ -11,8 +11,8 @@ class CheckForUnknownFunctions
11
11
  def check(input,output)
12
12
  self.settable ||= lambda { |ref| false }
13
13
  input.each_line do |line|
14
- line.scan(/\[:function, "(.*?)"/).each do |match|
15
- output.puts $1 unless MapFormulaeToRuby::FUNCTIONS.has_key?($1)
14
+ line.scan(/\[:function, :["']?([A-Z ]+)['"]?/).each do |match|
15
+ output.puts $1 unless MapFormulaeToRuby::FUNCTIONS.has_key?($1.to_sym)
16
16
  end
17
17
  end
18
18
  end
@@ -2,8 +2,6 @@ require_relative 'extract_formulae'
2
2
 
3
3
  class ExtractArrayFormulae < ExtractFormulae
4
4
 
5
- attr_accessor :array_range
6
-
7
5
  def start_formula(type,attributes)
8
6
  return unless type == 'array' && attributes.assoc('ref')
9
7
  @array_range = attributes.assoc('ref').last
@@ -11,13 +9,15 @@ class ExtractArrayFormulae < ExtractFormulae
11
9
  end
12
10
 
13
11
  def write_formula
14
- return false if @formula.empty?
15
- output.write @ref
16
- output.write "\t"
17
- output.write @array_range
18
- output.write "\t"
19
- output.write @formula.join.gsub(/[\n\r]+/,'')
20
- output.write "\n"
12
+ return if @formula.empty?
13
+ formula_text = @formula.join.gsub(/[\r\n]+/,'')
14
+ ast = CachingFormulaParser.parse(formula_text)
15
+ unless ast
16
+ $stderr.puts "Could not parse #{@sheet_name} #{@ref} #{formula_text}"
17
+ exit
18
+ end
19
+ # FIXME: Should leave in original form rather than converting to ast?
20
+ @output[[@sheet_name, @ref]] = [@array_range, ast]
21
21
  end
22
22
 
23
23
  end