excel_to_code 0.3.18.beta.1 → 0.3.20

Sign up to get free protection for your applications and to get access to all the features.
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