mittens 0.3.0 → 0.3.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.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +2 -1
  4. data/README.md +2 -2
  5. data/Rakefile +12 -5
  6. data/ext/mittens/extconf.rb +3 -1
  7. data/lib/mittens/version.rb +1 -1
  8. data/vendor/snowball/.github/workflows/ci.yml +144 -17
  9. data/vendor/snowball/.github/workflows/coverage.yml +117 -0
  10. data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
  11. data/vendor/snowball/.gitignore +33 -7
  12. data/vendor/snowball/CONTRIBUTING.rst +7 -0
  13. data/vendor/snowball/COPYING +1 -1
  14. data/vendor/snowball/GNUmakefile +736 -298
  15. data/vendor/snowball/NEWS +1394 -7
  16. data/vendor/snowball/README.rst +8 -8
  17. data/vendor/snowball/ada/generate/generate.adb +5 -5
  18. data/vendor/snowball/ada/src/stemmer.adb +177 -188
  19. data/vendor/snowball/ada/src/stemmer.ads +86 -89
  20. data/vendor/snowball/ada/src/stemwords.adb +21 -5
  21. data/vendor/snowball/algorithms/czech.sbl +255 -0
  22. data/vendor/snowball/algorithms/danish.sbl +22 -10
  23. data/vendor/snowball/algorithms/english.sbl +7 -4
  24. data/vendor/snowball/algorithms/esperanto.sbl +6 -7
  25. data/vendor/snowball/algorithms/estonian.sbl +9 -4
  26. data/vendor/snowball/algorithms/finnish.sbl +33 -21
  27. data/vendor/snowball/algorithms/german.sbl +5 -0
  28. data/vendor/snowball/algorithms/indonesian.sbl +115 -96
  29. data/vendor/snowball/algorithms/italian.sbl +26 -1
  30. data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
  31. data/vendor/snowball/algorithms/norwegian.sbl +17 -6
  32. data/vendor/snowball/algorithms/persian.sbl +387 -0
  33. data/vendor/snowball/algorithms/polish.sbl +177 -0
  34. data/vendor/snowball/algorithms/sesotho.sbl +115 -0
  35. data/vendor/snowball/algorithms/turkish.sbl +4 -4
  36. data/vendor/snowball/compiler/analyser.c +2002 -711
  37. data/vendor/snowball/compiler/driver.c +460 -298
  38. data/vendor/snowball/compiler/generator.c +439 -1965
  39. data/vendor/snowball/compiler/generator_ada.c +605 -430
  40. data/vendor/snowball/compiler/generator_c.c +2227 -0
  41. data/vendor/snowball/compiler/generator_csharp.c +365 -274
  42. data/vendor/snowball/compiler/generator_dart.c +1476 -0
  43. data/vendor/snowball/compiler/generator_go.c +348 -270
  44. data/vendor/snowball/compiler/generator_java.c +330 -233
  45. data/vendor/snowball/compiler/generator_js.c +534 -385
  46. data/vendor/snowball/compiler/generator_pascal.c +361 -320
  47. data/vendor/snowball/compiler/generator_php.c +1445 -0
  48. data/vendor/snowball/compiler/generator_python.c +399 -299
  49. data/vendor/snowball/compiler/generator_rust.c +370 -261
  50. data/vendor/snowball/compiler/generator_zig.c +1474 -0
  51. data/vendor/snowball/compiler/header.h +153 -86
  52. data/vendor/snowball/compiler/space.c +121 -63
  53. data/vendor/snowball/compiler/tokeniser.c +286 -182
  54. data/vendor/snowball/compiler/tokens.h +71 -0
  55. data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
  56. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
  57. data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
  58. data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
  59. data/vendor/snowball/cxx/.gitignore +4 -0
  60. data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
  61. data/vendor/snowball/cxx/stemmer.h +12 -0
  62. data/vendor/snowball/cxx/stemwords.cxx +176 -0
  63. data/vendor/snowball/cxx/utilities.cxx +2 -0
  64. data/vendor/snowball/dart/.gitignore +4 -0
  65. data/vendor/snowball/dart/analysis_options.yaml +1 -0
  66. data/vendor/snowball/dart/example/test_app.dart +65 -0
  67. data/vendor/snowball/dart/generate_algorithms.pl +21 -0
  68. data/vendor/snowball/dart/lib/snowball.dart +18 -0
  69. data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
  70. data/vendor/snowball/dart/pubspec.yaml +17 -0
  71. data/vendor/snowball/doc/libstemmer_dart_README +37 -0
  72. data/vendor/snowball/doc/libstemmer_js_README +2 -2
  73. data/vendor/snowball/doc/libstemmer_php_README +38 -0
  74. data/vendor/snowball/doc/libstemmer_python_README +3 -3
  75. data/vendor/snowball/examples/stemwords.c +18 -3
  76. data/vendor/snowball/go/README.md +12 -4
  77. data/vendor/snowball/go/env.go +12 -14
  78. data/vendor/snowball/go/stemwords/main.go +2 -1
  79. data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
  80. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
  81. data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
  82. data/vendor/snowball/javascript/base-stemmer.js +219 -252
  83. data/vendor/snowball/javascript/stemwords.js +86 -58
  84. data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
  85. data/vendor/snowball/libstemmer/modules.txt +4 -0
  86. data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
  87. data/vendor/snowball/php/base-stemmer.php +453 -0
  88. data/vendor/snowball/php/stemwords.php +25 -0
  89. data/vendor/snowball/python/create_init.py +16 -21
  90. data/vendor/snowball/python/pyproject.toml +3 -0
  91. data/vendor/snowball/python/setup.py +5 -7
  92. data/vendor/snowball/python/snowballstemmer/among.py +1 -2
  93. data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
  94. data/vendor/snowball/python/stemwords.py +72 -63
  95. data/vendor/snowball/runtime/api.c +11 -42
  96. data/vendor/snowball/runtime/api.h +11 -9
  97. data/vendor/snowball/runtime/snowball_runtime.h +110 -0
  98. data/vendor/snowball/runtime/utilities.c +282 -106
  99. data/vendor/snowball/rust/src/main.rs +2 -2
  100. data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
  101. data/vendor/snowball/tests/compilertest +62 -0
  102. data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
  103. data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
  104. data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
  105. data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
  106. data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
  107. data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
  108. data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
  109. data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
  110. data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
  111. data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
  112. data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
  113. data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
  114. data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
  115. data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
  116. data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
  117. data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
  118. data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
  119. data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
  120. data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
  121. data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
  122. data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
  123. data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
  124. data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
  125. data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
  126. data/vendor/snowball/tests/runtime/among.sbl +19 -0
  127. data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
  128. data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
  129. data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
  130. data/vendor/snowball/tests/runtime/externals.sbl +22 -0
  131. data/vendor/snowball/tests/runtime/hop.sbl +12 -0
  132. data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
  133. data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
  134. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  135. data/vendor/snowball/tests/runtime/not.sbl +30 -0
  136. data/vendor/snowball/tests/runtime/or.sbl +17 -0
  137. data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
  138. data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
  139. data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
  140. data/vendor/snowball/tests/runtime/slice.sbl +27 -0
  141. data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
  142. data/vendor/snowball/tests/runtime/strings.sbl +66 -0
  143. data/vendor/snowball/tests/runtime/test.sbl +19 -0
  144. data/vendor/snowball/tests/stemtest.c +41 -1
  145. data/vendor/snowball/tests/syntax/canon.sbl +32 -0
  146. data/vendor/snowball/tests/syntax/canon.stderr +0 -0
  147. data/vendor/snowball/tests/syntax/canon.syntax +57 -0
  148. data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
  149. data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
  150. data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
  151. data/vendor/snowball/tests/syntax/inline.sbl +144 -0
  152. data/vendor/snowball/tests/syntax/inline.stderr +0 -0
  153. data/vendor/snowball/tests/syntax/inline.syntax +121 -0
  154. data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
  155. data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
  156. data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
  157. data/vendor/snowball/tests/syntax/loops.sbl +14 -0
  158. data/vendor/snowball/tests/syntax/loops.stderr +9 -0
  159. data/vendor/snowball/tests/syntax/loops.syntax +29 -0
  160. data/vendor/snowball/tests/syntax/noops.sbl +76 -0
  161. data/vendor/snowball/tests/syntax/noops.stderr +27 -0
  162. data/vendor/snowball/tests/syntax/noops.syntax +135 -0
  163. data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
  164. data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
  165. data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
  166. data/vendor/snowball/tests/syntax/unused.sbl +18 -0
  167. data/vendor/snowball/tests/syntax/unused.stderr +7 -0
  168. data/vendor/snowball/tests/syntax/unused.syntax +3 -0
  169. data/vendor/snowball/zig/env.zig +599 -0
  170. data/vendor/snowball/zig/generate_algorithms.pl +19 -0
  171. data/vendor/snowball/zig/stemwords.zig +123 -0
  172. metadata +102 -4
  173. data/vendor/snowball/compiler/syswords.h +0 -86
  174. data/vendor/snowball/runtime/header.h +0 -62
