excel_to_code 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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