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.
- data/README +41 -0
- data/bin/excel_to_c +63 -0
- data/bin/excel_to_ruby +9 -0
- data/src/commands.rb +2 -0
- data/src/commands/excel_to_c.rb +858 -0
- data/src/commands/excel_to_ruby.rb +620 -0
- data/src/compile.rb +2 -0
- data/src/compile/c.rb +5 -0
- data/src/compile/c/compile_to_c.rb +62 -0
- data/src/compile/c/compile_to_c_header.rb +26 -0
- data/src/compile/c/compile_to_c_unit_test.rb +42 -0
- data/src/compile/c/excel_to_c_runtime.c +2029 -0
- data/src/compile/c/map_formulae_to_c.rb +184 -0
- data/src/compile/c/map_sheet_names_to_c_names.rb +19 -0
- data/src/compile/c/map_values_to_c.rb +85 -0
- data/src/compile/c/map_values_to_c_structs.rb +37 -0
- data/src/compile/ruby.rb +3 -0
- data/src/compile/ruby/compile_to_ruby.rb +33 -0
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +28 -0
- data/src/compile/ruby/excel_to_ruby_runtime.rb +1 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +95 -0
- data/src/compile/ruby/map_sheet_names_to_ruby_names.rb +19 -0
- data/src/compile/ruby/map_values_to_ruby.rb +65 -0
- data/src/excel.rb +5 -0
- data/src/excel/area.rb +93 -0
- data/src/excel/excel_functions.rb +84 -0
- data/src/excel/excel_functions/abs.rb +14 -0
- data/src/excel/excel_functions/add.rb +18 -0
- data/src/excel/excel_functions/and.rb +30 -0
- data/src/excel/excel_functions/apply_to_range.rb +17 -0
- data/src/excel/excel_functions/average.rb +12 -0
- data/src/excel/excel_functions/choose.rb +18 -0
- data/src/excel/excel_functions/cosh.rb +9 -0
- data/src/excel/excel_functions/count.rb +9 -0
- data/src/excel/excel_functions/counta.rb +8 -0
- data/src/excel/excel_functions/divide.rb +23 -0
- data/src/excel/excel_functions/excel_equal.rb +20 -0
- data/src/excel/excel_functions/excel_if.rb +8 -0
- data/src/excel/excel_functions/excel_match.rb +51 -0
- data/src/excel/excel_functions/find.rb +39 -0
- data/src/excel/excel_functions/iferror.rb +10 -0
- data/src/excel/excel_functions/index.rb +48 -0
- data/src/excel/excel_functions/left.rb +12 -0
- data/src/excel/excel_functions/less_than.rb +26 -0
- data/src/excel/excel_functions/less_than_or_equal.rb +26 -0
- data/src/excel/excel_functions/max.rb +12 -0
- data/src/excel/excel_functions/min.rb +12 -0
- data/src/excel/excel_functions/mod.rb +15 -0
- data/src/excel/excel_functions/more_than.rb +26 -0
- data/src/excel/excel_functions/more_than_or_equal.rb +26 -0
- data/src/excel/excel_functions/multiply.rb +24 -0
- data/src/excel/excel_functions/negative.rb +12 -0
- data/src/excel/excel_functions/not_equal.rb +19 -0
- data/src/excel/excel_functions/number_argument.rb +30 -0
- data/src/excel/excel_functions/pi.rb +7 -0
- data/src/excel/excel_functions/pmt.rb +16 -0
- data/src/excel/excel_functions/power.rb +18 -0
- data/src/excel/excel_functions/round.rb +13 -0
- data/src/excel/excel_functions/rounddown.rb +14 -0
- data/src/excel/excel_functions/roundup.rb +17 -0
- data/src/excel/excel_functions/string_join.rb +19 -0
- data/src/excel/excel_functions/subtotal.rb +13 -0
- data/src/excel/excel_functions/subtract.rb +18 -0
- data/src/excel/excel_functions/sum.rb +8 -0
- data/src/excel/excel_functions/sumif.rb +7 -0
- data/src/excel/excel_functions/sumifs.rb +74 -0
- data/src/excel/excel_functions/sumproduct.rb +32 -0
- data/src/excel/excel_functions/vlookup.rb +49 -0
- data/src/excel/formula_peg.rb +238 -0
- data/src/excel/formula_peg.txt +45 -0
- data/src/excel/reference.rb +56 -0
- data/src/excel/table.rb +108 -0
- data/src/excel_to_code.rb +7 -0
- data/src/extract.rb +13 -0
- data/src/extract/check_for_unknown_functions.rb +20 -0
- data/src/extract/extract_array_formulae.rb +23 -0
- data/src/extract/extract_formulae.rb +36 -0
- data/src/extract/extract_named_references.rb +38 -0
- data/src/extract/extract_relationships.rb +10 -0
- data/src/extract/extract_shared_formulae.rb +23 -0
- data/src/extract/extract_shared_strings.rb +20 -0
- data/src/extract/extract_simple_formulae.rb +18 -0
- data/src/extract/extract_table.rb +24 -0
- data/src/extract/extract_values.rb +29 -0
- data/src/extract/extract_worksheet_dimensions.rb +11 -0
- data/src/extract/extract_worksheet_names.rb +10 -0
- data/src/extract/extract_worksheet_table_relationships.rb +10 -0
- data/src/extract/simple_extract_from_xml.rb +19 -0
- data/src/rewrite.rb +10 -0
- data/src/rewrite/ast_copy_formula.rb +42 -0
- data/src/rewrite/ast_expand_array_formulae.rb +180 -0
- data/src/rewrite/rewrite_array_formulae.rb +71 -0
- data/src/rewrite/rewrite_array_formulae_to_arrays.rb +18 -0
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +56 -0
- data/src/rewrite/rewrite_formulae_to_ast.rb +24 -0
- data/src/rewrite/rewrite_merge_formulae_and_values.rb +18 -0
- data/src/rewrite/rewrite_relationship_id_to_filename.rb +22 -0
- data/src/rewrite/rewrite_shared_formulae.rb +38 -0
- data/src/rewrite/rewrite_values_to_ast.rb +28 -0
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +90 -0
- data/src/rewrite/rewrite_worksheet_names.rb +20 -0
- data/src/simplify.rb +16 -0
- data/src/simplify/count_formula_references.rb +58 -0
- data/src/simplify/identify_dependencies.rb +56 -0
- data/src/simplify/identify_repeated_formula_elements.rb +37 -0
- data/src/simplify/inline_formulae.rb +77 -0
- data/src/simplify/map_formulae_to_values.rb +157 -0
- data/src/simplify/remove_cells.rb +18 -0
- data/src/simplify/replace_arrays_with_single_cells.rb +27 -0
- data/src/simplify/replace_blanks.rb +58 -0
- data/src/simplify/replace_common_elements_in_formulae.rb +19 -0
- data/src/simplify/replace_formulae_with_calculated_values.rb +21 -0
- data/src/simplify/replace_indirects_with_references.rb +44 -0
- data/src/simplify/replace_named_references.rb +82 -0
- data/src/simplify/replace_ranges_with_array_literals.rb +54 -0
- data/src/simplify/replace_shared_strings.rb +49 -0
- data/src/simplify/replace_table_references.rb +71 -0
- data/src/simplify/replace_values_with_constants.rb +47 -0
- data/src/simplify/simplify_arithmetic.rb +54 -0
- data/src/util.rb +2 -0
- data/src/util/not_supported_exception.rb +2 -0
- data/src/util/try.rb +9 -0
- metadata +207 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
require_relative '../compile'
|
2
|
+
require_relative '../excel/excel_functions'
|
3
|
+
require_relative '../util'
|
4
|
+
|
5
|
+
class FormulaeCalculator
|
6
|
+
include ExcelFunctions
|
7
|
+
end
|
8
|
+
|
9
|
+
class MapFormulaeToValues
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@value_for_ast = MapValuesToRuby.new
|
13
|
+
@calculator = FormulaeCalculator.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def map(ast)
|
17
|
+
return ast unless ast.is_a?(Array)
|
18
|
+
operator = ast[0]
|
19
|
+
if respond_to?(operator)
|
20
|
+
send(operator,*ast[1..-1])
|
21
|
+
else
|
22
|
+
[operator,*ast[1..-1].map {|a| map(a) }]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def prefix(operator,argument)
|
27
|
+
argument_value = value(map(argument))
|
28
|
+
return [:prefix, operator, map(argument)] if argument_value == :not_a_value
|
29
|
+
return ast_for_value(argument_value || 0) if operator == "+"
|
30
|
+
ast_for_value(@calculator.negative(argument_value))
|
31
|
+
end
|
32
|
+
|
33
|
+
def arithmetic(left,operator,right)
|
34
|
+
l = value(map(left))
|
35
|
+
r = value(map(right))
|
36
|
+
if (l != :not_a_value) && (r != :not_a_value)
|
37
|
+
formula_value(operator.last,l,r)
|
38
|
+
else
|
39
|
+
[:arithmetic,map(left),operator,map(right)]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias :comparison :arithmetic
|
44
|
+
|
45
|
+
def percentage(number)
|
46
|
+
ast_for_value(value([:percentage, number]))
|
47
|
+
end
|
48
|
+
|
49
|
+
def string_join(*args)
|
50
|
+
values = args.map { |a| value(map(a)) } # FIXME: These eval statements are really bugging me. Must find a better solution
|
51
|
+
if values.any? { |a| a == :not_a_value }
|
52
|
+
[:string_join,*args.map { |a| map(a) }]
|
53
|
+
else
|
54
|
+
ast_for_value(@calculator.string_join(*values))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
FUNCTIONS_THAT_SHOULD_NOT_BE_CONVERTED = %w{TODAY RAND RANDBETWEEN INDIRECT}
|
59
|
+
|
60
|
+
def function(name,*args)
|
61
|
+
if FUNCTIONS_THAT_SHOULD_NOT_BE_CONVERTED.include?(name)
|
62
|
+
[:function,name,*args.map { |a| map(a) }]
|
63
|
+
elsif respond_to?("map_#{name.downcase}")
|
64
|
+
send("map_#{name.downcase}",*args)
|
65
|
+
else
|
66
|
+
values = args.map { |a| value(map(a)) }
|
67
|
+
if values.any? { |a| a == :not_a_value }
|
68
|
+
[:function,name,*args.map { |a| map(a) }]
|
69
|
+
else
|
70
|
+
formula_value(name,*values)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def map_index(array,row_number,column_number = :not_specified)
|
76
|
+
return map_index_with_only_two_arguments(array,row_number) if column_number == :not_specified
|
77
|
+
|
78
|
+
array_mapped = map(array)
|
79
|
+
row_as_number = value(map(row_number))
|
80
|
+
column_as_number = value(map(column_number))
|
81
|
+
|
82
|
+
return [:function, "INDEX", array_mapped, map(row_number), map(column_number)] if row_as_number == :not_a_value || column_as_number == :not_a_value
|
83
|
+
|
84
|
+
array_as_values = array_as_values(array)
|
85
|
+
return [:function, "INDEX", array_mapped, map(row_number), map(column_number)] unless array_as_values
|
86
|
+
|
87
|
+
result = @calculator.send(MapFormulaeToRuby::FUNCTIONS["INDEX"],array_as_values,row_as_number,column_as_number)
|
88
|
+
result = ast_for_value(result) unless result.is_a?(Array)
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
def map_index_with_only_two_arguments(array,row_number)
|
93
|
+
array_mapped = map(array)
|
94
|
+
row_as_number = value(map(row_number))
|
95
|
+
return [:function, "INDEX", array_mapped, map(row_number)] if row_as_number == :not_a_value
|
96
|
+
array_as_values = array_as_values(array)
|
97
|
+
return [:function, "INDEX", array_mapped, map(row_number)] unless array_as_values
|
98
|
+
result = @calculator.send(MapFormulaeToRuby::FUNCTIONS["INDEX"],array_as_values,row_as_number)
|
99
|
+
result = ast_for_value(result) unless result.is_a?(Array)
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
def array_as_values(array_mapped)
|
104
|
+
case array_mapped.first
|
105
|
+
when :array
|
106
|
+
array_mapped[1..-1].map do |row|
|
107
|
+
row[1..-1].map do |cell|
|
108
|
+
cell
|
109
|
+
end
|
110
|
+
end
|
111
|
+
when :cell, :sheet_reference, :blank, :number, :percentage, :string, :error, :boolean_true, :boolean_false
|
112
|
+
[[array_mapped]]
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
def value(ast)
|
121
|
+
return extract_values_from_array(ast) if ast.first == :array
|
122
|
+
return :not_a_value unless @value_for_ast.respond_to?(ast.first)
|
123
|
+
eval(@value_for_ast.send(*ast))
|
124
|
+
end
|
125
|
+
|
126
|
+
def extract_values_from_array(ast)
|
127
|
+
ast[1..-1].map do |row|
|
128
|
+
row[1..-1].map do |cell|
|
129
|
+
v = value(cell)
|
130
|
+
return :not_a_value if v == :not_a_value
|
131
|
+
v
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def formula_value(ast_name,*arguments)
|
137
|
+
raise NotSupportedException.new("#{ast_name.inspect} function not recognised in #{MapFormulaeToRuby::FUNCTIONS.inspect}") unless MapFormulaeToRuby::FUNCTIONS.has_key?(ast_name)
|
138
|
+
ast_for_value(@calculator.send(MapFormulaeToRuby::FUNCTIONS[ast_name],*arguments))
|
139
|
+
end
|
140
|
+
|
141
|
+
def ast_for_value(value)
|
142
|
+
case value
|
143
|
+
when Numeric; [:number,value.inspect]
|
144
|
+
when true; [:boolean_true]
|
145
|
+
when false; [:boolean_false]
|
146
|
+
when Symbol;
|
147
|
+
raise NotSupportedException.new("Error #{value.inspect} not recognised") unless MapFormulaeToRuby::REVERSE_ERRORS[value.inspect]
|
148
|
+
[:error,MapFormulaeToRuby::REVERSE_ERRORS[value.inspect]]
|
149
|
+
when String; [:string,value]
|
150
|
+
when Array; [:array,*value.map { |row| [:row, *row.map { |c| ast_for_value(c) }]}]
|
151
|
+
when nil; [:blank]
|
152
|
+
else
|
153
|
+
raise NotSupportedException.new("Ast for #{value.inspect} of class #{value.class} not recognised")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
class RemoveCells
|
3
|
+
|
4
|
+
attr_accessor :cells_to_keep
|
5
|
+
|
6
|
+
def self.rewrite(*args)
|
7
|
+
self.new.rewrite(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def rewrite(input,output)
|
11
|
+
input.lines do |line|
|
12
|
+
ref = line[/^(.*?)\t/,1]
|
13
|
+
if cells_to_keep.has_key?(ref)
|
14
|
+
output.puts line
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative '../excel'
|
2
|
+
|
3
|
+
class ReplaceArraysWithSingleCells
|
4
|
+
|
5
|
+
def self.replace(*args)
|
6
|
+
self.new.replace(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def replace(input,output)
|
10
|
+
|
11
|
+
input.lines do |line|
|
12
|
+
# Looks to match shared string lines
|
13
|
+
if line =~ /\[:array/
|
14
|
+
content = line.split("\t")
|
15
|
+
ast = eval(content.pop)
|
16
|
+
if ast.first == :array
|
17
|
+
output.puts "#{content.join("\t")}\t#{ast[1][1].inspect}"
|
18
|
+
else
|
19
|
+
output.puts line
|
20
|
+
end
|
21
|
+
else
|
22
|
+
output.puts line
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class ReplaceBlanksAst
|
2
|
+
|
3
|
+
attr_accessor :references, :default_sheet_name
|
4
|
+
|
5
|
+
def initialize(references, default_sheet_name)
|
6
|
+
@references, @default_sheet_name = references, default_sheet_name
|
7
|
+
end
|
8
|
+
|
9
|
+
def map(ast)
|
10
|
+
return ast unless ast.is_a?(Array)
|
11
|
+
operator = ast[0]
|
12
|
+
if respond_to?(operator)
|
13
|
+
send(operator,*ast[1..-1])
|
14
|
+
else
|
15
|
+
[operator,*ast[1..-1].map {|a| map(a) }]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sheet_reference(sheet,reference)
|
20
|
+
if references[sheet].has_key?(reference.last.gsub('$',''))
|
21
|
+
[:sheet_reference,sheet,reference]
|
22
|
+
else
|
23
|
+
[:blank]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def cell(reference)
|
28
|
+
if references[default_sheet_name].has_key?(reference.gsub('$',''))
|
29
|
+
[:cell,reference]
|
30
|
+
else
|
31
|
+
[:blank]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class ReplaceBlanks
|
39
|
+
|
40
|
+
attr_accessor :references, :default_sheet_name
|
41
|
+
|
42
|
+
def self.replace(*args)
|
43
|
+
self.new.replace(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def replace(input,output)
|
47
|
+
rewriter = ReplaceBlanksAst.new(references,default_sheet_name)
|
48
|
+
input.lines do |line|
|
49
|
+
# Looks to match lines with references
|
50
|
+
if line =~ /\[:cell/
|
51
|
+
ref, ast = line.split("\t")
|
52
|
+
output.puts "#{ref}\t#{rewriter.map(eval(ast)).inspect}"
|
53
|
+
else
|
54
|
+
output.puts line
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class ReplaceCommonElementsInFormulae
|
2
|
+
|
3
|
+
def self.replace(*args)
|
4
|
+
self.new.replace(*args)
|
5
|
+
end
|
6
|
+
|
7
|
+
def replace(input,common,output)
|
8
|
+
common = common.readlines.map do |a|
|
9
|
+
ref, element = a.split("\t")
|
10
|
+
[element.strip,"[:cell, \"#{ref}\"]",ref]
|
11
|
+
end.sort
|
12
|
+
input.lines do |line|
|
13
|
+
common.each do |element,cell,ref|
|
14
|
+
line.gsub!(element,cell)
|
15
|
+
end
|
16
|
+
output.puts line
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'map_formulae_to_values'
|
2
|
+
|
3
|
+
class ReplaceFormulaeWithCalculatedValues
|
4
|
+
|
5
|
+
def self.replace(*args)
|
6
|
+
self.new.replace(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def replace(input,output)
|
10
|
+
rewriter = MapFormulaeToValues.new
|
11
|
+
input.lines do |line|
|
12
|
+
begin
|
13
|
+
ref, ast = line.split("\t")
|
14
|
+
output.puts "#{ref}\t#{rewriter.map(eval(ast)).inspect}"
|
15
|
+
rescue Exception => e
|
16
|
+
puts "Exception at line #{line}"
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative '../excel/formula_peg'
|
2
|
+
|
3
|
+
class ReplaceIndirectsWithReferencesAst
|
4
|
+
|
5
|
+
def map(ast)
|
6
|
+
return ast unless ast.is_a?(Array)
|
7
|
+
operator = ast[0]
|
8
|
+
if respond_to?(operator)
|
9
|
+
send(operator,*ast[1..-1])
|
10
|
+
else
|
11
|
+
[operator,*ast[1..-1].map {|a| map(a) }]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def function(name,*args)
|
16
|
+
if name == "INDIRECT" && args.size == 1 && args[0][0] == :string
|
17
|
+
Formula.parse(args[0][1]).to_ast[1]
|
18
|
+
else
|
19
|
+
puts "indirect #{[:function,name,*args.map { |a| map(a) }].inspect} not replaced" if name == "INDIRECT"
|
20
|
+
[:function,name,*args.map { |a| map(a) }]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class ReplaceIndirectsWithReferences
|
27
|
+
|
28
|
+
def self.replace(*args)
|
29
|
+
self.new.replace(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def replace(input,output)
|
33
|
+
rewriter = ReplaceIndirectsWithReferencesAst.new
|
34
|
+
input.lines do |line|
|
35
|
+
# Looks to match lines with references
|
36
|
+
if line =~ /"INDIRECT"/
|
37
|
+
ref, ast = line.split("\t")
|
38
|
+
output.puts "#{ref}\t#{rewriter.map(eval(ast)).inspect}"
|
39
|
+
else
|
40
|
+
output.puts line
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class NamedReferences
|
2
|
+
|
3
|
+
attr_accessor :named_references
|
4
|
+
|
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
|
12
|
+
end
|
13
|
+
|
14
|
+
def reference_for(sheet,named_reference)
|
15
|
+
sheet = sheet.downcase
|
16
|
+
named_reference = named_reference.downcase
|
17
|
+
if @named_references.has_key?(sheet)
|
18
|
+
@named_references[sheet][named_reference] || @named_references[""][named_reference] || [:error, "#NAME?"]
|
19
|
+
else
|
20
|
+
@named_references[""][named_reference] || [:error, "#NAME?"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
class ReplaceNamedReferencesAst
|
27
|
+
|
28
|
+
attr_accessor :named_references, :default_sheet_name
|
29
|
+
|
30
|
+
def initialize(named_references, default_sheet_name)
|
31
|
+
@named_references, @default_sheet_name = named_references, default_sheet_name
|
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
|
+
[operator,*ast[1..-1].map {|a| map(a) }]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sheet_reference(sheet,reference)
|
45
|
+
if reference.first == :named_reference
|
46
|
+
named_references.reference_for(sheet,reference.last)
|
47
|
+
else
|
48
|
+
[:sheet_reference,sheet,reference]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def named_reference(name)
|
53
|
+
named_references.reference_for(default_sheet_name,name)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class ReplaceNamedReferences
|
60
|
+
|
61
|
+
attr_accessor :sheet_name
|
62
|
+
|
63
|
+
def self.replace(values,sheet_name,named_references,output)
|
64
|
+
self.new.replace(values,sheet_name,named_references,output)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Rewrites ast with named references
|
68
|
+
def replace(values,named_references,output)
|
69
|
+
named_references = NamedReferences.new(named_references.readlines)
|
70
|
+
rewriter = ReplaceNamedReferencesAst.new(named_references,sheet_name)
|
71
|
+
values.lines do |line|
|
72
|
+
# Looks to match shared string lines
|
73
|
+
if line =~ /\[:named_reference/
|
74
|
+
cols = line.split("\t")
|
75
|
+
ast = cols.pop
|
76
|
+
output.puts "#{cols.join("\t")}\t#{rewriter.map(eval(ast)).inspect}"
|
77
|
+
else
|
78
|
+
output.puts line
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative '../excel'
|
2
|
+
|
3
|
+
class ReplaceRangesWithArrayLiteralsAst
|
4
|
+
def map(ast)
|
5
|
+
if ast.is_a?(Array)
|
6
|
+
operator = ast.shift
|
7
|
+
if respond_to?(operator)
|
8
|
+
send(operator,*ast)
|
9
|
+
else
|
10
|
+
[operator,*ast.map {|a| map(a) }]
|
11
|
+
end
|
12
|
+
else
|
13
|
+
return ast
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def sheet_reference(sheet,reference)
|
18
|
+
if reference.first == :area
|
19
|
+
area = Area.for("#{reference[1]}:#{reference[2]}")
|
20
|
+
area.to_array_literal(sheet)
|
21
|
+
else
|
22
|
+
[:sheet_reference,sheet,reference]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def area(start,finish)
|
27
|
+
area = Area.for("#{start}:#{finish}")
|
28
|
+
area.to_array_literal
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class ReplaceRangesWithArrayLiterals
|
34
|
+
|
35
|
+
def self.replace(*args)
|
36
|
+
self.new.replace(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def replace(input,output)
|
40
|
+
rewriter = ReplaceRangesWithArrayLiteralsAst.new
|
41
|
+
|
42
|
+
input.lines do |line|
|
43
|
+
# Looks to match shared string lines
|
44
|
+
if line =~ /\[:area/
|
45
|
+
content = line.split("\t")
|
46
|
+
ast = eval(content.pop)
|
47
|
+
output.puts "#{content.join("\t")}\t#{rewriter.map(ast).inspect}"
|
48
|
+
else
|
49
|
+
output.puts line
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|