excel_to_code 0.1.20 → 0.1.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/src/commands/excel_to_x.rb +2 -1
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/excel_to_c_runtime.c +18 -0
- data/src/compile/c/map_formulae_to_c.rb +1 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +1 -0
- data/src/excel/excel_functions.rb +2 -0
- data/src/excel/excel_functions/isnumber.rb +8 -0
- data/src/excel/formula_peg.rb +6 -2
- data/src/excel/formula_peg.txt +3 -2
- data/src/excel/table.rb +19 -1
- data/src/simplify.rb +1 -0
- data/src/simplify/replace_offsets_with_references.rb +27 -7
- data/src/simplify/replace_string_join_on_ranges.rb +75 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4ebae76600b8d182956b7c1f6a0d98617cb7aeb
|
4
|
+
data.tar.gz: 07751fc9af519871db9f60f6011b8de21ee0e6f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46b666e419461ce012bf8e2f84e2c6b97996943c4d41590c3260e10bbf3de827b4ca6e96caa10f99d16afa80785fea8c6179dc36b0e7f3c58b1a3886f1853daf
|
7
|
+
data.tar.gz: e694dff8476a31eea15131fadb9fa44ddf8d77839ce3ab5e525d40a4ba4307d97ebbfa485a4b9bb4847a05663cbe2970db576d92ab52d21c327904f4f6df97d4
|
data/src/commands/excel_to_x.rb
CHANGED
@@ -415,7 +415,7 @@ class ExcelToX
|
|
415
415
|
return @named_references if @named_references
|
416
416
|
@named_references = {}
|
417
417
|
i = input('Named references')
|
418
|
-
i.
|
418
|
+
i.each_line do |line|
|
419
419
|
sheet, name, ref = *line.split("\t")
|
420
420
|
key = sheet.size != 0 ? [sheet, name] : name
|
421
421
|
@named_references[key] = eval(ref)
|
@@ -616,6 +616,7 @@ class ExcelToX
|
|
616
616
|
# The result of the indirect might be a range, which we need to simplify
|
617
617
|
replace ReplaceRangesWithArrayLiterals, [name, 'Formulae'], [name, 'Formulae']
|
618
618
|
replace ReplaceArithmeticOnRanges, [name, 'Formulae'], [name, 'Formulae']
|
619
|
+
replace ReplaceStringJoinOnRanges, [name, 'Formulae'], [name, 'Formulae']
|
619
620
|
replace ReplaceArraysWithSingleCells, [name, 'Formulae'], [name, 'Formulae']
|
620
621
|
replace WrapFormulaeThatReturnArraysAndAReNotInArrays, [name, 'Formulae'], [name, 'Formulae']
|
621
622
|
end
|
data/src/compile/c/a.out
CHANGED
Binary file
|
@@ -60,6 +60,7 @@ static ExcelValue hlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, E
|
|
60
60
|
static ExcelValue iferror(ExcelValue value, ExcelValue value_if_error);
|
61
61
|
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v);
|
62
62
|
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue row_number_v);
|
63
|
+
static ExcelValue excel_isnumber(ExcelValue number);
|
63
64
|
static ExcelValue large(ExcelValue array_v, ExcelValue k_v);
|
64
65
|
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v);
|
65
66
|
static ExcelValue left_1(ExcelValue string_v);
|
@@ -503,6 +504,14 @@ static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v) {
|
|
503
504
|
return result;
|
504
505
|
}
|
505
506
|
|
507
|
+
static ExcelValue excel_isnumber(ExcelValue potential_number) {
|
508
|
+
if(potential_number.type == ExcelNumber) {
|
509
|
+
return TRUE;
|
510
|
+
} else {
|
511
|
+
return FALSE;
|
512
|
+
}
|
513
|
+
}
|
514
|
+
|
506
515
|
static ExcelValue excel_if(ExcelValue condition, ExcelValue true_case, ExcelValue false_case ) {
|
507
516
|
CHECK_FOR_PASSED_ERROR(condition)
|
508
517
|
|
@@ -2588,6 +2597,15 @@ int test_functions() {
|
|
2588
2597
|
assert(rank(THREE, rank_2_v, ZERO).type == ExcelError);
|
2589
2598
|
|
2590
2599
|
|
2600
|
+
// Test the ISNUMBER function
|
2601
|
+
assert(excel_isnumber(ONE).type == ExcelBoolean);
|
2602
|
+
assert(excel_isnumber(ONE).number == 1);
|
2603
|
+
assert(excel_isnumber(BLANK).type == ExcelBoolean);
|
2604
|
+
assert(excel_isnumber(BLANK).number == 0);
|
2605
|
+
assert(excel_isnumber(new_excel_string("Hello")).type == ExcelBoolean);
|
2606
|
+
assert(excel_isnumber(new_excel_string("Hello")).number == 0);
|
2607
|
+
assert(excel_isnumber(TRUE).type == ExcelBoolean);
|
2608
|
+
assert(excel_isnumber(TRUE).number == 0);
|
2591
2609
|
|
2592
2610
|
// Release memory
|
2593
2611
|
free_all_allocated_memory();
|
data/src/excel/formula_peg.rb
CHANGED
@@ -117,13 +117,13 @@ class Formula < RubyPeg
|
|
117
117
|
|
118
118
|
def table_reference
|
119
119
|
node :table_reference do
|
120
|
-
table_name && ignore { terminal("[") } && (range_structured_reference || complex_structured_reference || simple_structured_reference) && ignore { terminal("]") }
|
120
|
+
table_name && ignore { terminal("[") } && (range_structured_reference || short_range_structured_reference || complex_structured_reference || overly_structured_reference || simple_structured_reference) && ignore { terminal("]") }
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
124
|
def local_table_reference
|
125
125
|
node :local_table_reference do
|
126
|
-
ignore { terminal("[") } && (range_structured_reference || complex_structured_reference || overly_structured_reference || simple_structured_reference) && ignore { terminal("]") }
|
126
|
+
ignore { terminal("[") } && (range_structured_reference || short_range_structured_reference || complex_structured_reference || overly_structured_reference || simple_structured_reference) && ignore { terminal("]") }
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -135,6 +135,10 @@ class Formula < RubyPeg
|
|
135
135
|
terminal(/\[[^\u005d]*\],\[[^\u005d]*\]:\[[^\u005d]*\]/)
|
136
136
|
end
|
137
137
|
|
138
|
+
def short_range_structured_reference
|
139
|
+
terminal(/\[[^\u005d]*\]:\[[^\u005d]*\]/)
|
140
|
+
end
|
141
|
+
|
138
142
|
def complex_structured_reference
|
139
143
|
terminal(/\[[^\u005d]*\],\[[^\u005d]*\]/)
|
140
144
|
end
|
data/src/excel/formula_peg.txt
CHANGED
@@ -18,10 +18,11 @@ percentage := /[-+]?[0-9]+\.?[0-9]*/ `'%'
|
|
18
18
|
number := /[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/
|
19
19
|
operator := '+' | '-' | '/' | '*' | '^'
|
20
20
|
external_reference := /\[\d+\]!?/ any_internal_reference
|
21
|
-
table_reference := table_name `'[' (range_structured_reference | complex_structured_reference | simple_structured_reference) `']'
|
22
|
-
local_table_reference := `'[' (range_structured_reference | complex_structured_reference | overly_structured_reference | simple_structured_reference) `']'
|
21
|
+
table_reference := table_name `'[' (range_structured_reference | short_range_structured_reference | complex_structured_reference | overly_structured_reference | simple_structured_reference) `']'
|
22
|
+
local_table_reference := `'[' (range_structured_reference | short_range_structured_reference | complex_structured_reference | overly_structured_reference | simple_structured_reference) `']'
|
23
23
|
table_name = /[.\p{Word}_]+/
|
24
24
|
range_structured_reference = /\[[^\u005d]*\],\[[^\u005d]*\]:\[[^\u005d]*\]/
|
25
|
+
short_range_structured_reference = /\[[^\u005d]*\]:\[[^\u005d]*\]/
|
25
26
|
complex_structured_reference = /\[[^\u005d]*\],\[[^\u005d]*\]/
|
26
27
|
overly_structured_reference = `'[' simple_structured_reference `']'
|
27
28
|
simple_structured_reference = /[^\u005d]*/
|
data/src/excel/table.rb
CHANGED
@@ -14,17 +14,20 @@ class Table
|
|
14
14
|
|
15
15
|
def reference_for(table_name,structured_reference,calling_worksheet,calling_cell)
|
16
16
|
raise NotSupportedException.new("Local table reference not supported in #{structured_reference.inspect}") unless table_name
|
17
|
+
|
17
18
|
case structured_reference
|
18
19
|
when /\[#Headers\],\[(.*?)\]:\[(.*?)\]/io
|
19
20
|
column_number_start = @column_name_array.find_index($1.strip.downcase)
|
20
21
|
column_number_finish = @column_name_array.find_index($2.strip.downcase)
|
21
22
|
return ref_error unless column_number_start && column_number_finish
|
22
23
|
ast_for_area @area.excel_start.offset(0,column_number_start), @area.excel_start.offset(0,column_number_finish)
|
24
|
+
|
23
25
|
when /\[#Totals\],\[(.*?)\]:\[(.*?)\]/io
|
24
26
|
column_number_start = @column_name_array.find_index($1.strip.downcase)
|
25
27
|
column_number_finish = @column_name_array.find_index($2.strip.downcase)
|
26
28
|
return ref_error unless column_number_start && column_number_finish
|
27
29
|
ast_for_area @area.excel_start.offset(@area.height,column_number_start), @area.excel_start.offset(@area.height,column_number_finish)
|
30
|
+
|
28
31
|
when /\[#This Row\],\[(.*?)\]:\[(.*?)\]/io
|
29
32
|
r = Reference.for(calling_cell)
|
30
33
|
r.calculate_excel_variables
|
@@ -32,15 +35,24 @@ class Table
|
|
32
35
|
column_number_start = @column_name_array.find_index($1.strip.downcase)
|
33
36
|
column_number_finish = @column_name_array.find_index($2.strip.downcase)
|
34
37
|
return ref_error unless column_number_start && column_number_finish
|
35
|
-
ast_for_area @area.excel_start.offset(row - @area.excel_start.excel_row_number,column_number_start), @area.excel_start.offset(row - @area.excel_start.excel_row_number,column_number_finish)
|
38
|
+
ast_for_area @area.excel_start.offset(row - @area.excel_start.excel_row_number,column_number_start), @area.excel_start.offset(row - @area.excel_start.excel_row_number,column_number_finish)
|
39
|
+
|
40
|
+
when /\[(.*?)\]:\[(.*?)\]/io
|
41
|
+
column_number_start = @column_name_array.find_index($1.strip.downcase)
|
42
|
+
column_number_finish = @column_name_array.find_index($2.strip.downcase)
|
43
|
+
return ref_error unless column_number_start && column_number_finish
|
44
|
+
ast_for_area @area.excel_start.offset(1,column_number_start), @area.excel_start.offset(@area.height - @number_of_total_rows,column_number_finish)
|
45
|
+
|
36
46
|
when /\[#Headers\],\[(.*?)\]/io
|
37
47
|
column_number = @column_name_array.find_index($1.strip.downcase)
|
38
48
|
return ref_error unless column_number
|
39
49
|
ast_for_cell @area.excel_start.offset(0,column_number)
|
50
|
+
|
40
51
|
when /\[#Totals\],\[(.*?)\]/io
|
41
52
|
column_number = @column_name_array.find_index($1.strip.downcase)
|
42
53
|
return ref_error unless column_number
|
43
54
|
ast_for_cell @area.excel_start.offset(@area.height,column_number)
|
55
|
+
|
44
56
|
when /\[#This Row\],\[(.*?)\]/io
|
45
57
|
r = Reference.for(calling_cell)
|
46
58
|
r.calculate_excel_variables
|
@@ -48,6 +60,7 @@ class Table
|
|
48
60
|
column_number = @column_name_array.find_index($1.strip.downcase)
|
49
61
|
return ref_error unless column_number
|
50
62
|
ast_for_cell @area.excel_start.offset(row - @area.excel_start.excel_row_number,column_number)
|
63
|
+
|
51
64
|
when /#Headers/io
|
52
65
|
if calling_worksheet == @worksheet && @data_area.includes?(calling_cell)
|
53
66
|
r = Reference.for(calling_cell)
|
@@ -56,6 +69,7 @@ class Table
|
|
56
69
|
else
|
57
70
|
ast_for_area @area.excel_start.offset(0,0), @area.excel_start.offset(0,@area.width)
|
58
71
|
end
|
72
|
+
|
59
73
|
when /#Totals/io
|
60
74
|
if calling_worksheet == @worksheet && @data_area.includes?(calling_cell)
|
61
75
|
r = Reference.for(calling_cell)
|
@@ -64,15 +78,19 @@ class Table
|
|
64
78
|
else
|
65
79
|
ast_for_area @area.excel_start.offset(@area.height,0), @area.excel_start.offset(@area.height,@area.width)
|
66
80
|
end
|
81
|
+
|
67
82
|
when /#Data/io, ""
|
68
83
|
ast_for_area @data_area.excel_start, @data_area.excel_finish
|
84
|
+
|
69
85
|
when /#All/io, ""
|
70
86
|
ast_for_area @area.excel_start, @area.excel_finish
|
87
|
+
|
71
88
|
when /#This Row/io
|
72
89
|
r = Reference.for(calling_cell)
|
73
90
|
r.calculate_excel_variables
|
74
91
|
row = r.excel_row_number
|
75
92
|
ast_for_area "#{@area.excel_start.excel_column}#{row}", "#{@area.excel_finish.excel_column}#{row}"
|
93
|
+
|
76
94
|
else
|
77
95
|
if calling_worksheet == @worksheet && @data_area.includes?(calling_cell)
|
78
96
|
r = Reference.for(calling_cell)
|
data/src/simplify.rb
CHANGED
@@ -18,3 +18,4 @@ require_relative "simplify/replace_values_with_constants"
|
|
18
18
|
require_relative "simplify/sort_into_calculation_order"
|
19
19
|
require_relative "simplify/replace_arithmetic_on_ranges"
|
20
20
|
require_relative "simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays"
|
21
|
+
require_relative "simplify/replace_string_join_on_ranges"
|
@@ -17,19 +17,39 @@ class ReplaceOffsetsWithReferencesAst
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def function(name,*args)
|
20
|
-
if name == "OFFSET"
|
21
|
-
|
22
|
-
elsif name == "OFFSET" && args.size == 3 && args[0][0] == :cell && args[1][0] == :number && args[2][0] == :number
|
23
|
-
replace_offset(args[0][1], args[1][1], args[2][1])
|
20
|
+
if name == "OFFSET"
|
21
|
+
try_to_replace_offset(*args)
|
24
22
|
else
|
25
|
-
puts "offset in #{[:function,name,*args.map { |a| map(a) }].inspect} not replaced" if name == "INDIRECT"
|
26
23
|
[:function,name,*args.map { |a| map(a) }]
|
27
24
|
end
|
28
25
|
end
|
29
26
|
|
30
|
-
def
|
27
|
+
def try_to_replace_offset(reference, row_offset, column_offset, height = [:number, 1], width = [:number, 1])
|
28
|
+
if [row_offset, column_offset, height, width].all? { |a| a.first == :number }
|
29
|
+
if reference.first == :cell
|
30
|
+
offset_cell(reference, row_offset, column_offset, height, width)
|
31
|
+
elsif reference.first == :sheet_reference && reference[2].first == :cell
|
32
|
+
[:sheet_reference, reference[1], offset_cell(reference[2], row_offset, column_offset, height, width)]
|
33
|
+
else
|
34
|
+
puts "#{[:function, "OFFSET", reference, row_offset, column_offset, height, width]} not replaced"
|
35
|
+
[:function, "OFFSET", reference, row_offset, column_offset, height, width]
|
36
|
+
end
|
37
|
+
else
|
38
|
+
puts "#{[:function, "OFFSET", reference, row_offset, column_offset, height, width]} not replaced"
|
39
|
+
[:function, "OFFSET", reference, row_offset, column_offset, height, width]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def offset_cell(reference, row_offset, column_offset, height, width)
|
44
|
+
|
45
|
+
reference = reference[1]
|
46
|
+
row_offset = row_offset[1].to_i
|
47
|
+
column_offset = column_offset[1].to_i
|
48
|
+
height = height[1].to_i
|
49
|
+
width = width[1].to_i
|
50
|
+
|
31
51
|
@replacements_made_in_the_last_pass += 1
|
32
|
-
reference = Reference.for(reference.gsub
|
52
|
+
reference = Reference.for(reference.gsub("$",""))
|
33
53
|
start_reference = reference.offset(row_offset.to_i, column_offset.to_i)
|
34
54
|
end_reference = reference.offset(row_offset.to_i + height.to_i - 1, column_offset.to_i + width.to_i - 1)
|
35
55
|
if start_reference == end_reference
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class ReplaceStringJoinOnRangesAST
|
2
|
+
|
3
|
+
def map(ast)
|
4
|
+
return ast unless ast.is_a?(Array)
|
5
|
+
operator = ast[0]
|
6
|
+
if respond_to?(operator)
|
7
|
+
send(operator,*ast[1..-1])
|
8
|
+
else
|
9
|
+
[operator,*ast[1..-1].map {|a| map(a) }]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def string_join(*strings)
|
14
|
+
# Make sure there is actually a conversion to do
|
15
|
+
return [:string_join, *strings] unless strings.any? { |s| s.first == :array }
|
16
|
+
# Now work out the largest dimensions
|
17
|
+
# Arrays look like this [:array, [:row, 1, 2, 3], [:row, 4, 5, 6]]
|
18
|
+
max_rows = 0
|
19
|
+
max_columns = 0
|
20
|
+
strings.each do |s|
|
21
|
+
next unless s.first == :array
|
22
|
+
r = s.length - 1 # -1 beause first element is :array
|
23
|
+
c = r > 0 ? s[1].length - 1 : 0 # check if rows, if there are, columns is length of that array -1 for initial :row symbol
|
24
|
+
max_columns = c if c > max_columns
|
25
|
+
max_rows = r if r > max_rows
|
26
|
+
end
|
27
|
+
|
28
|
+
result = [:array]
|
29
|
+
(0...max_rows).each do |row_index|
|
30
|
+
row = [:row]
|
31
|
+
(0...max_columns).each do |column_index|
|
32
|
+
column = [:string_join]
|
33
|
+
strings.each do |string|
|
34
|
+
column << select_from(string, row_index, column_index)
|
35
|
+
end
|
36
|
+
row << column
|
37
|
+
end
|
38
|
+
result << row
|
39
|
+
end
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def select_from(maybe_array, row_index, column_index)
|
44
|
+
return map(maybe_array) unless maybe_array.first == :array
|
45
|
+
row = maybe_array[row_index+1]
|
46
|
+
return [:error, "#VALUE!"] unless row
|
47
|
+
cell = row[column_index+1]
|
48
|
+
return [:error, "#VALUE!"] unless cell
|
49
|
+
map(cell)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class ReplaceStringJoinOnRanges
|
57
|
+
|
58
|
+
def self.replace(*args)
|
59
|
+
self.new.replace(*args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def replace(input,output)
|
63
|
+
rewriter = ReplaceStringJoinOnRangesAST.new
|
64
|
+
input.each_line do |line|
|
65
|
+
# Looks to match lines with references
|
66
|
+
if line =~ /:string_join/ && line =~ /:array/
|
67
|
+
content = line.split("\t")
|
68
|
+
ast = eval(content.pop)
|
69
|
+
output.puts "#{content.join("\t")}\t#{rewriter.map(ast).inspect}"
|
70
|
+
else
|
71
|
+
output.puts line
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: excel_to_code
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Counsell, Green on Black Ltd
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubypeg
|
@@ -137,6 +137,7 @@ files:
|
|
137
137
|
- src/excel/excel_functions/iferror.rb
|
138
138
|
- src/excel/excel_functions/index.rb
|
139
139
|
- src/excel/excel_functions/int.rb
|
140
|
+
- src/excel/excel_functions/isnumber.rb
|
140
141
|
- src/excel/excel_functions/large.rb
|
141
142
|
- src/excel/excel_functions/left.rb
|
142
143
|
- src/excel/excel_functions/less_than.rb
|
@@ -224,6 +225,7 @@ files:
|
|
224
225
|
- src/simplify/replace_offsets_with_references.rb
|
225
226
|
- src/simplify/replace_ranges_with_array_literals.rb
|
226
227
|
- src/simplify/replace_shared_strings.rb
|
228
|
+
- src/simplify/replace_string_join_on_ranges.rb
|
227
229
|
- src/simplify/replace_table_references.rb
|
228
230
|
- src/simplify/replace_values_with_constants.rb
|
229
231
|
- src/simplify/simplify_arithmetic.rb
|