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
@@ -2,6 +2,10 @@ class ExtractArrayFormulaForCell
2
2
 
3
3
  attr_accessor :row_offset, :column_offset
4
4
 
5
+ def initialize
6
+ @fc = CachingFormulaParser.instance
7
+ end
8
+
5
9
  def map(ast)
6
10
  case ast.first
7
11
  when :array; map_array(ast)
@@ -15,7 +19,7 @@ class ExtractArrayFormulaForCell
15
19
  if ast.length == 2
16
20
  @row_offset = 0
17
21
  else
18
- return [:error, "#N/A"]
22
+ return @fc.map([:error, :"#N/A"])
19
23
  end
20
24
  end
21
25
 
@@ -23,36 +27,39 @@ class ExtractArrayFormulaForCell
23
27
  if ast[1].length == 2
24
28
  @column_offset = 0
25
29
  else
26
- return [:error, "#N/A"]
30
+ return @fc.map([:error, :"#N/A"])
27
31
  end
28
32
  end
29
33
 
30
34
  ast[@row_offset+1][@column_offset+1] # plus ones to skip tthe [:array,[:row,"cell"]] symbols
31
35
  end
32
36
 
33
- FUNCTIONS_THAT_CAN_RETURN_ARRAYS = %w{INDEX MMULT}
37
+ FUNCTIONS_THAT_CAN_RETURN_ARRAYS = { INDEX: true, MMULT: true}
34
38
 
35
39
  def map_function(ast)
36
- return ast unless FUNCTIONS_THAT_CAN_RETURN_ARRAYS.include?(ast[1])
37
- [:function, "INDEX", ast, [:number, (@row_offset+1).to_s], [:number, (column_offset+1).to_s]]
40
+ return ast unless FUNCTIONS_THAT_CAN_RETURN_ARRAYS.has_key?(ast[1])
41
+ [:function, :INDEX, ast, @fc.map([:number, (@row_offset+1)]), @fc.map([:number, (column_offset+1)])]
38
42
  end
39
43
 
40
44
  end
41
45
 
42
46
  class RewriteArrayFormulae
43
- def self.rewrite(input,output)
44
- new.rewrite(input,output)
47
+ def self.rewrite(*args)
48
+ new.rewrite(*args)
45
49
  end
46
50
 
47
- def rewrite(input,output)
48
- input.each_line do |line|
49
- ref, array_range, formula = line.split("\t")
50
- array_formula(formula,array_range,output)
51
+ def rewrite(input)
52
+ @output = {}
53
+ input.each do |ref, details|
54
+ sheet = ref[0]
55
+ array_range = details[0]
56
+ ast = details[1]
57
+ array_formula(sheet, array_range, ast)
51
58
  end
59
+ @output
52
60
  end
53
61
 
54
- def array_formula(formula,array_range,output)
55
- array_ast = eval(formula)
62
+ def array_formula(sheet, array_range, array_ast)
56
63
  array_range = "#{array_range}:#{array_range}" unless array_range.include?(':')
57
64
  array_range = Area.for(array_range)
58
65
  array_range.calculate_excel_variables
@@ -64,7 +71,7 @@ class RewriteArrayFormulae
64
71
  mapper.row_offset = row
65
72
  mapper.column_offset = column
66
73
  ref = start_reference.offset(row,column)
67
- output.puts "#{ref}\t#{mapper.map(array_ast).inspect}"
74
+ @output[[sheet.to_sym, ref.to_sym]] = mapper.map(array_ast)
68
75
  end
69
76
  end
70
77
 
@@ -3,30 +3,58 @@ require_relative '../excel'
3
3
  class RewriteCellReferencesToIncludeSheetAst
4
4
 
5
5
  attr_accessor :worksheet
6
+
7
+ def initialize
8
+ @fp = CachingFormulaParser.instance
9
+ end
6
10
 
11
+ # FIXME: UGh.
7
12
  def map(ast)
8
- if ast.is_a?(Array)
9
- operator = ast.shift
10
- if respond_to?(operator)
11
- send(operator,*ast)
13
+ r = do_map(ast)
14
+ ast.replace(r) unless r.object_id == ast.object_id
15
+ ast
16
+ end
17
+
18
+ def do_map(ast)
19
+ return ast unless ast.is_a?(Array)
20
+ return cell(ast) if ast[0] == :cell
21
+ return area(ast) if ast[0] == :area
22
+ return sheet_reference(ast) if ast[0] == :sheet_reference
23
+ ast.each.with_index do |a,i|
24
+ next unless a.is_a?(Array)
25
+ case a[0]
26
+ when :cell
27
+ ast[i] = cell(a)
28
+ when :area
29
+ ast[i] = area(a)
30
+ when :sheet_reference
31
+ ast[i] = sheet_reference(a)
12
32
  else
