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