excel_to_code 0.3.18.beta.1 → 0.3.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 295445022d29e71173d548f2fbfae87804c2fc19f6ba111c5efdec35657cf7a6
4
- data.tar.gz: 025fd8e7080d061732be7f357ff242e2fb51fe64bb985c33c2703b8ae342cf01
3
+ metadata.gz: 6fe18bdd631dff8de227380800892b66163f2fe7f130891440c7f2e12818a28f
4
+ data.tar.gz: f0087a3035784f3fedee75f30064beb7f359e7e0f6d79ff0789f6a23aa5ad908
5
5
  SHA512:
6
- metadata.gz: 6049e42f81bcecf1942c68141cc735406f0227c59c77d0008249bc8e7c3f7480f9090b554ceb62ab27a519861cc00a11c0ccd2673b89de91213fbd90336a12fd
7
- data.tar.gz: aa5aba794f98a99481171bf0753f090495f556c8da38a9ff37be8a1c0994fe92b9df15a9bf1f5f787120a23061c17ca6436cf486b2844dbbd628b4cc7cfe4d80
6
+ metadata.gz: 95f10de60d4ab71095c011ef1857d3467d2d1c88993e75aa560dd16293ee7448c98677715e9fca7fdb937c2793f64a74d20f746cfcfa30d0a636cc9c2eb31587
7
+ data.tar.gz: 945373d97eb9523d8e6c3151249822af8bc55f3453ded5182d6979dbd5392b35ad1a4919fff367211330304e02164f59b640c6758e3978926b346ef9c7a6df0a
@@ -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
@@ -613,7 +622,7 @@ class ExcelToX
613
622
  end
614
623
 
615
624
  def check_all_functions_implemented
616
- functions_that_are_removed_during_compilation = [:INDIRECT, :OFFSET, :ROW, :COLUMN, :TRANSPOSE]
625
+ functions_that_are_removed_during_compilation = [:INDIRECT, :OFFSET, :ROW, :COLUMN, :TRANSPOSE, :'_xlfn.SINGLE']
617
626
  functions_used = CachingFormulaParser.instance.functions_used.keys
618
627
  functions_used.delete_if do |f|
619
628
  MapFormulaeToRuby::FUNCTIONS[f]
@@ -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 < 20
1145
+ end while replacements_made_in_the_last_pass > 0 && number_of_passes < 50
1137
1146
  end
1138
1147
 
1139
1148
 
@@ -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
- #define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { return value_name; }; name = number_from(value_name);
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)
@@ -2022,18 +2215,30 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
2022
2215
 
