mittens 0.3.3 → 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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +1 -1
  5. data/ext/mittens/ext.c +68 -30
  6. data/ext/mittens/extconf.rb +2 -0
  7. data/lib/mittens/version.rb +1 -1
  8. data/vendor/snowball/.github/workflows/ci.yml +43 -3
  9. data/vendor/snowball/.github/workflows/coverage.yml +8 -5
  10. data/vendor/snowball/GNUmakefile +46 -16
  11. data/vendor/snowball/NEWS +437 -13
  12. data/vendor/snowball/README.rst +4 -4
  13. data/vendor/snowball/algorithms/earlymodernenglish.sbl +301 -0
  14. data/vendor/snowball/algorithms/french.sbl +1 -1
  15. data/vendor/snowball/algorithms/ukrainian.sbl +1251 -0
  16. data/vendor/snowball/compiler/analyser.c +460 -156
  17. data/vendor/snowball/compiler/driver.c +41 -36
  18. data/vendor/snowball/compiler/generator.c +24 -1
  19. data/vendor/snowball/compiler/generator_ada.c +46 -45
  20. data/vendor/snowball/compiler/generator_c.c +1094 -187
  21. data/vendor/snowball/compiler/generator_csharp.c +34 -11
  22. data/vendor/snowball/compiler/generator_dart.c +39 -11
  23. data/vendor/snowball/compiler/generator_go.c +6 -6
  24. data/vendor/snowball/compiler/generator_java.c +29 -8
  25. data/vendor/snowball/compiler/generator_js.c +65 -38
  26. data/vendor/snowball/compiler/generator_pascal.c +69 -26
  27. data/vendor/snowball/compiler/generator_php.c +33 -12
  28. data/vendor/snowball/compiler/generator_python.c +23 -4
  29. data/vendor/snowball/compiler/generator_rust.c +37 -13
  30. data/vendor/snowball/compiler/generator_zig.c +6 -6
  31. data/vendor/snowball/compiler/header.h +85 -14
  32. data/vendor/snowball/compiler/space.c +67 -38
  33. data/vendor/snowball/compiler/tokeniser.c +28 -16
  34. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +2 -2
  35. data/vendor/snowball/csharp/Snowball/Stemmer.cs +1 -4
  36. data/vendor/snowball/cxx/generate_algorithms.pl +5 -4
  37. data/vendor/snowball/cxx/{utilities.cxx → snowball_runtime.cxx} +1 -1
  38. data/vendor/snowball/cxx/stemmer.h +4 -0
  39. data/vendor/snowball/cxx/stemwords.cxx +36 -6
  40. data/vendor/snowball/dart/lib/src/snowball.dart +2 -2
  41. data/vendor/snowball/dart/pubspec.yaml +1 -1
  42. data/vendor/snowball/doc/libstemmer_c_README +3 -0
  43. data/vendor/snowball/doc/libstemmer_php_README +1 -1
  44. data/vendor/snowball/doc/libstemmer_python_README +8 -6
  45. data/vendor/snowball/examples/stemwords.c +4 -14
  46. data/vendor/snowball/go/env.go +16 -4
  47. data/vendor/snowball/include/libstemmer.h +0 -3
  48. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +4 -4
  49. data/vendor/snowball/javascript/base-stemmer.js +27 -40
  50. data/vendor/snowball/javascript/stemwords.js +34 -10
  51. data/vendor/snowball/libstemmer/libstemmer_c.in +5 -5
  52. data/vendor/snowball/libstemmer/mkmodules.pl +5 -3
  53. data/vendor/snowball/libstemmer/modules.txt +4 -2
  54. data/vendor/snowball/pascal/SnowballProgram.pas +2 -2
  55. data/vendor/snowball/php/base-stemmer.php +2 -2
  56. data/vendor/snowball/python/setup.py +5 -1
  57. data/vendor/snowball/python/snowballstemmer/basestemmer.py +1 -2
  58. data/vendor/snowball/runtime/api.c +14 -3
  59. data/vendor/snowball/runtime/api.h +13 -9
  60. data/vendor/snowball/runtime/{utilities.c → snowball_runtime.c} +115 -181
  61. data/vendor/snowball/runtime/snowball_runtime.h +35 -24
  62. data/vendor/snowball/rust/src/snowball/snowball_env.rs +1 -1
  63. data/vendor/snowball/tests/compilertest +3 -3
  64. data/vendor/snowball/tests/runtime/among.sbl +96 -2
  65. data/vendor/snowball/tests/runtime/attachinsert.sbl +4 -0
  66. data/vendor/snowball/tests/runtime/debug.out +7 -0
  67. data/vendor/snowball/tests/runtime/debug.sbl +9 -0
  68. data/vendor/snowball/tests/runtime/integertests.sbl +22 -0
  69. data/vendor/snowball/tests/runtime/marklimit.sbl +30 -0
  70. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  71. data/vendor/snowball/tests/runtime/reverse.sbl +11 -0
  72. data/vendor/snowball/tests/stemtest.c +2 -5
  73. data/vendor/snowball/tests/syntax/aeslicefrom.sbl +7 -0
  74. data/vendor/snowball/tests/syntax/canon.sbl +14 -0
  75. data/vendor/snowball/tests/syntax/canon.syntax +36 -0
  76. data/vendor/snowball/tests/syntax/inline.syntax +4 -4
  77. data/vendor/snowball/tests/syntax/localvar.sbl +38 -0
  78. data/vendor/snowball/tests/syntax/localvar.stderr +3 -0
  79. data/vendor/snowball/tests/syntax/localvar.syntax +76 -0
  80. data/vendor/snowball/tests/syntax/unused.sbl +5 -1
  81. data/vendor/snowball/tests/syntax/unused.stderr +3 -1
  82. data/vendor/snowball/zig/env.zig +24 -4
  83. metadata +14 -4
@@ -1,18 +1,70 @@
1
1
  #include <assert.h>
2
+ #include <limits.h>
2
3
  #include <stdio.h> /* for fprintf etc */
3
4
  #include <stdlib.h> /* for exit */
4
5
  #include <string.h> /* for strlen */
5
6
  #include "header.h"
6
7
 
8
+ // C90 guarantees 31 significant initial characters in an internal identifier.
9
+ #define C_MAX_ID_LEN 31
10
+
7
11
  /* Define this to get warning messages when optimisations can't be used. */
8
12
  /* #define OPTIMISATION_WARNINGS */
9
13
 
14
+ // Show how sparse the N-way dispatches in the among tables are:
15
+ // #define REPORT_SPARSE_AMONG_NWAYS
16
+
17
+ // Flag bit in among result codes which indicates an "Among Function Scenario".
18
+ #define AFS_FLAG 0x4000
19
+
10
20
  /* prototype functions for recursive use: */
11
21
 
12
22
  static void generate(struct generator * g, struct node * p);
13
23
  static void w(struct generator * g, const char * s);
14
24
  static void writef(struct generator * g, const char * s, struct node * p);
15
25
 
26
+ static bool can_error_(struct node * p);
27
+
28
+ // `can_error(routine_name)` returns true if the C implementation of
29
+ // `routine_name` can return -1. -1 indicates an internal or system type error
30
+ // (e.g. memory allocation failed or the slice wasn't valid) for C (for C++
31
+ // we instead throw exceptions for such cases).
32
+ static bool can_error(struct name * n) {
33
+ return can_error_(n->definition->left);
34
+ }
35
+
36
+ static bool can_error_(struct node * p) {
37
+ while (p) {
38
+ switch (p->type) {
39
+ case c_sliceto:
40
+ case c_assignto:
41
+ case c_slicefrom:
42
+ case c_attach:
43
+ case c_insert:
44
+ case c_stringassign:
45
+ return true;
46
+ case c_call:
47
+ // Recursive functions are rare so just assume the worst.
48
+ if (p->name->recursive) return true;
49
+ if (can_error(p->name)) return true;
50
+ break;
51
+ case c_among: {
52
+ struct among * x = p->among;
53
+ if (x->unique_function_count == 0) break;
54
+ for (int i = 0; i < x->literalstring_count; ++i) {
55
+ struct name * function = x->v[i].function;
56
+ if (function && can_error(function)) return true;
57
+ }
58
+ break;
59
+ }
60
+ }
61
+ if (p->left && can_error_(p->left)) return true;
62
+ if (p->aux && can_error_(p->aux)) return true;
63
+ p = p->right;
64
+ }
65
+ return false;
66
+ }
67
+
16
68
  /* Write routines for items from the syntax tree */
17
69
 
18
70
  static void write_relop(struct generator * g, int relop) {
@@ -38,6 +90,31 @@ static void write_varname(struct generator * g, struct name * p) {
38
90
  */
39
91
  write_char(g, "sbirxg"[p->type]);
40
92
  write_char(g, '_');
93
+ if (g->options->target_lang == LANG_C) {
94
+ if (SIZE(p->s) > C_MAX_ID_LEN - 2) {
95
+ // We aim to generate C90 code, and C90 only guarantees 31
96
+ // significant initial characters in internal identifiers.
97
+ // C99 raised this to 63, and modern implementations are
98
+ // likely to have a high limit or not impose one, but it
99
+ // is easy to generate an identifier based on the number
100
+ // instead. A Snowball name must start with a letter so
101
+ // this can't collide.
102
+ write_int(g, p->count);
103
+ return;
104
+ }
105
+ } else {
106
+ assert(g->options->target_lang == LANG_CPLUSPLUS);
107
+ for (int i = SIZE(p->s) - 1; i > 0; --i) {
108
+ if (p->s[i] == '_' && p->s[i - 1] == '_') {
109
+ // C++ reserves identifiers containing a double underscore
110
+ // so generate an identifier based on the number instead.
111
+ // A Snowball name must start with a letter so this can't
112
+ // collide.
113
+ write_int(g, p->count);
114
+ return;
115
+ }
116
+ }
117
+ }
41
118
  }
42
119
  write_s(g, p->s);
43
120
  }