13
- [operator,*ast.map {|a| map(a) }]
33
+ do_map(a)
14
34
  end
15
- else
16
- return ast
17
35
  end
36
+ ast
18
37
  end
19
38
 
20
- def cell(ref)
21
- [:sheet_reference, worksheet, [:cell, ref]]
39
+ def cell(ast)
40
+ ast[1] = ast[1].to_s.gsub('$','').to_sym
41
+ @fp.map([:sheet_reference, worksheet, ast.dup])
22
42
  end
23
43
 
24
- def area(start,finish)
25
- [:sheet_reference, worksheet, [:area, start, finish]]
44
+ def area(ast)
45
+ ast[1] = ast[1].to_s.gsub('$','').to_sym
46
+ ast[2] = ast[2].to_s.gsub('$','').to_sym
47
+ @fp.map([:sheet_reference, worksheet, ast.dup])
26
48
  end
27
49
 
28
- def sheet_reference(sheet_name,reference)
29
- [:sheet_reference, sheet_name, reference]
50
+ def sheet_reference(ast)
51
+ if ast[2][0] == :cell
52
+ ast[2][1] = ast[2][1].to_s.gsub('$','').to_sym
53
+ elsif ast[2][1] == :area
54
+ ast[2][1] = ast[2][1].to_s.gsub('$','').to_sym
55
+ ast[2][2] = ast[2][2].to_s.gsub('$','').to_sym
56
+ end
57
+ @fp.map(ast)
30
58
  end
31
59
 
32
60
  end
@@ -1,45 +1,44 @@
1
1
  require_relative 'ast_copy_formula'
2
2
 
3
3
  class RewriteSharedFormulae
4
- def self.rewrite(input, shared_targets, output)
5
- new.rewrite(input, shared_targets, output)
4
+ def self.rewrite(*args)
5
+ new.rewrite(*args)
6
6
  end
7
7
 
8
- def rewrite(input, shared_target_file, output)
9
- shared_targets = Hash.new
10
- shared_target_file.each_line do |l|
11
- ref, number = *l.split("\t")
12
- shared_targets[ref.strip] = number.strip
13
- end
8
+ def rewrite(formula_shared, formula_shared_targets)
9
+ @output = {}
10
+ @formula_shared_targets = formula_shared_targets
14
11
 
15
- input.each_line do |line|
16
- ref, copy_range, shared_formula_identifier, formula = line.split("\t")
17
- share_formula(ref, formula, copy_range, shared_formula_identifier, shared_targets, output)
12
+ formula_shared.each do |ref, a|
13
+ copy_range = a[0]
14
+ shared_formula_identifier = a[1]
15
+ shared_ast = a[2]
16
+ share_formula(ref, shared_ast, copy_range, shared_formula_identifier)
18
17
  end
18
+ @output
19
19
  end
20
20
 
21
- def share_formula(ref, formula, copy_range, shared_formula_identifier, shared_targets, output)
22
- shared_ast = eval(formula)
21
+ def share_formula(ref, shared_ast, copy_range, shared_formula_identifier)
23
22
  copier = AstCopyFormula.new
24
23
  copy_range = Area.for(copy_range)
25
24
  copy_range.calculate_excel_variables
26
25
 
27
26
  start_reference = copy_range.excel_start
28
27
 
29
- r = Reference.for(ref)
28
+ r = Reference.for(ref.last)
30
29
  r.calculate_excel_variables
31
30
 
32
31
  offset_from_formula_to_start_rows = start_reference.excel_row_number - r.excel_row_number
33
32
  offset_from_formula_to_start_columns = start_reference.excel_column_number - r.excel_column_number
34
33
 
35
34
  copy_range.offsets.each do |row,column|
36
- new_ref = start_reference.offset(row,column)
37
- next unless shared_targets.include?(new_ref)
38
- next unless shared_formula_identifier == shared_targets[new_ref]
35
+ new_ref = [ref.first.to_sym, start_reference.offset(row,column).to_sym]
36
+ next unless @formula_shared_targets.include?(new_ref)
37
+ next unless shared_formula_identifier == @formula_shared_targets[new_ref]
39
38
  copier.rows_to_move = row + offset_from_formula_to_start_rows
40
39
  copier.columns_to_move = column + offset_from_formula_to_start_columns
41
40
  ast = copier.copy(shared_ast)
42
- output.puts "#{new_ref}\t#{ast.inspect}"
41
+ @output[new_ref] = ast
43
42
  end
44
43
  end
45
44
 
@@ -1,5 +1,7 @@
1
1
  require_relative '../excel/formula_peg'
2
2
 
3
+ #FIXME: NOT NEEDED
4
+
3
5
  class RewriteValuesToAst
4
6
 
5
7
  def self.rewrite(input,output)
