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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/ext/mittens/ext.c +68 -30
- data/ext/mittens/extconf.rb +2 -0
- data/lib/mittens/version.rb +1 -1
- data/vendor/snowball/.github/workflows/ci.yml +43 -3
- data/vendor/snowball/.github/workflows/coverage.yml +8 -5
- data/vendor/snowball/GNUmakefile +46 -16
- data/vendor/snowball/NEWS +437 -13
- data/vendor/snowball/README.rst +4 -4
- data/vendor/snowball/algorithms/earlymodernenglish.sbl +301 -0
- data/vendor/snowball/algorithms/french.sbl +1 -1
- data/vendor/snowball/algorithms/ukrainian.sbl +1251 -0
- data/vendor/snowball/compiler/analyser.c +460 -156
- data/vendor/snowball/compiler/driver.c +41 -36
- data/vendor/snowball/compiler/generator.c +24 -1
- data/vendor/snowball/compiler/generator_ada.c +46 -45
- data/vendor/snowball/compiler/generator_c.c +1094 -187
- data/vendor/snowball/compiler/generator_csharp.c +34 -11
- data/vendor/snowball/compiler/generator_dart.c +39 -11
- data/vendor/snowball/compiler/generator_go.c +6 -6
- data/vendor/snowball/compiler/generator_java.c +29 -8
- data/vendor/snowball/compiler/generator_js.c +65 -38
- data/vendor/snowball/compiler/generator_pascal.c +69 -26
- data/vendor/snowball/compiler/generator_php.c +33 -12
- data/vendor/snowball/compiler/generator_python.c +23 -4
- data/vendor/snowball/compiler/generator_rust.c +37 -13
- data/vendor/snowball/compiler/generator_zig.c +6 -6
- data/vendor/snowball/compiler/header.h +85 -14
- data/vendor/snowball/compiler/space.c +67 -38
- data/vendor/snowball/compiler/tokeniser.c +28 -16
- data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +2 -2
- data/vendor/snowball/csharp/Snowball/Stemmer.cs +1 -4
- data/vendor/snowball/cxx/generate_algorithms.pl +5 -4
- data/vendor/snowball/cxx/{utilities.cxx → snowball_runtime.cxx} +1 -1
- data/vendor/snowball/cxx/stemmer.h +4 -0
- data/vendor/snowball/cxx/stemwords.cxx +36 -6
- data/vendor/snowball/dart/lib/src/snowball.dart +2 -2
- data/vendor/snowball/dart/pubspec.yaml +1 -1
- data/vendor/snowball/doc/libstemmer_c_README +3 -0
- data/vendor/snowball/doc/libstemmer_php_README +1 -1
- data/vendor/snowball/doc/libstemmer_python_README +8 -6
- data/vendor/snowball/examples/stemwords.c +4 -14
- data/vendor/snowball/go/env.go +16 -4
- data/vendor/snowball/include/libstemmer.h +0 -3
- data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +4 -4
- data/vendor/snowball/javascript/base-stemmer.js +27 -40
- data/vendor/snowball/javascript/stemwords.js +34 -10
- data/vendor/snowball/libstemmer/libstemmer_c.in +5 -5
- data/vendor/snowball/libstemmer/mkmodules.pl +5 -3
- data/vendor/snowball/libstemmer/modules.txt +4 -2
- data/vendor/snowball/pascal/SnowballProgram.pas +2 -2
- data/vendor/snowball/php/base-stemmer.php +2 -2
- data/vendor/snowball/python/setup.py +5 -1
- data/vendor/snowball/python/snowballstemmer/basestemmer.py +1 -2
- data/vendor/snowball/runtime/api.c +14 -3
- data/vendor/snowball/runtime/api.h +13 -9
- data/vendor/snowball/runtime/{utilities.c → snowball_runtime.c} +115 -181
- data/vendor/snowball/runtime/snowball_runtime.h +35 -24
- data/vendor/snowball/rust/src/snowball/snowball_env.rs +1 -1
- data/vendor/snowball/tests/compilertest +3 -3
- data/vendor/snowball/tests/runtime/among.sbl +96 -2
- data/vendor/snowball/tests/runtime/attachinsert.sbl +4 -0
- data/vendor/snowball/tests/runtime/debug.out +7 -0
- data/vendor/snowball/tests/runtime/debug.sbl +9 -0
- data/vendor/snowball/tests/runtime/integertests.sbl +22 -0
- data/vendor/snowball/tests/runtime/marklimit.sbl +30 -0
- data/vendor/snowball/tests/runtime/naming.sbl +20 -0
- data/vendor/snowball/tests/runtime/reverse.sbl +11 -0
- data/vendor/snowball/tests/stemtest.c +2 -5
- data/vendor/snowball/tests/syntax/aeslicefrom.sbl +7 -0
- data/vendor/snowball/tests/syntax/canon.sbl +14 -0
- data/vendor/snowball/tests/syntax/canon.syntax +36 -0
- data/vendor/snowball/tests/syntax/inline.syntax +4 -4
- data/vendor/snowball/tests/syntax/localvar.sbl +38 -0
- data/vendor/snowball/tests/syntax/localvar.stderr +3 -0
- data/vendor/snowball/tests/syntax/localvar.syntax +76 -0
- data/vendor/snowball/tests/syntax/unused.sbl +5 -1
- data/vendor/snowball/tests/syntax/unused.stderr +3 -1
- data/vendor/snowball/zig/env.zig +24 -4
- 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
|
-
|
|
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
|
|
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
|
-
|
|
167
|
+
w(g, "~N~-~M}");
|
|
75
168
|
}
|
|
76
169
|
|
|
77
|
-
static void wlitref(struct generator * g, symbol * p) { /* write ref to literal array */
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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->
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1754
|
-
|
|
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
|
-
|
|
1762
|
-
|
|
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
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
if (
|
|
1777
|
-
|
|
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
|
-
|
|
1784
|
-
|
|
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
|
-
|
|
1787
|
-
|
|
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
|
-
|
|
2561
|
+
write_newline(g);
|
|
2562
|
+
w(g, "~-~M};~N");
|
|
2563
|
+
}
|
|
1793
2564
|
|
|
1794
|
-
|
|
2565
|
+
static void generate_stringliterals(struct generator * g) {
|
|
2566
|
+
struct str * saved_outbuf = g->outbuf;
|
|
2567
|
+
g->outbuf = g->declarations;
|
|
1795
2568
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
for (
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
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
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
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 *
|
|
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 =
|
|
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 *
|
|
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 =
|
|
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->
|
|
1879
|
-
w(g, "~Mreturn SN_new_env(sizeof(
|
|
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, "~
|
|
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, " =
|
|
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
|
-
|
|
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
|
-
|
|
1942
|
-
|
|
2827
|
+
w(g, "~Nextern void ~pclose_env(struct SN_env * z) {~N~+");
|
|
2828
|
+
w(g, "~Mif (!z) return;~N");
|
|
1943
2829
|
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
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");
|
|
2876
|
+
"#endif~N~N"); /* for C++ */
|
|
1994
2877
|
|
|
1995
|
-
w(g, "~N"
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
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 (
|
|
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 (
|
|
2143
|
-
write_string(g,
|
|
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
|
-
"~
|
|
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 (
|
|
2184
|
-
write_string(g,
|
|
3083
|
+
if (o->externals_prefix) {
|
|
3084
|
+
write_string(g, o->externals_prefix);
|
|
2185
3085
|
}
|
|
2186
3086
|
w(g, "stem(z);~N"
|
|
2187
|
-
"~Mreturn
|
|
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
|
|