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.
- checksums.yaml +4 -4
- data/src/commands/excel_to_c.rb +39 -92
- data/src/commands/excel_to_ruby.rb +9 -35
- data/src/commands/excel_to_x.rb +515 -536
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/compile_named_reference_setters.rb +4 -6
- data/src/compile/c/compile_to_c.rb +34 -21
- data/src/compile/c/compile_to_c_header.rb +7 -7
- data/src/compile/c/excel_to_c_runtime.c +8 -4
- data/src/compile/c/map_formulae_to_c.rb +85 -86
- data/src/compile/c/map_values_to_c.rb +7 -1
- data/src/compile/c/map_values_to_c_structs.rb +1 -1
- data/src/compile/ruby/compile_to_ruby.rb +14 -11
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +17 -10
- data/src/compile/ruby/map_formulae_to_ruby.rb +56 -56
- data/src/compile/ruby/map_values_to_ruby.rb +14 -2
- data/src/excel/area.rb +6 -8
- data/src/excel/excel_functions/hlookup.rb +1 -1
- data/src/excel/excel_functions/vlookup.rb +1 -1
- data/src/excel/formula_peg.rb +1 -1
- data/src/excel/formula_peg.txt +1 -1
- data/src/excel/reference.rb +4 -3
- data/src/excel/table.rb +4 -4
- data/src/extract.rb +1 -0
- data/src/extract/check_for_unknown_functions.rb +2 -2
- data/src/extract/extract_array_formulae.rb +9 -9
- data/src/extract/extract_everything.rb +140 -0
- data/src/extract/extract_formulae.rb +30 -20
- data/src/extract/extract_named_references.rb +37 -22
- data/src/extract/extract_relationships.rb +16 -3
- data/src/extract/extract_shared_formulae.rb +8 -11
- data/src/extract/extract_shared_formulae_targets.rb +1 -6
- data/src/extract/extract_shared_strings.rb +21 -8
- data/src/extract/extract_simple_formulae.rb +11 -6
- data/src/extract/extract_table.rb +26 -13
- data/src/extract/extract_values.rb +35 -11
- data/src/extract/extract_worksheet_dimensions.rb +13 -3
- data/src/extract/extract_worksheet_names.rb +16 -3
- data/src/extract/extract_worksheet_table_relationships.rb +16 -4
- data/src/extract/simple_extract_from_xml.rb +9 -11
- data/src/rewrite.rb +3 -0
- data/src/rewrite/ast_copy_formula.rb +5 -1
- data/src/rewrite/ast_expand_array_formulae.rb +71 -59
- data/src/rewrite/caching_formula_parser.rb +110 -0
- data/src/rewrite/rewrite_array_formulae.rb +21 -14
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +41 -13
- data/src/rewrite/rewrite_shared_formulae.rb +17 -18
- data/src/rewrite/rewrite_values_to_ast.rb +2 -0
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +28 -25
- data/src/simplify.rb +1 -0
- data/src/simplify/count_formula_references.rb +22 -23
- data/src/simplify/emergency_array_formula_replace_indirect_bodge.rb +44 -0
- data/src/simplify/identify_dependencies.rb +7 -8
- data/src/simplify/identify_repeated_formula_elements.rb +5 -6
- data/src/simplify/inline_formulae.rb +48 -48
- data/src/simplify/map_formulae_to_values.rb +197 -79
- data/src/simplify/remove_cells.rb +13 -6
- data/src/simplify/replace_arithmetic_on_ranges.rb +42 -28
- data/src/simplify/replace_arrays_with_single_cells.rb +11 -5
- data/src/simplify/replace_column_with_column_number.rb +31 -23
- data/src/simplify/replace_common_elements_in_formulae.rb +16 -17
- data/src/simplify/replace_indirects_with_references.rb +26 -21
- data/src/simplify/replace_named_references.rb +26 -31
- data/src/simplify/replace_offsets_with_references.rb +33 -34
- data/src/simplify/replace_ranges_with_array_literals.rb +48 -20
- data/src/simplify/replace_shared_strings.rb +15 -13
- data/src/simplify/replace_string_join_on_ranges.rb +7 -9
- data/src/simplify/replace_table_references.rb +16 -11
- data/src/simplify/replace_values_with_constants.rb +6 -4
- data/src/simplify/simplify_arithmetic.rb +33 -19
- data/src/simplify/sort_into_calculation_order.rb +13 -13
- data/src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb +21 -13
- metadata +19 -2
@@ -7,12 +7,19 @@ class RemoveCells
|
|
7
7
|
self.new.rewrite(*args)
|
8
8
|
end
|
9
9
|
|
10
|
-
def rewrite(
|
11
|
-
|
12
|
-
ref
|
13
|
-
if cells_to_keep.has_key?(ref)
|
14
|
-
output.puts line
|
15
|
-
end
|
10
|
+
def rewrite(formulae)
|
11
|
+
formulae.delete_if do |ref, ast|
|
12
|
+
delete_ref?(ref)
|
16
13
|
end
|
14
|
+
formulae
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete_ref?(ref)
|
18
|
+
sheet = ref.first
|
19
|
+
cell = ref.last
|
20
|
+
cells_to_keep_in_sheet = cells_to_keep[sheet]
|
21
|
+
return true unless cells_to_keep_in_sheet
|
22
|
+
return false if cells_to_keep_in_sheet.has_key?(cell)
|
23
|
+
true
|
17
24
|
end
|
18
25
|
end
|
@@ -2,43 +2,57 @@ class ReplaceArithmeticOnRangesAst
|
|
2
2
|
|
3
3
|
def map(ast)
|
4
4
|
return ast unless ast.is_a?(Array)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
else
|
9
|
-
[operator,*ast[1..-1].map {|a| map(a) }]
|
10
|
-
end
|
5
|
+
arithmetic(ast) if ast.first == :arithmetic
|
6
|
+
ast.each { |a| map(a) }
|
7
|
+
ast
|
11
8
|
end
|
12
9
|
|
13
|
-
|
10
|
+
# Format [:artithmetic, left, operator, right]
|
11
|
+
# should have removed arithmetic with more than one operator
|
12
|
+
# in an earlier transformation
|
13
|
+
def arithmetic(ast)
|
14
|
+
left, operator, right = ast[1], ast[2], ast[3]
|
15
|
+
# Three different options, array on the left, array on the right, or both
|
16
|
+
# array on the left first
|
14
17
|
if left.first == :array && right.first != :array
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
map(right)
|
19
|
+
ast.replace(
|
20
|
+
array_map(left) do |cell|
|
21
|
+
[:arithmetic, map(cell), operator, right]
|
22
|
+
end
|
23
|
+
)
|
24
|
+
|
25
|
+
# array on the right next
|
19
26
|
elsif left.first != :array && right.first == :array
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
map(left)
|
28
|
+
ast.replace(
|
29
|
+
array_map(right) do |cell|
|
30
|
+
[:arithmetic, left, operator, map(cell)]
|
31
|
+
end
|
32
|
+
)
|
33
|
+
|
34
|
+
# now array both sides
|
24
35
|
elsif left.first == :array && right.first == :array
|
25
|
-
|
26
|
-
|
27
|
-
row
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
cell
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
ast.replace(
|
37
|
+
left.map.with_index do |row, i|
|
38
|
+
if row == :array
|
39
|
+
row
|
40
|
+
else
|
41
|
+
row.map.with_index do |cell, j|
|
42
|
+
if cell == :row
|
43
|
+
cell
|
44
|
+
elsif i >= left.length || i >= right.length || j >= left.first.length || j >= right.first.length
|
45
|
+
[:error, "#VALUE!"]
|
46
|
+
else
|
47
|
+
[:arithmetic, map(left[i][j]), operator, map(right[i][j])]
|
48
|
+
end
|
36
49
|
end
|
37
50
|
end
|
38
51
|
end
|
39
|
-
|
52
|
+
)
|
40
53
|
else
|
41
|
-
|
54
|
+
map(left)
|
55
|
+
map(right)
|
42
56
|
end
|
43
57
|
end
|
44
58
|
|
@@ -1,5 +1,14 @@
|
|
1
1
|
require_relative '../excel'
|
2
2
|
|
3
|
+
class ReplaceArraysWithSingleCellsAst
|
4
|
+
|
5
|
+
def map(ast)
|
6
|
+
return ast unless ast.first == :array
|
7
|
+
ast[1][1]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
3
12
|
class ReplaceArraysWithSingleCells
|
4
13
|
|
5
14
|
def self.replace(*args)
|
@@ -8,16 +17,13 @@ class ReplaceArraysWithSingleCells
|
|
8
17
|
|
9
18
|
def replace(input,output)
|
10
19
|
|
20
|
+
replacer = ReplaceArraysWithSingleCellsAst.new
|
11
21
|
input.each_line do |line|
|
12
22
|
# Looks to match shared string lines
|
13
23
|
if line =~ /\[:array/
|
14
24
|
content = line.split("\t")
|
15
25
|
ast = eval(content.pop)
|
16
|
-
|
17
|
-
output.puts "#{content.join("\t")}\t#{ast[1][1].inspect}"
|
18
|
-
else
|
19
|
-
output.puts line
|
20
|
-
end
|
26
|
+
output.puts "#{content.join("\t")}\t#{replacer.map(ast).inspect}"
|
21
27
|
else
|
22
28
|
output.puts line
|
23
29
|
end
|
@@ -1,34 +1,42 @@
|
|
1
1
|
class ReplaceColumnWithColumnNumberAST
|
2
2
|
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :count_replaced
|
4
|
+
attr_accessor :replacement_made
|
4
5
|
|
5
6
|
def initialize
|
6
|
-
@
|
7
|
+
@count_replaced = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def replace(ast)
|
11
|
+
@replacement_made = false
|
12
|
+
map(ast)
|
13
|
+
@replacement_made
|
7
14
|
end
|
8
15
|
|
9
16
|
def map(ast)
|
10
17
|
return ast unless ast.is_a?(Array)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
else
|
15
|
-
[operator,*ast[1..-1].map {|a| map(a) }]
|
16
|
-
end
|
18
|
+
function(ast) if ast[0] == :function
|
19
|
+
ast.each { |a| map(a) }
|
20
|
+
ast
|
17
21
|
end
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
# Should be of the form [:function, "COLUMN", [:sheet_reference, sheet, ref]]
|
24
|
+
|
25
|
+
REF_TYPES = {:cell => true, :sheet_reference => true}
|
26
|
+
|
27
|
+
def function(ast)
|
28
|
+
return unless ast[1] == :COLUMN
|
29
|
+
return unless ast.size == 3
|
30
|
+
return unless REF_TYPES.has_key?(ast[2][0])
|
31
|
+
if ast[2][0] == :cell
|
32
|
+
reference = Reference.for(ast[2][1])
|
33
|
+
elsif ast[2][0] == :sheet_reference
|
34
|
+
reference = Reference.for(ast[2][2][1])
|
31
35
|
end
|
36
|
+
reference.calculate_excel_variables
|
37
|
+
@count_replaced += 1
|
38
|
+
@replacement_made = true
|
39
|
+
ast.replace( CachingFormulaParser.map([:number, reference.excel_column_number]))
|
32
40
|
end
|
33
41
|
|
34
42
|
end
|
@@ -40,19 +48,19 @@ class ReplaceColumnWithColumnNumber
|
|
40
48
|
self.new.replace(*args)
|
41
49
|
end
|
42
50
|
|
43
|
-
attr_accessor :
|
51
|
+
attr_accessor :count_replaced
|
44
52
|
|
45
53
|
def replace(input,output)
|
46
54
|
rewriter = ReplaceColumnWithColumnNumberAST.new
|
47
55
|
input.each_line do |line|
|
48
56
|
# Looks to match lines with references
|
49
|
-
if line =~
|
57
|
+
if line =~ /:COLUMN/
|
50
58
|
ref, ast = line.split("\t")
|
51
59
|
output.puts "#{ref}\t#{rewriter.map(eval(ast)).inspect}"
|
52
60
|
else
|
53
61
|
output.puts line
|
54
62
|
end
|
55
63
|
end
|
56
|
-
@
|
64
|
+
@count_replaced = rewriter.count_replaced
|
57
65
|
end
|
58
66
|
end
|
@@ -5,29 +5,28 @@ class ReplaceCommonElementsInFormulae
|
|
5
5
|
end
|
6
6
|
|
7
7
|
attr_accessor :common_elements
|
8
|
+
attr_accessor :common_elements_used
|
8
9
|
|
9
|
-
def replace(input,
|
10
|
-
@
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
input.each_line do |line|
|
16
|
-
ref, formula = line.split("\t")
|
17
|
-
output.puts "#{ref}\t#{replace_repeated_formulae(eval(formula)).inspect}"
|
10
|
+
def replace(input, common_elements)
|
11
|
+
@common_elements_used ||= Hash.new { |h, k| h[k] = 0 }
|
12
|
+
@common_elements = common_elements
|
13
|
+
input.each do |ref, ast|
|
14
|
+
replace_repeated_formulae(ast)
|
18
15
|
end
|
16
|
+
input
|
19
17
|
end
|
18
|
+
|
19
|
+
VALUES = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true, :row => true}
|
20
20
|
|
21
21
|
def replace_repeated_formulae(ast)
|
22
22
|
return ast unless ast.is_a?(Array)
|
23
|
-
return ast if
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
replace_repeated_formulae(a)
|
23
|
+
return ast if VALUES.has_key?(ast.first)
|
24
|
+
replacement = @common_elements[ast]
|
25
|
+
if replacement
|
26
|
+
@common_elements_used[replacement] += 1
|
27
|
+
ast.replace(replacement)
|
28
|
+
else
|
29
|
+
ast.each { |a| replace_repeated_formulae(a) }
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
@@ -2,32 +2,37 @@ require_relative '../excel/formula_peg'
|
|
2
2
|
|
3
3
|
class ReplaceIndirectsWithReferencesAst
|
4
4
|
|
5
|
-
attr_accessor :
|
5
|
+
attr_accessor :count_replaced
|
6
|
+
attr_accessor :replacement_made
|
6
7
|
|
7
8
|
def initialize
|
8
|
-
@
|
9
|
+
@count_replaced = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def replace(ast)
|
13
|
+
@replacement_made = false
|
14
|
+
map(ast)
|
15
|
+
@replacement_made
|
9
16
|
end
|
10
17
|
|
11
18
|
def map(ast)
|
12
19
|
return ast unless ast.is_a?(Array)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
[operator,*ast[1..-1].map {|a| map(a) }]
|
18
|
-
end
|
20
|
+
function(ast) if ast[0] == :function
|
21
|
+
ast.each { |a| map(a) }
|
22
|
+
ast
|
19
23
|
end
|
20
24
|
|
21
|
-
def function(
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@
|
27
|
-
args[0]
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
def function(ast)
|
26
|
+
return unless ast[1] == :INDIRECT
|
27
|
+
args = ast[2..-1]
|
28
|
+
if args[0][0] == :string
|
29
|
+
@count_replaced += 1
|
30
|
+
@replacement_made = true
|
31
|
+
ast.replace(CachingFormulaParser.parse(args[0][1]))
|
32
|
+
elsif args[0][0] == :error
|
33
|
+
@count_replaced += 1
|
34
|
+
@replacement_made = true
|
35
|
+
ast.replace(args[0])
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -39,19 +44,19 @@ class ReplaceIndirectsWithReferences
|
|
39
44
|
self.new.replace(*args)
|
40
45
|
end
|
41
46
|
|
42
|
-
attr_accessor :
|
47
|
+
attr_accessor :count_replaced
|
43
48
|
|
44
49
|
def replace(input,output)
|
45
50
|
rewriter = ReplaceIndirectsWithReferencesAst.new
|
46
51
|
input.each_line do |line|
|
47
52
|
# Looks to match lines with references
|
48
|
-
if line =~
|
53
|
+
if line =~ /:INDIRECT/
|
49
54
|
ref, ast = line.split("\t")
|
50
55
|
output.puts "#{ref}\t#{rewriter.map(eval(ast)).inspect}"
|
51
56
|
else
|
52
57
|
output.puts line
|
53
58
|
end
|
54
59
|
end
|
55
|
-
@
|
60
|
+
@count_replaced = rewriter.count_replaced
|
56
61
|
end
|
57
62
|
end
|
@@ -3,22 +3,15 @@ class NamedReferences
|
|
3
3
|
attr_accessor :named_references
|
4
4
|
|
5
5
|
def initialize(refs)
|
6
|
-
@named_references =
|
7
|
-
refs.each do |line|
|
8
|
-
sheet, name, reference = line.split("\t")
|
9
|
-
@named_references[sheet.downcase] ||= {}
|
10
|
-
@named_references[sheet.downcase][name.downcase] = eval(reference)
|
11
|
-
end
|
6
|
+
@named_references = refs
|
12
7
|
end
|
13
8
|
|
14
9
|
def reference_for(sheet,named_reference)
|
15
10
|
sheet = sheet.downcase
|
16
|
-
named_reference = named_reference.downcase
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@named_references[""][named_reference] || [:error, "#NAME?"]
|
21
|
-
end
|
11
|
+
named_reference = named_reference.downcase.to_sym
|
12
|
+
@named_references[[sheet, named_reference]] ||
|
13
|
+
@named_references[named_reference] ||
|
14
|
+
[:error, :"#NAME?"]
|
22
15
|
end
|
23
16
|
|
24
17
|
end
|
@@ -27,30 +20,32 @@ class ReplaceNamedReferencesAst
|
|
27
20
|
|
28
21
|
attr_accessor :named_references, :default_sheet_name
|
29
22
|
|
30
|
-
def initialize(named_references, default_sheet_name)
|
23
|
+
def initialize(named_references, default_sheet_name = nil)
|
31
24
|
@named_references, @default_sheet_name = named_references, default_sheet_name
|
25
|
+
@named_references = NamedReferences.new(@named_references) unless @named_references.is_a?(NamedReferences)
|
32
26
|
end
|
33
27
|
|
34
28
|
def map(ast)
|
35
29
|
return ast unless ast.is_a?(Array)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
else
|
40
|
-
[operator,*ast[1..-1].map {|a| map(a) }]
|
30
|
+
case ast[0]
|
31
|
+
when :sheet_reference; sheet_reference(ast)
|
32
|
+
when :named_reference; named_reference(ast)
|
41
33
|
end
|
34
|
+
ast.each { |a| map(a) }
|
35
|
+
ast
|
42
36
|
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
38
|
+
# Format [:sheet_reference, sheet, reference]
|
39
|
+
def sheet_reference(ast)
|
40
|
+
reference = ast[2]
|
41
|
+
return unless reference.first == :named_reference
|
42
|
+
sheet = ast[1]
|
43
|
+
ast.replace(named_references.reference_for(sheet, reference.last))
|
50
44
|
end
|
51
45
|
|
52
|
-
|
53
|
-
|
46
|
+
# Format [:named_reference, name]
|
47
|
+
def named_reference(ast)
|
48
|
+
ast.replace(named_references.reference_for(default_sheet_name, ast[1]))
|
54
49
|
end
|
55
50
|
|
56
51
|
end
|
@@ -58,15 +53,15 @@ end
|
|
58
53
|
|
59
54
|
class ReplaceNamedReferences
|
60
55
|
|
61
|
-
attr_accessor :sheet_name
|
56
|
+
attr_accessor :sheet_name, :named_references
|
62
57
|
|
63
|
-
def self.replace(
|
64
|
-
self.new.replace(
|
58
|
+
def self.replace(*args)
|
59
|
+
self.new.replace(*args)
|
65
60
|
end
|
66
61
|
|
67
62
|
# Rewrites ast with named references
|
68
|
-
def replace(values,
|
69
|
-
named_references = NamedReferences.new(named_references
|
63
|
+
def replace(values,output)
|
64
|
+
named_references = NamedReferences.new(@named_references)
|
70
65
|
rewriter = ReplaceNamedReferencesAst.new(named_references,sheet_name)
|
71
66
|
values.each_line do |line|
|
72
67
|
# Looks to match shared string lines
|