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,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
|