@@ -20,6 +20,10 @@ class WorksheetDimension
20
20
  def map_column(start,finish)
21
21
  ["#{start}#{first_row}","#{finish}#{last_row}"]
22
22
  end
23
+
24
+ def inspect
25
+ area.inspect
26
+ end
23
27
 
24
28
  end
25
29
 
@@ -29,37 +33,36 @@ class MapColumnAndRowRangeAst
29
33
 
30
34
  def initialize(default_worksheet_name,worksheet_dimensions)
31
35
  @default_worksheet_name, @worksheet_dimensions = default_worksheet_name, worksheet_dimensions
36
+ @worksheet_names = []
32
37
  end
33
38
 
34
39
  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
40
+ return ast unless ast.is_a?(Array)
41
+ case ast[0]
42
+ when :sheet_reference; sheet_reference(ast)
43
+ when :row_range; row_range(ast)
44
+ when :column_range; column_range(ast)
44
45
  end
46
+ ast.each {|e| map(e)}
45
47
  end
46
48
 
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
49
+ # Of the form [:sheet_reference, sheet_name, reference]
50
+ def sheet_reference(ast)
51
+ @worksheet_names.push(ast[1].to_sym) #FIXME: Remove once all symbols
52
+ map(ast[2])
53
+ @worksheet_names.pop
55
54
  end
56
55
 
57
- def row_range(start,finish)
58
- [:area,*worksheet_dimensions[default_worksheet_name].map_row(start,finish)]
56
+ # Of the form [:row_range, start, finish]
57
+ def row_range(ast)
58
+ worksheet = @worksheet_names.last || @default_worksheet_name
59
+ ast.replace([:area,*worksheet_dimensions[worksheet].map_row(ast[1],ast[2])])
59
60
  end
60
61
 
61
- def column_range(start,finish)
62
- [:area,*worksheet_dimensions[default_worksheet_name].map_column(start,finish)]
62
+ # Of the form [:column_range, start, finish]
63
+ def column_range(ast)
64
+ worksheet = @worksheet_names.last || @default_worksheet_name
65
+ ast.replace([:area,*worksheet_dimensions[worksheet].map_column(ast[1],ast[2])])
63
66
  end
64
67
 
65
68
  end
@@ -86,10 +89,10 @@ class RewriteWholeRowColumnReferencesToAreas
86
89
  end
87
90
 
88
91
  def worksheet_dimensions=(worksheet_dimensions)
89
- @dimensions = Hash[worksheet_dimensions.readlines.map do |line|
90
- worksheet_name, area = line.split("\t")
91
- [worksheet_name,WorksheetDimension.new(area)]
92
- end]
92
+ @dimensions = {}
93
+ worksheet_dimensions.each do |name, area|
94
+ @dimensions[name] = WorksheetDimension.new(area)
95
+ end
93
96
  @mapper = nil
94
97
  end
95
98
 
data/src/simplify.rb CHANGED
@@ -19,3 +19,4 @@ require_relative "simplify/sort_into_calculation_order"
19
19
  require_relative "simplify/replace_arithmetic_on_ranges"
20
20
  require_relative "simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays"
21
21
  require_relative "simplify/replace_string_join_on_ranges"
22
+ require_relative "simplify/emergency_array_formula_replace_indirect_bodge.rb"
@@ -1,5 +1,6 @@
1
1
  class CountFormulaReferences
2
2
 
3
+ # FIXME: Do we need these accessors
3
4
  attr_accessor :references
4
5
  attr_accessor :dependencies
5
6
  attr_accessor :current_sheet
@@ -8,25 +9,22 @@ class CountFormulaReferences
8
9
  @references = references
9
10
  @dependencies = dependencies
10
11
  dependencies.default_proc = lambda do |hash,key|
11
- hash[key] = {}
12
+ hash[key] = 0
12
13
  end
13
14
  @current_sheet = []
14
15
  end
15
16
 
16
17
  def count(references)
18
+ # FIXME: Why do we have this references instance variable?
17
19
  @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
20
+ references.each do |full_ref,ast|
21
+ @dependencies[full_ref] ||= 0
22
+ count_dependencies_for(full_ref.first,full_ref.last,ast)
24
23
  end
25
24
  return dependencies
26
25
  end
27
26
 
28
27
  def count_dependencies_for(sheet,ref,ast)
29
- @dependencies[sheet][ref] ||= 0
30
28
  current_sheet.push(sheet)
31
29
  map(ast)
32
30
  current_sheet.pop
@@ -34,26 +32,27 @@ class CountFormulaReferences
34
32
 
35
33
  def map(ast)
36
34
  return ast unless ast.is_a?(Array)
