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
@@ -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