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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 295445022d29e71173d548f2fbfae87804c2fc19f6ba111c5efdec35657cf7a6
4
- data.tar.gz: 025fd8e7080d061732be7f357ff242e2fb51fe64bb985c33c2703b8ae342cf01
3
+ metadata.gz: 454aafc3c0b81b49d23e47d43c6c9481e05bfd2f203a54cbb4f843a443c2a940
4
+ data.tar.gz: f91a141c594eade7eb307230be6b942d909081266e6ea13124ebce0539174161
5
5
  SHA512:
6
- metadata.gz: 6049e42f81bcecf1942c68141cc735406f0227c59c77d0008249bc8e7c3f7480f9090b554ceb62ab27a519861cc00a11c0ccd2673b89de91213fbd90336a12fd
7
- data.tar.gz: aa5aba794f98a99481171bf0753f090495f556c8da38a9ff37be8a1c0994fe92b9df15a9bf1f5f787120a23061c17ca6436cf486b2844dbbd628b4cc7cfe4d80
6
+ metadata.gz: ff5152ff1c3e2db4456ade2ae8602cf68d52cc62dfcaa4bd8e102d1c3083a42bb32b337d4618ca3bfacf97bec9bcf35eddaea3e9e3a9ecc10aba2a52d75d68bf
7
+ data.tar.gz: aa9fe6581068e15768d53e2b9a9d2692383c487559808a32f1e86df41d0c3ba5b3b50a01884c7b7e5086c8142ba507b48ab6498a10fbad26a4c71b4ddb57f147
@@ -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 < 20
1145
+ end while replacements_made_in_the_last_pass > 0 && number_of_passes < 50
1137
1146
  end
1138
1147
 
1139
1148
 
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
- #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)
@@ -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',
@@ -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'
@@ -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] || [: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
 
@@ -1,4 +1,4 @@
1
1
  class ExcelToCode
2
- def self.version() "0.3.18.beta.1" end
2
+ def self.version() "0.3.18.beta.2" 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.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-07-16 00:00:00.000000000 Z
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.6
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