37
- operator = ast[0]
38
- if respond_to?(operator)
39
- send(operator,*ast[1..-1])
40
- else
41
- ast[1..-1].each do |a|
42
- map(a)
43
- end
35
+ case ast[0]
36
+ when :sheet_reference; sheet_reference(ast)
37
+ when :cell; cell(ast)
38
+ else; ast.each { |a| map(a) }
44
39
  end
40
+ ast
45
41
  end
46
42
 
47
- def sheet_reference(sheet,reference)
48
- ref = reference.last.gsub('$','')
49
- @dependencies[sheet][ref] ||= 0
50
- @dependencies[sheet][ref] += 1
43
+ # Format [:sheet_reference, sheet, reference]
44
+ def sheet_reference(ast)
45
+ sheet = ast[1]
46
+ reference = ast[2]
47
+ ref = reference.last.to_s.gsub('$','').to_sym
48
+ @dependencies[[sheet, ref]] += 1
51
49
  end
52
50
 
53
- def cell(reference)
54
- ref = reference.gsub('$','')
55
- @dependencies[current_sheet.last][ref] ||= 0
56
- @dependencies[current_sheet.last][ref] += 1
51
+ # Format [:cell, reference]
52
+ def cell(ast)
53
+ reference = ast[1]
54
+ ref = reference.to_s.gsub('$','').to_sym
55
+ @dependencies[[current_sheet.last, ref]] += 1
57
56
  end
58
57
 
59
58
  end
@@ -0,0 +1,44 @@
1
+ require_relative '../excel/formula_peg'
2
+
3
+ class EmergencyArrayFormulaReplaceIndirectBodge
4
+
5
+ attr_accessor :current_sheet_name
6
+ attr_accessor :references
7
+
8
+ def initialize
9
+ @indirect_replacer = ReplaceIndirectsWithReferencesAst.new
10
+ @formulae_to_value_replacer = MapFormulaeToValues.new
11
+ @inline_formulae_replacer = InlineFormulaeAst.new
12
+ end
13
+
14
+ def replace(ast)
15
+ map(ast)
16
+ ast
17
+ end
18
+
19
+ def map(ast)
20
+ return ast unless ast.is_a?(Array)
21
+ function(ast) if ast[0] == :function && ast[1] == :INDIRECT
22
+ ast.each { |a| map(a) }
23
+ ast
24
+ end
25
+
26
+ def function(ast)
27
+ new_ast = deep_copy(ast)
28
+ @inline_formulae_replacer.references = @references
29
+ @inline_formulae_replacer.current_sheet_name = [@current_sheet_name]
30
+ @inline_formulae_replacer.map(new_ast)
31
+ @formulae_to_value_replacer.map(new_ast)
32
+ @indirect_replacer.replace(new_ast)
33
+ ast.replace(new_ast)
34
+ ast
35
+ end
36
+
37
+ def deep_copy(ast)
38
+ return ast if ast.is_a?(Symbol)
39
+ return ast.dup unless ast.is_a?(Array)
40
+ ast.map do |a|
41
+ deep_copy(a)
42
+ end
43
+ end
44
+ end
@@ -15,9 +15,9 @@ class IdentifyDependencies
15
15
 
16
16
  def add_depedencies_for(sheet,cell = :all)
17
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)
18
+ references.each do |ref,ast|
19
+ next unless ref.first == sheet
20
+ recursively_add_dependencies_for(sheet,ref.last)
21
21
  end
22
22
  else
23
23
  recursively_add_dependencies_for(sheet,cell)
@@ -27,8 +27,7 @@ class IdentifyDependencies
27
27
  def recursively_add_dependencies_for(sheet,cell)
28
28
  return if dependencies[sheet].has_key?(cell)
29
29
  dependencies[sheet][cell] = true
30
- return unless references.has_key?(sheet)
31
- ast = references[sheet][cell]
30
+ ast = references[[sheet,cell]]
32
31
  return unless ast
33
32
  current_sheet.push(sheet)
34
33
  map(ast)
@@ -41,16 +40,16 @@ class IdentifyDependencies
41
40
  if respond_to?(operator)
42
41
  send(operator,*ast[1..-1])
43
42
  else
44
- [operator,*ast[1..-1].map {|a| map(a) }]
43
+ ast.each {|a| map(a) }
45
44
  end
46
45
  end
47
46
 
48
47
  def sheet_reference(sheet,reference)
49
- recursively_add_dependencies_for(sheet,reference.last.gsub('$',''))
48
+ recursively_add_dependencies_for(sheet,Reference.for(reference.last).unfix.to_sym)
50
49
  end
51
50
 
52
51
  def cell(reference)
53
- recursively_add_dependencies_for(current_sheet.last,reference.gsub('$',''))
52
+ recursively_add_dependencies_for(current_sheet.last,Reference.for(reference).unfix.to_sym)
54
53
  end
55
54
 
56
55
  end