excel_to_code 0.3.18.beta.1 → 0.3.18.beta.2
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 +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
|