@@ -1,72 +1,66 @@
1
1
  #include <assert.h>
2
+ #include <stdio.h> /* for fprintf etc */
2
3
  #include <stdlib.h> /* for exit */
3
4
  #include <string.h> /* for strlen */
4
- #include <stdio.h> /* for fprintf etc */
5
5
  #include "header.h"
6
6
 
7
- /* prototypes */
7
+ /* prototype functions for recursive use: */
8
8
 
9
9
  static void generate(struct generator * g, struct node * p);
10
10
  static void w(struct generator * g, const char * s);
11
11
  static void writef(struct generator * g, const char * s, struct node * p);
12
12
 
13
- static int new_label(struct generator * g) {
14
- return g->next_label++;
15
- }
13
+ /* Write routines for items from the syntax tree */
16
14
 
17
- static struct str * vars_newname(struct generator * g) {
18
- struct str * output;
19
- g->var_number++;
20
- output = str_new();
21
- str_append_string(output, "c");
22
- str_append_int(output, g->var_number);
23
- return output;
15
+ static void write_relop(struct generator * g, int relop) {
16
+ // Relational operators are the same as C.
17
+ write_c_relop(g, relop);
24
18
  }
25
19
 
26
- /* Write routines for items from the syntax tree */
27
-
28
20
  static void write_varname(struct generator * g, struct name * p) {
29
- int ch = "SBIrxg"[p->type];
30
- if (p->type != t_external) {
31
- write_char(g, ch);
21
+ if (p->type == t_external) {
22
+ if (g->options->externals_prefix) {
23
+ write_string(g, g->options->externals_prefix);
24
+ }
25
+ } else {
26
+ // We use the same naming scheme for both global and local variables.
27
+ write_char(g, "SBIrxg"[p->type]);
32
28
  write_char(g, '_');
33
29
  }
34
30
  write_s(g, p->s);
35
31
  }
36
32
 
33
+ /* Reference to variable, e.g. when assigning to or using in an expression. */
37
34
  static void write_varref(struct generator * g, struct name * p) {
38
- /* In c#, references look just the same */
35
+ /* In C#, references look just the same */
39
36
  write_varname(g, p);
40
37
  }
41
38
 
42
39
  static void write_literal_string(struct generator * g, symbol * p) {
43
- write_string(g, "\"");
40
+ write_char(g, '"');
44
41
  for (int i = 0; i < SIZE(p); i++) {
45
42
  int ch = p[i];
46
- if (32 <= ch && ch < 0x590 && ch != 127) {
43
+ // Write out ASCII and lower Unicode printables as literal characters.
44
+ // Use escapes for anything over 0x590 as a crude way to avoid LTR
45
+ // characters affecting the rendering of source character order in
46
+ // confusing ways.
47
+ if ((32 <= ch && ch < 127) || (0xa0 < ch && ch < 0x590)) {
47
48
  if (ch == '"' || ch == '\\') write_char(g, '\\');
48
49
  // Our C# generator uses ENC_WIDECHARS so we need to convert.
49
50
  write_wchar_as_utf8(g, ch);
50
51
  } else {
51
- // Use escapes for anything over 0x590 as a crude way to avoid
52
- // LTR characters affecting the rendering of source character
53
- // order in confusing ways.
54
52
  write_string(g, "\\u");
55
53
  write_hex4(g, ch);
56
54
  }
57
55
  }
58
- write_string(g, "\"");
59
- }
60
-
61
- static void write_margin(struct generator * g) {
62
- for (int i = 0; i < g->margin; i++) write_string(g, " ");
56
+ write_char(g, '"');
63
57
  }
64
58
 
65
59
  static void write_comment(struct generator * g, struct node * p) {
66
60
  if (!g->options->comments) return;
67
61
  write_margin(g);
68
62
  write_string(g, "// ");
69
- write_comment_content(g, p);
63
+ write_comment_content(g, p, NULL);
70
64
  write_newline(g);
71
65
  }
72
66
 
@@ -90,7 +84,7 @@ static void append_restore_string(struct node * p, struct str * out, struct str
90
84
  str_append_string(out, "cursor = ");
91
85
  if (p->mode != m_forward) str_append_string(out, "limit - ");
92
86
  str_append(out, savevar);
93
- str_append_string(out, ";");
87
+ str_append_ch(out, ';');
94
88
  }
95
89
 
96
90
  static void write_restorecursor(struct generator * g, struct node * p, struct str * savevar) {
@@ -115,7 +109,7 @@ static void wgotol(struct generator * g, int n) {
115
109
  write_margin(g);
116
110
  write_string(g, "goto lab");
117
111
  write_int(g, n);
118
- write_string(g, ";");
112
+ write_char(g, ';');
119
113
  write_newline(g);
120
114
  }
121
115
 
@@ -134,7 +128,7 @@ static void write_failure(struct generator * g) {
134
128
  default:
135
129
  write_string(g, "goto lab");
136
130
  write_int(g, g->failure_label);
137
- write_string(g, ";");
131
+ write_char(g, ';');
138
132
  g->label_used = 1;
139
133
  }
140
134
  write_newline(g);
@@ -162,7 +156,6 @@ static void write_check_limit(struct generator * g, struct node * p) {
162
156
 
163
157
  /* Formatted write. */
164
158
  static void writef(struct generator * g, const char * input, struct node * p) {
165
- (void)p;
166
159
  int i = 0;
167
160
 
168
161
  while (input[i]) {
@@ -199,6 +192,32 @@ static void writef(struct generator * g, const char * input, struct node * p) {
199
192
  write_s(g, g->B[j]);
200
193
  continue;
201
194
  }
195
+ case 'F': { // Among function dispatcher.
196
+ struct among * x = p->among;
197
+ if (x->function_count == 0) {
198
+ write_string(g, "null");
199
+ continue;
200
+ }
201
+
202
+ if (x->function_count == 1) {
203
+ // Only one different function used in this among.
204
+ struct amongvec * v = x->b;
205
+ for (int j = 0; j < x->literalstring_count; j++) {
206
+ if (v[j].function) {
207
+ write_varref(g, v[j].function);
208
+ goto continue_outer_loop;
209
+ }
210
+ }
211
+ fprintf(stderr, "function_count == 1 but no among functions\n");
212
+ exit(1);
213
+ continue_outer_loop:
214
+ continue;
215
+ }
216
+
217
+ w(g, "af_");
218
+ write_int(g, x->number);
219
+ continue;
220
+ }
202
221
  case 'I': {
203
222
  int j = input[i++] - '0';
204
223
  if (j < 0 || j > (int)(sizeof(g->I) / sizeof(g->I[0])))
@@ -207,26 +226,17 @@ static void writef(struct generator * g, const char * input, struct node * p) {
207
226
  continue;
208
227
  }
209
228
  case 'V':
210
- case 'W': {
211
- int j = input[i++] - '0';
212
- if (j < 0 || j > (int)(sizeof(g->V) / sizeof(g->V[0])))
213
- goto invalid_escape2;
214
- if (ch == 'V')
215
- write_varref(g, g->V[j]);
216
- else
217
- write_varname(g, g->V[j]);
229
+ write_varref(g, p->name);
218
230
  continue;
219
- }
220
- case 'L': {
221
- int j = input[i++] - '0';
222
- if (j < 0 || j > (int)(sizeof(g->L) / sizeof(g->L[0])))
223
- goto invalid_escape2;
224
- write_literal_string(g, g->L[j]);
231
+ case 'W':
232
+ write_varname(g, p->name);
233
+ continue;
234
+ case 'L':
235
+ write_literal_string(g, p->literalstring);
225
236
  continue;
226
- }
227
237
  case '+': g->margin++; continue;
228
238
  case '-': g->margin--; continue;
229
- case 'n': write_string(g, g->options->name); continue;
239
+ case 'n': write_s(g, g->options->name); continue;
230
240
  default:
231
241
  printf("Invalid escape sequence ~%c in writef(g, \"%s\", p)\n",
232
242
  ch, input);
@@ -247,39 +257,53 @@ static void generate_AE(struct generator * g, struct node * p) {
247
257
  const char * s;
248
258
  switch (p->type) {
249
259
  case c_name:
250
- write_varref(g, p->name); break;
260
+ write_varref(g, p->name);
261
+ break;
251
262
  case c_number:
252
- write_int(g, p->number); break;
263
+ write_int(g, p->number);
264
+ break;
253
265
  case c_maxint:
254
- write_string(g, "int.MaxValue"); break;
266
+ write_string(g, "int.MaxValue");
267
+ break;
255
268
  case c_minint:
256
- write_string(g, "int.MinValue"); break;
269
+ write_string(g, "int.MinValue");
270
+ break;
257
271
  case c_neg:
258
- write_char(g, '-'); generate_AE(g, p->right); break;
272
+ write_char(g, '-');
273
+ generate_AE(g, p->right);
274
+ break;
259
275
  case c_multiply:
260
- s = " * "; goto label0;
261
- case c_plus:
262
- s = " + "; goto label0;
263
- case c_minus:
264
- s = " - "; goto label0;
276
+ s = " * ";
277
+ goto label0;
265
278
  case c_divide:
266
279
  s = " / ";
280
+ goto label0;
281
+ case c_plus:
282
+ s = " + ";
283
+ goto label0;
284
+ case c_minus:
285
+ s = " - ";
267
286
  label0:
268
- write_char(g, '('); generate_AE(g, p->left);
269
- write_string(g, s); generate_AE(g, p->right); write_char(g, ')'); break;
287
+ write_char(g, '(');
288
+ generate_AE(g, p->left);
289
+ write_string(g, s);
290
+ generate_AE(g, p->right);
291
+ write_char(g, ')');
292
+ break;
270
293
  case c_cursor:
271
- w(g, "cursor"); break;
294
+ w(g, "cursor");
295
+ break;
272
296
  case c_limit:
273
- w(g, p->mode == m_forward ? "limit" : "limit_backward"); break;
274
- case c_lenof: /* Same as sizeof() for C#. */
275
- case c_sizeof:
276
- g->V[0] = p->name;
277
- w(g, "~V0.Length");
297
+ w(g, p->mode == m_forward ? "limit" : "limit_backward");
278
298
  break;
279
299
  case c_len: /* Same as size() for C#. */
280
300
  case c_size:
281
301
  w(g, "current.Length");
282
302
  break;
303
+ case c_lenof: /* Same as sizeof() for C#. */
304
+ case c_sizeof:
305
+ writef(g, "~V.Length", p);
306
+ break;
283
307
  }
284
308
  }
285
309
 
@@ -294,13 +318,15 @@ static void generate_bra(struct generator * g, struct node * p) {
294
318
 
295
319
  static void generate_and(struct generator * g, struct node * p) {
296
320
  struct str * savevar = NULL;
297
- if (K_needed(g, p->left)) {
321
+ if (K_needed_for_and(p->left)) {
298
322
  savevar = vars_newname(g);
299
323
  }
300
324
 
301
325
  write_comment(g, p);
302
326
 
303
- if (savevar) write_savecursor(g, p, savevar);
327
+ if (savevar) {
328
+ write_savecursor(g, p, savevar);
329
+ }
304
330
 
305
331
  p = p->left;
306
332
  while (p) {
@@ -316,7 +342,7 @@ static void generate_and(struct generator * g, struct node * p) {
316
342
 
317
343
  static void generate_or(struct generator * g, struct node * p) {
318
344
  struct str * savevar = NULL;
319
- if (K_needed(g, p->left)) {
345
+ if (K_needed_for_or(p->left)) {
320
346
  savevar = vars_newname(g);
321
347
  }
322
348
 
@@ -327,7 +353,7 @@ static void generate_or(struct generator * g, struct node * p) {
327
353
  int out_lab = new_label(g);
328
354
  write_comment(g, p);
329
355
 
330
- if (savevar) {
356
+ if (savevar && K_needed_node_on_f(p)) {
331
357
  write_block_start(g);
332
358
  write_savecursor(g, p, savevar);
333
359
  }
@@ -342,13 +368,16 @@ static void generate_or(struct generator * g, struct node * p) {
342
368
  exit(1);
343
369
  }
344
370
  while (p->right != NULL) {
345
- g->failure_label = new_label(g);
371
+ int label = new_label(g);
372
+ g->failure_label = label;
346
373
  g->label_used = 0;
347
374
  generate(g, p);
348
375
  wgotol(g, out_lab);
349
376
  if (g->label_used)
350
377
  wsetl(g, g->failure_label);
351
- if (savevar) write_restorecursor(g, p, savevar);
378
+ if (savevar) {
379
+ write_restorecursor(g, p, savevar);
380
+ }
352
381
  p = p->right;
353
382
  }
354
383
 
@@ -375,10 +404,9 @@ static void generate_backwards(struct generator * g, struct node * p) {
375
404
  w(g, "~Mcursor = limit_backward;~N");
376
405
  }
377
406
 
378
-
379
407
  static void generate_not(struct generator * g, struct node * p) {
380
408
  struct str * savevar = NULL;
381
- if (K_needed(g, p->left)) {
409
+ if (K_needed_node_on_f(p->left)) {
382
410
  savevar = vars_newname(g);
383
411
  }
384
412
 
@@ -392,25 +420,24 @@ static void generate_not(struct generator * g, struct node * p) {
392
420
  write_savecursor(g, p, savevar);
393
421
  }
394
422
 
395
- g->failure_label = new_label(g);
423
+ int label = new_label(g);
424
+ g->failure_label = label;
396
425
  g->label_used = 0;
397
426
  str_clear(g->failure_str);
398
427
  generate(g, p->left);
399
428
 
400
- {
401
- int l = g->failure_label;
402
- int u = g->label_used;
429
+ int l = g->failure_label;
430
+ int u = g->label_used;
403
431
 
404
- g->label_used = used;
405
- g->failure_label = a0;
406
- str_delete(g->failure_str);
407
- g->failure_str = a1;
432
+ g->label_used = used;
433
+ g->failure_label = a0;
434
+ str_delete(g->failure_str);
435
+ g->failure_str = a1;
408
436
 
409
- write_failure(g);
437
+ write_failure(g);
410
438
 
411
- if (u)
412
- wsetl(g, l);
413
- }
439
+ if (u)
440
+ wsetl(g, l);
414
441
 
415
442
  if (savevar) {
416
443
  write_restorecursor(g, p, savevar);
@@ -419,14 +446,14 @@ static void generate_not(struct generator * g, struct node * p) {
419
446
  }
420
447
  }
421
448
 
422
-
423
449
  static void generate_try(struct generator * g, struct node * p) {
424
450
  struct str * savevar = NULL;
425
- if (K_needed(g, p->left)) {
451
+ if (K_needed(p->left)) {
426
452
  savevar = vars_newname(g);
427
453
  }
428
454
 
429
- g->failure_label = new_label(g);
455
+ int label = new_label(g);
456
+ g->failure_label = label;
430
457
  g->label_used = 0;
431
458
  str_clear(g->failure_str);
432
459
 
@@ -450,14 +477,12 @@ static void generate_try(struct generator * g, struct node * p) {
450
477
 
451
478
  static void generate_set(struct generator * g, struct node * p) {
452
479
  write_comment(g, p);
453
- g->V[0] = p->name;
454
- writef(g, "~M~V0 = true;~N", p);
480
+ writef(g, "~M~V = true;~N", p);
455
481
  }
456
482
 
457
483
  static void generate_unset(struct generator * g, struct node * p) {
458
484
  write_comment(g, p);
459
- g->V[0] = p->name;
460
- writef(g, "~M~V0 = false;~N", p);
485
+ writef(g, "~M~V = false;~N", p);
461
486
  }
462
487
 
463
488
  static void generate_fail(struct generator * g, struct node * p) {
@@ -467,10 +492,9 @@ static void generate_fail(struct generator * g, struct node * p) {
467
492
  }
468
493
 
469
494
  /* generate_test() also implements 'reverse' */
470
-
471
495
  static void generate_test(struct generator * g, struct node * p) {
472
496
  struct str * savevar = NULL;
473
- if (K_needed(g, p->left)) {
497
+ if (K_needed(p->left)) {
474
498
  savevar = vars_newname(g);
475
499
  }
476
500
 
@@ -492,7 +516,7 @@ static void generate_test(struct generator * g, struct node * p) {
492
516
 
493
517
  static void generate_do(struct generator * g, struct node * p) {
494
518
  struct str * savevar = NULL;
495
- if (K_needed(g, p->left)) {
519
+ if (K_needed(p->left)) {
496
520
  savevar = vars_newname(g);
497
521
  }
498
522
 
@@ -505,10 +529,10 @@ static void generate_do(struct generator * g, struct node * p) {
505
529
  if (p->left->type == c_call) {
506
530
  /* Optimise do <call> */
507
531
  write_comment(g, p->left);
508
- g->V[0] = p->left->name;
509
- w(g, "~M~V0();~N");
532
+ writef(g, "~M~V();~N", p->left);
510
533
  } else {
511
- g->failure_label = new_label(g);
534
+ int label = new_label(g);
535
+ g->failure_label = label;
512
536
  g->label_used = 0;
513
537
  str_clear(g->failure_str);
514
538
 
@@ -537,15 +561,15 @@ static void generate_GO_grouping(struct generator * g, struct node * p, int is_g
537
561
  struct grouping * q = p->name->grouping;
538
562
  g->S[0] = p->mode == m_forward ? "" : "_b";
539
563
  g->S[1] = complement ? "in" : "out";
540
- g->V[0] = p->name;
541
564
  g->I[0] = q->smallest_ch;
542
565
  g->I[1] = q->largest_ch;
543
566
  if (is_goto) {
544
- w(g, "~Mif (~S1_grouping~S0(~V0, ~I0, ~I1, true) < 0)~N~f~N");
567
+ writef(g, "~Mif (~S1_grouping~S0(~V, ~I0, ~I1, true) < 0)~N~f~N", p);
545
568
  } else {
546
- w(g, "~{~N"
547
- "~Mint ret = ~S1_grouping~S0(~V0, ~I0, ~I1, true);~N"
548
- "~Mif (ret < 0)~N~f~N");
569
+ writef(g, "~{~N"
570
+ "~Mint ret = ~S1_grouping~S0(~V, ~I0, ~I1, true);~N"
571
+ "~Mif (ret < 0)~N~f~N", p);
572
+
549
573
  if (p->mode == m_forward)
550
574
  w(g, "~Mcursor += ret;~N");
551
575
  else
@@ -554,7 +578,7 @@ static void generate_GO_grouping(struct generator * g, struct node * p, int is_g
554
578
  }
555
579
  }
556
580
 
557
- static void generate_GO(struct generator * g, struct node * p, int style) {
581
+ static void generate_GO(struct generator * g, struct node * p, int is_goto) {
558
582
  write_comment(g, p);
559
583
 
560
584
  int used = g->label_used;
@@ -564,22 +588,21 @@ static void generate_GO(struct generator * g, struct node * p, int style) {
564
588
  w(g, "~Mwhile (true)~N~{");
565
589
 
566
590
  struct str * savevar = NULL;
567
- if (style == 1 || repeat_restore(g, p->left)) {
591
+ if (is_goto || repeat_restore(p->left)) {
568
592
  savevar = vars_newname(g);
569
593
  write_savecursor(g, p, savevar);
570
594
  }
571
595
 
572
- g->failure_label = new_label(g);
596
+ int label = new_label(g);
597
+ g->failure_label = label;
573
598
  g->label_used = 0;
574
599
  str_clear(g->failure_str);
575
600
  generate(g, p->left);
576
601
 
577
- if (style == 1) {
578
- /* include for goto; omit for gopast */
579
- write_restorecursor(g, p, savevar);
580
- }
581
-
602
+ /* include for goto; omit for gopast */
603
+ if (is_goto) write_restorecursor(g, p, savevar);
582
604
  w(g, "~Mbreak;~N");
605
+
583
606
  if (g->label_used)
584
607
  wsetl(g, g->failure_label);
585
608
  if (savevar) {
@@ -598,8 +621,8 @@ static void generate_GO(struct generator * g, struct node * p, int style) {
598
621
  }
599
622
 
600
623
  static void generate_loop(struct generator * g, struct node * p) {
601
- struct str * loopvar = vars_newname(g);
602
624
  write_comment(g, p);
625
+ struct str * loopvar = vars_newname(g);
603
626
  g->B[0] = str_data(loopvar);
604
627
  w(g, "~Mfor (int ~B0 = ");
605
628
  generate_AE(g, p->AE);
@@ -613,22 +636,24 @@ static void generate_loop(struct generator * g, struct node * p) {
613
636
  str_delete(loopvar);
614
637
  }
615
638
 
616
- static void generate_repeat_or_atleast(struct generator * g, struct node * p, struct str * atleast_case) {
639
+ static void generate_repeat_or_atleast(struct generator * g, struct node * p, struct str * loopvar) {
617
640
  writef(g, "~Mwhile (true)~N~{", p);
618
641
 
619
642
  struct str * savevar = NULL;
620
- if (repeat_restore(g, p->left)) {
643
+ if (repeat_restore(p->left)) {
621
644
  savevar = vars_newname(g);
622
645
  write_savecursor(g, p, savevar);
623
646
  }
624
647
 
625
- g->failure_label = new_label(g);
648
+ int label = new_label(g);
649
+ g->failure_label = label;
626
650
  g->label_used = 0;
627
651
  str_clear(g->failure_str);
652
+
628
653
  generate(g, p->left);
629
654
 
630
- if (atleast_case != NULL) {
631
- g->B[0] = str_data(atleast_case);
655
+ if (loopvar != NULL) {
656
+ g->B[0] = str_data(loopvar);
632
657
  w(g, "~M~B0--;~N");
633
658
  }
634
659
 
@@ -651,10 +676,10 @@ static void generate_repeat(struct generator * g, struct node * p) {
651
676
  }
652
677
 
653
678
  static void generate_atleast(struct generator * g, struct node * p) {
654
- struct str * loopvar = vars_newname(g);
655
-
656
679
  write_comment(g, p);
680
+
657
681
  w(g, "~{");
682
+ struct str * loopvar = vars_newname(g);
658
683
  g->B[0] = str_data(loopvar);
659
684
  w(g, "~Mint ~B0 = ");
660
685
  generate_AE(g, p->AE);
@@ -677,12 +702,6 @@ static void generate_atleast(struct generator * g, struct node * p) {
677
702
  str_delete(loopvar);
678
703
  }
679
704
 
680
- static void generate_setmark(struct generator * g, struct node * p) {
681
- write_comment(g, p);
682
- g->V[0] = p->name;
683
- writef(g, "~M~V0 = cursor;~N", p);
684
- }
685
-
686
705
  static void generate_tomark(struct generator * g, struct node * p) {
687
706
  write_comment(g, p);
688
707
  g->S[0] = p->mode == m_forward ? ">" : "<";
@@ -694,14 +713,6 @@ static void generate_tomark(struct generator * g, struct node * p) {
694
713
  w(g, "~Mcursor = "); generate_AE(g, p->AE); writef(g, ";~N", p);
695
714
  }
696
715
 
697
- static void generate_atmark(struct generator * g, struct node * p) {
698
- write_comment(g, p);
699
- w(g, "~Mif (cursor != "); generate_AE(g, p->AE); writef(g, ")~N", p);
700
- write_block_start(g);
701
- write_failure(g);
702
- write_block_end(g);
703
- }
704
-
705
716
  static void generate_hop(struct generator * g, struct node * p) {
706
717
  write_comment(g, p);
707
718
  g->S[0] = p->mode == m_forward ? "+" : "-";
@@ -725,24 +736,12 @@ static void generate_hop(struct generator * g, struct node * p) {
725
736
  writef(g, "~}", p);
726
737
  }
727
738
 
728
- static void generate_delete(struct generator * g, struct node * p) {
729
- write_comment(g, p);
730
- writef(g, "~Mslice_del();~N", p);
731
- }
732
-
733
739
  static void generate_tolimit(struct generator * g, struct node * p) {
734
740
  write_comment(g, p);
735
741
  g->S[0] = p->mode == m_forward ? "limit" : "limit_backward";
736
742
  writef(g, "~Mcursor = ~S0;~N", p);
737
743
  }
738
744
 
739
- static void generate_atlimit(struct generator * g, struct node * p) {
740
- write_comment(g, p);
741
- g->S[0] = p->mode == m_forward ? "limit" : "limit_backward";
742
- g->S[1] = p->mode == m_forward ? "<" : ">";
743
- write_failure_if(g, "cursor ~S1 ~S0", p);
744
- }
745
-
746
745
  static void generate_leftslice(struct generator * g, struct node * p) {
747
746
  write_comment(g, p);
748
747
  g->S[0] = p->mode == m_forward ? "bra" : "ket";
@@ -757,14 +756,12 @@ static void generate_rightslice(struct generator * g, struct node * p) {
757
756
 
758
757
  static void generate_assignto(struct generator * g, struct node * p) {
759
758
  write_comment(g, p);
760
- g->V[0] = p->name;
761
- writef(g, "~Massign_to(~V0);~N", p);
759
+ writef(g, "~Massign_to(~V);~N", p);
762
760
  }
763
761
 
764
762
  static void generate_sliceto(struct generator * g, struct node * p) {
765
763
  write_comment(g, p);
766
- g->V[0] = p->name;
767
- writef(g, "~Mslice_to(~V0);~N", p);
764
+ writef(g, "~Mslice_to(~V);~N", p);
768
765
  }
769
766
 
770
767
  static void generate_address(struct generator * g, struct node * p) {
@@ -773,12 +770,13 @@ static void generate_address(struct generator * g, struct node * p) {
773
770
  write_literal_string(g, b);
774
771
  } else {
775
772
  write_varref(g, p->name);
773
+ write_string(g, ".ToString()");
776
774
  }
777
775
  }
778
776
 
779
777
  static void generate_insert(struct generator * g, struct node * p, int style) {
780
- int keep_c = style == c_attach;
781
778
  write_comment(g, p);
779
+ int keep_c = style == c_attach;
782
780
  if (p->mode == m_backward) keep_c = !keep_c;
783
781
  if (keep_c) w(g, "~{~Mint c = cursor;~N");
784
782
  writef(g, "~Minsert(cursor, cursor, ", p);
@@ -787,11 +785,12 @@ static void generate_insert(struct generator * g, struct node * p, int style) {
787
785
  if (keep_c) w(g, "~Mcursor = c;~N~}");
788
786
  }
789
787
 
790
- static void generate_assignfrom(struct generator * g, struct node * p) {
791
- int keep_c = p->mode == m_forward; /* like 'attach' */
792
-
788
+ static void generate_stringassign(struct generator * g, struct node * p) {
793
789
  write_comment(g, p);
794
- if (keep_c) writef(g, "~{~Mint c = cursor;~N", p);
790
+ int keep_c = p->mode == m_forward; /* like 'attach' */
791
+ if (keep_c) {
792
+ writef(g, "~{~Mint c = cursor;~N", p);
793
+ }
795
794
  if (p->mode == m_forward) {
796
795
  writef(g, "~Minsert(cursor, limit, ", p);
797
796
  } else {
@@ -799,19 +798,26 @@ static void generate_assignfrom(struct generator * g, struct node * p) {
799
798
  }
800
799
  generate_address(g, p);
801
800
  writef(g, ");~N", p);
802
- if (keep_c) w(g, "~Mcursor = c;~N~}");
801
+ if (keep_c) {
802
+ w(g, "~Mcursor = c;~N~}");
803
+ }
803
804
  }
804
805
 
805
806
  static void generate_slicefrom(struct generator * g, struct node * p) {
806
807
  write_comment(g, p);
808
+ if (p->literalstring && SIZE(p->literalstring) == 0) {
809
+ w(g, "~Mslice_del();~N");
810
+ return;
811
+ }
807
812
  w(g, "~Mslice_from(");
808
813
  generate_address(g, p);
809
- writef(g, ");~N", p);
814
+ w(g, ");~N");
810
815
  }
811
816
 
812
817
  static void generate_setlimit(struct generator * g, struct node * p) {
813
- struct str * varname = vars_newname(g);
814
818
  write_comment(g, p);
819
+ struct str * varname = vars_newname(g);
820
+
815
821
  if (p->left && p->left->type == c_tomark) {
816
822
  /* Special case for:
817
823
  *
@@ -823,6 +829,7 @@ static void generate_setlimit(struct generator * g, struct node * p) {
823
829
  */
824
830
  struct node * q = p->left;
825
831
  write_comment(g, q);
832
+
826
833
  g->S[0] = q->mode == m_forward ? ">" : "<";
827
834
  w(g, "~Mif (cursor ~S0 "); generate_AE(g, q->AE); w(g, ")~N");
828
835
  write_block_start(g);
@@ -831,13 +838,17 @@ static void generate_setlimit(struct generator * g, struct node * p) {
831
838
 
832
839
  g->B[0] = str_data(varname);
833
840
  if (p->mode == m_forward) {
834
- w(g, "~Mint ~B0 = limit - cursor;~N");
841
+ w(g, "~Mint ~B0 = limit;~N");
835
842
  w(g, "~Mlimit = ");
843
+ generate_AE(g, q->AE);
844
+ w(g, ";~N");
845
+ w(g, "~M~B0 -= limit;~N");
836
846
  } else {
837
847
  w(g, "~Mint ~B0 = limit_backward;~N");
838
848
  w(g, "~Mlimit_backward = ");
849
+ generate_AE(g, q->AE);
850
+ w(g, ";~N");
839
851
  }
840
- generate_AE(g, q->AE); writef(g, ";~N", q);
841
852
 
842
853
  if (p->mode == m_forward) {
843
854
  str_assign(g->failure_str, "limit += ");
@@ -890,32 +901,60 @@ static void generate_setlimit(struct generator * g, struct node * p) {
890
901
  static void generate_dollar(struct generator * g, struct node * p) {
891
902
  write_comment(g, p);
892
903
 
893
- struct str * savevar = vars_newname(g);
894
- g->V[0] = p->name;
904
+ int used = g->label_used;
905
+ int a0 = g->failure_label;
906
+ struct str * a1 = str_copy(g->failure_str);
907
+ int label = new_label(g);
908
+ g->failure_label = label;
909
+ g->label_used = 0;
910
+ str_clear(g->failure_str);
895
911
 
896
- str_assign(g->failure_str, "copy_from(");
897
- str_append(g->failure_str, savevar);
898
- str_append_string(g->failure_str, ");");
912
+ struct str * savevar = vars_newname(g);
899
913
  g->B[0] = str_data(savevar);
914
+
900
915
  writef(g, "~{~MEnv ~B0 = new Env(this);~N"
901
- "~Mcurrent = ~V0;~N"
916
+ "~Mcurrent = ~V;~N"
902
917
  "~Mcursor = 0;~N"
903
918
  "~Mlimit = current.Length;~N", p);
919
+ if (p->left->possible_signals == -1) {
920
+ /* Assume failure. */
921
+ w(g, "~Mbool ~B0_f = true;~N");
922
+ }
923
+
904
924
  generate(g, p->left);
905
925
 
906
- write_margin(g);
907
- write_str(g, g->failure_str);
908
- write_newline(g);
926
+ if (p->left->possible_signals == -1) {
927
+ /* Mark success. */
928
+ g->B[0] = str_data(savevar);
929
+ w(g, "~M~B0_f = false;~N");
930
+ }
931
+
932
+ if (g->label_used)
933
+ wsetl(g, g->failure_label);
934
+
935
+ g->label_used = used;
936
+ g->failure_label = a0;
937
+ str_delete(g->failure_str);
938
+ g->failure_str = a1;
939
+
940
+ g->B[0] = str_data(savevar);
941
+ writef(g, "~Mcopy_from(~B0);~N", p);
909
942
 
943
+ if (p->left->possible_signals == 0) {
944
+ // p->left always signals f.
945
+ w(g, "~M~f~N");
946
+ } else if (p->left->possible_signals == -1) {
947
+ write_failure_if(g, "~B0_f", p);
948
+ }
910
949
  w(g, "~}");
950
+
911
951
  str_delete(savevar);
912
952
  }
913
953
 
914
954
  static void generate_integer_assign(struct generator * g, struct node * p, const char * s) {
915
955
  write_comment(g, p);
916
- g->V[0] = p->name;
917
956
  g->S[0] = s;
918
- w(g, "~M~V0 ~S0 ");
957
+ writef(g, "~M~V ~S0 ", p);
919
958
  generate_AE(g, p->AE);
920
959
  w(g, ";~N");
921
960
  }
@@ -923,7 +962,7 @@ static void generate_integer_assign(struct generator * g, struct node * p, const
923
962
  static void generate_integer_test(struct generator * g, struct node * p) {
924
963
  write_comment(g, p);
925
964
  int relop = p->type;
926
- int optimise_to_return = (g->failure_label == x_return && p->right && p->right->type == c_functionend);
965
+ int optimise_to_return = tailcallable(g, p);
927
966
  if (optimise_to_return) {
928
967
  w(g, "~Mreturn ");
929
968
  p->right = NULL;
@@ -933,8 +972,7 @@ static void generate_integer_test(struct generator * g, struct node * p) {
933
972
  relop ^= 1;
934
973
  }
935
974
  generate_AE(g, p->left);
936
- // Relational operators are the same as C.
937
- write_c_relop(g, relop);
975
+ write_relop(g, relop);
938
976
  generate_AE(g, p->AE);
939
977
  if (optimise_to_return) {
940
978
  w(g, ";~N");
@@ -947,24 +985,28 @@ static void generate_integer_test(struct generator * g, struct node * p) {
947
985
  }
948
986
 
949
987
  static void generate_call(struct generator * g, struct node * p) {
950
- int signals = check_possible_signals_list(g, p->name->definition, c_define, 0);
988
+ int signals = p->name->definition->possible_signals;
951
989
  write_comment(g, p);
952
- g->V[0] = p->name;
953
- if (g->failure_label == x_return &&
954
- (signals == 0 || (p->right && p->right->type == c_functionend))) {
955
- /* Always fails or tail call. */
956
- writef(g, "~Mreturn ~V0();~N", p);
990
+ if (tailcallable(g, p)) {
991
+ /* Tail call. */
992
+ writef(g, "~Mreturn ~V();~N", p);
993
+ p->right = NULL;
994
+ return;
995
+ }
996
+ if (just_return_on_fail(g) && signals == 0) {
997
+ /* Always fails. */
998
+ writef(g, "~Mreturn ~V();~N", p);
957
999
  return;
958
1000
  }
959
1001
  if (signals == 1) {
960
1002
  /* Always succeeds. */
961
- writef(g, "~M~V0();~N", p);
1003
+ writef(g, "~M~V();~N", p);
962
1004
  } else if (signals == 0) {
963
1005
  /* Always fails. */
964
- writef(g, "~M~V0();~N", p);
1006
+ writef(g, "~M~V();~N", p);
965
1007
  write_failure(g);
966
1008
  } else {
967
- w(g, "~Mif (!~V0())~N~+");
1009
+ writef(g, "~Mif (!~V())~N~+", p);
968
1010
  write_failure(g);
969
1011
  w(g, "~-");
970
1012
  }
@@ -976,68 +1018,102 @@ static void generate_grouping(struct generator * g, struct node * p, int complem
976
1018
  struct grouping * q = p->name->grouping;
977
1019
  g->S[0] = p->mode == m_forward ? "" : "_b";
978
1020
  g->S[1] = complement ? "out" : "in";
979
- g->V[0] = p->name;
980
1021
  g->I[0] = q->smallest_ch;
981
1022
  g->I[1] = q->largest_ch;
982
- w(g, "~Mif (~S1_grouping~S0(~V0, ~I0, ~I1, false) != 0)~N~f");
1023
+ if (tailcallable(g, p)) {
1024
+ writef(g, "~Mreturn (~S1_grouping~S0(~V, ~I0, ~I1, false) == 0);~N", p);
1025
+ p->right = NULL;
1026
+ } else {
1027
+ writef(g, "~Mif (~S1_grouping~S0(~V, ~I0, ~I1, false) != 0)~N~f", p);
1028
+ }
983
1029
  }
984
1030
 
985
1031
  static void generate_namedstring(struct generator * g, struct node * p) {
986
1032
  write_comment(g, p);
987
1033
  g->S[0] = p->mode == m_forward ? "" : "_b";
988
- g->V[0] = p->name;
989
- write_failure_if(g, "!(eq_s~S0(~V0))", p);
1034
+ if (tailcallable(g, p)) {
1035
+ writef(g, "~Mreturn eq_s~S0(~V);~N", p);
1036
+ p->right = NULL;
1037
+ } else {
1038
+ write_failure_if(g, "!(eq_s~S0(~V))", p);
1039
+ }
990
1040
  }
991
1041
 
992
1042
  static void generate_literalstring(struct generator * g, struct node * p) {
993
- symbol * b = p->literalstring;
994
1043
  write_comment(g, p);
995
1044
  g->S[0] = p->mode == m_forward ? "" : "_b";
996
- g->L[0] = b;
997
- write_failure_if(g, "!(eq_s~S0(~L0))", p);
1045
+ if (tailcallable(g, p)) {
1046
+ writef(g, "~Mreturn eq_s~S0(~L);~N", p);
1047
+ p->right = NULL;
1048
+ } else {
1049
+ write_failure_if(g, "!(eq_s~S0(~L))", p);
1050
+ }
998
1051
  }
999
1052
 
1000
1053
  static void generate_define(struct generator * g, struct node * p) {
1001
- struct name * q = p->name;
1002
- if (q->type == t_routine && !q->used) return;
1003
-
1004
1054
  write_newline(g);
1005
1055
  write_comment(g, p);
1006
1056
 
1057
+ struct name * q = p->name;
1007
1058
  if (q->type == t_routine) {
1008
1059
  g->S[0] = "private";
1009
1060
  } else {
1010
- g->S[0] = "protected override";
1061
+ if (SIZE(q->s) == 4 && memcmp(q->s, "stem", 4) == 0) {
1062
+ g->S[0] = "protected override";
1063
+ } else {
1064
+ g->S[0] = "protected";
1065
+ }
1011
1066
  }
1012
- g->V[0] = q;
1013
- w(g, "~M~S0 bool ~V0()~N~M{~+~N");
1014
-
1015
- /* Save output. */
1016
- struct str * saved_output = g->outbuf;
1017
- g->outbuf = str_new();
1067
+ writef(g, "~M~S0 bool ~V()~N~M{~+~N", p);
1018
1068
 
1019
1069
  g->next_label = 0;
1020
1070
  g->var_number = 0;
1021
1071
 
1022
- if (p->amongvar_needed) w(g, "~Mint among_var;~N");
1072
+ if (q->amongvar_needed) {
1073
+ w(g, "~Mint among_var;~N");
1074
+ }
1075
+
1076
+ /* Declare localised variables. */
1077
+ for (struct name * name = g->analyser->names; name; name = name->next) {
1078
+ if (name->local_to == q) {
1079
+ switch (name->type) {
1080
+ case t_string:
1081
+ // String variables not localised for C# currently.
1082
+ w(g, "~M");
1083
+ write_string(g, g->options->string_class);
1084
+ write_char(g, ' ');
1085
+ write_varname(g, name);
1086
+ write_string(g, " = new ");
1087
+ write_string(g, g->options->string_class);
1088
+ w(g, "();~N");
1089
+ break;
1090
+ case t_integer:
1091
+ w(g, "~Mint ");
1092
+ write_varname(g, name);
1093
+ w(g, ";~N");
1094
+ break;
1095
+ case t_boolean:
1096
+ w(g, "~Mbool ");
1097
+ write_varname(g, name);
1098
+ w(g, ";~N");
1099
+ break;
1100
+ }
1101
+ }
1102
+ }
1103
+
1023
1104
  str_clear(g->failure_str);
1024
1105
  g->failure_label = x_return;
1025
1106
  g->label_used = 0;
1026
- int signals = check_possible_signals_list(g, p->left, c_define, 0);
1027
1107
 
1028
1108
  /* Generate function body. */
1029
1109
  generate(g, p->left);
1030
1110
  if (p->left->right) {
1031
1111
  assert(p->left->right->type == c_functionend);
1032
- if (signals) {
1112
+ if (p->left->possible_signals) {
1033
1113
  generate(g, p->left->right);
1034
1114
  }
1035
1115
  }
1036
1116
  w(g, "~}");
1037
-
1038
- str_append(saved_output, g->outbuf);
1039
- str_delete(g->outbuf);
1040
- g->outbuf = saved_output;
1041
1117
  }
1042
1118
 
1043
1119
  static void generate_functionend(struct generator * g, struct node * p) {
@@ -1054,14 +1130,17 @@ static void generate_substring(struct generator * g, struct node * p) {
1054
1130
  g->I[0] = x->number;
1055
1131
 
1056
1132
  if (x->amongvar_needed) {
1057
- writef(g, "~Mamong_var = find_among~S0(a_~I0);~N", p);
1133
+ writef(g, "~Mamong_var = find_among~S0(a_~I0, ~F);~N", p);
1058
1134
  if (!x->always_matches) {
1059
1135
  write_failure_if(g, "among_var == 0", p);
1060
1136
  }
1061
1137
  } else if (x->always_matches) {
1062
- writef(g, "~Mfind_among~S0(a_~I0);~N", p);
1138
+ writef(g, "~Mfind_among~S0(a_~I0, ~F);~N", p);
1139
+ } else if (x->command_count == 0 && tailcallable(g, p)) {
1140
+ writef(g, "~Mreturn find_among~S0(a_~I0, ~F) != 0;~N", p);
1141
+ x->node->right = NULL;
1063
1142
  } else {
1064
- write_failure_if(g, "find_among~S0(a_~I0) == 0", p);
1143
+ write_failure_if(g, "find_among~S0(a_~I0, ~F) == 0", p);
1065
1144
  }
1066
1145
  }
1067
1146
 
@@ -1082,14 +1161,15 @@ static void generate_among(struct generator * g, struct node * p) {
1082
1161
  for (int i = 1; i <= x->command_count; i++) {
1083
1162
  g->I[0] = i;
1084
1163
  /* Put a block around each case which seems to workaround bogus
1085
- * compiler errors (typically with repeat reports at the same
1164
+ * C# compiler errors (typically with repeat reports at the same
1086
1165
  * location):
1087
1166
  *
1088
1167
  * dutchStemmer.generated.cs(543,25): error CS0165: Use of unassigned local variable `c5'
1089
1168
  *
1090
1169
  * The c5 variable is initialised at point of declaration and we
1091
1170
  * don't `goto` into the block it is declared in from outside so
1092
- * this seems to be buggy code flow analysis in the compiler.
1171
+ * this seems to be buggy code flow analysis in the C# compiler.
1172
+ *
1093
1173
  * Unclear where to usefully report mono bugs in 2025 so I've
1094
1174
  * not tried.
1095
1175
  */
@@ -1101,10 +1181,23 @@ static void generate_among(struct generator * g, struct node * p) {
1101
1181
  }
1102
1182
  }
1103
1183
 
1104
- static void generate_booltest(struct generator * g, struct node * p) {
1184
+ static void generate_booltest(struct generator * g, struct node * p, int inverted) {
1105
1185
  write_comment(g, p);
1106
- g->V[0] = p->name;
1107
- write_failure_if(g, "!(~V0)", p);
1186
+ if (tailcallable(g, p)) {
1187
+ // Optimise at end of function.
1188
+ if (inverted) {
1189
+ writef(g, "~Mreturn !~V;~N", p);
1190
+ } else {
1191
+ writef(g, "~Mreturn ~V;~N", p);
1192
+ }
1193
+ p->right = NULL;
1194
+ return;
1195
+ }
1196
+ if (inverted) {
1197
+ write_failure_if(g, "~V", p);
1198
+ } else {
1199
+ write_failure_if(g, "!~V", p);
1200
+ }
1108
1201
  }
1109
1202
 
1110
1203
  static void generate_false(struct generator * g, struct node * p) {
@@ -1148,25 +1241,21 @@ static void generate(struct generator * g, struct node * p) {
1148
1241
  case c_repeat: generate_repeat(g, p); break;
1149
1242
  case c_loop: generate_loop(g, p); break;
1150
1243
  case c_atleast: generate_atleast(g, p); break;
1151
- case c_setmark: generate_setmark(g, p); break;
1152
1244
  case c_tomark: generate_tomark(g, p); break;
1153
- case c_atmark: generate_atmark(g, p); break;
1154
1245
  case c_hop: generate_hop(g, p); break;
1155
- case c_delete: generate_delete(g, p); break;
1156
1246
  case c_next: generate_next(g, p); break;
1157
1247
  case c_tolimit: generate_tolimit(g, p); break;
1158
- case c_atlimit: generate_atlimit(g, p); break;
1159
1248
  case c_leftslice: generate_leftslice(g, p); break;
1160
1249
  case c_rightslice: generate_rightslice(g, p); break;
1161
1250
  case c_assignto: generate_assignto(g, p); break;
1162
1251
  case c_sliceto: generate_sliceto(g, p); break;
1163
- case c_assign: generate_assignfrom(g, p); break;
1252
+ case c_stringassign: generate_stringassign(g, p); break;
1164
1253
  case c_insert:
1165
1254
  case c_attach: generate_insert(g, p, p->type); break;
1166
1255
  case c_slicefrom: generate_slicefrom(g, p); break;
1167
1256
  case c_setlimit: generate_setlimit(g, p); break;
1168
1257
  case c_dollar: generate_dollar(g, p); break;
1169
- case c_mathassign: generate_integer_assign(g, p, "="); break;
1258
+ case c_assign: generate_integer_assign(g, p, "="); break;
1170
1259
  case c_plusassign: generate_integer_assign(g, p, "+="); break;
1171
1260
  case c_minusassign: generate_integer_assign(g, p, "-="); break;
1172
1261
  case c_multiplyassign:generate_integer_assign(g, p, "*="); break;
@@ -1186,7 +1275,8 @@ static void generate(struct generator * g, struct node * p) {
1186
1275
  case c_literalstring: generate_literalstring(g, p); break;
1187
1276
  case c_among: generate_among(g, p); break;
1188
1277
  case c_substring: generate_substring(g, p); break;
1189
- case c_booltest: generate_booltest(g, p); break;
1278
+ case c_booltest: generate_booltest(g, p, false); break;
1279
+ case c_not_booltest: generate_booltest(g, p, true); break;
1190
1280
  case c_false: generate_false(g, p); break;
1191
1281
  case c_true: break;
1192
1282
  case c_debug: generate_debug(g, p); break;
@@ -1208,7 +1298,7 @@ static void generate_class_begin(struct generator * g) {
1208
1298
  w(g, "#pragma warning disable 0162~N~N");
1209
1299
 
1210
1300
  w(g, "~Mnamespace ");
1211
- w(g, g->options->package);
1301
+ write_string(g, g->options->package);
1212
1302
  w(g, "~N~{");
1213
1303
 
1214
1304
  w(g, "~Musing System;~N");
@@ -1224,7 +1314,7 @@ static void generate_class_begin(struct generator * g) {
1224
1314
  w(g, "~M/// ~N");
1225
1315
  w(g, "~M[System.CodeDom.Compiler.GeneratedCode(\"Snowball\", \"" SNOWBALL_VERSION "\")]~N");
1226
1316
  w(g, "~Mpublic partial class ~n : ");
1227
- w(g, g->options->parent_class_name);
1317
+ write_string(g, g->options->parent_class_name);
1228
1318
  w(g, "~N~{");
1229
1319
  }
1230
1320
 
@@ -1235,97 +1325,96 @@ static void generate_class_end(struct generator * g) {
1235
1325
  w(g, "~N");
1236
1326
  }
1237
1327
 
1238
- static void generate_among_table(struct generator * g, struct among * x, const char * type) {
1328
+ static void generate_among_table(struct generator * g, struct among * x) {
1239
1329
  write_newline(g);
1240
1330
  write_comment(g, x->node);
1241
1331
 
1242
1332
  struct amongvec * v = x->b;
1243
1333
 
1244
1334
  g->I[0] = x->number;
1245
- g->S[0] = type;
1246
- w(g, "~M~S0a_~I0 = new[] ~N~M{~N~+");
1335
+ w(g, "~Mprivate static readonly Among[] a_~I0 = new[] ~N~M{~N~+");
1247
1336
  for (int i = 0; i < x->literalstring_count; i++) {
1248
- g->I[0] = v[i].i;
1249
- g->I[1] = v[i].result;
1250
- g->L[0] = v[i].b;
1251
- g->S[0] = i < x->literalstring_count - 1 ? "," : "";
1252
-
1253
- w(g, "~Mnew Among(~L0, ~I0, ~I1");
1254
- if (v[i].function != NULL) {
1255
- w(g, ", ");
1256
- write_varname(g, v[i].function);
1337
+ if (i) w(g, ",~N");
1338
+ g->I[3] = v[i].i;
1339
+ g->I[4] = v[i].result;
1340
+ g->I[5] = v[i].function_index;
1341
+
1342
+ w(g, "~Mnew Among(");
1343
+ write_literal_string(g, v[i].b);
1344
+ w(g, ", ~I3, ~I4, ~I5)");
1345
+ }
1346
+ w(g, "~N~-~M};~N");
1347
+
1348
+ if (x->function_count <= 1) return;
1349
+
1350
+ w(g, "~N~Mprivate bool af_~I0() {~N~+");
1351
+ w(g, "~Mswitch (af) {~N~+");
1352
+ for (int n = 1; n <= x->function_count; n++) {
1353
+ w(g, "~Mcase ");
1354
+ write_int(g, n);
1355
+ w(g, ": return ");
1356
+ for (int i = 0; i < x->literalstring_count; i++) {
1357
+ if (v[i].function_index == n) {
1358
+ write_varref(g, v[i].function);
1359
+ w(g, "();~N");
1360
+ break;
1361
+ }
1257
1362
  }
1258
- w(g, ")~S0~N");
1259
1363
  }
1260
- w(g, "~-~M};~N");
1364
+ w(g, "~-~M}~N");
1365
+ w(g, "~Mreturn false;~N");
1366
+ w(g, "~-~M}~N");
1261
1367
  }
1262
1368
 
1263
1369
  static void generate_amongs(struct generator * g) {
1264
1370
  for (struct among * x = g->analyser->amongs; x; x = x->next) {
1265
- if (x->function_count) {
1266
- g->I[0] = x->number;
1267
- g->I[1] = x->literalstring_count;
1268
-
1269
- w(g, "~N~Mprivate readonly Among[] a_~I0;~N");
1270
- } else {
1271
- generate_among_table(g, x, "private static readonly Among[] ");
1272
- }
1371
+ generate_among_table(g, x);
1273
1372
  }
1274
1373
  w(g, "~N");
1275
-
1276
- if (g->analyser->among_with_function_count == 0) return;
1277
-
1278
- w(g, "~M/// <summary>~N");
1279
- w(g, "~M/// Initializes a new instance of the <see cref=\"~n\"/> class.~N");
1280
- w(g, "~M/// </summary>~N");
1281
- w(g, "~M/// ~N");
1282
- w(g, "~Mpublic ~n()~N~{");
1283
- for (struct among * x = g->analyser->amongs; x; x = x->next) {
1284
- if (x->function_count) {
1285
- generate_among_table(g, x, "");
1286
- }
1287
- }
1288
-
1289
- w(g, "~}~N~N");
1290
1374
  }
1291
1375
 
1292
1376
  static void generate_grouping_table(struct generator * g, struct grouping * q) {
1293
1377
  symbol * b = q->b;
1294
1378
 
1295
- g->V[0] = q->name;
1296
-
1297
- w(g, "~Mprivate const string ~V0 = ");
1379
+ w(g, "~Mprivate const string ");
1380
+ write_varname(g, q->name);
1381
+ write_string(g, " = ");
1298
1382
  write_literal_string(g, b);
1299
1383
  w(g, ";~N");
1300
1384
  }
1301
1385
 
1302
1386
  static void generate_groupings(struct generator * g) {
1303
1387
  for (struct grouping * q = g->analyser->groupings; q; q = q->next) {
1304
- if (q->name->used)
1305
- generate_grouping_table(g, q);
1388
+ generate_grouping_table(g, q);
1306
1389
  }
1307
1390
  }
1308
1391
 
1309
1392
  static void generate_members(struct generator * g) {
1310
- int wrote_members = false;
1393
+ bool wrote_members = false;
1311
1394
 
1312
1395
  for (struct name * q = g->analyser->names; q; q = q->next) {
1313
- g->V[0] = q;
1396
+ if (q->local_to) continue;
1314
1397
  switch (q->type) {
1315
1398
  case t_string:
1316
1399
  w(g, "~Mprivate ");
1317
- w(g, g->options->string_class);
1318
- w(g, " ~W0 = new ");
1319
- w(g, g->options->string_class);
1400
+ write_string(g, g->options->string_class);
1401
+ write_char(g, ' ');
1402
+ write_varname(g, q);
1403
+ write_string(g, " = new ");
1404
+ write_string(g, g->options->string_class);
1320
1405
  w(g, "();~N");
1321
1406
  wrote_members = true;
1322
1407
  break;
1323
1408
  case t_integer:
1324
- w(g, "~Mprivate int ~W0;~N");
1409
+ w(g, "~Mprivate int ");
1410
+ write_varname(g, q);
1411
+ w(g, ";~N");
1325
1412
  wrote_members = true;
1326
1413
  break;
1327
1414
  case t_boolean:
1328
- w(g, "~Mprivate bool ~W0;~N");
1415
+ w(g, "~Mprivate bool ");
1416
+ write_varname(g, q);
1417
+ w(g, ";~N");
1329
1418
  wrote_members = true;
1330
1419
  break;
1331
1420
  }
@@ -1340,6 +1429,8 @@ static void generate_methods(struct generator * g) {
1340
1429
  }
1341
1430
 
1342
1431
  extern void generate_program_csharp(struct generator * g) {
1432
+ g->varname_prefix = "c";
1433
+
1343
1434
  g->outbuf = str_new();
1344
1435
  g->failure_str = str_new();
1345
1436