excel_to_code 0.1.13 → 0.1.16
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.
- checksums.yaml +4 -4
- data/src/commands/excel_to_x.rb +5 -0
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/excel_to_c_runtime.c +110 -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/mmult.rb +26 -0
- data/src/rewrite/ast_expand_array_formulae.rb +1 -3
- data/src/rewrite/rewrite_array_formulae.rb +1 -1
- data/src/simplify.rb +2 -0
- data/src/simplify/replace_arithmetic_on_ranges.rb +83 -0
- data/src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb +28 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37491ae4c70dc1d7a26a9d2487bb5e993791eb4b
|
4
|
+
data.tar.gz: ac3feebc2f5dcff782cd919d125146ad18a0865a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d92241f090a56b107cfc75f3f066a1ede7cfec0f9b246f824702eb55749b52cd8de42b1f529b249c52a74e48b0698494f5c04623394a03a120d4feff3d74d99
|
7
|
+
data.tar.gz: c16f7d74c3986ddd28cf83def0453ce5efa31e983b6d548e5c789aa79782bc236fdc5b4dd48233f1894fdb2b87e0f3ed16770e56f875c8f94aca156d613c2f32
|
data/src/commands/excel_to_x.rb
CHANGED
@@ -564,6 +564,9 @@ class ExcelToX
|
|
564
564
|
replace r, [name, 'Formulae'], "Workbook tables", [name, 'Formulae']
|
565
565
|
|
566
566
|
replace ReplaceRangesWithArrayLiterals, [name, 'Formulae'], [name, 'Formulae']
|
567
|
+
replace ReplaceArithmeticOnRanges, [name, 'Formulae'], [name, 'Formulae']
|
568
|
+
replace ReplaceArraysWithSingleCells, [name, 'Formulae'], [name, 'Formulae']
|
569
|
+
replace WrapFormulaeThatReturnArraysAndAReNotInArrays, [name, 'Formulae'], [name, 'Formulae']
|
567
570
|
end
|
568
571
|
end
|
569
572
|
|
@@ -612,7 +615,9 @@ class ExcelToX
|
|
612
615
|
|
613
616
|
# The result of the indirect might be a range, which we need to simplify
|
614
617
|
replace ReplaceRangesWithArrayLiterals, [name, 'Formulae'], [name, 'Formulae']
|
618
|
+
replace ReplaceArithmeticOnRanges, [name, 'Formulae'], [name, 'Formulae']
|
615
619
|
replace ReplaceArraysWithSingleCells, [name, 'Formulae'], [name, 'Formulae']
|
620
|
+
replace WrapFormulaeThatReturnArraysAndAReNotInArrays, [name, 'Formulae'], [name, 'Formulae']
|
616
621
|
end
|
617
622
|
end
|
618
623
|
|
data/src/compile/c/a.out
CHANGED
Binary file
|
@@ -67,6 +67,7 @@ static ExcelValue excel_log(ExcelValue number);
|
|
67
67
|
static ExcelValue excel_log_2(ExcelValue number, ExcelValue base);
|
68
68
|
static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
|
69
69
|
static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
|
70
|
+
static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v);
|
70
71
|
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
|
71
72
|
static ExcelValue negative(ExcelValue a_v);
|
72
73
|
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
|
@@ -1071,6 +1072,57 @@ static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
|
1071
1072
|
return new_excel_number(smallest_number_found);
|
1072
1073
|
}
|
1073
1074
|
|
1075
|
+
static ExcelValue mmult_error(ExcelValue a_v, ExcelValue b_v) {
|
1076
|
+
int rows = a_v.rows > b_v.rows ? a_v.rows : b_v.rows;
|
1077
|
+
int columns = a_v.columns > b_v.columns ? a_v.columns : b_v.columns;
|
1078
|
+
int i, j;
|
1079
|
+
|
1080
|
+
ExcelValue *result = (ExcelValue*) new_excel_value_array(rows*columns);
|
1081
|
+
|
1082
|
+
for(i=0; i<rows; i++) {
|
1083
|
+
for(j=0; j<columns; j++) {
|
1084
|
+
result[(i*columns) + j] = VALUE;
|
1085
|
+
}
|
1086
|
+
}
|
1087
|
+
return new_excel_range(result, rows, columns);
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v) {
|
1091
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
1092
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
1093
|
+
if(a_v.type != ExcelRange) { return VALUE;}
|
1094
|
+
if(b_v.type != ExcelRange) { return VALUE;}
|
1095
|
+
if(a_v.columns != b_v.rows) { return mmult_error(a_v, b_v); }
|
1096
|
+
int n = a_v.columns;
|
1097
|
+
int a_rows = a_v.rows;
|
1098
|
+
int a_columns = a_v.columns;
|
1099
|
+
int b_rows = b_v.rows;
|
1100
|
+
int b_columns = b_v.columns;
|
1101
|
+
ExcelValue *result = (ExcelValue*) new_excel_value_array(a_rows*b_columns);
|
1102
|
+
int i, j, k;
|
1103
|
+
double sum;
|
1104
|
+
ExcelValue *array_a = a_v.array;
|
1105
|
+
ExcelValue *array_b = b_v.array;
|
1106
|
+
|
1107
|
+
ExcelValue a;
|
1108
|
+
ExcelValue b;
|
1109
|
+
|
1110
|
+
for(i=0; i<a_rows; i++) {
|
1111
|
+
for(j=0; j<b_columns; j++) {
|
1112
|
+
sum = 0;
|
1113
|
+
for(k=0; k<n; k++) {
|
1114
|
+
a = array_a[(i*a_columns)+k];
|
1115
|
+
b = array_b[(k*b_columns)+j];
|
1116
|
+
if(a.type != ExcelNumber) { return mmult_error(a_v, b_v); }
|
1117
|
+
if(b.type != ExcelNumber) { return mmult_error(a_v, b_v); }
|
1118
|
+
sum = sum + (a.number * b.number);
|
1119
|
+
}
|
1120
|
+
result[(i*b_columns)+j] = new_excel_number(sum);
|
1121
|
+
}
|
1122
|
+
}
|
1123
|
+
return new_excel_range(result, a_rows, b_columns);
|
1124
|
+
}
|
1125
|
+
|
1074
1126
|
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v) {
|
1075
1127
|
CHECK_FOR_PASSED_ERROR(a_v)
|
1076
1128
|
CHECK_FOR_PASSED_ERROR(b_v)
|
@@ -2414,6 +2466,64 @@ int test_functions() {
|
|
2414
2466
|
// Two argument variant allows LOG base to be specified
|
2415
2467
|
assert(excel_log_2(new_excel_number(8),new_excel_number(2)).number == 3.0);
|
2416
2468
|
assert(excel_log_2(new_excel_number(8),new_excel_number(0)).type == ExcelError);
|
2469
|
+
|
2470
|
+
// Test MMULT (Matrix multiplication)
|
2471
|
+
ExcelValue mmult_1[] = { ONE, TWO, THREE, FOUR};
|
2472
|
+
ExcelValue mmult_2[] = { FOUR, THREE, TWO, ONE};
|
2473
|
+
ExcelValue mmult_3[] = { ONE, TWO};
|
2474
|
+
ExcelValue mmult_4[] = { THREE, FOUR};
|
2475
|
+
ExcelValue mmult_5[] = { ONE, BLANK, THREE, FOUR};
|
2476
|
+
|
2477
|
+
ExcelValue mmult_1_v = new_excel_range( mmult_1, 2, 2);
|
2478
|
+
ExcelValue mmult_2_v = new_excel_range( mmult_2, 2, 2);
|
2479
|
+
ExcelValue mmult_3_v = new_excel_range( mmult_3, 1, 2);
|
2480
|
+
ExcelValue mmult_4_v = new_excel_range( mmult_4, 2, 1);
|
2481
|
+
ExcelValue mmult_5_v = new_excel_range( mmult_5, 2, 2);
|
2482
|
+
|
2483
|
+
// Treat the ranges as matrices and multiply them
|
2484
|
+
ExcelValue mmult_result_1_v = mmult(mmult_1_v, mmult_2_v);
|
2485
|
+
assert(mmult_result_1_v.type == ExcelRange);
|
2486
|
+
assert(mmult_result_1_v.rows == 2);
|
2487
|
+
assert(mmult_result_1_v.columns == 2);
|
2488
|
+
ExcelValue *mmult_result_1_a = mmult_result_1_v.array;
|
2489
|
+
assert(mmult_result_1_a[0].number == 8);
|
2490
|
+
assert(mmult_result_1_a[1].number == 5);
|
2491
|
+
assert(mmult_result_1_a[2].number == 20);
|
2492
|
+
assert(mmult_result_1_a[3].number == 13);
|
2493
|
+
|
2494
|
+
ExcelValue mmult_result_2_v = mmult(mmult_3_v, mmult_4_v);
|
2495
|
+
assert(mmult_result_2_v.type == ExcelRange);
|
2496
|
+
assert(mmult_result_2_v.rows == 1);
|
2497
|
+
assert(mmult_result_2_v.columns == 1);
|
2498
|
+
ExcelValue *mmult_result_2_a = mmult_result_2_v.array;
|
2499
|
+
assert(mmult_result_2_a[0].number == 11);
|
2500
|
+
|
2501
|
+
// Return an error if any cells are not numbers
|
2502
|
+
ExcelValue mmult_result_3_v = mmult(mmult_5_v, mmult_2_v);
|
2503
|
+
assert(mmult_result_3_v.type == ExcelRange);
|
2504
|
+
assert(mmult_result_3_v.rows == 2);
|
2505
|
+
assert(mmult_result_3_v.columns == 2);
|
2506
|
+
ExcelValue *mmult_result_3_a = mmult_result_3_v.array;
|
2507
|
+
assert(mmult_result_3_a[0].type == ExcelError);
|
2508
|
+
assert(mmult_result_3_a[1].type == ExcelError);
|
2509
|
+
assert(mmult_result_3_a[2].type == ExcelError);
|
2510
|
+
assert(mmult_result_3_a[3].type == ExcelError);
|
2511
|
+
|
2512
|
+
// Returns errors if arguments are not ranges
|
2513
|
+
// FIXME: Should work in edge case where passed two numbers which excel treats as ranges with one row and column
|
2514
|
+
ExcelValue mmult_result_4_v = mmult(ONE, mmult_2_v);
|
2515
|
+
assert(mmult_result_4_v.type == ExcelError);
|
2516
|
+
|
2517
|
+
// Returns errors if the ranges aren't the right size to multiply
|
2518
|
+
ExcelValue mmult_result_5_v = mmult(mmult_1_v, mmult_3_v);
|
2519
|
+
assert(mmult_result_5_v.type == ExcelRange);
|
2520
|
+
assert(mmult_result_5_v.rows == 2);
|
2521
|
+
assert(mmult_result_5_v.columns == 2);
|
2522
|
+
ExcelValue *mmult_result_5_a = mmult_result_5_v.array;
|
2523
|
+
assert(mmult_result_5_a[0].type == ExcelError);
|
2524
|
+
assert(mmult_result_5_a[1].type == ExcelError);
|
2525
|
+
assert(mmult_result_5_a[2].type == ExcelError);
|
2526
|
+
assert(mmult_result_5_a[3].type == ExcelError);
|
2417
2527
|
|
2418
2528
|
// Release memory
|
2419
2529
|
free_all_allocated_memory();
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ExcelFunctions
|
2
|
+
|
3
|
+
def mmult(array_a, array_b)
|
4
|
+
return array_a if array_a.is_a?(Symbol)
|
5
|
+
return array_b if array_b.is_a?(Symbol)
|
6
|
+
return :value unless array_a.is_a?(Array)
|
7
|
+
return :value unless array_b.is_a?(Array)
|
8
|
+
|
9
|
+
columns = array_a.first.length
|
10
|
+
rows = array_b.length
|
11
|
+
error = Array.new([rows, columns].max) { Array.new([rows, columns].max, :value) }
|
12
|
+
return error unless columns == rows
|
13
|
+
return error unless array_a.all? { |a| a.all? { |b| b.is_a?(Numeric) }}
|
14
|
+
return error unless array_b.all? { |a| a.all? { |b| b.is_a?(Numeric) }}
|
15
|
+
result = Array.new(array_a.length) { Array.new(array_b.first.length) }
|
16
|
+
indices = (0...rows).to_a
|
17
|
+
result.map.with_index do |row, i|
|
18
|
+
row.map.with_index do |cell, j|
|
19
|
+
indices.inject(0) do |sum, n|
|
20
|
+
sum = sum + (array_a[i][n] * array_b[n][j])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -64,7 +64,7 @@ class AstExpandArrayFormulae
|
|
64
64
|
end]
|
65
65
|
end
|
66
66
|
|
67
|
-
FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS = %w{AVERAGE COUNT COUNTA MAX MIN SUM SUMPRODUCT}
|
67
|
+
FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS = %w{AVERAGE COUNT COUNTA MAX MIN SUM SUMPRODUCT MMULT}
|
68
68
|
|
69
69
|
def function(name,*arguments)
|
70
70
|
if FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS.include?(name)
|
@@ -82,9 +82,7 @@ class AstExpandArrayFormulae
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def map_match(*args)
|
85
|
-
$DEBUGN = true
|
86
85
|
a = array_map args, 'MATCH', false, true, false
|
87
|
-
$DEBUGN = false
|
88
86
|
a
|
89
87
|
end
|
90
88
|
|
@@ -30,7 +30,7 @@ class ExtractArrayFormulaForCell
|
|
30
30
|
ast[@row_offset+1][@column_offset+1] # plus ones to skip tthe [:array,[:row,"cell"]] symbols
|
31
31
|
end
|
32
32
|
|
33
|
-
FUNCTIONS_THAT_CAN_RETURN_ARRAYS = %w{INDEX}
|
33
|
+
FUNCTIONS_THAT_CAN_RETURN_ARRAYS = %w{INDEX MMULT}
|
34
34
|
|
35
35
|
def map_function(ast)
|
36
36
|
return ast unless FUNCTIONS_THAT_CAN_RETURN_ARRAYS.include?(ast[1])
|
data/src/simplify.rb
CHANGED
@@ -16,3 +16,5 @@ require_relative "simplify/replace_common_elements_in_formulae"
|
|
16
16
|
require_relative "simplify/replace_arrays_with_single_cells"
|
17
17
|
require_relative "simplify/replace_values_with_constants"
|
18
18
|
require_relative "simplify/sort_into_calculation_order"
|
19
|
+
require_relative "simplify/replace_arithmetic_on_ranges"
|
20
|
+
require_relative "simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays"
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class ReplaceArithmeticOnRangesAst
|
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 arithmetic(left, operator, right)
|
14
|
+
if left.first == :array && right.first != :array
|
15
|
+
mapped_right = map(right)
|
16
|
+
array_map(left) do |cell|
|
17
|
+
[:arithmetic, map(cell), operator, mapped_right]
|
18
|
+
end
|
19
|
+
elsif left.first != :array && right.first == :array
|
20
|
+
mapped_left = map(left)
|
21
|
+
array_map(right) do |cell|
|
22
|
+
[:arithmetic, mapped_left, operator, map(cell)]
|
23
|
+
end
|
24
|
+
elsif left.first == :array && right.first == :array
|
25
|
+
left.map.with_index do |row, i|
|
26
|
+
if row == :array
|
27
|
+
row
|
28
|
+
else
|
29
|
+
row.map.with_index do |cell, j|
|
30
|
+
if cell == :row
|
31
|
+
cell
|
32
|
+
elsif i >= left.length || i >= right.length || j >= left.first.length || j >= right.first.length
|
33
|
+
[:error, "#VALUE!"]
|
34
|
+
else
|
35
|
+
[:arithmetic, map(left[i][j]), operator, map(right[i][j])]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
[:arithmetic, map(left), operator, map(right)]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def array_map(array)
|
46
|
+
array.map do |row|
|
47
|
+
if row == :array
|
48
|
+
row
|
49
|
+
else
|
50
|
+
row.map do |column|
|
51
|
+
if column == :row
|
52
|
+
column
|
53
|
+
else
|
54
|
+
yield column
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
class ReplaceArithmeticOnRanges
|
65
|
+
|
66
|
+
def self.replace(*args)
|
67
|
+
self.new.replace(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def replace(input,output)
|
71
|
+
rewriter = ReplaceArithmeticOnRangesAst.new
|
72
|
+
input.each_line do |line|
|
73
|
+
# Looks to match lines with references
|
74
|
+
if line =~ /:arithmetic/ && line =~ /:array/
|
75
|
+
content = line.split("\t")
|
76
|
+
ast = eval(content.pop)
|
77
|
+
output.puts "#{content.join("\t")}\t#{rewriter.map(ast).inspect}"
|
78
|
+
else
|
79
|
+
output.puts line
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative '../excel'
|
2
|
+
|
3
|
+
class WrapFormulaeThatReturnArraysAndAReNotInArrays
|
4
|
+
|
5
|
+
def self.replace(*args)
|
6
|
+
self.new.replace(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def replace(input,output)
|
10
|
+
|
11
|
+
input.each_line do |line|
|
12
|
+
# Looks to match lines that contain formulae that return ranges, such as MMULT
|
13
|
+
if line =~ /"MMULT"/
|
14
|
+
content = line.split("\t")
|
15
|
+
ast = eval(content.pop)
|
16
|
+
if ast[0] == :function && ast[1] == "MMULT"
|
17
|
+
new_ast = [:function, "INDEX", ast, [:number, "1"], [:number, "1"]]
|
18
|
+
output.puts "#{content.join("\t")}\t#{new_ast.inspect}"
|
19
|
+
else
|
20
|
+
output.puts line
|
21
|
+
end
|
22
|
+
else
|
23
|
+
output.puts line
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
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.16
|
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-08-
|
11
|
+
date: 2013-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubypeg
|
@@ -145,6 +145,7 @@ files:
|
|
145
145
|
- src/excel/excel_functions/max.rb
|
146
146
|
- src/excel/excel_functions/mid.rb
|
147
147
|
- src/excel/excel_functions/min.rb
|
148
|
+
- src/excel/excel_functions/mmult.rb
|
148
149
|
- src/excel/excel_functions/mod.rb
|
149
150
|
- src/excel/excel_functions/more_than.rb
|
150
151
|
- src/excel/excel_functions/more_than_or_equal.rb
|
@@ -212,6 +213,7 @@ files:
|
|
212
213
|
- src/simplify/inline_formulae.rb
|
213
214
|
- src/simplify/map_formulae_to_values.rb
|
214
215
|
- src/simplify/remove_cells.rb
|
216
|
+
- src/simplify/replace_arithmetic_on_ranges.rb
|
215
217
|
- src/simplify/replace_arrays_with_single_cells.rb
|
216
218
|
- src/simplify/replace_column_with_column_number.rb
|
217
219
|
- src/simplify/replace_common_elements_in_formulae.rb
|
@@ -225,6 +227,7 @@ files:
|
|
225
227
|
- src/simplify/replace_values_with_constants.rb
|
226
228
|
- src/simplify/simplify_arithmetic.rb
|
227
229
|
- src/simplify/sort_into_calculation_order.rb
|
230
|
+
- src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb
|
228
231
|
- src/simplify.rb
|
229
232
|
- src/util/not_supported_exception.rb
|
230
233
|
- src/util/try.rb
|