excel_to_code 0.3.18.beta.1 → 0.3.18.beta.2
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 +12 -3
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out +0 -0
- data/src/compile/c/excel_to_c_runtime.c +211 -3
- data/src/compile/c/excel_to_c_runtime_test.c +31 -0
- data/src/compile/c/map_formulae_to_c.rb +3 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +2 -1
- data/src/excel/excel_functions.rb +2 -0
- data/src/excel/excel_functions/mround.rb +19 -0
- data/src/rewrite/ast_expand_array_formulae.rb +4 -0
- data/src/rewrite/rewrite_array_formulae.rb +2 -2
- data/src/simplify/replace_arrays_with_single_cells.rb +28 -0
- data/src/simplify/replace_common_elements_in_formulae.rb +1 -1
- data/src/simplify/replace_named_references.rb +16 -1
- data/src/simplify/replace_offsets_with_references.rb +39 -7
- data/src/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 454aafc3c0b81b49d23e47d43c6c9481e05bfd2f203a54cbb4f843a443c2a940
|
4
|
+
data.tar.gz: f91a141c594eade7eb307230be6b942d909081266e6ea13124ebce0539174161
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff5152ff1c3e2db4456ade2ae8602cf68d52cc62dfcaa4bd8e102d1c3083a42bb32b337d4618ca3bfacf97bec9bcf35eddaea3e9e3a9ecc10aba2a52d75d68bf
|
7
|
+
data.tar.gz: aa9fe6581068e15768d53e2b9a9d2692383c487559808a32f1e86df41d0c3ba5b3b50a01884c7b7e5086c8142ba507b48ab6498a10fbad26a4c71b4ddb57f147
|
data/src/commands/excel_to_x.rb
CHANGED
@@ -342,7 +342,7 @@ class ExcelToX
|
|
342
342
|
begin
|
343
343
|
parsed = CachingFormulaParser.parse(reference, treat_external_references_as_local)
|
344
344
|
if parsed
|
345
|
-
@named_references[name] = parsed
|
345
|
+
@named_references[name] = deep_copy(parsed)
|
346
346
|
else
|
347
347
|
$stderr.puts "Named reference #{name} #{reference} not parsed"
|
348
348
|
exit
|
@@ -363,6 +363,15 @@ class ExcelToX
|
|
363
363
|
|
364
364
|
end
|
365
365
|
|
366
|
+
def deep_copy(ast)
|
367
|
+
return ast if ast.is_a?(Symbol)
|
368
|
+
return ast if ast.is_a?(Numeric)
|
369
|
+
return ast.dup unless ast.is_a?(Array)
|
370
|
+
ast.map do |a|
|
371
|
+
deep_copy(a)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
366
375
|
# Named references can be simple cell references,
|
367
376
|
# or they can be ranges, or errors, or table references
|
368
377
|
# this function converts all the different types into
|
@@ -375,7 +384,7 @@ class ExcelToX
|
|
375
384
|
@named_references.each do |name, reference|
|
376
385
|
reference = table_reference_replacer.map(reference)
|
377
386
|
reference = @replace_ranges_with_array_literals_replacer.map(reference)
|
378
|
-
@named_references[name] = reference
|
387
|
+
@named_references[name] = deep_copy(reference)
|
379
388
|
end
|
380
389
|
|
381
390
|
end
|
@@ -1133,7 +1142,7 @@ class ExcelToX
|
|
1133
1142
|
replacements_made_in_the_last_pass += indirect_replacement.count_replaced
|
1134
1143
|
|
1135
1144
|
log.info "Pass #{number_of_passes}: Made #{replacements_made_in_the_last_pass} replacements"
|
1136
|
-
end while replacements_made_in_the_last_pass > 0 && number_of_passes <
|
1145
|
+
end while replacements_made_in_the_last_pass > 0 && number_of_passes < 50
|
1137
1146
|
end
|
1138
1147
|
|
1139
1148
|
|
data/src/compile/c/a.out
CHANGED
Binary file
|
Binary file
|
@@ -86,6 +86,7 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
|
|
86
86
|
static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
|
87
87
|
static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v);
|
88
88
|
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
|
89
|
+
static ExcelValue mround(ExcelValue value_v, ExcelValue multiple_v);
|
89
90
|
static ExcelValue na();
|
90
91
|
static ExcelValue negative(ExcelValue a_v);
|
91
92
|
static ExcelValue excel_not(ExcelValue a_v);
|
@@ -107,6 +108,9 @@ static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v);
|
|
107
108
|
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v);
|
108
109
|
static ExcelValue excel_int(ExcelValue number_v);
|
109
110
|
static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments);
|
111
|
+
static ExcelValue substitute_3(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v);
|
112
|
+
static ExcelValue substitute_4(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, ExcelValue occurrence_number_v);
|
113
|
+
static ExcelValue substitute(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, int occurrence);
|
110
114
|
static ExcelValue subtotal(ExcelValue type, int number_of_arguments, ExcelValue *arguments);
|
111
115
|
static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments);
|
112
116
|
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v );
|
@@ -305,7 +309,50 @@ static double number_from(ExcelValue v) {
|
|
305
309
|
return 0;
|
306
310
|
}
|
307
311
|
|
308
|
-
|
312
|
+
static char* string_from(ExcelValue v) {
|
313
|
+
char *string;
|
314
|
+
switch (v.type) {
|
315
|
+
case ExcelString:
|
316
|
+
return v.string;
|
317
|
+
break;
|
318
|
+
case ExcelNumber:
|
319
|
+
string = malloc(20);
|
320
|
+
if(string == 0) {
|
321
|
+
printf("Out of memory in string_from");
|
322
|
+
exit(-1);
|
323
|
+
}
|
324
|
+
snprintf(string,20,"%g",v.number);
|
325
|
+
free_later(string);
|
326
|
+
return string;
|
327
|
+
break;
|
328
|
+
case ExcelBoolean:
|
329
|
+
if(v.number == true) {
|
330
|
+
string = "TRUE";
|
331
|
+
free_later(string);
|
332
|
+
return string;
|
333
|
+
} else {
|
334
|
+
string = "FALSE";
|
335
|
+
free_later(string);
|
336
|
+
return string;
|
337
|
+
}
|
338
|
+
break;
|
339
|
+
case ExcelEmpty:
|
340
|
+
string = "";
|
341
|
+
free_later(string);
|
342
|
+
break;
|
343
|
+
case ExcelError:
|
344
|
+
conversion_error = 1;
|
345
|
+
return 0;
|
346
|
+
case ExcelRange:
|
347
|
+
conversion_error = 1;
|
348
|
+
return 0;
|
349
|
+
}
|
350
|
+
conversion_error = 1;
|
351
|
+
return 0;
|
352
|
+
}
|
353
|
+
|
354
|
+
#define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { conversion_error = 0; return value_name; }; name = number_from(value_name);
|
355
|
+
#define STRING(value_name, name) char *name; if(value_name.type == ExcelError) { conversion_error = 0; return value_name; }; name = string_from(value_name);
|
309
356
|
#define CHECK_FOR_CONVERSION_ERROR if(conversion_error) { conversion_error = 0; return VALUE; };
|
310
357
|
#define CHECK_FOR_PASSED_ERROR(name) if(name.type == ExcelError) return name;
|
311
358
|
|
@@ -465,6 +512,7 @@ static ExcelValue rate(ExcelValue periods_v, ExcelValue payment_v, ExcelValue pr
|
|
465
512
|
NUMBER(payment_v, payment)
|
466
513
|
NUMBER(presentValue_v, presentValue)
|
467
514
|
NUMBER(finalValue_v, finalValue)
|
515
|
+
CHECK_FOR_CONVERSION_ERROR;
|
468
516
|
|
469
517
|
// FIXME: Only implemented the case where payment is zero
|
470
518
|
if(payment != 0) {
|
@@ -474,6 +522,29 @@ static ExcelValue rate(ExcelValue periods_v, ExcelValue payment_v, ExcelValue pr
|
|
474
522
|
return EXCEL_NUMBER(pow((finalValue/(-presentValue)),(1.0/periods))-1.0);
|
475
523
|
}
|
476
524
|
|
525
|
+
static ExcelValue mround(ExcelValue value_v, ExcelValue multiple_v) {
|
526
|
+
CHECK_FOR_PASSED_ERROR(value_v)
|
527
|
+
CHECK_FOR_PASSED_ERROR(multiple_v)
|
528
|
+
|
529
|
+
NUMBER(value_v, value)
|
530
|
+
NUMBER(multiple_v, multiple)
|
531
|
+
CHECK_FOR_CONVERSION_ERROR;
|
532
|
+
|
533
|
+
if( (value < 0) != (multiple < 0)) {
|
534
|
+
return NUM;
|
535
|
+
}
|
536
|
+
|
537
|
+
if(value == 0) {
|
538
|
+
return ZERO;
|
539
|
+
}
|
540
|
+
|
541
|
+
if(multiple == 0) {
|
542
|
+
return ZERO;
|
543
|
+
}
|
544
|
+
|
545
|
+
return EXCEL_NUMBER(round(value / multiple) * multiple);
|
546
|
+
}
|
547
|
+
|
477
548
|
static ExcelValue excel_and(int array_size, ExcelValue *array) {
|
478
549
|
int i;
|
479
550
|
ExcelValue current_excel_value, array_result;
|
@@ -1777,12 +1848,11 @@ static ExcelValue rank(ExcelValue number_v, ExcelValue range_v, ExcelValue order
|
|
1777
1848
|
|
1778
1849
|
NUMBER(number_v, number)
|
1779
1850
|
NUMBER(order_v, order)
|
1851
|
+
CHECK_FOR_CONVERSION_ERROR
|
1780
1852
|
|
1781
1853
|
ExcelValue *array;
|
1782
1854
|
int size;
|
1783
1855
|
|
1784
|
-
CHECK_FOR_CONVERSION_ERROR
|
1785
|
-
|
1786
1856
|
if(range_v.type != ExcelRange) {
|
1787
1857
|
array = new_excel_value_array(1);
|
1788
1858
|
array[0] = range_v;
|
@@ -1934,6 +2004,129 @@ static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments) {
|
|
1934
2004
|
return EXCEL_STRING(string);
|
1935
2005
|
}
|
1936
2006
|
|
2007
|
+
static ExcelValue substitute_3(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v) {
|
2008
|
+
// -1 means all occurrences
|
2009
|
+
return substitute(string_v, old_string_v, new_string_v, -1);
|
2010
|
+
}
|
2011
|
+
|
2012
|
+
static ExcelValue substitute_4(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, ExcelValue occurrence_number_v) {
|
2013
|
+
CHECK_FOR_PASSED_ERROR(occurrence_number_v)
|
2014
|
+
NUMBER(occurrence_number_v,occurrence_number)
|
2015
|
+
CHECK_FOR_CONVERSION_ERROR
|
2016
|
+
return substitute(string_v, old_string_v, new_string_v, occurrence_number);
|
2017
|
+
}
|
2018
|
+
|
2019
|
+
// Pass < 0 to occurrence_n to replace all occurrences
|
2020
|
+
static ExcelValue substitute(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, int occurrence_n) {
|
2021
|
+
STRING(string_v, original)
|
2022
|
+
STRING(old_string_v, from_string)
|
2023
|
+
STRING(new_string_v, to_string)
|
2024
|
+
CHECK_FOR_CONVERSION_ERROR
|
2025
|
+
|
2026
|
+
char *new_string = 0; // Allocated below
|
2027
|
+
int original_length = strlen(original);
|
2028
|
+
int from_string_length = strlen(from_string);
|
2029
|
+
int to_string_length = strlen(to_string);
|
2030
|
+
int extra_space_per_replacement = (from_string_length < to_string_length) ? (to_string_length - from_string_length) : 0;
|
2031
|
+
|
2032
|
+
if(from_string_length == 0) {
|
2033
|
+
return string_v;
|
2034
|
+
}
|
2035
|
+
|
2036
|
+
int allocated_length = 0; // Adjusted below
|
2037
|
+
int space_for_number_of_replacements = 0; // Adjusted below
|
2038
|
+
int replacement_made = 0;
|
2039
|
+
int number_of_matches = 0;
|
2040
|
+
int insertion_point_offset = 0;
|
2041
|
+
char *insertion_point = 0;
|
2042
|
+
char *from_point = original;
|
2043
|
+
char *match_point = 0;
|
2044
|
+
|
2045
|
+
if(extra_space_per_replacement > 0) {
|
2046
|
+
space_for_number_of_replacements = 5;
|
2047
|
+
// Arbitrarily assume 5 replacements as a starting point, plus one for terminator
|
2048
|
+
allocated_length = original_length + (space_for_number_of_replacements * extra_space_per_replacement) + 1;
|
2049
|
+
} else {
|
2050
|
+
// Should be shorter or the same length
|
2051
|
+
allocated_length = original_length + 1;
|
2052
|
+
}
|
2053
|
+
|
2054
|
+
new_string = malloc(allocated_length);
|
2055
|
+
insertion_point = new_string;
|
2056
|
+
|
2057
|
+
if(new_string == 0) {
|
2058
|
+
printf("Out of memory in substitute");
|
2059
|
+
exit(-1);
|
2060
|
+
}
|
2061
|
+
|
2062
|
+
while((from_point-original) < original_length) {
|
2063
|
+
match_point = strstr(from_point, from_string);
|
2064
|
+
|
2065
|
+
// No match found
|
2066
|
+
if(match_point == NULL) {
|
2067
|
+
// No match ever found? return the original
|
2068
|
+
if(number_of_matches == 0) {
|
2069
|
+
break;
|
2070
|
+
}
|
2071
|
+
// Copy the remaining string into the target. We should always have space.
|
2072
|
+
replacement_made = 1;
|
2073
|
+
strcpy(insertion_point, from_point);
|
2074
|
+
break;
|
2075
|
+
}
|
2076
|
+
|
2077
|
+
number_of_matches = number_of_matches + 1;
|
2078
|
+
|
2079
|
+
// We may only want to replace a single occurrence
|
2080
|
+
if(occurrence_n > 0 && occurrence_n != number_of_matches) {
|
2081
|
+
// Copy the bit before
|
2082
|
+
memcpy(insertion_point, from_point, match_point - from_point);
|
2083
|
+
insertion_point = insertion_point + (match_point - from_point);
|
2084
|
+
|
2085
|
+
// Copy the original
|
2086
|
+
memcpy(insertion_point, from_string, from_string_length);
|
2087
|
+
insertion_point = insertion_point + from_string_length;
|
2088
|
+
|
2089
|
+
from_point = match_point + from_string_length;
|
2090
|
+
continue;
|
2091
|
+
}
|
2092
|
+
|
2093
|
+
// We want to replace this occurrence
|
2094
|
+
|
2095
|
+
// Check we hvae enough space for the replacement
|
2096
|
+
if(extra_space_per_replacement > 0 && (number_of_matches > space_for_number_of_replacements)) {
|
2097
|
+
space_for_number_of_replacements = space_for_number_of_replacements * 2;
|
2098
|
+
allocated_length = original_length + (space_for_number_of_replacements * extra_space_per_replacement) + 1;
|
2099
|
+
insertion_point_offset = insertion_point - new_string;
|
2100
|
+
new_string = realloc(new_string,allocated_length);
|
2101
|
+
insertion_point = new_string + insertion_point_offset;
|
2102
|
+
if(!new_string) {
|
2103
|
+
printf("Out of memory in string substitute realloc trying to increase length to %d", allocated_length);
|
2104
|
+
exit(-1);
|
2105
|
+
}
|
2106
|
+
}
|
2107
|
+
|
2108
|
+
replacement_made = 1;
|
2109
|
+
|
2110
|
+
// Copy up to the match
|
2111
|
+
memcpy(insertion_point, from_point, match_point - from_point);
|
2112
|
+
insertion_point = insertion_point + (match_point - from_point);
|
2113
|
+
|
2114
|
+
// Copy the replacement
|
2115
|
+
memcpy(insertion_point, to_string, to_string_length);
|
2116
|
+
insertion_point = insertion_point + to_string_length;
|
2117
|
+
|
2118
|
+
from_point = match_point + from_string_length;
|
2119
|
+
}
|
2120
|
+
|
2121
|
+
if(replacement_made == 1) {
|
2122
|
+
free_later(new_string);
|
2123
|
+
return EXCEL_STRING(new_string);
|
2124
|
+
} else {
|
2125
|
+
free(new_string);
|
2126
|
+
return string_v;
|
2127
|
+
}
|
2128
|
+
}
|
2129
|
+
|
1937
2130
|
static ExcelValue subtotal(ExcelValue subtotal_type_v, int number_of_arguments, ExcelValue *arguments) {
|
1938
2131
|
CHECK_FOR_PASSED_ERROR(subtotal_type_v)
|
1939
2132
|
NUMBER(subtotal_type_v,subtotal_type)
|
@@ -2821,6 +3014,11 @@ static ExcelValue curve_5(ExcelValue curveType, ExcelValue currentYear, ExcelVal
|
|
2821
3014
|
}
|
2822
3015
|
|
2823
3016
|
static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
3017
|
+
CHECK_FOR_PASSED_ERROR(currentYear_v)
|
3018
|
+
CHECK_FOR_PASSED_ERROR(startValue_v)
|
3019
|
+
CHECK_FOR_PASSED_ERROR(endValue_v)
|
3020
|
+
CHECK_FOR_PASSED_ERROR(duration_v)
|
3021
|
+
CHECK_FOR_PASSED_ERROR(startYear_v)
|
2824
3022
|
|
2825
3023
|
NUMBER(currentYear_v, currentYear)
|
2826
3024
|
NUMBER(startValue_v, startValue)
|
@@ -2844,6 +3042,11 @@ static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, Exce
|
|
2844
3042
|
}
|
2845
3043
|
|
2846
3044
|
static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
3045
|
+
CHECK_FOR_PASSED_ERROR(currentYear_v)
|
3046
|
+
CHECK_FOR_PASSED_ERROR(startValue_v)
|
3047
|
+
CHECK_FOR_PASSED_ERROR(endValue_v)
|
3048
|
+
CHECK_FOR_PASSED_ERROR(duration_v)
|
3049
|
+
CHECK_FOR_PASSED_ERROR(startYear_v)
|
2847
3050
|
|
2848
3051
|
NUMBER(currentYear_v, currentYear)
|
2849
3052
|
NUMBER(startValue_v, startValue)
|
@@ -2867,6 +3070,11 @@ static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v,
|
|
2867
3070
|
}
|
2868
3071
|
|
2869
3072
|
static ExcelValue lcurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
3073
|
+
CHECK_FOR_PASSED_ERROR(currentYear_v)
|
3074
|
+
CHECK_FOR_PASSED_ERROR(startValue_v)
|
3075
|
+
CHECK_FOR_PASSED_ERROR(endValue_v)
|
3076
|
+
CHECK_FOR_PASSED_ERROR(duration_v)
|
3077
|
+
CHECK_FOR_PASSED_ERROR(startYear_v)
|
2870
3078
|
|
2871
3079
|
NUMBER(currentYear_v, currentYear)
|
2872
3080
|
NUMBER(startValue_v, startValue)
|
@@ -1212,6 +1212,21 @@ int test_functions() {
|
|
1212
1212
|
assert(excel_ceiling_math(EXCEL_NUMBER(136), EXCEL_NUMBER(10), ZERO).number == 140);
|
1213
1213
|
assert(excel_ceiling_math(EXCEL_NUMBER(-136), EXCEL_NUMBER(10), ZERO).number == -130);
|
1214
1214
|
assert(excel_ceiling_math(EXCEL_NUMBER(-136), EXCEL_NUMBER(10), ONE).number == -140);
|
1215
|
+
|
1216
|
+
// MROUND
|
1217
|
+
assert(mround(EXCEL_NUMBER(1), ZERO).number == 0.0);
|
1218
|
+
assert(mround(ZERO, EXCEL_NUMBER(1)).number == 0.0);
|
1219
|
+
assert(mround(EXCEL_NUMBER(1), EXCEL_NUMBER(1)).number == 1.0);
|
1220
|
+
assert(mround(EXCEL_NUMBER(105), EXCEL_NUMBER(10)).number == 110);
|
1221
|
+
assert(mround(EXCEL_NUMBER(1.05), EXCEL_NUMBER(0.1)).number == 1.10);
|
1222
|
+
assert(mround(EXCEL_NUMBER(-1.05), EXCEL_NUMBER(-0.1)).number == -1.10);
|
1223
|
+
assert(mround(EXCEL_NUMBER(-1.05), EXCEL_NUMBER(0.1)).type == ExcelError);
|
1224
|
+
|
1225
|
+
// SUBSTITUTE 3
|
1226
|
+
assert_equal(EXCEL_STRING("Hello Bob, Dear Bob"), substitute_3(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bill"), EXCEL_STRING("Frank")), "substitute('Hello Bob, Dear Bob', 'Bill', 'Frank') = 'Hello Bob, Dear Bob')");
|
1227
|
+
assert_equal(EXCEL_STRING("Hello Frank, Dear Frank"), substitute_3(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bob"), EXCEL_STRING("Frank")), "substitute('Hello Bob, Dear Bob', 'Bob', 'Frank') = 'Hello Frank, Dear Frank')");
|
1228
|
+
assert_equal(EXCEL_STRING("Hello Bob, Dear Frank"), substitute_4(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bob"), EXCEL_STRING("Frank"), TWO), "substitute('Hello Bob, Dear Bob', 'Bob', 'Frank', 2) = 'Hello Bob, Dear Frank')");
|
1229
|
+
assert_equal(EXCEL_STRING("Hello Bob, Dear Bob"), substitute_4(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bob"), EXCEL_STRING("Frank"), THREE), "substitute('Hello Bob, Dear Bob', 'Bob', 'Frank', 3) = 'Hello Bob, Dear Bob')");
|
1215
1230
|
|
1216
1231
|
// curve (a custom climact function)
|
1217
1232
|
assert_equal(
|
@@ -1239,6 +1254,20 @@ int test_functions() {
|
|
1239
1254
|
"curve_5(blank, 2023, 0, 10, 10) == 5"
|
1240
1255
|
);
|
1241
1256
|
|
1257
|
+
// CONVERSION ERROR RESET
|
1258
|
+
assert(conversion_error == 0);
|
1259
|
+
assert_equal(
|
1260
|
+
VALUE,
|
1261
|
+
curve_5(
|
1262
|
+
EXCEL_STRING("s"),
|
1263
|
+
EXCEL_STRING("Should be a number"),
|
1264
|
+
VALUE,
|
1265
|
+
EXCEL_NUMBER(10),
|
1266
|
+
EXCEL_NUMBER(10)
|
1267
|
+
),
|
1268
|
+
"A messed up curve call"
|
1269
|
+
);
|
1270
|
+
assert(conversion_error == 0);
|
1242
1271
|
|
1243
1272
|
// Release memory
|
1244
1273
|
free_all_allocated_memory();
|
@@ -1249,6 +1278,8 @@ int test_functions() {
|
|
1249
1278
|
return 0;
|
1250
1279
|
}
|
1251
1280
|
|
1281
|
+
|
1282
|
+
|
1252
1283
|
int main() {
|
1253
1284
|
return test_functions();
|
1254
1285
|
}
|
@@ -75,6 +75,7 @@ class MapFormulaeToC < MapValuesToC
|
|
75
75
|
:'MIN' => 'min',
|
76
76
|
:'MMULT' => 'mmult',
|
77
77
|
:'MOD' => 'mod',
|
78
|
+
:'MROUND' => 'mround',
|
78
79
|
:'NA' => 'na',
|
79
80
|
:'NOT' => 'excel_not',
|
80
81
|
:'OR' => 'excel_or',
|
@@ -97,6 +98,8 @@ class MapFormulaeToC < MapValuesToC
|
|
97
98
|
:'ROUNDUP' => 'roundup',
|
98
99
|
:'string_join' => 'string_join',
|
99
100
|
:'SUBTOTAL' => 'subtotal',
|
101
|
+
:'SUBSTITUTE3' => 'substitute_3',
|
102
|
+
:'SUBSTITUTE4' => 'substitute_4',
|
100
103
|
:'SUM' => 'sum',
|
101
104
|
:'SUMIF2' => 'sumif_2',
|
102
105
|
:'SUMIF3' => 'sumif',
|
@@ -22,7 +22,6 @@ class MapFormulaeToRuby < MapValuesToRuby
|
|
22
22
|
:'AVERAGE' => 'average',
|
23
23
|
:'AVERAGEIFS' => 'averageifs',
|
24
24
|
:'CEILING' => 'ceiling',
|
25
|
-
:'_xlfn.CEILING.MATH' => 'ceiling',
|
26
25
|
:'CELL' => 'cell',
|
27
26
|
:'CHAR' => 'char',
|
28
27
|
:'CHOOSE' => 'choose',
|
@@ -60,6 +59,7 @@ class MapFormulaeToRuby < MapValuesToRuby
|
|
60
59
|
:'MIN' => 'min',
|
61
60
|
:'MMULT' => 'mmult',
|
62
61
|
:'MOD' => 'mod',
|
62
|
+
:'MROUND' => 'mround',
|
63
63
|
:'NA' => 'na',
|
64
64
|
:'NOT' => 'excel_not',
|
65
65
|
:'NPV' => 'npv',
|
@@ -89,6 +89,7 @@ class MapFormulaeToRuby < MapValuesToRuby
|
|
89
89
|
:'VALUE' => 'value',
|
90
90
|
:'VLOOKUP' => 'vlookup',
|
91
91
|
:'^' => 'power',
|
92
|
+
:'_xlfn.CEILING.MATH' => 'ceiling',
|
92
93
|
:'_xlfn.FORECAST.LINEAR' => 'forecast',
|
93
94
|
:'curve' => 'curve',
|
94
95
|
:'halfscurve' => 'halfscurve',
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ExcelFunctions
|
2
|
+
|
3
|
+
def mround(value, multiple)
|
4
|
+
value = number_argument(value)
|
5
|
+
multiple = number_argument(multiple)
|
6
|
+
return value if value.is_a?(Symbol)
|
7
|
+
return multiple if multiple.is_a?(Symbol)
|
8
|
+
|
9
|
+
# Must both have the same sign
|
10
|
+
return :num unless (value < 0) == (multiple < 0)
|
11
|
+
|
12
|
+
# Zeros just return zero
|
13
|
+
return 0 if value == 0
|
14
|
+
return 0 if multiple == 0
|
15
|
+
|
16
|
+
(value.to_f / multiple.to_f).round * multiple.to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -99,6 +99,10 @@ class AstExpandArrayFormulae
|
|
99
99
|
def map_match(ast)
|
100
100
|
array_map(ast, false, true, false)
|
101
101
|
end
|
102
|
+
|
103
|
+
def map_offset(ast)
|
104
|
+
array_map(ast, true, false, false, false, false)
|
105
|
+
end
|
102
106
|
|
103
107
|
def map_subtotal(ast)
|
104
108
|
array_map ast, false, *Array.new(ast.length-3,true)
|
@@ -34,11 +34,11 @@ class ExtractArrayFormulaForCell
|
|
34
34
|
ast[@row_offset+1][@column_offset+1] # plus ones to skip tthe [:array,[:row,"cell"]] symbols
|
35
35
|
end
|
36
36
|
|
37
|
-
FUNCTIONS_THAT_CAN_RETURN_ARRAYS = { INDEX: true, MMULT: true}
|
37
|
+
FUNCTIONS_THAT_CAN_RETURN_ARRAYS = { INDEX: true, MMULT: true, OFFSET: true}
|
38
38
|
|
39
39
|
def map_function(ast)
|
40
40
|
return ast unless FUNCTIONS_THAT_CAN_RETURN_ARRAYS.has_key?(ast[1])
|
41
|
-
[:function, :INDEX, ast, @fc.map([:number, (@row_offset+1)]), @fc.map([:number, (column_offset+1)])]
|
41
|
+
[:function, :INDEX, ast.dup, @fc.map([:number, (@row_offset+1)]), @fc.map([:number, (column_offset+1)])]
|
42
42
|
end
|
43
43
|
|
44
44
|
end
|
@@ -67,6 +67,8 @@ class ReplaceArraysWithSingleCellsAst
|
|
67
67
|
# Replacement made in check_match function
|
68
68
|
elsif ast[1] == :INDIRECT && check_indirect(ast)
|
69
69
|
# Replacement made in check function
|
70
|
+
elsif ast[1] == :OFFSET && check_offset(ast)
|
71
|
+
# Replacement made in check function
|
70
72
|
elsif ast[0] == :function && ast[1] == :INDEX && check_index(ast)
|
71
73
|
# Replacement made in check
|
72
74
|
end
|
@@ -147,6 +149,32 @@ class ReplaceArraysWithSingleCellsAst
|
|
147
149
|
true
|
148
150
|
end
|
149
151
|
|
152
|
+
def check_offset(ast)
|
153
|
+
replacement_made = false
|
154
|
+
#We ALWAYS leave the reference unchanged
|
155
|
+
#if ast[2] && ast[2].first == :array
|
156
|
+
# replacement_made = true
|
157
|
+
# ast[2] = try_and_convert_array(ast[2])
|
158
|
+
#end
|
159
|
+
if ast[3] && ast[3].first == :array
|
160
|
+
replacement_made = true
|
161
|
+
ast[3] = try_and_convert_array(ast[3])
|
162
|
+
end
|
163
|
+
if ast[4] && ast[4].first == :array
|
164
|
+
replacement_made = true
|
165
|
+
ast[4] = try_and_convert_array(ast[4])
|
166
|
+
end
|
167
|
+
if ast[5] && ast[5].first == :array
|
168
|
+
replacement_made = true
|
169
|
+
ast[5] = try_and_convert_array(ast[5])
|
170
|
+
end
|
171
|
+
if ast[6] && ast[6].first == :array
|
172
|
+
replacement_made = true
|
173
|
+
ast[6] = try_and_convert_array(ast[6])
|
174
|
+
end
|
175
|
+
replacement_made
|
176
|
+
end
|
177
|
+
|
150
178
|
def check_sumifs(ast)
|
151
179
|
replacement_made = false
|
152
180
|
i = 4
|
@@ -16,7 +16,7 @@ class ReplaceCommonElementsInFormulae
|
|
16
16
|
input
|
17
17
|
end
|
18
18
|
|
19
|
-
VALUES = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true, :row => true}
|
19
|
+
VALUES = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true, :row => true, :comparator => true}
|
20
20
|
|
21
21
|
def replace_repeated_formulae(ast)
|
22
22
|
return ast unless ast.is_a?(Array)
|
@@ -5,16 +5,31 @@ class NamedReferences
|
|
5
5
|
def initialize(refs, tables = {})
|
6
6
|
@named_references = refs
|
7
7
|
@table_data = tables
|
8
|
+
@deepCopyCache = {}
|
8
9
|
end
|
9
10
|
|
10
11
|
def reference_for(sheet,named_reference)
|
11
12
|
sheet = sheet.downcase
|
12
13
|
named_reference = named_reference.downcase.to_sym
|
13
|
-
@named_references[[sheet, named_reference]] ||
|
14
|
+
ref = @named_references[[sheet, named_reference]] ||
|
14
15
|
@named_references[named_reference] ||
|
15
16
|
@table_data[named_reference] ||
|
16
17
|
[:error, :"#NAME?"]
|
18
|
+
return @deepCopyCache[ref] if @deepCopyCache.key?(ref)
|
19
|
+
copy = deep_copy(ref)
|
20
|
+
@deepCopyCache[ref] = copy
|
21
|
+
return copy
|
17
22
|
end
|
23
|
+
|
24
|
+
def deep_copy(ast)
|
25
|
+
return ast if ast.is_a?(Symbol)
|
26
|
+
return ast if ast.is_a?(Numeric)
|
27
|
+
return ast.dup unless ast.is_a?(Array)
|
28
|
+
ast.map do |a|
|
29
|
+
deep_copy(a)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
18
33
|
|
19
34
|
end
|
20
35
|
|
@@ -27,22 +27,49 @@ class ReplaceOffsetsWithReferencesAst
|
|
27
27
|
reference = ast[2]
|
28
28
|
row_offset = ast[3]
|
29
29
|
column_offset = ast[4]
|
30
|
-
height = ast[5]
|
31
|
-
width = ast[6]
|
30
|
+
height = ast[5]
|
31
|
+
width = ast[6]
|
32
|
+
|
33
|
+
reference = reference.original unless %i{cell sheet_reference array}.include?(reference.first)
|
34
|
+
|
35
|
+
unless height
|
36
|
+
if reference.first != :array
|
37
|
+
height = [:number, 1.0]
|
38
|
+
else
|
39
|
+
height = [:number, reference.length - 1]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
unless width
|
44
|
+
if reference.first != :array
|
45
|
+
width = [:number, 1.0]
|
46
|
+
else
|
47
|
+
width = [:number, reference[1].length - 1]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if reference.first == :array
|
52
|
+
reference = reference[1][1].original
|
53
|
+
end
|
54
|
+
|
32
55
|
[row_offset, column_offset, height, width].each do |arg|
|
33
56
|
next unless arg.first == :error
|
34
57
|
ast.replace(arg)
|
35
58
|
return
|
36
59
|
end
|
60
|
+
|
37
61
|
return unless [row_offset, column_offset, height, width].all? { |a| a.first == :number }
|
62
|
+
|
38
63
|
if reference.first == :cell
|
39
|
-
ast.replace(offset_cell(reference, row_offset, column_offset, height, width))
|
64
|
+
ast.replace(offset_cell(reference, row_offset, column_offset, height, width, nil))
|
40
65
|
elsif reference.first == :sheet_reference && reference[2].first == :cell
|
41
|
-
ast.replace(
|
66
|
+
ast.replace(offset_cell(reference[2], row_offset, column_offset, height, width, reference[1]))
|
67
|
+
else
|
68
|
+
p "OFFSET reference is #{reference} from #{ast}, so not replacing"
|
42
69
|
end
|
43
70
|
end
|
44
71
|
|
45
|
-
def offset_cell(reference, row_offset, column_offset, height, width)
|
72
|
+
def offset_cell(reference, row_offset, column_offset, height, width, sheet)
|
46
73
|
|
47
74
|
reference = reference[1]
|
48
75
|
row_offset = row_offset[1].to_i
|
@@ -57,9 +84,14 @@ class ReplaceOffsetsWithReferencesAst
|
|
57
84
|
start_reference = reference.offset(row_offset.to_i, column_offset.to_i)
|
58
85
|
end_reference = reference.offset(row_offset.to_i + height.to_i - 1, column_offset.to_i + width.to_i - 1)
|
59
86
|
if start_reference == end_reference
|
60
|
-
|
87
|
+
if sheet
|
88
|
+
return [:sheet_reference, sheet, [:cell, start_reference]]
|
89
|
+
else
|
90
|
+
return [:cell, start_reference]
|
91
|
+
end
|
61
92
|
else
|
62
|
-
|
93
|
+
area = Area.for("#{start_reference}:#{end_reference}")
|
94
|
+
return area.to_array_literal(sheet)
|
63
95
|
end
|
64
96
|
end
|
65
97
|
|
data/src/version.rb
CHANGED
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.3.18.beta.
|
4
|
+
version: 0.3.18.beta.2
|
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: 2018-
|
11
|
+
date: 2018-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubypeg
|
@@ -254,6 +254,7 @@ files:
|
|
254
254
|
- src/excel/excel_functions/mod.rb
|
255
255
|
- src/excel/excel_functions/more_than.rb
|
256
256
|
- src/excel/excel_functions/more_than_or_equal.rb
|
257
|
+
- src/excel/excel_functions/mround.rb
|
257
258
|
- src/excel/excel_functions/multiply.rb
|
258
259
|
- src/excel/excel_functions/na.rb
|
259
260
|
- src/excel/excel_functions/negative.rb
|
@@ -369,7 +370,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
369
370
|
version: 1.3.1
|
370
371
|
requirements: []
|
371
372
|
rubyforge_project:
|
372
|
-
rubygems_version: 2.7.
|
373
|
+
rubygems_version: 2.7.7
|
373
374
|
signing_key:
|
374
375
|
specification_version: 4
|
375
376
|
summary: Convert many .xlsx and .xlsm files into equivalent Ruby or C code that can
|