@@ -65,27 +142,94 @@ static void wlitch(struct generator * g, int ch) {
65
142
  }
66
143
  }
67
144
 
68
- static void wlitarray(struct generator * g, symbol * p) { /* write literal array */
69
- write_string(g, "{ ");
145
+ static void wlitarray(struct generator * g, const symbol * p) { /* write literal array */
146
+ if (SIZE(p) <= 8) {
147
+ // Short literals are common (even after string pooling) so write them
148
+ // on one line to improve readability of the generated code.
149
+ write_string(g, "{ ");
150
+ for (int i = 0; i < SIZE(p); i++) {
151
+ if (i) write_string(g, ", ");
152
+ wlitch(g, p[i]);
153
+ }
154
+ write_string(g, " }");
155
+ return;
156
+ }
157
+
158
+ w(g, "{~N~+~M");
70
159
  for (int i = 0; i < SIZE(p); i++) {
71
- if (i) write_string(g, ", ");
160
+ if (i % 8 == 0) {
161
+ if (i) w(g, ",~N~M");
162
+ } else {
163
+ write_string(g, ", ");
164
+ }
72
165
  wlitch(g, p[i]);
73
166
  }
74
- write_string(g, " }");
167
+ w(g, "~N~-~M}");
75
168
  }
76
169
 
77
- static void wlitref(struct generator * g, symbol * p) { /* write ref to literal array */
78
- if (SIZE(p) == 0) {
170
+ static void wlitref(struct generator * g, const symbol * p) { /* write ref to literal array */
171
+ int len = SIZE(p);
172
+ if (len == 0) {
79
173
  write_char(g, '0');
80
- } else {
81
- struct str * s = g->outbuf;
82
- g->outbuf = g->declarations;
83
- write_string(g, "static const symbol s_"); write_int(g, g->literalstring_count); write_string(g, "[] = ");
84
- wlitarray(g, p);
85
- write_string(g, ";\n");
86
- g->outbuf = s;
87
- write_string(g, "s_"); write_int(g, g->literalstring_count);
88
- g->literalstring_count++;
174
+ return;
175
+ }
176
+
177
+ // We want to avoid generating duplicate literals (because the C/C++
178
+ // compiler can't easily merge them because they're required to have
179
+ // different addresses - the compiler would need to prove that nothing
180
+ // relies on the addresses being different, but the pointers are
181
+ // passed to the runtime code in a different compilation unit, so
182
+ // it clearly can't unless -flto is used, and not necessarily even
183
+ // then).
184
+ //
185
+ // Similarly, we also want to merge literals where one is a prefix of
186
+ // another.
187
+ //
188
+ // Currently we use a naive algorithm which scans all the literals
189
+ // we've allocated so far for each new literal string. This will be
190
+ // O(n²) but for real world Snowball stemmers n is a few hundred at
191
+ // most so this is plenty fast enough in practice.
192
+ struct c_literalstring * s = g->c_literalstrings;
193
+ struct c_literalstring ** append_to = &(g->c_literalstrings);
194
+ int n = 0;
195
+ while (s) {
196
+ int s_len = SIZE(s->b);
197
+ int min_len = len < s_len ? len : s_len;
198
+ if (memcmp(p, s->b, min_len * sizeof(symbol)) == 0) {
199
+ if (len > s_len) {
200
+ // Replace entry with one which is longer but has the
201
+ // same prefix.
202
+ s->b = p;
203
+ }
204
+ break;
205
+ }
206
+ ++n;
207
+ append_to = &s->next;
208
+ s = s->next;
209
+ }
210
+ if (!s) {
211
+ NEW(c_literalstring, elt);
212
+ elt->next = NULL;
213
+ elt->b = p;
214
+ *append_to = elt;
215
+ }
216
+ write_string(g, "s_");
217
+ write_int(g, n);
218
+ }
219
+
220
+ // Used to generate the coverage logging code when -coverage is used.
221
+ static void write_c_string_literal(struct generator * g, const symbol * s) {
222
+ for (int i = 0; i != SIZE(s); ++i) {
223
+ symbol ch = s[i];
224
+ if (32 <= ch && ch < 127) {
225
+ if (ch == '\"' || ch == '\\') {
226
+ write_char(g, '\\');
227
+ }
228
+ write_char(g, ch);
229
+ } else {
230
+ write_char(g, '\\');
231
+ write_octal3(g, ch);
232
+ }
89
233
  }
90
234
  }
91
235
 
@@ -221,32 +365,6 @@ static void writef(struct generator * g, const char * input, struct node * p) {
221
365
  write_s(g, g->B[j]);
222
366
  continue;
223
367
  }
224
- case 'F': { // Among function dispatcher.
225
- struct among * x = p->among;
226
- if (x->function_count == 0) {
227
- write_char(g, '0');
228
- continue;
229
- }
230
-
231
- if (x->function_count == 1) {
232
- // Only one different function used in this among.
233
- struct amongvec * v = x->b;
234
- for (int j = 0; j < x->literalstring_count; j++) {
235
- if (v[j].function) {
236
- write_varref(g, v[j].function);
237
- goto continue_outer_loop;
238
- }
239
- }
240
- fprintf(stderr, "function_count == 1 but no among functions\n");
241
- exit(1);
242
- continue_outer_loop:
243
- continue;
244
- }
245
-
246
- w(g, "af_");
247
- write_int(g, x->number);
248
- continue;
249
- }
250
368
  case 'I':
251
369
  case 'J':
252
370
  case 'c': {
@@ -309,7 +427,13 @@ static void w(struct generator * g, const char * s) {
309
427
  static void write_propagating_error(struct generator * g, const char * s,
310
428
  int keep_c,
311
429
  struct node *p) {
430
+ bool error_possible = true;
312
431
  if (g->options->target_lang == LANG_CPLUSPLUS) {
432
+ error_possible = false;
433
+ } else if (p->type == c_call && !can_error(p->name)) {
434
+ error_possible = false;
435
+ }
436
+ if (!error_possible) {
313
437
  if (keep_c) {
314
438
  write_block_start(g);
315
439
  w(g, "~Mint saved_c = z->c;~N");
@@ -1129,7 +1253,8 @@ static void generate_call(struct generator * g, struct node * p) {
1129
1253
  if (just_return_on_fail(g)) {
1130
1254
  write_block_start(g);
1131
1255
  writef(g, "~Mint ret = ~V(z);~N", p);
1132
- if (g->options->target_lang == LANG_CPLUSPLUS) {
1256
+ if (g->options->target_lang == LANG_CPLUSPLUS ||
1257
+ !can_error(p->name)) {
1133
1258
  writef(g, "~Mif (ret == 0) return ret;~N", p);
1134
1259
  } else {
1135
1260
  /* For C, we need to propagate both failures and runtime errors so
@@ -1147,7 +1272,8 @@ static void generate_call(struct generator * g, struct node * p) {
1147
1272
  write_propagating_error(g, "~V(z)", false, p);
1148
1273
  writef(g, "~M~f~N", p);
1149
1274
  } else {
1150
- if (g->options->target_lang == LANG_CPLUSPLUS) {
1275
+ if (g->options->target_lang == LANG_CPLUSPLUS ||
1276
+ !can_error(p->name)) {
1151
1277
  writef(g, "~Mif (!~V(z)) ~f~N", p);
1152
1278
  } else {
1153
1279
  write_block_start(g);
@@ -1234,7 +1360,10 @@ static void generate_define(struct generator * g, struct node * p) {
1234
1360
  }
1235
1361
  writef(g, "int ~V(struct SN_env * z) {~N~+", p);
1236
1362
 
1237
- if (q->amongvar_needed) {
1363
+ // The among implementation we use for C requires among_var for any
1364
+ // among with functions.
1365
+ if ((g->options->coverage ? q->has_among : q->has_among_function) ||
1366
+ amongvar_needed(p->left)) {
1238
1367
  w(g, "~Mint among_var;~N");
1239
1368
  }
1240
1369
 
@@ -1263,6 +1392,44 @@ static void generate_define(struct generator * g, struct node * p) {
1263
1392
  }
1264
1393
  }
1265
1394
 
1395
+ if (g->options->coverage && q->type == t_external) {
1396
+ w(g, "~Mstatic int coverage_emitted = 0;~N");
1397
+ w(g, "~Mif (!coverage_emitted) {~N~+");
1398
+ w(g, "~Mcoverage_emitted = 1;~N");
1399
+ for (struct among * x = g->analyser->amongs; x; x = x->next) {
1400
+ if (!x->used) continue;
1401
+ g->S[0] = g->analyser->tokeniser->file;
1402
+ g->I[1] = x->number;
1403
+ if (!x->always_matches) {
1404
+ /* If the among matches the empty string without a gating
1405
+ * function then the "no match" case is impossible and so not
1406
+ * useful to include in a coverage report.
1407
+ */
1408
+ g->I[0] = x->node->line_number,
1409
+ w(g, "~Mfputs(\"~S0:~I0: among ~I1 no match\\n\", stderr);~N");
1410
+ }
1411
+ g->I[3] = x->literalstring_count;
1412
+ for (int c = 0; c < x->literalstring_count; c++) {
1413
+ /* Report every case once, then unused cases will appear (and
1414
+ * we can decrement each count when generating the coverage
1415
+ * report).
1416
+ */
1417
+ const struct amongvec * e = x->v + c;
1418
+ g->I[0] = e->line_number;
1419
+ g->I[2] = e->string_index;
1420
+ w(g, "~Mfputs(\"~S0:~I0: among ~I1 : ~I2 of ~I3 string '");
1421
+ write_c_string_literal(g, e->b);
1422
+ w(g, "'\\n\", stderr);~N");
1423
+ if (e->function) {
1424
+ w(g, "~Mfputs(\"~S0:~I0: among ~I1 : ~I2 of ~I3 func-f '");
1425
+ write_c_string_literal(g, e->b);
1426
+ w(g, "'\\n\", stderr);~N");
1427
+ }
1428
+ }
1429
+ }
1430
+ w(g, "~-~M}~N");
1431
+ }
1432
+
1266
1433
  g->next_label = 0;
1267
1434
  g->var_number = 0;
1268
1435
 
@@ -1286,13 +1453,17 @@ static void generate_functionend(struct generator * g, struct node * p) {
1286
1453
  w(g, "~Mreturn 1;~N");
1287
1454
  }
1288
1455
 
1456
+ static int among_mode(struct among * x) {
1457
+ return (x->substring ? x->substring : x->node)->mode;
1458
+ }
1459
+
1289
1460
  static void generate_substring(struct generator * g, struct node * p) {
1290
1461
  write_comment(g, p);
1291
1462
 
1292
1463
  struct among * x = p->among;
1293
1464
  int block = -1;
1294
1465
  unsigned int bitmap = 0;
1295
- struct amongvec * among_cases = x->b;
1466
+ struct amongvec * among_cases = x->v;
1296
1467
  int empty_case = -1;
1297
1468
  int n_cases = 0;
1298
1469
  symbol cases[2];
@@ -1300,7 +1471,6 @@ static void generate_substring(struct generator * g, struct node * p) {
1300
1471
 
1301
1472
  g->S[0] = p->mode == m_forward ? "" : "_b";
1302
1473
  g->I[0] = x->number;
1303
- g->I[1] = x->literalstring_count;
1304
1474
 
1305
1475
  /* In forward mode with non-ASCII UTF-8 characters, the first byte
1306
1476
  * of the string will often be the same, so instead look at the last
@@ -1398,11 +1568,208 @@ static void generate_substring(struct generator * g, struct node * p) {
1398
1568
  #endif
1399
1569
  }
1400
1570
 
1401
- if (x->amongvar_needed) {
1402
- writef(g, "~Mamong_var = find_among~S0(z, a_~I0, ~I1, ~F);~N", p);
1571
+ if (g->options->coverage || x->amongvar_needed || x->function_count) {
1572
+ if (x->c0_used) {
1573
+ write_block_start(g);
1574
+ w(g, "~Mint c0 = z->c;~N");
1575
+ }
1576
+ writef(g, "~Mamong_var = find_among~S0(z, a_~I0);~N", p);
1577
+ if (x->function_count) {
1578
+ // The C/C++ find_among()/find_among_b() helper function doesn't
1579
+ // call among functions itself, but instead returns an among
1580
+ // function scenario (AFS) code.
1581
+ //
1582
+ // For an among with functions, we generate C code which handles an
1583
+ // AFS code by calling the appropriate among function. If it
1584
+ // signals t then among_var is set appropriately and we continue
1585
+ // as normal. If it fails it adjusts among_var and the cursor;
1586
+ // if there is a chain of among functions (e.g. lovins.sbl has two
1587
+ // such chains of length 5) then it loops to follow along the
1588
+ // chain, checking further among functions until either one
1589
+ // succeeds or it reaches the end of the chain.
1590
+ //
1591
+ // This approach minimises calling of among functions, which is
1592
+ // good since an among function can be arbitrarily expensive (we
1593
+ // will call the same among functions as the original among
1594
+ // implementation did). It also avoids needing a dispatch function
1595
+ // or dynamic load-time relocations.
1596
+ int among_function_chains = false;
1597
+ for (int i = 0; i < x->af_count; ++i) {
1598
+ struct among_function_scenario * scenario = &x->af[i];
1599
+ if ((scenario->t_result & AFS_FLAG) ||
1600
+ (scenario->f_result & AFS_FLAG)) {
1601
+ among_function_chains = true;
1602
+ break;
1603
+ }
1604
+ }
1605
+ int mask = x->af_count - 1;
1606
+ if (mask != 0) {
1607
+ // Use smallest all-1 mask that works.
1608
+ mask |= mask >> 1;
1609
+ mask |= mask >> 2;
1610
+ mask |= mask >> 4;
1611
+ mask |= mask >> 8;
1612
+ }
1613
+ w(g, "~Mif ((among_var & 0x");
1614
+ write_hex4(g, AFS_FLAG);
1615
+ if (among_function_chains || mask == 0) {
1616
+ // If there are among function chains then we wrap in:
1617
+ //
1618
+ // do {
1619
+ // ...
1620
+ // break;
1621
+ // } while (1);
1622
+ //
1623
+ // and use `continue;` in the case where we need to follow a
1624
+ // chain.
1625
+ //
1626
+ // If (mask == 0), there's only one among function scenario
1627
+ // so we don't emit the `switch` and instead wrap in a dummy
1628
+ // loop so that `break;` still works:
1629
+ //
1630
+ // do {
1631
+ // ...
1632
+ // } while (0);
1633
+ w(g, ")) do {~N~+");
1634
+ } else {
1635
+ w(g, ")) {~N~+");
1636
+ }
1637
+ w(g, "~Mint c = z->c;~N");
1638
+ assert(x->af_count <= AFS_FLAG);
1639
+ if (mask != 0) {
1640
+ // Don't emit a switch if there's only one case.
1641
+ w(g, "~Mswitch (among_var & 0x");
1642
+ write_hex(g, mask);
1643
+ w(g, ") {~N~+");
1644
+ }
1645
+ for (int i = 0; i < x->af_count; ++i) {
1646
+ struct among_function_scenario * scenario = &x->af[i];
1647
+ struct name * q = scenario->function;
1648
+ if (q == NULL) {
1649
+ // With `-coverage` the AFS index is an among_vec index,
1650
+ // with unused entries indicated by ->function == NULL.
1651
+ assert(g->options->coverage);
1652
+ continue;
1653
+ }
1654
+ int cursor_adjustment = scenario->cursor_adjustment;
1655
+ int t_result = scenario->t_result;
1656
+ int f_result = scenario->f_result;
1657
+ g->I[0] = i;
1658
+ if (mask != 0) {
1659
+ w(g, "~Mcase ~I0: {~+~N");
1660
+ }
1661
+ w(g, "~Mint ret = ");
1662
+ write_varref(g, q);
1663
+ w(g, "(z);~N");
1664
+
1665
+ // ret > 0: function signalled t.
1666
+ w(g, "~Mif (ret > 0) { ");
1667
+ if (K_needed(q->definition)) {
1668
+ // Restore cursor if routine may have changed it.
1669
+ w(g, "z->c = c; ");
1670
+ }
1671
+ assert((t_result & AFS_FLAG) == 0);
1672
+ g->I[0] = t_result;
1673
+ w(g, "among_var = ~I0; break; }~N");
1674
+ if (g->options->target_lang == LANG_C && can_error(q)) {
1675
+ // The original C among implementation swallowed an error
1676
+ // return from an among function. In practice, none of
1677
+ // the shipped algorithms use among functions which can
1678
+ // error, but with the new among approach we can statically
1679
+ // check if the among function can error and only emit code
1680
+ // to handle it if it can happen.
1681
+ w(g, "~Mif (ret < 0) return ret;~N");
1682
+ }
1683
+ if (g->options->coverage) {
1684
+ const struct amongvec * e = x->v + i;
1685
+ g->S[0] = g->analyser->tokeniser->file;
1686
+ g->I[0] = e->line_number;
1687
+ g->I[1] = x->number;
1688
+ g->I[2] = e->string_index;
1689
+ g->I[3] = x->literalstring_count;
1690
+ w(g, "~Mfputs(\"~S0:~I0: among ~I1 : ~I2 of ~I3 func-f '");
1691
+ write_c_string_literal(g, e->b);
1692
+ w(g, "'\\n\", stderr);~N");
1693
+ }
1694
+ g->I[2] = cursor_adjustment;
1695
+ g->I[3] = f_result;
1696
+ g->S[0] = (among_mode(x) == m_forward) ? "+" : "-";
1697
+ if (f_result) {
1698
+ assert(cursor_adjustment >= 0);
1699
+ if (cursor_adjustment > 0) {
1700
+ w(g, "~Mz->c = c0 ~S0 ~I2;~N");
1701
+ }
1702
+ } else {
1703
+ // among_var == 0 means the among signals f and the cursor
1704
+ // will get restored when that signal is handled.
1705
+ assert(cursor_adjustment == -1);
1706
+ }
1707
+ w(g, "~Mamong_var = ~I3;~N");
1708
+ if ((f_result & AFS_FLAG)) {
1709
+ assert(among_function_chains);
1710
+ w(g, "~Mcontinue;~N");
1711
+ } else if (mask != 0) {
1712
+ w(g, "~Mbreak;~N");
1713
+ }
1714
+ if (mask != 0) {
1715
+ w(g, "~-~M}~N");
1716
+ }
1717
+ }
1718
+ if (mask != 0) {
1719
+ w(g, "~-~M}~N");
1720
+ }
1721
+ if (among_function_chains) {
1722
+ w(g, "~Mbreak;~N~-"
1723
+ "~M} while (1);~N");
1724
+ } else if (mask == 0) {
1725
+ w(g, "~-"
1726
+ "~M} while (0);~N");
1727
+ } else {
1728
+ w(g, "~-"
1729
+ "~M}~N");
1730
+ }
1731
+ // Note: In general the same function may be called by more than
1732
+ // one scenario (e.g. from different among actions with the same
1733
+ // gating function).
1734
+ }
1735
+ if (g->options->coverage) {
1736
+ // With -coverage enabled, we build the among table to return a
1737
+ // unique value for each among string, and generate a table to map
1738
+ // that to the among_var value.
1739
+ g->S[0] = g->analyser->tokeniser->file;
1740
+ g->I[1] = x->number;
1741
+ write_block_start(g);
1742
+ w(g, "~Mstatic const int t[] = { 0");
1743
+ for (int c = 0; c < x->literalstring_count; ++c) {
1744
+ write_string(g, ", ");
1745
+ write_int(g, among_cases[c].result);
1746
+ }
1747
+ w(g, " };~N");
1748
+ w(g, "~Mswitch (among_var) {~N~+");
1749
+ g->I[0] = x->node->line_number,
1750
+ w(g, "~Mcase 0: fputs(\"~S0:~I0: among ~I1 no match\\n\", stderr); break;~N");
1751
+ g->I[3] = x->literalstring_count;
1752
+ for (int c = 0; c < x->literalstring_count; ++c) {
1753
+ const struct amongvec * e = x->v + c;
1754
+ g->I[0] = e->line_number;
1755
+ g->I[2] = e->string_index;
1756
+ w(g, "~Mcase ");
1757
+ write_int(g, c + 1);
1758
+ w(g, ": fputs(\"~S0:~I0: among ~I1 : ~I2 of ~I3 string '");
1759
+ write_c_string_literal(g, e->b);
1760
+ w(g, "'\\n\", stderr); break;~N");
1761
+ }
1762
+ w(g, "~-~M}~N");
1763
+ g->I[0] = AFS_FLAG;
1764
+ w(g, "~Mif (!(among_var & ~I0)) among_var = t[among_var];~N");
1765
+ write_block_end(g);
1766
+ }
1403
1767
  if (!x->always_matches) {
1404
1768
  writef(g, "~Mif (!among_var) ~f~N", p);
1405
1769
  }
1770
+ if (x->c0_used) {
1771
+ write_block_end(g);
1772
+ }
1406
1773
  return;
1407
1774
  }
1408
1775
 
@@ -1421,12 +1788,12 @@ static void generate_substring(struct generator * g, struct node * p) {
1421
1788
  }
1422
1789
 
1423
1790
  if (x->always_matches) {
1424
- writef(g, "~Mfind_among~S0(z, a_~I0, ~I1, ~F);~N", p);
1791
+ writef(g, "~Mfind_among~S0(z, a_~I0);~N", p);
1425
1792
  } else if (x->command_count == 0 && tailcallable(g, p)) {
1426
- writef(g, "~Mreturn find_among~S0(z, a_~I0, ~I1, ~F) != 0;~N", p);
1793
+ writef(g, "~Mreturn find_among~S0(z, a_~I0) != 0;~N", p);
1427
1794
  x->node->right = NULL;
1428
1795
  } else {
1429
- writef(g, "~Mif (!find_among~S0(z, a_~I0, ~I1, ~F)) ~f~N", p);
1796
+ writef(g, "~Mif (!find_among~S0(z, a_~I0)) ~f~N", p);
1430
1797
  }
1431
1798
  }
1432
1799
 
@@ -1568,6 +1935,7 @@ static void generate(struct generator * g, struct node * p) {
1568
1935
 
1569
1936
  static void generate_head(struct generator * g) {
1570
1937
  struct options * o = g->options;
1938
+ bool wide = (o->encoding == ENC_WIDECHARS);
1571
1939
  if (o->cheader) {
1572
1940
  int quoted = (o->cheader[0] == '<' || o->cheader[0] == '"');
1573
1941
  w(g, "#include ");
@@ -1593,11 +1961,22 @@ static void generate_head(struct generator * g) {
1593
1961
  w(g, "#include <limits.h>~N");
1594
1962
  }
1595
1963
  w(g, "#include <stddef.h>~N~N");
1964
+ if (g->options->coverage) {
1965
+ w(g, "#include <stdio.h>~N");
1966
+ }
1596
1967
 
1597
1968
  if (o->target_lang == LANG_CPLUSPLUS) {
1598
1969
  w(g, "~Mtypedef ");
1599
1970
  write_string(g, o->package);
1600
1971
  w(g, "::~n::SN_local SN_local;~N~N");
1972
+
1973
+ if (g->analyser->amongs && !wide) {
1974
+ w(g, "~M#ifdef SNOWBALL_BIGENDIAN~N");
1975
+ w(g, "~M#define S(W) ((0x##W & 0xff) << 8 | 0x##W >> 8)~N");
1976
+ w(g, "~M#else~N");
1977
+ w(g, "~M#define S(W) (0x##W)~N");
1978
+ w(g, "~M#endif~N~N");
1979
+ }
1601
1980
  return;
1602
1981
  }
1603
1982
 
@@ -1627,7 +2006,7 @@ static void generate_head(struct generator * g) {
1627
2006
 
1628
2007
  for (struct name * name = g->analyser->names; name; name = name->next) {
1629
2008
  if (!name->local_to && name->type == t_boolean) {
1630
- if (g->options->target_lang == LANG_CPLUSPLUS) {
2009
+ if (o->target_lang == LANG_CPLUSPLUS) {
1631
2010
  w(g, "~Mbool ");
1632
2011
  } else {
1633
2012
  w(g, "~Munsigned char ");
@@ -1647,12 +2026,20 @@ static void generate_head(struct generator * g) {
1647
2026
 
1648
2027
  w(g, "~-~M};~N~N");
1649
2028
 
1650
- if (g->options->target_lang == LANG_C) {
2029
+ if (o->target_lang == LANG_C) {
1651
2030
  w(g, "typedef struct SN_local SN_local;~N~N");
1652
2031
  }
1653
2032
  }
1654
2033
 
1655
- const char * vp = g->options->variables_prefix;
2034
+ if (g->analyser->amongs && !wide) {
2035
+ w(g, "~M#ifdef SNOWBALL_BIGENDIAN~N");
2036
+ w(g, "~M#define S(W) ((0x##W & 0xff) << 8 | 0x##W >> 8)~N");
2037
+ w(g, "~M#else~N");
2038
+ w(g, "~M#define S(W) (0x##W)~N");
2039
+ w(g, "~M#endif~N~N");
2040
+ }
2041
+
2042
+ const char * vp = o->variables_prefix;
1656
2043
  if (vp) {
1657
2044
  for (struct name * q = g->analyser->names; q; q = q->next) {
1658
2045
  if (q->local_to) continue;
@@ -1680,7 +2067,7 @@ static void generate_head(struct generator * g) {
1680
2067
  "}~N~N");
1681
2068
  break;
1682
2069
  case t_boolean:
1683
- if (g->options->target_lang == LANG_CPLUSPLUS) {
2070
+ if (o->target_lang == LANG_CPLUSPLUS) {
1684
2071
  w(g, "extern bool ");
1685
2072
  } else {
1686
2073
  w(g, "extern int ");
@@ -1727,98 +2114,602 @@ static void generate_routine_declarations(struct generator * g) {
1727
2114
  }
1728
2115
  }
1729
2116
 
1730
- static void generate_among_table(struct generator * g, struct among * x) {
1731
- write_newline(g);
1732
- write_comment(g, x->node);
1733
-
1734
- struct amongvec * v = x->b;
1735
-
1736
- g->I[0] = x->number;
1737
- for (int i = 0; i < x->literalstring_count; i++) {
1738
- if (v[i].size) {
1739
- g->I[1] = i;
1740
- g->I[2] = v[i].size;
1741
- w(g, "static const symbol s_~I0_~I1[~I2] = ");
1742
- wlitarray(g, v[i].b);
1743
- w(g, ";~N");
2117
+ static symbol xfix_ch(struct amongvec * v, int i, bool forwards) {
2118
+ assert(i < v->size);
2119
+ return v->b[forwards ? i : v->size - 1 - i];
2120
+ }
2121
+
2122
+ // The amongvec is sorted such that common suffix/prefix strings are
2123
+ // consecutive - more precisely:
2124
+ // * if `forwards`, by byte string order of the prefixes;
2125
+ // * if `!forwards`, by byte string order of the reversed suffixes.
2126
+ // We take advantage of this and pass in a range of entries (via start index
2127
+ // `lo` and end index `hi`) and just look at that range, shrinking it for
2128
+ // recursive calls.
2129
+ //
2130
+ // "xfix" is the suffix or prefix depending on the direction.
2131
+ static int build_among_table_(struct generator * g, struct among * x,
2132
+ int lo, int hi, int xfix_len,
2133
+ int forwards, int longest_sub) {
2134
+ struct amongvec * v = x->v;
2135
+ bool wide = (g->options->encoding == ENC_WIDECHARS);
2136
+
2137
+ assert(lo <= hi);
2138
+ assert(xfix_len >= 0);
2139
+ assert(xfix_len <= v[lo].size);
2140
+ for (int i = lo + 1; i <= hi; ++i) {
2141
+ assert(xfix_len < v[i].size);
2142
+ symbol * b0 = v[lo].b;
2143
+ symbol * b = v[i].b;
2144
+ if (!forwards) {
2145
+ b0 += v[lo].size - xfix_len;
2146
+ b += v[i].size - xfix_len;
2147
+ }
2148
+ assert(memcmp(b0, b, xfix_len * sizeof(symbol)) == 0);
2149
+ }
2150
+
2151
+ int exact = 0;
2152
+ if (v[lo].size == xfix_len) {
2153
+ // The current prefix/suffix is exactly present in this among.
2154
+ struct amongvec * v_exact = v + lo;
2155
+ exact = g->options->coverage ? lo + 1 : v_exact->result;
2156
+ if (exact < 0) exact = AFS_FLAG - 1;
2157
+ assert(exact != 0);
2158
+ if (v_exact->function_index) {
2159
+ int cursor_adjustment;
2160
+ if (v_exact->i < 0) {
2161
+ cursor_adjustment = -1;
2162
+ } else {
2163
+ cursor_adjustment = v[v_exact->i].size;
2164
+ }
2165
+ // If the among function signals t, the among result is t_result
2166
+ // (i.e. variable `exact`)
2167
+ // If the among function signals f:
2168
+ // * If cursor_adjustment == -1 then f_result is 0 as is the among
2169
+ // result (i.e. no match)
2170
+ // * Otherwise:
2171
+ // + cursor_adjustment is applied to the cursor value on entry
2172
+ // (add for forwards; subtract for backwards)
2173
+ // + the among result is f_result
2174
+
2175
+ struct name * function = v_exact->function;
2176
+ int t_result = exact;
2177
+ int f_result = longest_sub;
2178
+ if (g->options->coverage) {
2179
+ // With -coverage use the among_vec index as the AFS index.
2180
+ exact = lo;
2181
+ if (exact >= x->af_count) x->af_count = exact + 1;
2182
+ x->af[exact].function = function;
2183
+ x->af[exact].t_result = t_result;
2184
+ x->af[exact].f_result = f_result;
2185
+ x->af[exact].cursor_adjustment = cursor_adjustment;
2186
+ x->c0_used = x->c0_used || (f_result && cursor_adjustment > 0);
2187
+ } else {
2188
+ bool add = true;
2189
+ for (int i = 0; i < x->af_count; ++i) {
2190
+ if (x->af[i].function == function &&
2191
+ x->af[i].t_result == t_result &&
2192
+ x->af[i].f_result == f_result &&
2193
+ x->af[i].cursor_adjustment == cursor_adjustment) {
2194
+ exact = i;
2195
+ add = false;
2196
+ break;
2197
+ }
2198
+ }
2199
+ if (add) {
2200
+ x->af[x->af_count].function = function;
2201
+ x->af[x->af_count].t_result = t_result;
2202
+ x->af[x->af_count].f_result = f_result;
2203
+ x->af[x->af_count].cursor_adjustment = cursor_adjustment;
2204
+ x->c0_used = x->c0_used || (f_result && cursor_adjustment > 0);
2205
+ exact = x->af_count++;
2206
+ }
2207
+ }
2208
+ exact |= AFS_FLAG;
2209
+ }
2210
+ if (lo == hi) {
2211
+ return -exact;
1744
2212
  }
2213
+ ++lo;
1745
2214
  }
1746
2215
 
1747
- g->I[1] = x->literalstring_count;
1748
- if (g->options->coverage) {
1749
- g->I[1] = g->I[1] * 2 + 1;
1750
- }
1751
- w(g, "~Mstatic const struct among a_~I0[~I1] = {~N");
2216
+ int offset = SIZE(x->table);
1752
2217
 
1753
- for (int i = 0; i < x->literalstring_count; i++) {
1754
- if (i) w(g, ",~N");
1755
- g->I[1] = i;
1756
- g->I[2] = v[i].size;
1757
- g->I[3] = (v[i].i >= 0 ? v[i].i - i : 0);
1758
- g->I[4] = v[i].result;
1759
- g->I[5] = v[i].function_index;
2218
+ symbol min = xfix_ch(v + lo, xfix_len, forwards);
2219
+ symbol max = xfix_ch(v + hi, xfix_len, forwards);
1760
2220
 
1761
- if (g->options->comments) {
1762
- w(g, "/*~J1 */ ");
2221
+ if (min == max) {
2222
+ // All entries with the current prefix/suffix have the same next byte.
2223
+ // Check following bytes until we find where that stops being the
2224
+ // case.
2225
+ int old_xfix_len = xfix_len;
2226
+ int size_limit = v[lo].size < v[hi].size ? v[lo].size : v[hi].size;
2227
+ while (++xfix_len < size_limit) {
2228
+ symbol lo_ch = xfix_ch(v + lo, xfix_len, forwards);
2229
+ symbol hi_ch = xfix_ch(v + hi, xfix_len, forwards);
2230
+ if (lo_ch != hi_ch) break;
1763
2231
  }
1764
- w(g, "{ ~I2, ");
1765
- if (v[i].size == 0) {
1766
- w(g, "0,");
2232
+
2233
+ // We only encode a segment of length two or more, since a 1-way switch
2234
+ // is one word shorter and slightly easier to decode than segment of
2235
+ // length 1.
2236
+ int segment_len = xfix_len - old_xfix_len;
2237
+ if (!wide && segment_len > 255) {
2238
+ // We could encode this by splitting the segment, but it's not a
2239
+ // limit we're realistically going to get anywhere near in a real
2240
+ // stemming algorithm.
2241
+ printf("Sorry, we don't currently support an among segment > 255 bytes in non-wide mode\n");
2242
+ exit(1);
2243
+ }
2244
+ if (segment_len > 1) {
2245
+ // Emit a segment to check for.
2246
+ //
2247
+ // non-wide:
2248
+ //
2249
+ // 0 2 RES_IES <'i' 'e' packed into bytes>
2250
+ // ^exact ^length
2251
+ // (top byte 0) ^--- NB this is negated for exact
2252
+ //
2253
+ // wide:
2254
+ //
2255
+ // 0 2 0 RES_IES 'i' 'e'
2256
+ // ^exact ^length
2257
+ // ^--- NB this is negated for exact
2258
+ if (exact) longest_sub = exact;
2259
+ int entry_len = wide ? segment_len + 4 : ((segment_len + 1) >> 1) + 3;
2260
+ int new_size = SIZE(x->table) + entry_len;
2261
+ x->table = resize_b(x->table, new_size);
2262
+ if (!wide)
2263
+ x->table_endianness = resize_s(x->table_endianness, new_size);
2264
+ x->table[offset] = exact;
2265
+ x->table[offset + 1] = segment_len;
2266
+ if (wide) {
2267
+ x->table[offset + 2] = 0;
2268
+ }
2269
+ if (min > max) {
2270
+ // exact can only be zero here if there is nothing in the among
2271
+ // with the specified prefix, which shouldn't happen.
2272
+ assert(exact);
2273
+ x->table[offset + (wide ? 3 : 2)] = -exact;
2274
+ } else {
2275
+ int ptr = build_among_table_(g, x, lo, hi, xfix_len, forwards,
2276
+ exact ? exact : longest_sub);
2277
+ x->table[offset + (wide ? 3 : 2)] = ptr;
2278
+ }
2279
+ symbol * from = v[lo].b;
2280
+ if (forwards) from += old_xfix_len; else from += v[lo].size - old_xfix_len - segment_len;
2281
+ if (wide) {
2282
+ symbol * to = &(x->table[offset + 4]);
2283
+ for (int i = 0; i < segment_len; ++i) {
2284
+ to[i] = from[i];
2285
+ }
2286
+ } else {
2287
+ symbol * to = &(x->table[offset + 3]);
2288
+ for (int i = 1; i < segment_len; i += 2) {
2289
+ *to++ = from[i - 1] | (from[i] << 8);
2290
+ }
2291
+ if (segment_len & 1) {
2292
+ *to = from[segment_len - 1];
2293
+ }
2294
+ // Flag segment data as needing byteswapping on big-endian
2295
+ // platforms.
2296
+ memset(&x->table_endianness[offset + 3], 1,
2297
+ (segment_len + 1) >> 1);
2298
+ }
2299
+ int len = SIZE(x->table) - offset;
2300
+ for (struct among_subtree * t = x->subtree; t; t = t->next) {
2301
+ if (t->len == len) {
2302
+ if (memcmp(x->table + t->start,
2303
+ x->table + offset,
2304
+ len * sizeof(x->table[0])) == 0) {
2305
+ SET_SIZE(x->table, offset);
2306
+ if (!wide) {
2307
+ SET_SIZE(x->table_endianness, offset);
2308
+ }
2309
+ return t->start;
2310
+ }
2311
+ }
2312
+ }
2313
+ NEW(among_subtree, t);
2314
+ t->start = offset;
2315
+ t->len = len;
2316
+ t->next = x->subtree;
2317
+ x->subtree = t;
2318
+ if (offset >= 0x8000) {
2319
+ printf("%s:%d: generated among table is too large! "
2320
+ "Please open bug against snowball compiler\n",
2321
+ g->analyser->tokeniser->file, x->node->line_number);
2322
+ exit(1);
2323
+ }
2324
+ return offset;
2325
+ }
2326
+ xfix_len = old_xfix_len;
2327
+ }
2328
+
2329
+ assert(min <= max);
2330
+ int min_length_match = INT_MAX;
2331
+ for (int i = lo; i <= hi; i++) {
2332
+ if (v[i].size < min_length_match) min_length_match = v[i].size;
2333
+ }
2334
+ // FIXME: If we stored this in each entry we could sometimes shortcut
2335
+ // knowing there's no way any prefixes/suffixes can match.
2336
+ (void)min_length_match;
2337
+
2338
+ if (exact) longest_sub = exact;
2339
+ int lo1 = lo;
2340
+ int hi0 = hi;
2341
+ if (max > min && hi - lo > 1) {
2342
+ // There are more than two cases and the bounds are not adjacent,
2343
+ // but there might only be two different next characters, which
2344
+ // means this might still be a two-way switch on the next
2345
+ // character.
2346
+ while (hi - lo1 > 1) {
2347
+ symbol ch = xfix_ch(v + lo1 + 1, xfix_len, forwards);
2348
+ if (ch != min) break;
2349
+ ++lo1;
2350
+ }
2351
+ while (hi0 - lo1 > 1) {
2352
+ symbol ch = xfix_ch(v + hi0 - 1, xfix_len, forwards);
2353
+ if (ch != max) break;
2354
+ --hi0;
2355
+ }
2356
+ }
2357
+ if ((max > min && hi0 - lo1 == 1) && min > 0) {
2358
+ // Only the two end values of the range are used. This case is common
2359
+ // (approaching half the ranges we generate) and the most extreme is a
2360
+ // gap of 150 between 'a' and 0xf8. We encode such cases by swapping
2361
+ // the min and max values, and only storing two pointers. This reduces
2362
+ // the table size, which reduces the working set size and so is more
2363
+ // cache friendly.
2364
+ //
2365
+ // Do an 2-way dispatch on the next code-unit:
2366
+ //
2367
+ // non-wide:
2368
+ //
2369
+ // 0 's'|('d'<<8) OFFSET_D OFFSET_S
2370
+ // ^----------^-----NB negated for exact
2371
+ //
2372
+ // wide:
2373
+ //
2374
+ // 0 's' 'd' OFFSET_D OFFSET_S
2375
+ // ^----------^-----NB negated for exact
2376
+ //
2377
+ // This is encoded by swapping the range ends compared to an N-way
2378
+ // dispatch. The edge case where min is zero would collide with the
2379
+ // encoding of a segment, so we encode that as N-way (U+0000 is not
2380
+ // likely to appear in real world stemmer code).
2381
+ int entry_len = 4 + (int)wide;
2382
+ x->table = resize_b(x->table, SIZE(x->table) + entry_len);
2383
+ x->table[offset] = exact;
2384
+
2385
+ int off = offset + 1;
2386
+ if (wide) {
2387
+ x->table[off++] = max;
2388
+ x->table[off++] = min;
2389
+ } else {
2390
+ x->table[off++] = max | (min << 8);
2391
+ }
2392
+ x->table[off++] = build_among_table_(g, x,
2393
+ lo, lo1,
2394
+ xfix_len + 1,
2395
+ forwards,
2396
+ exact ? exact : longest_sub);
2397
+ x->table[off++] = build_among_table_(g, x,
2398
+ hi0, hi,
2399
+ xfix_len + 1,
2400
+ forwards,
2401
+ exact ? exact : longest_sub);
2402
+ } else {
2403
+ // Do an N-way dispatch on the next code-unit:
2404
+ //
2405
+ // non-wide:
2406
+ //
2407
+ // 0 'd'|('s'<<8) OFFSET_D 0 0 ... OFFSET_S
2408
+ // ^-----------------^-----NB negated for exact
2409
+ //
2410
+ // wide:
2411
+ //
2412
+ // 0 'd' 's' OFFSET_D 0 0 ... OFFSET_S
2413
+ // ^-----------------^-----NB negated for exact
2414
+ #ifdef REPORT_SPARSE_AMONG_NWAYS
2415
+ // Report showing sparseness of n-ways:
2416
+ if (max != min) {
2417
+ int n = 0;
2418
+ int l = lo;
2419
+ while (l <= hi) {
2420
+ ++n;
2421
+ symbol ch = xfix_ch(v + l, xfix_len, forwards);
2422
+ int h = l;
2423
+ while (h < hi && ch == xfix_ch(v + h + 1, xfix_len, forwards)) {
2424
+ ++h;
2425
+ }
2426
+ l = h + 1;
2427
+ }
2428
+ printf("+++ NWAY window %d:%d %d of %d %.1f%%:",
2429
+ min, max, n, max - min + 1,
2430
+ 100 * n / (double)(max - min + 1));
2431
+ l = lo;
2432
+ while (l <= hi) {
2433
+ ++n;
2434
+ symbol ch = xfix_ch(v + l, xfix_len, forwards);
2435
+ printf(" 0x%x", ch);
2436
+ int h = l;
2437
+ while (h < hi && ch == xfix_ch(v + h + 1, xfix_len, forwards)) {
2438
+ ++h;
2439
+ }
2440
+ l = h + 1;
2441
+ }
2442
+ printf("\n");
2443
+ }
2444
+ #endif
2445
+ int entry_len = (max - min) + 1 + 2 + (int)wide;
2446
+ x->table = resize_b(x->table, SIZE(x->table) + entry_len);
2447
+ int off = offset;
2448
+ x->table[off++] = exact;
2449
+ if (wide) {
2450
+ x->table[off++] = min;
2451
+ x->table[off++] = max;
1767
2452
  } else {
1768
- w(g, "s_~I0_~I1,");
2453
+ x->table[off++] = min | (max << 8);
2454
+ }
2455
+ for (int i = 0; i < max - min + 1; ++i) {
2456
+ x->table[off + i] = 0;
2457
+ }
2458
+ int l = lo;
2459
+ while (l <= hi) {
2460
+ symbol ch = xfix_ch(v + l, xfix_len, forwards);
2461
+ int h = l;
2462
+ while (h < hi && ch == xfix_ch(v + h + 1, xfix_len, forwards)) {
2463
+ ++h;
2464
+ }
2465
+ int r = build_among_table_(g, x,
2466
+ l, h,
2467
+ xfix_len + 1,
2468
+ forwards,
2469
+ exact ? exact : longest_sub);
2470
+ x->table[off + (ch - min)] = r;
2471
+ l = h + 1;
1769
2472
  }
1770
- w(g, " ~I3, ~I4, ~I5}");
1771
2473
  }
1772
- if (g->options->coverage) {
1773
- w(g, ",~N");
1774
- g->S[1] = g->analyser->tokeniser->file;
1775
- for (int i = 0; i < x->literalstring_count; i++) {
1776
- if (g->options->comments) {
1777
- w(g, "/* coverage */ ");
2474
+
2475
+ int len = SIZE(x->table) - offset;
2476
+ for (struct among_subtree * t = x->subtree; t; t = t->next) {
2477
+ if (t->len == len) {
2478
+ if (memcmp(x->table + t->start,
2479
+ x->table + offset,
2480
+ len * sizeof(x->table[0])) == 0) {
2481
+ SET_SIZE(x->table, offset);
2482
+ return t->start;
1778
2483
  }
1779
- g->I[1] = x->b[i].line_number;
1780
- g->I[2] = x->b[i].string_index;
1781
- w(g, "{ ~I0, (const symbol*)\"~S1:~I1\", 0, ~I2, 0 },~N");
1782
2484
  }
1783
- if (x->always_matches) {
1784
- g->I[0] = -1;
2485
+ }
2486
+ NEW(among_subtree, t);
2487
+ t->start = offset;
2488
+ t->len = len;
2489
+ t->next = x->subtree;
2490
+ x->subtree = t;
2491
+
2492
+ if (offset >= 0x8000) {
2493
+ printf("%s:%d: generated among table is too large! "
2494
+ "Please open bug against snowball compiler\n",
2495
+ g->analyser->tokeniser->file, x->node->line_number);
2496
+ exit(1);
2497
+ }
2498
+ return offset;
2499
+ }
2500
+
2501
+ static void build_among_table(struct generator * g, struct among * x) {
2502
+ // Build a table which encodes an among as a state machine where each
2503
+ // transition is either an O(1) multi-way dispatch on the next
2504
+ // byte/character or a check that a particular string of bytes/characters
2505
+ // follows (in UTF-8 it works in bytes; for fixed-width encodings it works
2506
+ // in characters).
2507
+ if (x->function_count) {
2508
+ // Each among case with a function creates an among function scenario,
2509
+ // but some may be identical in which case they are merged. This means
2510
+ // x->function_count is an upper bound on the number of entries we
2511
+ // need.
2512
+ //
2513
+ // With -coverage we use the amongvec index as the af index.
2514
+ bool coverage = g->options->coverage;
2515
+ NEWVEC(among_function_scenario, af,
2516
+ coverage ? x->literalstring_count : x->function_count);
2517
+ if (coverage) {
2518
+ for (int i = 0; i < x->literalstring_count; ++i) {
2519
+ af[i] = (struct among_function_scenario){0};
2520
+ }
1785
2521
  }
1786
- if (g->options->comments) {
1787
- w(g, "/* coverage */ ");
2522
+ x->af = af;
2523
+ }
2524
+
2525
+ // 512 is large enough for ~90% of amongs.
2526
+ x->table = create_b(512);
2527
+ x->table_endianness = create_s(g->options->encoding == ENC_WIDECHARS ? 1 : 512);
2528
+ int root = build_among_table_(g, x,
2529
+ 0, x->literalstring_count - 1, 0,
2530
+ (among_mode(x) == m_forward), 0);
2531
+ assert(root == 0);
2532
+ }
2533
+
2534
+ static void generate_among_table(struct generator * g, struct among * x) {
2535
+ write_newline(g);
2536
+ write_comment(g, x->node);
2537
+ bool wide = (g->options->encoding == ENC_WIDECHARS);
2538
+ symbol * b = x->table;
2539
+ byte * e = x->table_endianness;
2540
+ g->I[0] = x->number;
2541
+ w(g, "~Mstatic const unsigned short a_~I0[] = {~N~+");
2542
+ write_margin(g);
2543
+ for (int i = 0; i < SIZE(b); i++) {
2544
+ if (i > 0) {
2545
+ if ((i & 7)) {
2546
+ write_string(g, ", ");
2547
+ } else {
2548
+ w(g, ",~N~M");
2549
+ }
2550
+ }
2551
+ if (i < SIZE(e) && e[i]) {
2552
+ write_string(g, "S(");
2553
+ write_hex4(g, (int)b[i]);
2554
+ write_char(g, ')');
2555
+ } else {
2556
+ write_string(g, "0x");
2557
+ write_hex4(g, (int)b[i]);
2558
+ if (!wide) write_char(g, ' ');
1788
2559
  }
1789
- g->I[1] = x->node->line_number;
1790
- w(g, "{ ~I0, (const symbol*)\"~S1:~I1\", 0, 0, 0 },~N");
1791
2560
  }
1792
- w(g, "~N};~N");
2561
+ write_newline(g);
2562
+ w(g, "~-~M};~N");
2563
+ }
1793
2564
 
1794
- if (x->function_count <= 1) return;
2565
+ static void generate_stringliterals(struct generator * g) {
2566
+ struct str * saved_outbuf = g->outbuf;
2567
+ g->outbuf = g->declarations;
1795
2568
 
1796
- w(g, "~N~Mstatic int af_~I0(struct SN_env * z) {~N~+");
1797
- w(g, "~Mswitch (z->af) {~N~+");
1798
- for (int n = 1; n <= x->function_count; n++) {
1799
- w(g, "~Mcase ");
1800
- write_int(g, n);
1801
- w(g, ": return ");
1802
- for (int i = 0; i < x->literalstring_count; i++) {
1803
- if (v[i].function_index == n) {
1804
- write_varref(g, v[i].function);
1805
- w(g, "(z);~N");
1806
- break;
2569
+ int eliminated = 0;
2570
+ int n_s = 0;
2571
+ for (struct c_literalstring * s = g->c_literalstrings; s; s = s->next) {
2572
+ if (s->b) {
2573
+ int n_r = 0;
2574
+ for (struct c_literalstring * r = g->c_literalstrings; r; r = r->next) {
2575
+ if (r->b) {
2576
+ int d = (SIZE(s->b) - SIZE(r->b));
2577
+ if (d > 0) {
2578
+ // Look for r as a substring of s.
2579
+ int len = SIZE(r->b) * sizeof(symbol);
2580
+ for ( ; d; --d) {
2581
+ if (memcmp(s->b + d, r->b, len) == 0) {
2582
+ r->b = NULL;
2583
+ w(g, "~M#define s_");
2584
+ write_int(g, n_r);
2585
+ w(g, " (s_");
2586
+ write_int(g, n_s);
2587
+ w(g, " + ");
2588
+ write_int(g, d);
2589
+ w(g, ")~N");
2590
+ ++eliminated;
2591
+ break;
2592
+ }
2593
+ }
2594
+ }
2595
+ }
2596
+ ++n_r;
1807
2597
  }
1808
2598
  }
2599
+ ++n_s;
1809
2600
  }
1810
- w(g, "~-~M}~N");
1811
- w(g, "~Mreturn -1;~N");
1812
- w(g, "~-~M}~N");
2601
+
2602
+ struct pool_string {
2603
+ symbol * b;
2604
+ int n;
2605
+ };
2606
+ int n_strings = n_s - eliminated;
2607
+ NEWVEC(pool_string, strings, n_strings);
2608
+ {
2609
+ int n = 0, i = 0;
2610
+ struct c_literalstring * s = g->c_literalstrings;
2611
+ while (s) {
2612
+ if (s->b) {
2613
+ if (i >= n_strings) {
2614
+ printf("Miscounted string pool entries\n");
2615
+ exit(1);
2616
+ }
2617
+ strings[i].b = copy_b(s->b);
2618
+ strings[i].n = n;
2619
+ ++i;
2620
+ }
2621
+ ++n;
2622
+ struct c_literalstring * to_free = s;
2623
+ s = s->next;
2624
+ FREE(to_free);
2625
+ }
2626
+ g->c_literalstrings = NULL;
2627
+ assert(i == n_strings);
2628
+ n_strings = i;
2629
+ }
2630
+
2631
+ // We want to find the minimal superstring, which is an NP-complete
2632
+ // problem. Sometimes n_strings will be small enough that we could
2633
+ // actually do an exhaustive search, but in those cases the extra savings
2634
+ // are going to be very small anyway.
2635
+ //
2636
+ // So we use a simple greedy algorithm which should find a reasonably good
2637
+ // solution. Each step finds the pair of strings with the largest overlap
2638
+ // (if there are multiple pairs with the same largest overlap then we pick
2639
+ // the pair which minimises the combined length).
2640
+ while (n_strings > 1) {
2641
+ int best_a = 0, best_b = 0, best_overlap = 0, best_combined = 0;
2642
+ for (int i = 1; i < n_strings; ++i) {
2643
+ for (int j = 0; j < i; ++j) {
2644
+ int overlap;
2645
+ if (SIZE(strings[i].b) >= SIZE(strings[j].b)) {
2646
+ overlap = SIZE(strings[j].b);
2647
+ } else {
2648
+ overlap = SIZE(strings[i].b);
2649
+ }
2650
+ while (--overlap >= best_overlap) {
2651
+ if (memcmp(strings[i].b,
2652
+ strings[j].b + SIZE(strings[j].b) - overlap,
2653
+ overlap * sizeof(symbol)) == 0) {
2654
+ int combined = SIZE(strings[i].b) + SIZE(strings[j].b);
2655
+ if (best_overlap == overlap) {
2656
+ if (combined >= best_combined) continue;
2657
+ }
2658
+ best_a = j;
2659
+ best_b = i;
2660
+ best_overlap = overlap;
2661
+ best_combined = combined;
2662
+ } else if (memcmp(strings[i].b + SIZE(strings[i].b) - overlap,
2663
+ strings[j].b,
2664
+ overlap * sizeof(symbol)) == 0) {
2665
+ int combined = SIZE(strings[i].b) + SIZE(strings[j].b);
2666
+ if (best_overlap == overlap) {
2667
+ if (combined >= best_combined) continue;
2668
+ }
2669
+ best_a = i;
2670
+ best_b = j;
2671
+ best_overlap = overlap;
2672
+ best_combined = combined;
2673
+ }
2674
+ }
2675
+ }
2676
+ }
2677
+ if (best_overlap == 0) break;
2678
+ --n_strings;
2679
+ w(g, "~M#define s_");
2680
+ write_int(g, strings[best_b].n);
2681
+ w(g, " (s_");
2682
+ write_int(g, strings[best_a].n);
2683
+ w(g, " + ");
2684
+ write_int(g, SIZE(strings[best_a].b) - best_overlap);
2685
+ w(g, ")~N");
2686
+ strings[best_a].b =
2687
+ add_to_b(strings[best_a].b, strings[best_b].b + best_overlap,
2688
+ SIZE(strings[best_b].b) - best_overlap);
2689
+ lose_b(strings[best_b].b);
2690
+ strings[best_b] = strings[n_strings];
2691
+ }
2692
+
2693
+ for (int i = 0; i < n_strings; ++i) {
2694
+ w(g, "~Mstatic const symbol s_");
2695
+ write_int(g, strings[i].n);
2696
+ w(g, "[] = ");
2697
+ wlitarray(g, strings[i].b);
2698
+ w(g, ";~N");
2699
+ lose_b(strings[i].b);
2700
+ }
2701
+ FREE(strings);
2702
+
2703
+ g->outbuf = saved_outbuf;
1813
2704
  }
1814
2705
 
1815
2706
  static void generate_amongs(struct generator * g) {
1816
- struct str * s = g->outbuf;
2707
+ struct str * saved_outbuf = g->outbuf;
1817
2708
  g->outbuf = g->declarations;
1818
2709
  for (struct among * x = g->analyser->amongs; x; x = x->next) {
1819
- if (x->used) generate_among_table(g, x);
2710
+ if (x->used && !x->duplicate) generate_among_table(g, x);
1820
2711
  }
1821
- g->outbuf = s;
2712
+ g->outbuf = saved_outbuf;
1822
2713
  }
1823
2714
 
1824
2715
  static void set_bit(symbol * b, int i) { b[i >> 3] |= 1 << (i & 7); }
@@ -1863,67 +2754,60 @@ static void generate_grouping_table(struct generator * g, struct grouping * q) {
1863
2754
  }
1864
2755
 
1865
2756
  static void generate_groupings(struct generator * g) {
1866
- struct str * s = g->outbuf;
2757
+ struct str * saved_outbuf = g->outbuf;
1867
2758
  g->outbuf = g->declarations;
1868
2759
  for (struct grouping * q = g->analyser->groupings; q; q = q->next) {
1869
2760
  generate_grouping_table(g, q);
1870
2761
  }
1871
- g->outbuf = s;
2762
+ g->outbuf = saved_outbuf;
1872
2763
  }
1873
2764
 
1874
2765
  static void generate_create(struct generator * g) {
2766
+ if (g->analyser->variable_count == 0) return;
2767
+
1875
2768
  w(g, "~N"
1876
2769
  "extern struct SN_env * ~pcreate_env(void) {~N~+");
1877
2770
 
1878
- if (g->analyser->variable_count == 0) {
1879
- w(g, "~Mreturn SN_new_env(sizeof(struct SN_env));~N");
2771
+ if (g->analyser->name_count[t_string] == 0) {
2772
+ w(g, "~Mreturn SN_new_env(sizeof(SN_local));~N");
1880
2773
  } else {
1881
2774
  w(g, "~Mstruct SN_env * z = SN_new_env(sizeof(SN_local));~N"
1882
2775
  "~Mif (z) {~N~+");
1883
2776
 
2777
+ // SN_new_env() initialises the allocated size to all-zero-bits, so
2778
+ // assigning NULL here is only needed on platforms where NULL doesn't
2779
+ // have an all-zero-bits representation in memory. The C standard
2780
+ // allows that, but there don't seem to be any current such platforms.
2781
+ // There doesn't seem an easy way to only enable this code when it is
2782
+ // useful though.
2783
+ //
2784
+ // To simplify handling a failure to allocate a string variable, if
2785
+ // there are multiple non-localised string variables we initialise them
2786
+ // all to NULL first, then try to allocate them in a second pass. We
2787
+ // don't need to do this when there's only one because in that case we
2788
+ // can't have a partially successful allocation.
2789
+ if (g->analyser->name_count[t_string] > 1) {
2790
+ for (struct name * name = g->analyser->names; name; name = name->next) {
2791
+ if (!name->local_to && name->type == t_string) {
2792
+ w(g, "~M");
2793
+ write_varref(g, name);
2794
+ w(g, " = NULL;~N");
2795
+ }
2796
+ }
2797
+ write_newline(g);
2798
+ }
2799
+
1884
2800
  for (struct name * name = g->analyser->names; name; name = name->next) {
1885
2801
  if (!name->local_to) {
1886
2802
  switch (name->type) {
1887
2803
  case t_string:
1888
- w(g, "~M");
1889
- write_varref(g, name);
1890
- w(g, " = NULL;~N");
1891
- break;
1892
- case t_integer:
1893
- w(g, "~M");
2804
+ w(g, "~Mif ((");
1894
2805
  write_varref(g, name);
1895
- w(g, " = 0;~N");
2806
+ w(g, " = create_s()) == NULL) {~N~+"
2807
+ "~M~pclose_env(z);~N"
2808
+ "~Mreturn NULL;~N~-"
2809
+ "~M}~N");
1896
2810
  break;
1897
- case t_boolean:
1898
- w(g, "~M");
1899
- write_varref(g, name);
1900
- if (g->options->target_lang == LANG_CPLUSPLUS) {
1901
- w(g, " = false;~N");
1902
- } else {
1903
- w(g, " = 0;~N");
1904
- }
1905
- break;
1906
- }
1907
- }
1908
- }
1909
-
1910
- if (g->analyser->name_count[t_string] > 0) {
1911
- write_newline(g);
1912
-
1913
- // To simplify error handling, we initialise all strings to NULL
1914
- // above, then try to allocate them in a second pass.
1915
- for (struct name * name = g->analyser->names; name; name = name->next) {
1916
- if (!name->local_to) {
1917
- switch (name->type) {
1918
- case t_string:
1919
- w(g, "~Mif ((");
1920
- write_varref(g, name);
1921
- w(g, " = create_s()) == NULL) {~N~+"
1922
- "~M~pclose_env(z);~N"
1923
- "~Mreturn NULL;~N~-"
1924
- "~M}~N");
1925
- break;
1926
- }
1927
2811
  }
1928
2812
  }
1929
2813
  }
@@ -1936,21 +2820,20 @@ static void generate_create(struct generator * g) {
1936
2820
  }
1937
2821
 
1938
2822
  static void generate_close(struct generator * g) {
1939
- w(g, "~Nextern void ~pclose_env(struct SN_env * z) {~N~+");
2823
+ // If there are no string variables then our close_env is just
2824
+ // SN_delete_env so we #define it to that in the header.
2825
+ if (g->analyser->name_count[t_string] == 0) return;
1940
2826
 
1941
- if (g->analyser->name_count[t_string] > 0) {
1942
- w(g, "~Mif (!z) return;~N");
2827
+ w(g, "~Nextern void ~pclose_env(struct SN_env * z) {~N~+");
2828
+ w(g, "~Mif (!z) return;~N");
1943
2829
 
1944
- for (struct name * name = g->analyser->names; name; name = name->next) {
1945
- if (!name->local_to && name->type == t_string) {
1946
- w(g, "~Mlose_s(");
1947
- write_varref(g, name);
1948
- w(g, ");~N");
1949
- }
2830
+ for (struct name * name = g->analyser->names; name; name = name->next) {
2831
+ if (!name->local_to && name->type == t_string) {
2832
+ w(g, "~Mlose_s(");
2833
+ write_varref(g, name);
2834
+ w(g, ");~N");
1950
2835
  }
1951
2836
  }
1952
-
1953
- // Note: SN_delete_env() no-ops if z is NULL so we don't to gate this call.
1954
2837
  w(g, "~MSN_delete_env(z);~N"
1955
2838
  "~-}~N~N");
1956
2839
  }
@@ -1990,12 +2873,22 @@ static void generate_header_file(struct generator * g) {
1990
2873
  if (o->target_lang == LANG_C) {
1991
2874
  w(g, "#ifdef __cplusplus~N"
1992
2875
  "extern \"C\" {~N"
1993
- "#endif~N"); /* for C++ */
2876
+ "#endif~N~N"); /* for C++ */
1994
2877
 
1995
- w(g, "~N"
1996
- "extern struct SN_env * ~pcreate_env(void);~N"
1997
- "extern void ~pclose_env(struct SN_env * z);~N"
1998
- "~N");
2878
+ w(g, "struct SN_env;~N~N");
2879
+
2880
+ if (g->analyser->variable_count == 0) {
2881
+ w(g, "#define ~pcreate_env SN_new_env_no_vars~N");
2882
+ } else {
2883
+ w(g, "extern struct SN_env * ~pcreate_env(void);~N");
2884
+ }
2885
+
2886
+ if (g->analyser->name_count[t_string] == 0) {
2887
+ w(g, "#define ~pclose_env SN_delete_env~N");
2888
+ } else {
2889
+ w(g, "extern void ~pclose_env(struct SN_env * z);~N");
2890
+ }
2891
+ write_newline(g);
1999
2892
  }
2000
2893
 
2001
2894
  const char * vp = o->variables_prefix;
@@ -2098,7 +2991,7 @@ static void generate_header_file(struct generator * g) {
2098
2991
 
2099
2992
  for (struct name * name = g->analyser->names; name; name = name->next) {
2100
2993
  if (!name->local_to && name->type == t_boolean) {
2101
- if (g->options->target_lang == LANG_CPLUSPLUS) {
2994
+ if (o->target_lang == LANG_CPLUSPLUS) {
2102
2995
  w(g, "~Mbool ");
2103
2996
  } else {
2104
2997
  w(g, "~Munsigned char ");
@@ -2139,8 +3032,8 @@ static void generate_header_file(struct generator * g) {
2139
3032
  for (struct name * q = g->analyser->names; q; q = q->next) {
2140
3033
  if (!q->local_to && q->type == t_external) {
2141
3034
  w(g, "~Mstatic int ");
2142
- if (g->options->externals_prefix) {
2143
- write_string(g, g->options->externals_prefix);
3035
+ if (o->externals_prefix) {
3036
+ write_string(g, o->externals_prefix);
2144
3037
  }
2145
3038
  write_s(g, q->s);
2146
3039
  w(g, "(struct SN_env * z);~N~N");
@@ -2165,14 +3058,21 @@ static void generate_header_file(struct generator * g) {
2165
3058
  "~Mthrow;~N"
2166
3059
  "~-~M}~N");
2167
3060
  }
3061
+ if (o->encoding == ENC_WIDECHARS) {
3062
+ g->S[0] = "std::wstring";
3063
+ g->S[1] = "wchar_t";
3064
+ } else {
3065
+ g->S[0] = "std::string";
3066
+ g->S[1] = "char";
3067
+ }
2168
3068
  w(g, "~-~M}~N~N"
2169
3069
  "~M~~~n() {~N~+"
2170
3070
  "~Mclose_env();~N"
2171
3071
  "~-~M}~N~N"
2172
- "~Mstd::string operator()(const std::string& word) override {~N~+"
3072
+ "~M~S0 operator()(const ~S0& word) override {~N~+"
2173
3073
  "~Mstruct SN_env* z = &(zlocal.z);~N"
2174
3074
  "~Mconst symbol* s = reinterpret_cast<const symbol*>(word.data());~N"
2175
- "~Mint s_size = word.size() > INT_MAX ? INT_MAX : word.size();~N"
3075
+ "~Mint s_size = word.size() > INT_MAX ? INT_MAX : static_cast<int>(word.size());~N"
2176
3076
  "~Mreplace_s(z, 0, z->l, s_size, s);~N"
2177
3077
  "~Mz->c = 0;~N"
2178
3078
  "~M");
@@ -2180,11 +3080,11 @@ static void generate_header_file(struct generator * g) {
2180
3080
  write_string(g, "::");
2181
3081
  write_s(g, o->name);
2182
3082
  write_string(g, "::");
2183
- if (g->options->externals_prefix) {
2184
- write_string(g, g->options->externals_prefix);
3083
+ if (o->externals_prefix) {
3084
+ write_string(g, o->externals_prefix);
2185
3085
  }
2186
3086
  w(g, "stem(z);~N"
2187
- "~Mreturn std::string(reinterpret_cast<const char*>(z->p), SIZE(z->p));~N"
3087
+ "~Mreturn ~S0(reinterpret_cast<const ~S1*>(z->p), SIZE(z->p));~N"
2188
3088
  "~-~M}~N"
2189
3089
  "~-~M};~N~N");
2190
3090
 
@@ -2193,6 +3093,12 @@ static void generate_header_file(struct generator * g) {
2193
3093
  }
2194
3094
 
2195
3095
  extern void generate_program_c(struct generator * g) {
3096
+ // Build the among tables first as we need the among_function_scenario
3097
+ // list to generate code for amongs with functions.
3098
+ for (struct among * x = g->analyser->amongs; x; x = x->next) {
3099
+ build_among_table(g, x);
3100
+ }
3101
+
2196
3102
  g->outbuf = str_new();
2197
3103
  g->failure_str = str_new();
2198
3104
  write_start_comment(g, "/* ", " */");
@@ -2206,6 +3112,7 @@ extern void generate_program_c(struct generator * g) {
2206
3112
  generate(g, p);
2207
3113
  }
2208
3114
 
3115
+ generate_stringliterals(g);
2209
3116
  generate_amongs(g);
2210
3117
  generate_groupings(g);
2211
3118