excel_to_code 0.3.18.beta.1 → 0.3.18.beta.2

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