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,24 @@
1
+ require_relative '../excel/formula_peg'
2
+
3
+ class RewriteFormulaeToAst
4
+
5
+ def self.rewrite(input,output)
6
+ self.new.rewrite(input,output)
7
+ end
8
+
9
+ # input should be in the form: 'thing\tthing\tformula\n' where the last field is always a forumla
10
+ # output will be in the form 'thing\tthing\tast\n'
11
+ def rewrite(input,output)
12
+ input.lines.with_index do |line,i|
13
+ line =~ /^(.*\t)(.*?)$/
14
+ output.write $1
15
+ ast = Formula.parse($2)
16
+ if ast
17
+ output.puts ast.to_ast[1].to_s
18
+ else
19
+ $stderr.puts "Formula not parsed on line #{i+1}: #{line}"
20
+ output.puts "[:parse_error,#{line.inspect}]"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ class RewriteMergeFormulaeAndValues
2
+ def self.rewrite(*args)
3
+ new.rewrite(*args)
4
+ end
5
+
6
+ def rewrite(values,shared_formulae,array_formula,simple_formulae,output)
7
+ shared_formulae = Hash[shared_formulae.readlines.map { |line| [line[/(.*?)\t/,1],line]}]
8
+ array_formula = Hash[array_formula.readlines.map { |line| [line[/(.*?)\t/,1],line]}]
9
+ simple_formulae = Hash[simple_formulae.readlines.map { |line| [line[/(.*?)\t/,1],line]}]
10
+
11
+ values.lines do |line|
12
+ ref = line[/(.*?)\t/,1]
13
+ output.puts simple_formulae[ref] || array_formula[ref] || shared_formulae[ref] || line
14
+ end
15
+ end
16
+
17
+ end
18
+
@@ -0,0 +1,22 @@
1
+ class RewriteRelationshipIdToFilename
2
+
3
+ def self.rewrite(*args)
4
+ self.new.rewrite(*args)
5
+ end
6
+
7
+ def rewrite(input,relationships_file,output)
8
+ relationships_file.rewind
9
+ relationships_file.rewind
10
+ relationships = Hash[relationships_file.readlines.map { |line| line.split("\t")}]
11
+ input.lines do |line|
12
+ parts = line.split("\t")
13
+ rid = parts.pop.strip
14
+ if relationships.has_key?(rid)
15
+ output.puts "#{parts.join("\t")}#{parts.size > 0 ? "\t" : ""}#{relationships[rid].strip}"
16
+ else
17
+ $stderr.puts "Warning, #{rid.inspect} not found in relationships file #{relationships.inspect}"
18
+ outputs.puts "Warning, #{rid.inspect} not found in relationships file #{relationships.inspect}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'ast_copy_formula'
2
+
3
+ class RewriteSharedFormulae
4
+ def self.rewrite(input,output)
5
+ new.rewrite(input,output)
6
+ end
7
+
8
+ def rewrite(input,output)
9
+ input.lines do |line|
10
+ ref, copy_range, formula = line.split("\t")
11
+ share_formula(ref,formula,copy_range,output)
12
+ end
13
+ end
14
+
15
+ def share_formula(ref,formula,copy_range,output)
16
+ shared_ast = eval(formula)
17
+ copier = AstCopyFormula.new
18
+ copy_range = Area.for(copy_range)
19
+ copy_range.calculate_excel_variables
20
+
21
+ start_reference = copy_range.excel_start
22
+
23
+ r = Reference.for(ref)
24
+ r.calculate_excel_variables
25
+
26
+ offset_from_formula_to_start_rows = start_reference.excel_row_number - r.excel_row_number
27
+ offset_from_formula_to_start_columns = start_reference.excel_column_number - r.excel_column_number
28
+
29
+ copy_range.offsets.each do |row,column|
30
+ new_ref = start_reference.offset(row,column)
31
+ copier.rows_to_move = row + offset_from_formula_to_start_rows
32
+ copier.columns_to_move = column + offset_from_formula_to_start_columns
33
+ ast = copier.copy(shared_ast)
34
+ output.puts "#{new_ref}\t#{ast.inspect}"
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../excel/formula_peg'
2
+
3
+ class RewriteValuesToAst
4
+
5
+ def self.rewrite(input,output)
6
+ self.new.rewrite(input,output)
7
+ end
8
+
9
+ # input should be in the form: 'thing\tthing\tformula\n' where the last field is always a forumla
10
+ # output will be in the form 'thing\tthing\tast\n'
11
+ def rewrite(input,output)
12
+ input.lines do |line|
13
+ line =~ /^(.*?)\t(.*?)\t(.*)\n/
14
+ ref, type, value = $1, $2, $3
15
+ ast = case type
16
+ when 'b'; value == "1" ? [:boolean_true] : [:boolean_false]
17
+ when 's'; [:shared_string,value]
18
+ when 'n'; [:number,value]
19
+ when 'e'; [:error,value]
20
+ when 'str'; [:string,value]
21
+ else
22
+ $stderr.puts "Type #{type} not known in #{line}"
23
+ [:parse_error,line.inspect]
24
+ end
25
+ output.puts "#{ref}\t#{ast}"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,90 @@
1
+ require_relative '../excel'
2
+
3
+ class WorksheetDimension
4
+
5
+ attr_reader :first_column, :last_column, :first_row, :last_row
6
+
7
+ def initialize(area)
8
+ @area = Area.for(area)
9
+ @area.calculate_excel_variables
10
+ @first_column = @area.excel_start.excel_column
11
+ @last_column = @area.excel_finish.excel_column
12
+ @first_row = @area.excel_start.excel_row
13
+ @last_row = @area.excel_finish.excel_row
14
+ end
15
+
16
+ def map_row(start,finish)
17
+ ["#{first_column}#{start}","#{last_column}#{finish}"]
18
+ end
19
+
20
+ def map_column(start,finish)
21
+ ["#{start}#{first_row}","#{finish}#{last_row}"]
22
+ end
23
+
24
+ end
25
+
26
+ class MapColumnAndRowRangeAst
27
+
28
+ attr_accessor :default_worksheet_name, :worksheet_dimensions
29
+
30
+ def initialize(default_worksheet_name,worksheet_dimensions)
31
+ @default_worksheet_name, @worksheet_dimensions = default_worksheet_name, worksheet_dimensions
32
+ end
33
+
34
+ def map(ast)
35
+ if ast.is_a?(Array)
36
+ operator = ast.shift
37
+ if respond_to?(operator)
38
+ send(operator,*ast)
39
+ else
40
+ [operator,*ast.map {|a| map(a) }]
41
+ end
42
+ else
43
+ return ast
44
+ end
45
+ end
46
+
47
+ def sheet_reference(sheet_name,reference)
48
+ if reference.first == :row_range
49
+ [:sheet_reference,sheet_name,[:area,*worksheet_dimensions[sheet_name].map_row(reference[1],reference[2])]]
50
+ elsif reference.first == :column_range
51
+ [:sheet_reference,sheet_name,[:area,*worksheet_dimensions[sheet_name].map_column(reference[1],reference[2])]]
52
+ else
53
+ [:sheet_reference,sheet_name,reference]
54
+ end
55
+ end
56
+
57
+ def row_range(start,finish)
58
+ [:area,*worksheet_dimensions[default_worksheet_name].map_row(start,finish)]
59
+ end
60
+
61
+ def column_range(start,finish)
62
+ [:area,*worksheet_dimensions[default_worksheet_name].map_column(start,finish)]
63
+ end
64
+
65
+ end
66
+
67
+ class RewriteWholeRowColumnReferencesToAreas
68
+
69
+ def self.rewrite(input,default_worksheet_name,worksheet_dimensions,output)
70
+ new.rewrite(input,default_worksheet_name,worksheet_dimensions,output)
71
+ end
72
+
73
+ def rewrite(input,default_worksheet_name,worksheet_dimensions,output)
74
+ dimensions = Hash[worksheet_dimensions.readlines.map do |line|
75
+ worksheet_name, area = line.split("\t")
76
+ [worksheet_name,WorksheetDimension.new(area)]
77
+ end]
78
+ mapper = MapColumnAndRowRangeAst.new(default_worksheet_name,dimensions)
79
+ input.lines do |line|
80
+ if line =~ /(:column_range|:row_range)/
81
+ content = line.split("\t")
82
+ ast = eval(content.pop)
83
+ output.puts "#{content.join("\t")}\t#{mapper.map(ast).inspect}"
84
+ else
85
+ output.puts line
86
+ end
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,20 @@
1
+ class RewriteWorksheetNames
2
+
3
+ def self.rewrite(worksheet_names,relationships,output)
4
+ self.new.rewrite(worksheet_names,relationships,output)
5
+ end
6
+
7
+ # Expects worksheet names in the form:
8
+ # name\trelationship_id\n
9
+ # Expects relationships in the form:
10
+ # relationship_id\tfilename\n
11
+ # Outputs worksheet names in the form:
12
+ # name\tfilename\n
13
+ def rewrite(worksheet_names,relationships,output)
14
+ relationships = Hash[relationships.readlines.map { |line| line.split("\t")}]
15
+ worksheet_names.lines do |line|
16
+ rid, name = line.split("\t")
17
+ output.puts "#{name.strip}\t#{relationships[rid].strip}"
18
+ end
19
+ end
20
+ end
data/src/simplify.rb ADDED
@@ -0,0 +1,16 @@
1
+ require_relative "simplify/replace_shared_strings"
2
+ require_relative "simplify/replace_named_references"
3
+ require_relative "simplify/replace_table_references"
4
+ require_relative "simplify/replace_ranges_with_array_literals"
5
+ require_relative "simplify/replace_blanks"
6
+ require_relative "simplify/inline_formulae"
7
+ require_relative "simplify/replace_formulae_with_calculated_values"
8
+ require_relative "simplify/replace_indirects_with_references"
9
+ require_relative "simplify/simplify_arithmetic"
10
+ require_relative "simplify/identify_dependencies"
11
+ require_relative "simplify/remove_cells"
12
+ require_relative "simplify/count_formula_references"
13
+ require_relative "simplify/identify_repeated_formula_elements"
14
+ require_relative "simplify/replace_common_elements_in_formulae"
15
+ require_relative "simplify/replace_arrays_with_single_cells"
16
+ require_relative "simplify/replace_values_with_constants"
@@ -0,0 +1,58 @@
1
+ class CountFormulaReferences
2
+
3
+ attr_accessor :references
4
+ attr_accessor :dependencies
5
+ attr_accessor :current_sheet
6
+
7
+ def initialize(references = {}, dependencies = {})
8
+ @references = references
9
+ @dependencies = dependencies
10
+ dependencies.default_proc = lambda do |hash,key|
11
+ hash[key] = {}
12
+ end
13
+ @current_sheet = []
14
+ end
15
+
16
+ def count(references)
17
+ @references = references
18
+ references.each do |sheet,cells|
19
+ current_sheet << sheet
20
+ cells.each do |ref,ast|
21
+ count_dependencies_for(sheet,ref,ast)
22
+ end
23
+ current_sheet.pop
24
+ end
25
+ return dependencies
26
+ end
27
+
28
+ def count_dependencies_for(sheet,ref,ast)
29
+ current_sheet.push(sheet)
30
+ map(ast)
31
+ current_sheet.pop
32
+ end
33
+
34
+ def map(ast)
35
+ return ast unless ast.is_a?(Array)
36
+ operator = ast[0]
37
+ if respond_to?(operator)
38
+ send(operator,*ast[1..-1])
39
+ else
40
+ ast[1..-1].each do |a|
41
+ map(a)
42
+ end
43
+ end
44
+ end
45
+
46
+ def sheet_reference(sheet,reference)
47
+ ref = reference.last.gsub('$','')
48
+ @dependencies[sheet][ref] ||= 0
49
+ @dependencies[sheet][ref] += 1
50
+ end
51
+
52
+ def cell(reference)
53
+ ref = reference.gsub('$','')
54
+ @dependencies[current_sheet.last][ref] ||= 0
55
+ @dependencies[current_sheet.last][ref] += 1
56
+ end
57
+
58
+ end
@@ -0,0 +1,56 @@
1
+ class IdentifyDependencies
2
+
3
+ attr_accessor :references
4
+ attr_accessor :dependencies
5
+ attr_accessor :current_sheet
6
+
7
+ def initialize(references = {}, dependencies = {})
8
+ @references = references
9
+ @dependencies = dependencies
10
+ dependencies.default_proc = lambda do |hash,key|
11
+ hash[key] = {}
12
+ end
13
+ @current_sheet = []
14
+ end
15
+
16
+ def add_depedencies_for(sheet,cell = :all)
17
+ if cell == :all
18
+ return unless references.has_key?(sheet)
19
+ references[sheet].each do |ref,ast|
20
+ recursively_add_dependencies_for(sheet,ref)
21
+ end
22
+ else
23
+ recursively_add_dependencies_for(sheet,cell)
24
+ end
25
+ end
26
+
27
+ def recursively_add_dependencies_for(sheet,cell)
28
+ return if dependencies[sheet].has_key?(cell)
29
+ dependencies[sheet][cell] = true
30
+ return unless references.has_key?(sheet)
31
+ ast = references[sheet][cell]
32
+ return unless ast
33
+ current_sheet.push(sheet)
34
+ map(ast)
35
+ current_sheet.pop
36
+ end
37
+
38
+ def map(ast)
39
+ return ast unless ast.is_a?(Array)
40
+ operator = ast[0]
41
+ if respond_to?(operator)
42
+ send(operator,*ast[1..-1])
43
+ else
44
+ [operator,*ast[1..-1].map {|a| map(a) }]
45
+ end
46
+ end
47
+
48
+ def sheet_reference(sheet,reference)
49
+ recursively_add_dependencies_for(sheet,reference.last.gsub('$',''))
50
+ end
51
+
52
+ def cell(reference)
53
+ recursively_add_dependencies_for(current_sheet.last,reference.gsub('$',''))
54
+ end
55
+
56
+ end
@@ -0,0 +1,37 @@
1
+ class IdentifyRepeatedFormulaElements
2
+
3
+ attr_accessor :references
4
+ attr_accessor :counted_elements
5
+ attr_accessor :bothered_threshold
6
+
7
+ def initialize
8
+ @references = {}
9
+ @counted_elements = {}
10
+ @counted_elements.default_proc = lambda do |hash,key|
11
+ hash[key] = 0
12
+ end
13
+ @bothered_threshold = 20
14
+ end
15
+
16
+ def count(references)
17
+ @references = references
18
+ references.each do |sheet,cells|
19
+ cells.each do |ref,ast|
20
+ identify_repeated_formulae(ast)
21
+ end
22
+ end
23
+ return @counted_elements
24
+ end
25
+
26
+
27
+ def identify_repeated_formulae(ast)
28
+ string = ast.to_s
29
+ return unless ast.is_a?(Array)
30
+ return if [:number,:string,:blank,:null,:error,:boolean_true,:boolean_false,:sheet_reference,:cell, :row].include?(ast.first)
31
+ return if string.length < bothered_threshold
32
+ @counted_elements[string] += 1
33
+ ast.each do |a|
34
+ identify_repeated_formulae(a)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,77 @@
1
+ class InlineFormulaeAst
2
+
3
+ attr_accessor :references, :current_sheet_name, :inline_ast
4
+
5
+ def initialize(references, current_sheet_name, inline_ast = nil)
6
+ @references, @current_sheet_name, @inline_ast = references, [current_sheet_name], inline_ast
7
+ @inline_ast ||= lambda { |sheet,reference,references| true }
8
+ end
9
+
10
+ def map(ast)
11
+ return ast unless ast.is_a?(Array)
12
+ operator = ast[0]
13
+ if respond_to?(operator)
14
+ send(operator,*ast[1..-1])
15
+ else
16
+ [operator,*ast[1..-1].map {|a| map(a) }]
17
+ end
18
+ end
19
+
20
+ def sheet_reference(sheet,reference)
21
+ if inline_ast.call(sheet,reference.last.upcase.gsub('$',''),references)
22
+ ast = references[sheet][reference.last.upcase.gsub('$','')]
23
+ if ast
24
+ current_sheet_name.push(sheet)
25
+ result = map(ast)
26
+ current_sheet_name.pop
27
+ else
28
+ result = [:blank]
29
+ end
30
+ else
31
+ result = [:sheet_reference,sheet,reference]
32
+ end
33
+ result
34
+ end
35
+
36
+ # TODO: Optimize by replacing contents of references hash with the inlined version
37
+ def cell(reference)
38
+ if inline_ast.call(current_sheet_name.last,reference.upcase.gsub('$',''),references)
39
+ ast = references[current_sheet_name.last][reference.upcase.gsub('$','')]
40
+ if ast
41
+ map(ast)
42
+ else
43
+ [:blank]
44
+ end
45
+ else
46
+ if current_sheet_name.size > 1
47
+ [:sheet_reference,current_sheet_name.last,[:cell,reference]]
48
+ else
49
+ [:cell,reference]
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+
57
+ class InlineFormulae
58
+
59
+ attr_accessor :references, :default_sheet_name, :inline_ast
60
+
61
+ def self.replace(*args)
62
+ self.new.replace(*args)
63
+ end
64
+
65
+ def replace(input,output)
66
+ rewriter = InlineFormulaeAst.new(references, default_sheet_name, inline_ast)
67
+ input.lines do |line|
68
+ # Looks to match lines with references
69
+ if line =~ /\[:cell/
70
+ ref, ast = line.split("\t")
71
+ output.puts "#{ref}\t#{rewriter.map(eval(ast)).inspect}"
72
+ else
73
+ output.puts line
74
+ end
75
+ end
76
+ end
77
+ end