mittens 0.3.4 → 0.3.5

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +1 -1
  5. data/lib/mittens/version.rb +1 -1
  6. data/vendor/snowball/.github/workflows/ci.yml +43 -3
  7. data/vendor/snowball/.github/workflows/coverage.yml +8 -5
  8. data/vendor/snowball/GNUmakefile +46 -16
  9. data/vendor/snowball/NEWS +437 -13
  10. data/vendor/snowball/README.rst +4 -4
  11. data/vendor/snowball/algorithms/earlymodernenglish.sbl +301 -0
  12. data/vendor/snowball/algorithms/french.sbl +1 -1
  13. data/vendor/snowball/algorithms/ukrainian.sbl +1251 -0
  14. data/vendor/snowball/compiler/analyser.c +460 -156
  15. data/vendor/snowball/compiler/driver.c +41 -36
  16. data/vendor/snowball/compiler/generator.c +24 -1
  17. data/vendor/snowball/compiler/generator_ada.c +46 -45
  18. data/vendor/snowball/compiler/generator_c.c +1094 -187
  19. data/vendor/snowball/compiler/generator_csharp.c +34 -11
  20. data/vendor/snowball/compiler/generator_dart.c +39 -11
  21. data/vendor/snowball/compiler/generator_go.c +6 -6
  22. data/vendor/snowball/compiler/generator_java.c +29 -8
  23. data/vendor/snowball/compiler/generator_js.c +65 -38
  24. data/vendor/snowball/compiler/generator_pascal.c +69 -26
  25. data/vendor/snowball/compiler/generator_php.c +33 -12
  26. data/vendor/snowball/compiler/generator_python.c +23 -4
  27. data/vendor/snowball/compiler/generator_rust.c +37 -13
  28. data/vendor/snowball/compiler/generator_zig.c +6 -6
  29. data/vendor/snowball/compiler/header.h +85 -14
  30. data/vendor/snowball/compiler/space.c +67 -38
  31. data/vendor/snowball/compiler/tokeniser.c +28 -16
  32. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +2 -2
  33. data/vendor/snowball/csharp/Snowball/Stemmer.cs +1 -4
  34. data/vendor/snowball/cxx/generate_algorithms.pl +5 -4
  35. data/vendor/snowball/cxx/{utilities.cxx → snowball_runtime.cxx} +1 -1
  36. data/vendor/snowball/cxx/stemmer.h +4 -0
  37. data/vendor/snowball/cxx/stemwords.cxx +36 -6
  38. data/vendor/snowball/dart/lib/src/snowball.dart +2 -2
  39. data/vendor/snowball/dart/pubspec.yaml +1 -1
  40. data/vendor/snowball/doc/libstemmer_c_README +3 -0
  41. data/vendor/snowball/doc/libstemmer_php_README +1 -1
  42. data/vendor/snowball/doc/libstemmer_python_README +8 -6
  43. data/vendor/snowball/examples/stemwords.c +4 -14
  44. data/vendor/snowball/go/env.go +16 -4
  45. data/vendor/snowball/include/libstemmer.h +0 -3
  46. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +4 -4
  47. data/vendor/snowball/javascript/base-stemmer.js +27 -40
  48. data/vendor/snowball/javascript/stemwords.js +34 -10
  49. data/vendor/snowball/libstemmer/libstemmer_c.in +5 -5
  50. data/vendor/snowball/libstemmer/mkmodules.pl +5 -3
  51. data/vendor/snowball/libstemmer/modules.txt +4 -2
  52. data/vendor/snowball/pascal/SnowballProgram.pas +2 -2
  53. data/vendor/snowball/php/base-stemmer.php +2 -2
  54. data/vendor/snowball/python/setup.py +5 -1
  55. data/vendor/snowball/python/snowballstemmer/basestemmer.py +1 -2
  56. data/vendor/snowball/runtime/api.c +14 -3
  57. data/vendor/snowball/runtime/api.h +13 -9
  58. data/vendor/snowball/runtime/{utilities.c → snowball_runtime.c} +115 -181
  59. data/vendor/snowball/runtime/snowball_runtime.h +35 -24
  60. data/vendor/snowball/rust/src/snowball/snowball_env.rs +1 -1
  61. data/vendor/snowball/tests/compilertest +3 -3
  62. data/vendor/snowball/tests/runtime/among.sbl +96 -2
  63. data/vendor/snowball/tests/runtime/attachinsert.sbl +4 -0
  64. data/vendor/snowball/tests/runtime/debug.out +7 -0
  65. data/vendor/snowball/tests/runtime/debug.sbl +9 -0
  66. data/vendor/snowball/tests/runtime/integertests.sbl +22 -0
  67. data/vendor/snowball/tests/runtime/marklimit.sbl +30 -0
  68. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  69. data/vendor/snowball/tests/runtime/reverse.sbl +11 -0
  70. data/vendor/snowball/tests/stemtest.c +2 -5
  71. data/vendor/snowball/tests/syntax/aeslicefrom.sbl +7 -0
  72. data/vendor/snowball/tests/syntax/canon.sbl +14 -0
  73. data/vendor/snowball/tests/syntax/canon.syntax +36 -0
  74. data/vendor/snowball/tests/syntax/inline.syntax +4 -4
  75. data/vendor/snowball/tests/syntax/localvar.sbl +38 -0
  76. data/vendor/snowball/tests/syntax/localvar.stderr +3 -0
  77. data/vendor/snowball/tests/syntax/localvar.syntax +76 -0
  78. data/vendor/snowball/tests/syntax/unused.sbl +5 -1
  79. data/vendor/snowball/tests/syntax/unused.stderr +3 -1
  80. data/vendor/snowball/zig/env.zig +24 -4
  81. metadata +14 -4
@@ -36,6 +36,20 @@ extern void print_program(struct analyser * a) {
36
36
  if (a->program) print_node_(a->program, 0, "");
37
37
  }
38
38
 
