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