2023
2216
  if(current_value.type == ExcelString) {
2024
2217
  s = current_value.string;
2218
+ while(s[0] == ' ') {
2219
+ s = s + 1;
2220
+ }
2025
2221
  if(s[0] == '<') {
2026
2222
  if( s[1] == '>') {
2223
+ while(s[2] == ' ') {
2224
+ s = s + 1;
2225
+ }
2027
2226
  new_comparator = strndup(s+2,strlen(s)-2);
2028
2227
  free_later(new_comparator);
2029
2228
  criteria[i].type = NotEqual;
2030
2229
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2031
2230
  } else if(s[1] == '=') {
2231
+ while(s[2] == ' ') {
2232
+ s = s + 1;
2233
+ }
2032
2234
  new_comparator = strndup(s+2,strlen(s)-2);
2033
2235
  free_later(new_comparator);
2034
2236
  criteria[i].type = LessThanOrEqual;
2035
2237
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2036
2238
  } else {
2239
+ while(s[1] == ' ') {
2240
+ s = s + 1;
2241
+ }
2037
2242
  new_comparator = strndup(s+1,strlen(s)-1);
2038
2243
  free_later(new_comparator);
2039
2244
  criteria[i].type = LessThan;
@@ -2041,17 +2246,26 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
2041
2246
  }
2042
2247
  } else if(s[0] == '>') {
2043
2248
  if(s[1] == '=') {
2249
+ while(s[2] == ' ') {
2250
+ s = s + 1;
2251
+ }
2044
2252
  new_comparator = strndup(s+2,strlen(s)-2);
2045
2253
  free_later(new_comparator);
2046
2254
  criteria[i].type = MoreThanOrEqual;
2047
2255
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2048
2256
  } else {
2257
+ while(s[1] == ' ') {
2258
+ s = s + 1;
2259
+ }
2049
2260
  new_comparator = strndup(s+1,strlen(s)-1);
2050
2261
  free_later(new_comparator);
2051
2262
  criteria[i].type = MoreThan;
2052
2263
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2053
2264
  }
2054
2265
  } else if(s[0] == '=') {
2266
+ while(s[1] == ' ') {
2267
+ s = s + 1;
2268
+ }
2055
2269
  new_comparator = strndup(s+1,strlen(s)-1);
2056
2270
  free_later(new_comparator);
2057
2271
  criteria[i].type = Equal;
@@ -2273,18 +2487,30 @@ static ExcelValue countifs(int number_of_arguments, ExcelValue *arguments) {
2273
2487
 
2274
2488
  if(current_value.type == ExcelString) {
2275
2489
  s = current_value.string;
2490
+ while(s[0] == ' ') {
2491
+ s = s + 1;
2492
+ }
2276
2493
  if(s[0] == '<') {
2277
2494
  if( s[1] == '>') {
2495
+ while(s[2] == ' ') {
2496
+ s = s + 1;
2497
+ }
2278
2498
  new_comparator = strndup(s+2,strlen(s)-2);
2279
2499
  free_later(new_comparator);
2280
2500
  criteria[i].type = NotEqual;
2281
2501
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2282
2502
  } else if(s[1] == '=') {
2503
+ while(s[2] == ' ') {
2504
+ s = s + 1;
2505
+ }
2283
2506
  new_comparator = strndup(s+2,strlen(s)-2);
2284
2507
  free_later(new_comparator);
2285
2508
  criteria[i].type = LessThanOrEqual;
2286
2509
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2287
2510
  } else {
2511
+ while(s[1] == ' ') {
2512
+ s = s + 1;
2513
+ }
2288
2514
  new_comparator = strndup(s+1,strlen(s)-1);
2289
2515
  free_later(new_comparator);
2290
2516
  criteria[i].type = LessThan;
@@ -2292,17 +2518,26 @@ static ExcelValue countifs(int number_of_arguments, ExcelValue *arguments) {
2292
2518
  }
2293
2519
  } else if(s[0] == '>') {
2294
2520
  if(s[1] == '=') {
2521
+ while(s[2] == ' ') {
2522
+ s = s + 1;
2523
+ }
2295
2524
  new_comparator = strndup(s+2,strlen(s)-2);
2296
2525
  free_later(new_comparator);
2297
2526
  criteria[i].type = MoreThanOrEqual;
2298
2527
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2299
2528
  } else {
2529
+ while(s[1] == ' ') {
2530
+ s = s + 1;
2531
+ }
2300
2532
  new_comparator = strndup(s+1,strlen(s)-1);
2301
2533
  free_later(new_comparator);
2302
2534
  criteria[i].type = MoreThan;
2303
2535
  criteria[i].comparator = EXCEL_STRING(new_comparator);
2304
2536
  }
2305
2537
  } else if(s[0] == '=') {
2538
+ while(s[1] == ' ') {
2539
+ s = s + 1;
2540
+ }
2306
2541
  new_comparator = strndup(s+1,strlen(s)-1);
2307
2542
  free_later(new_comparator);
2308
2543
  criteria[i].type = Equal;
@@ -2678,6 +2913,8 @@ static ExcelValue text(ExcelValue number_v, ExcelValue format_v) {
2678
2913
  snprintf(s, 99, "%'0.3f",number_v.number);
2679
2914
  } else if(strcmp(format_v.string,"0000") == 0) {
2680
2915
  snprintf(s, 99, "%04.0f",number_v.number);
2916
+ } else if(strcmp(format_v.string,"#,000") == 0) {
2917
+ snprintf(s, 99, "%'03.0f",number_v.number);
2681
2918
  } else {
2682
2919
  snprintf(s, 99, "Text format not recognised");
2683
2920
  }
@@ -2821,6 +3058,11 @@ static ExcelValue curve_5(ExcelValue curveType, ExcelValue currentYear, ExcelVal
2821
3058
  }
2822
3059
 
2823
3060
  static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
3061
+ CHECK_FOR_PASSED_ERROR(currentYear_v)
3062
+ CHECK_FOR_PASSED_ERROR(startValue_v)
3063
+ CHECK_FOR_PASSED_ERROR(endValue_v)
3064
+ CHECK_FOR_PASSED_ERROR(duration_v)
3065
+ CHECK_FOR_PASSED_ERROR(startYear_v)
2824
3066
 
2825
3067
  NUMBER(currentYear_v, currentYear)
2826
3068
  NUMBER(startValue_v, startValue)
@@ -2844,6 +3086,11 @@ static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, Exce
2844
3086
  }
2845
3087
 
2846
3088
  static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
3089
+ CHECK_FOR_PASSED_ERROR(currentYear_v)
3090
+ CHECK_FOR_PASSED_ERROR(startValue_v)
3091
+ CHECK_FOR_PASSED_ERROR(endValue_v)
3092
+ CHECK_FOR_PASSED_ERROR(duration_v)
3093
+ CHECK_FOR_PASSED_ERROR(startYear_v)
2847
3094
 
2848
3095
  NUMBER(currentYear_v, currentYear)
2849
3096
  NUMBER(startValue_v, startValue)
@@ -2867,6 +3114,11 @@ static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v,
2867
3114
  }
2868
3115
 
2869
3116
  static ExcelValue lcurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
3117
+ CHECK_FOR_PASSED_ERROR(currentYear_v)
3118
+ CHECK_FOR_PASSED_ERROR(startValue_v)
3119
+ CHECK_FOR_PASSED_ERROR(endValue_v)
3120
+ CHECK_FOR_PASSED_ERROR(duration_v)
3121
+ CHECK_FOR_PASSED_ERROR(startYear_v)
2870
3122
 
2871
3123
  NUMBER(currentYear_v, currentYear)
2872
3124
  NUMBER(startValue_v, startValue)
@@ -585,6 +585,8 @@ int test_functions() {
585
585
 
586
586
  ExcelValue sumifs_array_10f[] = { sumifs_array_3_v, EXCEL_STRING(">=3")};
587
587
  assert(sumifs(sumifs_array_3_v,2, sumifs_array_10f).number == 17);
588
+ ExcelValue sumifs_array_10g[] = { sumifs_array_3_v, EXCEL_STRING(" >= 3")};
589
+ assert(sumifs(sumifs_array_3_v,2, sumifs_array_10g).number == 17);
588
590
 
589
591
  // ... BLANK in check range should match empty strings, BLANK in criteria should match zero
590
592
  ExcelValue sumifs_array_11[] = { BLANK, EXCEL_NUMBER(0)};
@@ -658,6 +660,9 @@ int test_functions() {
658
660
  ExcelValue countifs_array_10e[] = { countifs_array_3_v, EXCEL_STRING(">3")};
659
661
  assert(countifs(2, countifs_array_10e).number == 3.0);
660
662
 
663
+ ExcelValue countifs_array_10e2[] = { countifs_array_3_v, EXCEL_STRING("> 3")};
664
+ assert(countifs(2, countifs_array_10e2).number == 3.0);
665
+
661
666
  ExcelValue countifs_array_10f[] = { countifs_array_3_v, EXCEL_STRING(">=3")};
662
667
  assert(countifs(2, countifs_array_10f).number == 4.0);
663
668
 
@@ -890,6 +895,8 @@ int test_functions() {
890
895
  assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("#,##")).string, "123,456,789") == 0);
891
896
  assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("#,##0")).string, "123,456,789") == 0);
892
897
  assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("#,##0.0")).string, "123,456,789.1") == 0);