39
+ static void free_among(struct among * q) {
40
+ FREE(q->v);
41
+ FREE(q->commands);
42
+ FREE(q->af);
43
+ lose_b(q->table);
44
+ lose_s(q->table_endianness);
45
+ while (q->subtree) {
46
+ struct among_subtree * t = q->subtree;
47
+ q->subtree = q->subtree->next;
48
+ FREE(t);
49
+ }
50
+ FREE(q);
51
+ }
52
+
39
53
  static struct node * new_node_at_line(struct analyser * a, int type, int line) {
40
54
  NEW(node, p);
41
55
  *p = (struct node){0};
@@ -309,6 +323,12 @@ static int read_AE_test(struct analyser * a) {
309
323
  case c_lt:
310
324
  case c_le:
311
325
  return t->token;
326
+ case c_slicefrom:
327
+ // In an AE c_slicefrom (`<-`) is not a valid token, so convert
328
+ // it into c_lt (`<`) then c_minus (`-`) so that code such as
329
+ // `$x<-1` works rather than giving a syntax error.
330
+ push_token(t, c_minus);
331
+ return c_lt;
312
332
  default:
313
333
  unexpected_token_error(a, "integer test expression");
314
334
  hold_token(t);
@@ -736,9 +756,11 @@ static int nodes_equivalent_(const struct node *p, const struct node *q) {
736
756
  return nodes_equivalent(p->right, q->right);
737
757
  }
738
758
 
739
- static struct node * make_among(struct analyser * a, struct node * p, struct node * substring) {
759
+ static struct node * make_among(struct analyser * a, struct node * p,
760
+ int literalstring_count,
761
+ struct node * substring) {
740
762
  NEW(among, x);
741
- NEWVEC(amongvec, v, p->number);
763
+ NEWVEC(amongvec, v, literalstring_count);
742
764
  struct node * q = p->left;
743
765
  struct node * starter = NULL;
744
766
  struct amongvec * w0 = v;
@@ -750,9 +772,8 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
750
772
 
751
773
  *x = (struct among){0};
752
774
  x->node = p;
753
- x->b = v;
775
+ x->v = v;
754
776
  x->shortest_size = INT_MAX;
755
- x->in_routine = a->current_routine;
756
777
 
757
778
  if (q->type == c_bra) {
758
779
  fprintf(stderr,
@@ -780,8 +801,9 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
780
801
  ++function->uses_in_among;
781
802
  check_routine_mode(a, function, direction);
782
803
  if (function->among_index == 0) {
783
- function->among_index = ++x->function_count;
804
+ function->among_index = ++x->unique_function_count;
784
805
  }
806
+ ++x->function_count;
785
807
  w1->function_index = function->among_index;
786
808
  } else {
787
809
  w1->function = NULL;
@@ -826,7 +848,10 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
826
848
  }
827
849
  q = q->right;
828
850
  }
829
- if (w1-v != p->number) { fprintf(stderr, "oh! %d %d\n", (int)(w1-v), p->number); exit(1); }
851
+ if (w1-v != literalstring_count) {
852
+ fprintf(stderr, "oh! %d %d\n", (int)(w1-v), literalstring_count);
853
+ exit(1);
854
+ }
830
855
  x->command_count = result - 1;
831
856
  {
832
857
  NEWVEC(node*, commands, x->command_count);
@@ -881,7 +906,7 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
881
906
 
882
907
  for (w = w0 - 1; w >= v; w--) {
883
908
  if (w->size < size && memcmp(w->b, b, w->size * sizeof(symbol)) == 0) {
884
- w0->i = w - v; /* fill in index of longest substring */
909
+ w0->i = (int)(w - v); /* fill in index of longest substring */
885
910
  break;
886
911
  }
887
912
  }
@@ -901,7 +926,7 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
901
926
  a->tokeniser->file, w0->line_number);
902
927
  }
903
928
 
904
- x->literalstring_count = p->number;
929
+ x->literalstring_count = literalstring_count;
905
930
  p->among = x;
906
931
 
907
932
  if (starter) {
@@ -1010,14 +1035,15 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
1010
1035
  p = and_node;
1011
1036
  --v[0].function->uses_in_among;
1012
1037
  }
1013
- FREE(x->commands);
1014
- FREE(x);
1015
- FREE(v);
1038
+ free_among(x);
1016
1039
  return p;
1017
1040
  }
1018
1041
 
1019
- if (x->function_count) {
1020
- if (a->current_routine) a->current_routine->among_with_function = true;
1042
+ if (a->current_routine) {
1043
+ a->current_routine->has_among = true;
1044
+ if (x->function_count) {
1045
+ a->current_routine->has_among_function = true;
1046
+ }
1021
1047
  }
1022
1048
 
1023
1049
  x->substring = substring;
@@ -1037,7 +1063,7 @@ static struct node * read_among(struct analyser * a) {
1037
1063
  struct node * substring = a->substring;
1038
1064
 
1039
1065
  a->substring = NULL;
1040
- p->number = 0; /* counts the number of literals */
1066
+ int literalstring_count = 0;
1041
1067
  if (!get_token(a, c_bra)) return p;
1042
1068
  while (true) {
1043
1069
  struct node * q;
@@ -1052,7 +1078,7 @@ static struct node * read_among(struct analyser * a) {
1052
1078
  } else {
1053
1079
  hold_token(t);
1054
1080
  }
1055
- p->number++;
1081
+ literalstring_count++;
1056
1082
  break;
1057
1083
  case c_bra:
1058
1084
  if (previous_token == c_bra) {
@@ -1072,11 +1098,13 @@ static struct node * read_among(struct analyser * a) {
1072
1098
  previous_token = token;
1073
1099
  continue;
1074
1100
  case c_ket:
1075
- if (p->number == 0) {
1101
+ if (literalstring_count == 0) {
1076
1102
  report_error_location(a);
1077
1103
  fprintf(stderr, "empty among(...)\n");
1078
1104
  }
1079
- if (t->error_count == 0) p = make_among(a, p, substring);
1105
+ if (t->error_count == 0) {
1106
+ p = make_among(a, p, literalstring_count, substring);
1107
+ }
1080
1108
  return p;
1081
1109
  }
1082
1110
  previous_token = token;
@@ -1125,6 +1153,41 @@ static int ae_uses_name(struct node * p, struct name * q) {
1125
1153
  return 0;
1126
1154
  }
1127
1155
 
1156
+ // Put integer tests nodes into a normalised form, so that we can merge
1157
+ // among actions with equivalent integer tests - e.g. from arabic.sbl:
1158
+ //
1159
+ // [substring] among (
1160
+ // // [...]
1161
+ // '{a}{n}' '{w}{n}' '{y}{n}' ($(len > 5) delete) // present
1162
+ // '{t}{m}{a}' ($(len >= 6) delete)
1163
+ // )
1164
+ static void normalise_comparison(struct node * n) {
1165
+ if (!n->left || !n->AE) return;
1166
+ if (n->left->type == c_number && n->AE->type != c_number) {
1167
+ // Swap operands so a number goes on the RHS.
1168
+ switch (n->type) {
1169
+ case c_gt: n->type = c_lt; break;
1170
+ case c_lt: n->type = c_gt; break;
1171
+ case c_ge: n->type = c_le; break;
1172
+ case c_le: n->type = c_ge; break;
1173
+ }
1174
+ struct node * p = n->left;
1175
+ n->left = n->AE;
1176
+ n->AE = p;
1177
+ }
1178
+ if (n->AE->type == c_number && abs(n->AE->number) < 32767) {
1179
+ if (n->type == c_gt) {
1180
+ // Normalise `$(n > 0)` to `$(n >= 1)`.
1181
+ n->type = c_ge;
1182
+ ++n->AE->number;
1183
+ } else if (n->type == c_lt) {
1184
+ // Normalise `$(n < 1)` to `$(n <= 0)`
1185
+ n->type = c_le;
1186
+ --n->AE->number;
1187
+ }
1188
+ }
1189
+ }
1190
+
1128
1191
  static struct node * read_C(struct analyser * a) {
1129
1192
  struct tokeniser * t = a->tokeniser;
1130
1193
  int token = read_token(t);
@@ -1401,7 +1464,6 @@ static struct node * read_C(struct analyser * a) {
1401
1464
  return new_node(a, token);
1402
1465
  case c_assignto:
1403
1466
  case c_sliceto: {
1404
- check_modifyable(a);
1405
1467
  struct node * n = new_node(a, token);
1406
1468
  if (get_token(a, c_name)) {
1407
1469
  name_to_node(a, n, t_string);
@@ -1501,6 +1563,14 @@ report_assumed_rel_op_error:
1501
1563
  // Assume `==` was meant to try to avoid an error avalanche.
1502
1564
  token = c_eq;
1503
1565
  goto handle_rel_op;
1566
+ case c_slicefrom:
1567
+ // In an AE c_slicefrom (`<-`) is not a valid token, so
1568
+ // convert it into c_lt (`<`) then c_minus (`-`) so
1569
+ // that code such as `$(x<-1)` works rather than giving
1570
+ // a syntax error.
1571
+ push_token(t, c_minus);
1572
+ token = c_lt;
1573
+ // FALLTHRU
1504
1574
  case c_eq:
1505
1575
  case c_ne:
1506
1576
  case c_gt:
@@ -1548,6 +1618,7 @@ handle_rel_op: ;
1548
1618
  n = new_node(a, token);
1549
1619
  n->left = lhs;
1550
1620
  n->AE = rhs;
1621
+ normalise_comparison(n);
1551
1622
  }
1552
1623
  get_token(a, c_ket);
1553
1624
  break;
@@ -1612,6 +1683,7 @@ handle_rel_op: ;
1612
1683
  p->left = new_node(a, c_name);
1613
1684
  p->left->name = q;
1614
1685
  p->AE = read_AE(a, NULL, 0);
1686
+ normalise_comparison(p);
1615
1687
  if (q) {
1616
1688
  q->value_used = true;
1617
1689
  mark_used_in(a, q, p);
@@ -1755,7 +1827,7 @@ handle_rel_op: ;
1755
1827
  }
1756
1828
  }
1757
1829
 
1758
- static int next_symbol(symbol * p, symbol * W, int utf8) {
1830
+ static int next_symbol(const symbol * p, symbol * W, int utf8) {
1759
1831
  if (utf8) {
1760
1832
  int ch;
1761
1833
  int j = get_utf8(p, & ch);
@@ -1767,7 +1839,7 @@ static int next_symbol(symbol * p, symbol * W, int utf8) {
1767
1839
  }
1768
1840
  }
1769
1841
 
1770
- static symbol * alter_grouping(symbol * p, symbol * q, int style, int utf8) {
1842
+ static symbol * alter_grouping(symbol * p, const symbol * q, int style, int utf8) {
1771
1843
  int j = 0;
1772
1844
  if (style == c_plus) {
1773
1845
  while (j < SIZE(q)) {
@@ -2045,15 +2117,21 @@ static void remove_dead_assignments(struct node * p, struct name * q) {
2045
2117
  if (p->right) remove_dead_assignments(p->right, q);
2046
2118
  }
2047
2119
 
2120
+ // Numeric values are picked so we can take the minimum to merge when joining
2121
+ // control flows.
2048
2122
  enum {
2049
2123
  // Not set on at least one code path leading to a use.
2050
- USE_BEFORE_SET,
2124
+ USE_BEFORE_SET = 0,
2051
2125
  // Need to keep checking.
2052
- UNKNOWN,
2126
+ UNKNOWN = 1,
2053
2127
  // Set on any code path leading to a use.
2054
- SET_BEFORE_ANY_USE
2128
+ SET_BEFORE_ANY_USE = 2
2055
2129
  };
2056
2130
 
2131
+ static inline int merge_r(int r1, int r2) {
2132
+ return r1 < r2 ? r1 : r2;
2133
+ }
2134
+
2057
2135
  /* Find out if every codepath in the command with node p to a use of variable v
2058
2136
  * sets v first.
2059
2137
  *
@@ -2061,31 +2139,28 @@ enum {
2061
2139
  * safely localised when it can, but they allow localising all variables which
2062
2140
  * can be trivially made local in existing stemmers.
2063
2141
  *
2064
- * p: the node of the command to check.
2065
- * func: the c_define of the routine/external this code is in.
2066
- * v: the variable to check.
2142
+ * p: the node of the command to check.
2143
+ * v: the variable to check.
2144
+ * catcher: pointer to USE_BEFORE_SET/UNKNOWN/SET_BEFORE_ANY_USE value to
2145
+ * update on signal `f`.
2067
2146
  */
2068
- static int always_set_before_use_(struct node * p, struct node * func,
2069
- struct name * v) {
2147
+ static int always_set_before_use_(struct node * p,
2148
+ struct name * v,
2149
+ int * catcher) {
2070
2150
  assert(p);
2071
2151
  switch (p->type) {
2072
2152
  case c_call: {
2073
- if (p->name->definition == func) {
2074
- /* We've recursed into the function we're considering
2075
- * localising this variable into, which means we can't
2076
- * localise it because then changes to the variable in
2077
- * the nested call won't be reflected after it returns.
2078
- */
2079
- return USE_BEFORE_SET;
2080
- }
2081
- // We know v is only referenced in the function we are checking.
2153
+ // We know v is only referenced in the function we are checking
2154
+ // (and we don't try to localise variables into recursive
2155
+ // functions, so we know this call can't re-enter the current
2156
+ // function).
2082
2157
  return UNKNOWN;
2083
2158
  }
2084
2159
  case c_among: {
2085
2160
  bool all_pass = true;
2086
2161
  struct among * x = p->among;
2087
2162
  for (int i = 1; i <= x->command_count; i++) {
2088
- int r = always_set_before_use_(x->commands[i - 1], func, v);
2163
+ int r = always_set_before_use_(x->commands[i - 1], v, catcher);
2089
2164
  if (r == USE_BEFORE_SET) return r;
2090
2165
  all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
2091
2166
  }
@@ -2094,49 +2169,89 @@ static int always_set_before_use_(struct node * p, struct node * func,
2094
2169
  }
2095
2170
  case c_or: {
2096
2171
  struct node * q = p->left;
2172
+ assert(q);
2097
2173
  bool all_pass = true;
2098
- while (q) {
2099
- int r = always_set_before_use_(q, func, v);
2174
+ while (q->right) {
2175
+ // `or` handles signal `f` from subcommands except the last.
2176
+ int r_f = UNKNOWN;
2177
+ int r = always_set_before_use_(q, v, &r_f);
2100
2178
  if (r == USE_BEFORE_SET) return r;
2101
- all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
2179
+ if (r_f == USE_BEFORE_SET) return r;
2180
+ all_pass = all_pass &&
2181
+ (r == SET_BEFORE_ANY_USE) &&
2182
+ (r_f == SET_BEFORE_ANY_USE);
2102
2183
  q = q->right;
2103
2184
  }
2185
+ // Signal `f` from the last subcommand propagates.
2186
+ int r = always_set_before_use_(q, v, catcher);
2187
+ if (r == USE_BEFORE_SET) return r;
2188
+ all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
2104
2189
  if (all_pass) return SET_BEFORE_ANY_USE;
2105
2190
  return UNKNOWN;
2106
2191
  }
2107
2192
  case c_and:
2108
2193
  case c_bra: {
2109
2194
  struct node * q = p->left;
2195
+ // If we execute this command, we'll definitely execute
2196
+ // sub-commands up to and including the first which could
2197
+ // fail.
2198
+ while (q) {
2199
+ int r = always_set_before_use_(q, v, catcher);
2200
+ if (r != UNKNOWN) {
2201
+ *catcher = r;
2202
+ return r;
2203
+ }
2204
+ bool last = (q->possible_signals <= 0);
2205
+ q = q->right;
2206
+ if (last) break;
2207
+ }
2208
+ // Any remaining subcommands may not get executed, so we don't
2209
+ // update *catcher for them.
2110
2210
  while (q) {
2111
- int r = always_set_before_use_(q, func, v);
2211
+ int r = always_set_before_use_(q, v, catcher);
2112
2212
  if (r != UNKNOWN) return r;
2113
2213
  q = q->right;
2114
2214
  }
2115
2215
  return UNKNOWN;
2116
2216
  }
2117
2217
  case c_backwards:
2118
- case c_not:
2119
2218
  case c_reverse:
2120
2219
  case c_test:
2121
- return always_set_before_use_(p->left, func, v);
2122
- case c_do:
2220
+ return always_set_before_use_(p->left, v, catcher);
2123
2221
  case c_fail:
2222
+ return always_set_before_use_(p->left, v, catcher);
2223
+ // FIXME: Could be?
2224
+ // *catcher = merge_r(r, *catcher);
2225
+ // return UNKNOWN;
2226
+ case c_repeat:
2227
+ case c_do:
2228
+ case c_not:
2229
+ case c_try: {
2230
+ int r_f = UNKNOWN;
2231
+ int r = always_set_before_use_(p->left, v, &r_f);
2232
+ return merge_r(r, r_f);
2233
+ }
2124
2234
  case c_gopast:
2125
2235
  case c_goto:
2126
- case c_try:
2127
- case c_repeat: {
2128
- if (always_set_before_use_(p->left, func, v) == USE_BEFORE_SET)
2236
+ // If the cursor is atlimit already the subcommand won't be
2237
+ // executed, so a SET_BEFORE_ANY_USE from it doesn't guarantee
2238
+ // that the variable will get set.
2239
+ if (always_set_before_use_(p->left, v, catcher) == USE_BEFORE_SET)
2129
2240
  return USE_BEFORE_SET;
2130
2241
  return UNKNOWN;
2131
- }
2132
2242
  case c_atleast:
2133
2243
  case c_loop:
2134
- if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET)
2244
+ if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET)
2135
2245
  return USE_BEFORE_SET;
2136
- return always_set_before_use_(p->left, func, v);
2246
+ // If AE has value <= 0 the subcommand won't be executed, so a
2247
+ // SET_BEFORE_ANY_USE from it doesn't guarantee that the variable
2248
+ // will get set.
2249
+ if (always_set_before_use_(p->left, v, catcher) == USE_BEFORE_SET)
2250
+ return USE_BEFORE_SET;
2251
+ return UNKNOWN;
2137
2252
  case c_assign:
2138
2253
  // Check AE first: `x = x + 1` uses `x` before it sets it.
2139
- if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET)
2254
+ if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET)
2140
2255
  return USE_BEFORE_SET;
2141
2256
  if (p->name == v)
2142
2257
  return SET_BEFORE_ANY_USE;
@@ -2167,7 +2282,7 @@ static int always_set_before_use_(struct node * p, struct node * func,
2167
2282
  return UNKNOWN;
2168
2283
  case c_hop:
2169
2284
  case c_tomark:
2170
- if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET)
2285
+ if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET)
2171
2286
  return USE_BEFORE_SET;
2172
2287
  return UNKNOWN;
2173
2288
  case c_stringassign:
@@ -2187,9 +2302,9 @@ static int always_set_before_use_(struct node * p, struct node * func,
2187
2302
  case c_minus:
2188
2303
  case c_multiply:
2189
2304
  case c_plus: {
2190
- int r = always_set_before_use_(p->left, func, v);
2305
+ int r = always_set_before_use_(p->left, v, catcher);
2191
2306
  if (r != UNKNOWN) return r;
2192
- return always_set_before_use_(p->right, func, v);
2307
+ return always_set_before_use_(p->right, v, catcher);
2193
2308
  }
2194
2309
  case c_eq:
2195
2310
  case c_ne:
@@ -2197,12 +2312,12 @@ static int always_set_before_use_(struct node * p, struct node * func,
2197
2312
  case c_ge:
2198
2313
  case c_lt:
2199
2314
  case c_le: {
2200
- int r = always_set_before_use_(p->left, func, v);
2315
+ int r = always_set_before_use_(p->left, v, catcher);
2201
2316
  if (r != UNKNOWN) return r;
2202
- return always_set_before_use_(p->AE, func, v);
2317
+ return always_set_before_use_(p->AE, v, catcher);
2203
2318
  }
2204
2319
  case c_neg:
2205
- return always_set_before_use_(p->right, func, v);
2320
+ return always_set_before_use_(p->right, v, catcher);
2206
2321
  case c_lenof:
2207
2322
  case c_sizeof:
2208
2323
  if (p->name == v) {
@@ -2217,9 +2332,15 @@ static int always_set_before_use_(struct node * p, struct node * func,
2217
2332
  case c_size:
2218
2333
  return UNKNOWN;
2219
2334
  case c_setlimit: {
2220
- int r = always_set_before_use_(p->aux, func, v);
2335
+ int r = always_set_before_use_(p->aux, v, catcher);
2221
2336
  if (r != UNKNOWN) return r;
2222
- return always_set_before_use_(p->left, func, v);
2337
+ r = always_set_before_use_(p->left, v, catcher);
2338
+ if (p->aux->possible_signals <= 0) {
2339
+ // aux can fail so left may not be executed.
2340
+ if (r == USE_BEFORE_SET) return r;
2341
+ return UNKNOWN;
2342
+ }
2343
+ return r;
2223
2344
  }
2224
2345
  case c_divideassign:
2225
2346
  case c_minusassign:
@@ -2228,7 +2349,7 @@ static int always_set_before_use_(struct node * p, struct node * func,
2228
2349
  if (p->name == v) {
2229
2350
  return USE_BEFORE_SET;
2230
2351
  }
2231
- if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET) {
2352
+ if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET) {
2232
2353
  return USE_BEFORE_SET;
2233
2354
  }
2234
2355
  return UNKNOWN;
@@ -2289,9 +2410,10 @@ static int always_set_before_use_(struct node * p, struct node * func,
2289
2410
  return USE_BEFORE_SET;
2290
2411
  }
2291
2412
 
2292
- static int always_set_before_use(struct node * p, struct node * func,
2293
- struct name * v) {
2294
- return always_set_before_use_(p, func, v) != USE_BEFORE_SET;
2413
+ static int always_set_before_use(struct node * p, struct name * v) {
2414
+ int r_f = UNKNOWN;
2415
+ int r = always_set_before_use_(p, v, &r_f);
2416
+ return merge_r(r, r_f) != USE_BEFORE_SET;
2295
2417
  }
2296
2418
 
2297
2419
  static void remove_routine(struct analyser * a, struct name * q) {
@@ -2536,6 +2658,33 @@ static int check_possible_signals(struct analyser * a, struct node * p) {
2536
2658
  }
2537
2659
  }
2538
2660
 
2661
+ // Can control flow from node `p` reach routine `func`?
2662
+ static bool recursion_check(struct node * p, struct name * func) {
2663
+ while (p) {
2664
+ switch (p->type) {
2665
+ case c_call:
2666
+ if (p->name == func) return true;
2667
+ if (p->name->definition && !p->name->visited) {
2668
+ p->name->visited = true;
2669
+ if (recursion_check(p->name->definition->left, func)) return true;
2670
+ }
2671
+ break;
2672
+ case c_among: {
2673
+ struct among * x = p->among;
2674
+ if (x->unique_function_count == 0) break;
2675
+ for (int i = 0; i < x->literalstring_count; ++i) {
2676
+ if (x->v[i].function == func) return true;
2677
+ }
2678
+ break;
2679
+ }
2680
+ }
2681
+ if (p->left && recursion_check(p->left, func)) return true;
2682
+ if (p->aux && recursion_check(p->aux, func)) return true;
2683
+ p = p->right;
2684
+ }
2685
+ return false;
2686
+ }
2687
+
2539
2688
  static void visit_routine(struct analyser * a, struct name * n);
2540
2689
 
2541
2690
  static void visit_node(struct analyser * a, struct node * p, struct name * func) {
@@ -2559,8 +2708,8 @@ static void visit_node(struct analyser * a, struct node * p, struct name * func)
2559
2708
  struct among * x = p->among;
2560
2709
  x->used = true;
2561
2710
  for (int i = 0; i < x->literalstring_count; ++i) {
2562
- if (x->b[i].function)
2563
- visit_routine(a, x->b[i].function);
2711
+ if (x->v[i].function)
2712
+ visit_routine(a, x->v[i].function);
2564
2713
  }
2565
2714
  for (int i = 0; i < x->command_count; ++i) {
2566
2715
  visit_node(a, x->commands[i], func);
@@ -2636,6 +2785,68 @@ static void visit_routine(struct analyser * a, struct name * n) {
2636
2785
  p->possible_signals = p->left->possible_signals;
2637
2786
  }
2638
2787
 
2788
+ static struct node * clone_node(struct analyser * a, struct node * n) {
2789
+ if (n == NULL) return n;
2790
+
2791
+ NEW(node, p);
2792
+ p->among = n->among;
2793
+ p->type = n->type;
2794
+ p->mode = n->mode;
2795
+ p->fixed_constant = n->fixed_constant;
2796
+ p->possible_signals = n->possible_signals;
2797
+ p->name = n->name;
2798
+ p->literalstring = n->literalstring;
2799
+ p->number = n->number;
2800
+ p->line_number = n->line_number;
2801
+
2802
+ p->left = clone_node(a, n->left);
2803
+ p->aux = clone_node(a, n->aux);
2804
+ p->right = clone_node(a, n->right);
2805
+ p->AE = clone_node(a, n->AE);
2806
+
2807
+ p->next = a->nodes;
2808
+ a->nodes = p;
2809
+ return p;
2810
+ }
2811
+
2812
+ // Inline any calls to routine `n` in the code starting at `p`.
2813
+ //
2814
+ // Returns true if any calls were inlined. If there are no remaining calls to
2815
+ // `n` anywhere in the program then n->definition will be set to NULL.
2816
+ static bool inline_calls(struct analyser * a,
2817
+ struct node * p,
2818
+ struct name * n) {
2819
+ bool r = false;
2820
+ while (p) {
2821
+ if (p->name == n && p->type == c_call) {
2822
+ p->type = c_bra;
2823
+ p->name = NULL;
2824
+ --n->references;
2825
+ if (n->references < 2) {
2826
+ // No more uses of n, so there is no need to clone the code.
2827
+ // We can also stop looking for uses of n to inline.
2828
+ p->left = n->definition->left;
2829
+ n->definition = NULL;
2830
+ return true;
2831
+ }
2832
+ p->left = clone_node(a, n->definition->left);
2833
+ p = p->right;
2834
+ r = true;
2835
+ continue;
2836
+ }
2837
+ if (inline_calls(a, p->left, n)) {
2838
+ if (!n->definition) return true;
2839
+ r = true;
2840
+ }
2841
+ if (inline_calls(a, p->aux, n)) {
2842
+ if (!n->definition) return true;
2843
+ r = true;
2844
+ }
2845
+ p = p->right;
2846
+ }
2847
+ return r;
2848
+ }
2849
+
2639
2850
  extern void read_program(struct analyser * a, unsigned localise_mask) {
2640
2851
  read_program_(a, -1);
2641
2852
  for (struct name * q = a->names; q; q = q->next) {
@@ -2667,62 +2878,66 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2667
2878
 
2668
2879
  for (struct name ** n_ptr = &(a->names); *n_ptr; ) {
2669
2880
  struct name * n = *n_ptr;
2670
- // Inline some routines.
2671
- //
2672
- // Currently we inline routines which are "simple" and only used once.
2673
- // Probably we should inline "simple" *OR* only used once, but to do
2674
- // that we probably need to clone the nodes for routines used more
2675
- // than once, and we need to handle updating things like
2676
- // amongvar_needed (which hasn't been set at this point).
2677
- //
2678
- // Note: the definition of a routine is counted as a reference to its
2679
- // name, so a routine only called once has 2 references.
2680
2881
  if (n->type == t_routine &&
2681
- n->references == 2 &&
2682
- n->uses_in_among == 0 &&
2882
+ n->references >= 2 &&
2683
2883
  !n->definition->left->right) {
2684
2884
  bool inline_routine = false;
2685
- switch (n->definition->left->type) {
2686
- case c_eq:
2687
- case c_ge:
2688
- case c_gt:
2689
- case c_le:
2690
- case c_lt:
2691
- case c_ne:
2692
- case c_booltest:
2693
- case c_not_booltest:
2694
- case c_grouping:
2695
- case c_non:
2696
- case c_goto_grouping:
2697
- case c_gopast_grouping:
2698
- case c_goto_non:
2699
- case c_gopast_non:
2700
- case c_literalstring:
2701
- case c_name:
2702
- case c_true:
2703
- case c_false:
2704
- case c_fail:
2705
- case c_set:
2706
- case c_unset:
2707
- case c_assign:
2708
- case c_plusassign:
2709
- case c_minusassign:
2710
- case c_multiplyassign:
2711
- case c_divideassign:
2712
- case c_tomark:
2713
- case c_tolimit:
2714
- case c_hop:
2715
- case c_next:
2716
- case c_insert:
2717
- case c_attach:
2718
- case c_leftslice:
2719
- case c_rightslice:
2720
- case c_sliceto:
2721
- case c_stringassign:
2722
- case c_slicefrom:
2723
- // Inline "simple" single-command routines.
2724
- inline_routine = true;
2725
- break;
2885
+ if (n->references == 2) {
2886
+ // Inline any routine only called once (where that call is not
2887
+ // as an among function).
2888
+ //
2889
+ // Note: the definition of a routine is counted as a reference
2890
+ // to its name, so a routine only called once has 2 references.
2891
+ inline_routine = (n->uses_in_among == 0);
2892
+ } else {
2893
+ // Inline any calls to routines which is simple enough that the
2894
+ // generated code to call the routine is likely to be a similar
2895
+ // size to the inlined code that replaces it.
2896
+ //
2897
+ // Currently we inline any non-compound command (including
2898
+ // non-compound commands we synthesise, such as `goto` applied
2899
+ // to a grouping).
2900
+ switch (n->definition->left->type) {
2901
+ case c_eq:
2902
+ case c_ge:
2903
+ case c_gt:
2904
+ case c_le:
2905
+ case c_lt:
2906
+ case c_ne:
2907
+ case c_booltest:
2908
+ case c_not_booltest:
2909
+ case c_grouping:
2910
+ case c_non:
2911
+ case c_goto_grouping:
2912
+ case c_gopast_grouping:
2913
+ case c_goto_non:
2914
+ case c_gopast_non:
2915
+ case c_literalstring:
2916
+ case c_name:
2917
+ case c_true:
2918
+ case c_false:
2919
+ case c_fail:
2920
+ case c_set:
2921
+ case c_unset:
2922
+ case c_assign:
2923
+ case c_plusassign:
2924
+ case c_minusassign:
2925
+ case c_multiplyassign:
2926
+ case c_divideassign:
2927
+ case c_tomark:
2928
+ case c_tolimit:
2929
+ case c_hop:
2930
+ case c_next:
2931
+ case c_insert:
2932
+ case c_attach:
2933
+ case c_leftslice:
2934
+ case c_rightslice:
2935
+ case c_sliceto:
2936
+ case c_stringassign:
2937
+ case c_slicefrom:
2938
+ inline_routine = true;
2939
+ break;
2940
+ }
2726
2941
  }
2727
2942
 
2728
2943
  if (inline_routine) {
@@ -2732,33 +2947,67 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2732
2947
  n->used->line_number,
2733
2948
  SIZE(n->s), n->s);
2734
2949
  #endif
2735
- struct node * call_site = n->used;
2736
- call_site->type = c_bra;
2737
- call_site->name = NULL;
2738
- call_site->left = n->definition->left;
2739
- remove_routine(a, n);
2740
- *n_ptr = n->next;
2741
- lose_s(n->s);
2742
- FREE(n);
2743
- continue;
2950
+ bool remove = false;
2951
+ for (struct name * r = a->names; r; r = r->next) {
2952
+ // Only routines and externals.
2953
+ if (!r->definition) continue;
2954
+ // Don't try to inline a routine into itself!
2955
+ if (r == n) continue;
2956
+ if (inline_calls(a, r->definition->left, n)) {
2957
+ // If n contains an among, r now does. Similarly
2958
+ // for an among with functions.
2959
+ r->has_among |= n->has_among;
2960
+ r->has_among_function |= n->has_among_function;
2961
+ if (n->definition == NULL) {
2962
+ // All calls to routine `n` have now been inlined
2963
+ // so remove it and move on to the next candidate
2964
+ // for inlining.
2965
+ remove = true;
2966
+ break;
2967
+ }
2968
+ }
2969
+ }
2970
+ if (remove) {
2971
+ remove_routine(a, n);
2972
+ *n_ptr = n->next;
2973
+ lose_s(n->s);
2974
+ FREE(n);
2975
+ continue;
2976
+ }
2744
2977
  }
2745
2978
  }
2746
2979
 
2747
2980
  n_ptr = &(n->next);
2748
2981
  }
2749
2982
 
2750
- // Add "functionend" nodes there so optimisations such as dead code
2751
- // elimination and tail call optimisation can easily see where the function
2752
- // ends.
2753
2983
  for (struct name * name = a->names; name; name = name->next) {
2754
- if (name->type != t_routine && name->type != t_external) continue;
2755
-
2756
2984
  struct node * p = name->definition;
2757
2985
  if (!p) {
2758
- // No definition - we'll report this later.
2986
+ // Either this name is not a routine/external, or it is missing
2987
+ // a definition (which gets reported later).
2759
2988
  continue;
2760
2989
  }
2990
+
2991
+ // Recursion is rare in real-world Snowball programs, and there are
2992
+ // some optimisations that we don't attempt if this flag is set.
2993
+ for (struct name * n = a->names; n; n = n->next) {
2994
+ n->visited = false;
2995
+ }
2996
+ name->recursive = recursion_check(p->left, name);
2997
+ #if 0
2998
+ if (name->recursive) {
2999
+ fprintf(stderr,
3000
+ "%s:%d: info: `%.*s` is recursive\n",
3001
+ a->tokeniser->file, name->definition->line_number,
3002
+ SIZE(name->s), name->s);
3003
+ }
3004
+ #endif
3005
+
3006
+ // Add "functionend" nodes so that optimisations such as dead code
3007
+ // elimination and tail call optimisation can easily see where the
3008
+ // function ends.
2761
3009
  assert(p->type == c_define);
3010
+ assert(p->left);
2762
3011
  assert(p->left->right == NULL);
2763
3012
  if (p->left->type == c_bra) {
2764
3013
  /* Put the "functionend" node at the end of the command list. */
@@ -2854,12 +3103,12 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2854
3103
  //
2855
3104
  // We only issue a warning about unreachability for routines here
2856
3105
  // to avoid excess diagnostics, since other types must be used in a
2857
- // routine which is not reachable (or will have been warned about as
3106
+ // routine which is reachable (or will have been warned about as
2858
3107
  // unused by the check above).
2859
3108
  if (q->type == t_routine) {
2860
3109
  fprintf(stderr, "%s:%d: warning: %s '%.*s' not reachable from any externals\n",
2861
3110
  a->tokeniser->file,
2862
- q->declaration_line_number,
3111
+ q->definition->line_number,
2863
3112
  name_of_type(q->type),
2864
3113
  SIZE(q->s), q->s);
2865
3114
  remove_routine(a, q);
@@ -2919,11 +3168,61 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2919
3168
  struct among * x = *a_ptr;
2920
3169
  if (!x->used) {
2921
3170
  *a_ptr = x->next;
3171
+ free_among(x);
2922
3172
  continue;
2923
3173
  }
2924
3174
 
2925
- x->number = among_count++;
2926
- if (x->function_count > 0) ++a->among_with_function_count;
3175
+ // Check for an already numbered among which is the same as this.
3176
+ for (struct among * y = a->amongs; y != x; y = y->next) {
3177
+ // Some of these are redundant to check, but doing so may allow
3178
+ // us to more quickly detect amongs which are different.
3179
+ if (y->literalstring_count != x->literalstring_count ||
3180
+ y->command_count != x->command_count ||
3181
+ y->nocommand_count != x->nocommand_count ||
3182
+ y->af_count != x->af_count ||
3183
+ y->function_count != x->function_count ||
3184
+ y->unique_function_count != x->unique_function_count ||
3185
+ y->amongvar_needed != x->amongvar_needed ||
3186
+ y->always_matches != x->always_matches ||
3187
+ y->same_action != x->same_action ||
3188
+ y->shortest_size != x->shortest_size ||
3189
+ y->longest_size != x->longest_size) {
3190
+ continue;
3191
+ }
3192
+ bool same = true;
3193
+ for (int i = 0; i != y->literalstring_count; ++i) {
3194
+ const struct amongvec * p = &x->v[i];
3195
+ const struct amongvec * q = &y->v[i];
3196
+ int p_size = p->size;
3197
+ if (p_size != q->size) {
3198
+ same = false;
3199
+ break;
3200
+ }
3201
+ if (p->function != q->function) {
3202
+ same = false;
3203
+ break;
3204
+ }
3205
+ const symbol * b_p = p->b;
3206
+ const symbol * b_q = q->b;
3207
+ if (memcmp(b_p, b_q, p_size * sizeof(symbol)) != 0) {
3208
+ same = false;
3209
+ break;
3210
+ }
3211
+ if (!nodes_equivalent(p->action, q->action)) {
3212
+ same = false;
3213
+ break;
3214
+ }
3215
+ }
3216
+ if (!same) continue;
3217
+ x->number = y->number;
3218
+ x->duplicate = true;
3219
+ break;
3220
+ }
3221
+
3222
+ if (!x->duplicate) {
3223
+ x->number = among_count++;
3224
+ if (x->function_count > 0) ++a->among_with_function_count;
3225
+ }
2927
3226
 
2928
3227
  for (int i = 1; i <= x->command_count; i++) {
2929
3228
  int merge_with = 0;
@@ -2949,15 +3248,15 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2949
3248
  // and subtract one from references to command indexes after
2950
3249
  // this one.
2951
3250
  for (int j = 0; j < x->literalstring_count; ++j) {
2952
- int diff = (x->b[j].result - i);
3251
+ int diff = (x->v[j].result - i);
2953
3252
  if (diff == 0) {
2954
- x->b[j].result = merge_with;
3253
+ x->v[j].result = merge_with;
2955
3254
  if (merge_with == 0) {
2956
- assert(x->b[j].action->type == c_bra);
2957
- x->b[j].action->left = NULL;
3255
+ assert(x->v[j].action->type == c_bra);
3256
+ x->v[j].action->left = NULL;
2958
3257
  }
2959
3258
  } else if (diff > 0) {
2960
- --x->b[j].result;
3259
+ --x->v[j].result;
2961
3260
  }
2962
3261
  }
2963
3262
  memmove(x->commands + (i - 1), x->commands + i,
@@ -2973,8 +3272,6 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2973
3272
  * find_among*() returns zero or not.
2974
3273
  */
2975
3274
  x->amongvar_needed = true;
2976
- if (x->in_routine)
2977
- x->in_routine->amongvar_needed = true;
2978
3275
  }
2979
3276
 
2980
3277
  a_ptr = &(x->next);
@@ -2983,10 +3280,13 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2983
3280
 
2984
3281
  /* Localise variables.
2985
3282
  *
2986
- * We localise variables which are only referenced in a single function
2987
- * (routine or external) and which are always set before being read within
2988
- * that function (since a function could rely on a variable's previous
2989
- * value surviving).
3283
+ * We localise variables which are only referenced in a single non-recursive
3284
+ * function (routine or external) and which are always set before being
3285
+ * read within that function (since a function could rely on a variable's
3286
+ * previous value surviving).
3287
+ *
3288
+ * This optimisation is done after inlining, since inlining can often move
3289
+ * all references to a variable into the same function.
2990
3290
  *
2991
3291
  * We could potentially localise variables referenced in multiple functions
2992
3292
  * provided that they are always set before use in every function they are
@@ -2995,18 +3295,24 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
2995
3295
  */
2996
3296
  memset(a->name_count, 0, sizeof(a->name_count));
2997
3297
  for (struct name * name = a->names; name; name = name->next) {
2998
- if (name->local_to != NULL) {
3298
+ if (name->local_to) {
2999
3299
  if (localise_mask & (1 << name->type)) {
3000
3300
  struct node * func = name->local_to->definition;
3001
- if (!always_set_before_use(func->left, func, name)) {
3301
+ if (name->local_to->recursive) {
3302
+ fprintf(stderr,
3303
+ "%s:%d: info: Could not localise %s `%.*s` into recursive routine `%.*s`\n",
3304
+ a->tokeniser->file, func->line_number,
3305
+ name_of_type(name->type),
3306
+ SIZE(name->s), name->s,
3307
+ SIZE(func->name->s), func->name->s);
3308
+ name->local_to = NULL;
3309
+ } else if (!always_set_before_use(func->left, name)) {
3002
3310
  fprintf(stderr,
3003
3311
  "%s:%d: info: Could not localise %s `%.*s` to routine `%.*s`\n",
3004
3312
  a->tokeniser->file, func->line_number,
3005
3313
  name_of_type(name->type),
3006
3314
  SIZE(name->s), name->s,
3007
3315
  SIZE(func->name->s), func->name->s);
3008
- report_s(stderr, name->s);
3009
- fprintf(stderr, "\n");
3010
3316
  name->local_to = NULL;
3011
3317
  }
3012
3318
  } else {
@@ -3073,9 +3379,7 @@ extern void close_analyser(struct analyser * a) {
3073
3379
  struct among * q = a->amongs;
3074
3380
  while (q) {
3075
3381
  struct among * q_next = q->next;
3076
- FREE(q->b);
3077
- FREE(q->commands);
3078
- FREE(q);
3382
+ free_among(q);
3079
3383
  q = q_next;
3080
3384
  }
3081
3385
  }