excel_to_code 0.1.13 → 0.1.16
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 +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
|