898
+ assert(strcmp(text(EXCEL_NUMBER(3.1), EXCEL_STRING("#,000")).string, "003") == 0);
899
+ assert(strcmp(text(EXCEL_NUMBER(1000.3), EXCEL_STRING("#,000")).string, "1,000") == 0);
893
900
  assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("!#,##0.0")).string, "Text format not recognised") == 0);
894
901
 
895
902
  // Test LOG
@@ -1212,6 +1219,21 @@ int test_functions() {
1212
1219
  assert(excel_ceiling_math(EXCEL_NUMBER(136), EXCEL_NUMBER(10), ZERO).number == 140);
1213
1220
  assert(excel_ceiling_math(EXCEL_NUMBER(-136), EXCEL_NUMBER(10), ZERO).number == -130);
1214
1221
  assert(excel_ceiling_math(EXCEL_NUMBER(-136), EXCEL_NUMBER(10), ONE).number == -140);
1222
+
1223
+ // MROUND
1224
+ assert(mround(EXCEL_NUMBER(1), ZERO).number == 0.0);
1225
+ assert(mround(ZERO, EXCEL_NUMBER(1)).number == 0.0);
1226
+ assert(mround(EXCEL_NUMBER(1), EXCEL_NUMBER(1)).number == 1.0);
1227
+ assert(mround(EXCEL_NUMBER(105), EXCEL_NUMBER(10)).number == 110);
1228
+ assert(mround(EXCEL_NUMBER(1.05), EXCEL_NUMBER(0.1)).number == 1.10);
1229
+ assert(mround(EXCEL_NUMBER(-1.05), EXCEL_NUMBER(-0.1)).number == -1.10);
1230
+ assert(mround(EXCEL_NUMBER(-1.05), EXCEL_NUMBER(0.1)).type == ExcelError);
1231
+
1232
+ // SUBSTITUTE 3
1233
+ 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')");
1234
+ 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')");
1235
+ 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')");
1236
+ 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
1237
 
1216
1238
  // curve (a custom climact function)
1217
1239
  assert_equal(
@@ -1239,6 +1261,20 @@ int test_functions() {
1239
1261
  "curve_5(blank, 2023, 0, 10, 10) == 5"
1240
1262
  );
1241
1263
 
1264
+ // CONVERSION ERROR RESET
1265
+ assert(conversion_error == 0);
1266
+ assert_equal(
1267
+ VALUE,
1268
+ curve_5(
1269
+ EXCEL_STRING("s"),
1270
+ EXCEL_STRING("Should be a number"),
1271
+ VALUE,
1272
+ EXCEL_NUMBER(10),
1273
+ EXCEL_NUMBER(10)
1274
+ ),
1275
+ "A messed up curve call"
1276
+ );
1277
+ assert(conversion_error == 0);
1242
1278
 
1243
1279
  // Release memory
1244
1280
  free_all_allocated_memory();
@@ -1249,6 +1285,8 @@ int test_functions() {
1249
1285
  return 0;
1250
1286
  }
1251
1287
 
1288
+
1289
+
1252
1290
  int main() {
1253
1291
  return test_functions();
1254
1292
  }
@@ -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
@@ -69,7 +69,7 @@ module ExcelFunctions
69
69
  when Numeric
70
70
  check_value == required_value
71
71
  when String
72
- required_value =~ /^(<=|>=|<|>)?([-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?)$/
72
+ required_value =~ /^\s*(<=|>=|<|>)?\s*([-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?)\s*$/
73
73
  if $1 && $2
74
74
  check_value.send($1,$2.to_f)
75
75
  elsif $2
@@ -20,9 +20,12 @@ module ExcelFunctions
20
20
  text(number*100, $1)+"%"
21
21
  when /^(0+)$/
22
22
  sprintf("%0#{$1.length}.0f", number)
23
- when /#[,.]#+(0[.,]0+)?/
23
+ when /^#[,.]#*(0[.,]0+)?(0*)$/
24
24
  formated_with_decimals = text(number, $1 || "0")
25
25
  parts = formated_with_decimals.split('.')
26
+ if $2 && parts[0].length < $2.length
27
+ parts[0] = ("0"*($2.length - parts[0].length))+parts[0]
28
+ end
26
29
  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
27
30
  parts.join('.')
28
31
  when /0[.,](0+)/
@@ -148,3 +148,5 @@ require_relative 'excel_functions/replace'
148
148
  require_relative 'excel_functions/rate'
149
149
 
150
150
  require_relative 'excel_functions/ceiling'
151
+
152
+ require_relative 'excel_functions/mround'
@@ -56,7 +56,7 @@ class Reference < String
56
56
  end
57
57
 
58
58
  def unfix
59
- gsub("$","")
59
+ Reference.for(gsub("$",""))
60
60
  end
61
61
 
62
62
  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,8 +67,12 @@ 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
74
+ elsif ast[1] == :'_xlfn.SINGLE'
75
+ ast.replace(try_and_convert_array(ast[2]))
72
76
  end
73
77
  end
74
78
  end
@@ -147,6 +151,32 @@ class ReplaceArraysWithSingleCellsAst
147
151
  true
148
152
  end
149
153
 
154
+ def check_offset(ast)
155
+ replacement_made = false
156
+ #We ALWAYS leave the reference unchanged
157
+ #if ast[2] && ast[2].first == :array
158
+ # replacement_made = true
159
+ # ast[2] = try_and_convert_array(ast[2])
160
+ #end
161
+ if ast[3] && ast[3].first == :array
162
+ replacement_made = true
163
+ ast[3] = try_and_convert_array(ast[3])
164
+ end
165
+ if ast[4] && ast[4].first == :array
166
+ replacement_made = true
167
+ ast[4] = try_and_convert_array(ast[4])
168
+ end
169
+ if ast[5] && ast[5].first == :array
170
+ replacement_made = true
171
+ ast[5] = try_and_convert_array(ast[5])
172
+ end
173
+ if ast[6] && ast[6].first == :array
174
+ replacement_made = true
175
+ ast[6] = try_and_convert_array(ast[6])
176
+ end
177
+ replacement_made
178
+ end
179
+
150
180
  def check_sumifs(ast)
151
181
  replacement_made = false
152
182
  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] || [:number, 1]
31
- width = ast[6] || [:number, 1]
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([:sheet_reference, reference[1], offset_cell(reference[2], row_offset, column_offset, height, width)])
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
- [:cell, start_reference]
87
+ if sheet
88
+ return [:sheet_reference, sheet, [:cell, start_reference]]
89
+ else
90
+ return [:cell, start_reference]
91
+ end
61
92
  else
62
- [:area, start_reference, end_reference]
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/util.rb CHANGED
@@ -1,2 +1 @@
1
- require_relative "util/not_supported_exception"
2
- require_relative "util/try"
1
+ require_relative "util/not_supported_exception"
data/src/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  class ExcelToCode
2
- def self.version() "0.3.18.beta.1" end
2
+ def self.version() "0.3.20" end
3
3
  end
4
4
 
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.1
4
+ version: 0.3.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Counsell, Green on Black Ltd
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-16 00:00:00.000000000 Z
11
+ date: 2022-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubypeg
@@ -174,9 +174,6 @@ files:
174
174
  - src/commands/excel_to_x.rb
175
175
  - src/compile.rb
176
176
  - src/compile/c.rb
177
- - src/compile/c/a.out
178
- - src/compile/c/a.out.dSYM/Contents/Info.plist
179
- - src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out
180
177
  - src/compile/c/compile_named_reference_setters.rb
181
178
  - src/compile/c/compile_to_c.rb
182
179
  - src/compile/c/compile_to_c_header.rb
@@ -254,6 +251,7 @@ files:
254
251
  - src/excel/excel_functions/mod.rb
255
252
  - src/excel/excel_functions/more_than.rb
256
253
  - src/excel/excel_functions/more_than_or_equal.rb
254
+ - src/excel/excel_functions/mround.rb
257
255
  - src/excel/excel_functions/multiply.rb
258
256
  - src/excel/excel_functions/na.rb
259
257
  - src/excel/excel_functions/negative.rb
@@ -347,13 +345,12 @@ files:
347
345
  - src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb
348
346
  - src/util.rb
349
347
  - src/util/not_supported_exception.rb
350
- - src/util/try.rb
351
348
  - src/version.rb
352
349
  homepage: http://github.com/tamc/excel_to_code
353
350
  licenses:
354
351
  - MIT
355
352
  metadata: {}
356
- post_install_message:
353
+ post_install_message:
357
354
  rdoc_options: []
358
355
  require_paths:
359
356
  - src
@@ -364,13 +361,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
364
361
  version: 2.3.0
365
362
  required_rubygems_version: !ruby/object:Gem::Requirement
366
363
  requirements:
367
- - - ">"
364
+ - - ">="
368
365
  - !ruby/object:Gem::Version
369
- version: 1.3.1
366
+ version: '0'
370
367
  requirements: []
371
- rubyforge_project:
372
- rubygems_version: 2.7.6
373
- signing_key:
368
+ rubygems_version: 3.2.22
369
+ signing_key:
374
370
  specification_version: 4
375
371
  summary: Convert many .xlsx and .xlsm files into equivalent Ruby or C code that can
376
372
  be executed without Excel
data/src/compile/c/a.out DELETED
Binary file
@@ -1,20 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>CFBundleDevelopmentRegion</key>
6
- <string>English</string>
7
- <key>CFBundleIdentifier</key>
8
- <string>com.apple.xcode.dsym.a.out</string>
9
- <key>CFBundleInfoDictionaryVersion</key>
10
- <string>6.0</string>
11
- <key>CFBundlePackageType</key>
12
- <string>dSYM</string>
13
- <key>CFBundleSignature</key>
14
- <string>????</string>
15
- <key>CFBundleShortVersionString</key>
16
- <string>1.0</string>
17
- <key>CFBundleVersion</key>
18
- <string>1</string>
19
- </dict>
20
- </plist>
data/src/util/try.rb DELETED
@@ -1,9 +0,0 @@
1
- class Object
2
- ##
3
- # @person ? @person.name : nil
4
- # vs
5
- # @person.try(:name)
6
- def try(method)
7
- send method if respond_to? method
8
- end
9
- end