mittens 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +2 -1
  4. data/README.md +2 -2
  5. data/Rakefile +12 -5
  6. data/ext/mittens/extconf.rb +3 -1
  7. data/lib/mittens/version.rb +1 -1
  8. data/vendor/snowball/.github/workflows/ci.yml +144 -17
  9. data/vendor/snowball/.github/workflows/coverage.yml +117 -0
  10. data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
  11. data/vendor/snowball/.gitignore +33 -7
  12. data/vendor/snowball/CONTRIBUTING.rst +7 -0
  13. data/vendor/snowball/COPYING +1 -1
  14. data/vendor/snowball/GNUmakefile +736 -298
  15. data/vendor/snowball/NEWS +1394 -7
  16. data/vendor/snowball/README.rst +8 -8
  17. data/vendor/snowball/ada/generate/generate.adb +5 -5
  18. data/vendor/snowball/ada/src/stemmer.adb +177 -188
  19. data/vendor/snowball/ada/src/stemmer.ads +86 -89
  20. data/vendor/snowball/ada/src/stemwords.adb +21 -5
  21. data/vendor/snowball/algorithms/czech.sbl +255 -0
  22. data/vendor/snowball/algorithms/danish.sbl +22 -10
  23. data/vendor/snowball/algorithms/english.sbl +7 -4
  24. data/vendor/snowball/algorithms/esperanto.sbl +6 -7
  25. data/vendor/snowball/algorithms/estonian.sbl +9 -4
  26. data/vendor/snowball/algorithms/finnish.sbl +33 -21
  27. data/vendor/snowball/algorithms/german.sbl +5 -0
  28. data/vendor/snowball/algorithms/indonesian.sbl +115 -96
  29. data/vendor/snowball/algorithms/italian.sbl +26 -1
  30. data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
  31. data/vendor/snowball/algorithms/norwegian.sbl +17 -6
  32. data/vendor/snowball/algorithms/persian.sbl +387 -0
  33. data/vendor/snowball/algorithms/polish.sbl +177 -0
  34. data/vendor/snowball/algorithms/sesotho.sbl +115 -0
  35. data/vendor/snowball/algorithms/turkish.sbl +4 -4
  36. data/vendor/snowball/compiler/analyser.c +2002 -711
  37. data/vendor/snowball/compiler/driver.c +460 -298
  38. data/vendor/snowball/compiler/generator.c +439 -1965
  39. data/vendor/snowball/compiler/generator_ada.c +605 -430
  40. data/vendor/snowball/compiler/generator_c.c +2227 -0
  41. data/vendor/snowball/compiler/generator_csharp.c +365 -274
  42. data/vendor/snowball/compiler/generator_dart.c +1476 -0
  43. data/vendor/snowball/compiler/generator_go.c +348 -270
  44. data/vendor/snowball/compiler/generator_java.c +330 -233
  45. data/vendor/snowball/compiler/generator_js.c +534 -385
  46. data/vendor/snowball/compiler/generator_pascal.c +361 -320
  47. data/vendor/snowball/compiler/generator_php.c +1445 -0
  48. data/vendor/snowball/compiler/generator_python.c +399 -299
  49. data/vendor/snowball/compiler/generator_rust.c +370 -261
  50. data/vendor/snowball/compiler/generator_zig.c +1474 -0
  51. data/vendor/snowball/compiler/header.h +153 -86
  52. data/vendor/snowball/compiler/space.c +121 -63
  53. data/vendor/snowball/compiler/tokeniser.c +286 -182
  54. data/vendor/snowball/compiler/tokens.h +71 -0
  55. data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
  56. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
  57. data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
  58. data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
  59. data/vendor/snowball/cxx/.gitignore +4 -0
  60. data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
  61. data/vendor/snowball/cxx/stemmer.h +12 -0
  62. data/vendor/snowball/cxx/stemwords.cxx +176 -0
  63. data/vendor/snowball/cxx/utilities.cxx +2 -0
  64. data/vendor/snowball/dart/.gitignore +4 -0
  65. data/vendor/snowball/dart/analysis_options.yaml +1 -0
  66. data/vendor/snowball/dart/example/test_app.dart +65 -0
  67. data/vendor/snowball/dart/generate_algorithms.pl +21 -0
  68. data/vendor/snowball/dart/lib/snowball.dart +18 -0
  69. data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
  70. data/vendor/snowball/dart/pubspec.yaml +17 -0
  71. data/vendor/snowball/doc/libstemmer_dart_README +37 -0
  72. data/vendor/snowball/doc/libstemmer_js_README +2 -2
  73. data/vendor/snowball/doc/libstemmer_php_README +38 -0
  74. data/vendor/snowball/doc/libstemmer_python_README +3 -3
  75. data/vendor/snowball/examples/stemwords.c +18 -3
  76. data/vendor/snowball/go/README.md +12 -4
  77. data/vendor/snowball/go/env.go +12 -14
  78. data/vendor/snowball/go/stemwords/main.go +2 -1
  79. data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
  80. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
  81. data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
  82. data/vendor/snowball/javascript/base-stemmer.js +219 -252
  83. data/vendor/snowball/javascript/stemwords.js +86 -58
  84. data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
  85. data/vendor/snowball/libstemmer/modules.txt +4 -0
  86. data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
  87. data/vendor/snowball/php/base-stemmer.php +453 -0
  88. data/vendor/snowball/php/stemwords.php +25 -0
  89. data/vendor/snowball/python/create_init.py +16 -21
  90. data/vendor/snowball/python/pyproject.toml +3 -0
  91. data/vendor/snowball/python/setup.py +5 -7
  92. data/vendor/snowball/python/snowballstemmer/among.py +1 -2
  93. data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
  94. data/vendor/snowball/python/stemwords.py +72 -63
  95. data/vendor/snowball/runtime/api.c +11 -42
  96. data/vendor/snowball/runtime/api.h +11 -9
  97. data/vendor/snowball/runtime/snowball_runtime.h +110 -0
  98. data/vendor/snowball/runtime/utilities.c +282 -106
  99. data/vendor/snowball/rust/src/main.rs +2 -2
  100. data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
  101. data/vendor/snowball/tests/compilertest +62 -0
  102. data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
  103. data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
  104. data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
  105. data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
  106. data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
  107. data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
  108. data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
  109. data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
  110. data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
  111. data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
  112. data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
  113. data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
  114. data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
  115. data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
  116. data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
  117. data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
  118. data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
  119. data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
  120. data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
  121. data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
  122. data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
  123. data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
  124. data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
  125. data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
  126. data/vendor/snowball/tests/runtime/among.sbl +19 -0
  127. data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
  128. data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
  129. data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
  130. data/vendor/snowball/tests/runtime/externals.sbl +22 -0
  131. data/vendor/snowball/tests/runtime/hop.sbl +12 -0
  132. data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
  133. data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
  134. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  135. data/vendor/snowball/tests/runtime/not.sbl +30 -0
  136. data/vendor/snowball/tests/runtime/or.sbl +17 -0
  137. data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
  138. data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
  139. data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
  140. data/vendor/snowball/tests/runtime/slice.sbl +27 -0
  141. data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
  142. data/vendor/snowball/tests/runtime/strings.sbl +66 -0
  143. data/vendor/snowball/tests/runtime/test.sbl +19 -0
  144. data/vendor/snowball/tests/stemtest.c +41 -1
  145. data/vendor/snowball/tests/syntax/canon.sbl +32 -0
  146. data/vendor/snowball/tests/syntax/canon.stderr +0 -0
  147. data/vendor/snowball/tests/syntax/canon.syntax +57 -0
  148. data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
  149. data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
  150. data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
  151. data/vendor/snowball/tests/syntax/inline.sbl +144 -0
  152. data/vendor/snowball/tests/syntax/inline.stderr +0 -0
  153. data/vendor/snowball/tests/syntax/inline.syntax +121 -0
  154. data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
  155. data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
  156. data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
  157. data/vendor/snowball/tests/syntax/loops.sbl +14 -0
  158. data/vendor/snowball/tests/syntax/loops.stderr +9 -0
  159. data/vendor/snowball/tests/syntax/loops.syntax +29 -0
  160. data/vendor/snowball/tests/syntax/noops.sbl +76 -0
  161. data/vendor/snowball/tests/syntax/noops.stderr +27 -0
  162. data/vendor/snowball/tests/syntax/noops.syntax +135 -0
  163. data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
  164. data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
  165. data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
  166. data/vendor/snowball/tests/syntax/unused.sbl +18 -0
  167. data/vendor/snowball/tests/syntax/unused.stderr +7 -0
  168. data/vendor/snowball/tests/syntax/unused.syntax +3 -0
  169. data/vendor/snowball/zig/env.zig +599 -0
  170. data/vendor/snowball/zig/generate_algorithms.pl +19 -0
  171. data/vendor/snowball/zig/stemwords.zig +123 -0
  172. metadata +102 -4
  173. data/vendor/snowball/compiler/syswords.h +0 -86
  174. data/vendor/snowball/runtime/header.h +0 -62
@@ -1,67 +1,67 @@
1
1
  #include <assert.h>
2
+ #include <stdio.h> /* for fprintf etc */
2
3
  #include <stdlib.h> /* for exit */
3
4
  #include <string.h> /* for strlen */
4
- #include <stdio.h> /* for fprintf etc */
5
5
  #include "header.h"
6
6
 
7
- /* prototypes */
7
+ /* prototype functions for recursive use: */
8
8
 
9
9
  static void generate(struct generator * g, struct node * p);
10
10
  static void w(struct generator * g, const char * s);
11
11
  static void writef(struct generator * g, const char * s, struct node * p);
12
12
 
13
- static int new_label(struct generator * g) {
14
- return g->next_label++;
15
- }
13
+ /* Write routines for items from the syntax tree */
16
14
 
17
- static struct str * vars_newname(struct generator * g) {
18
- struct str * output;
19
- g->var_number++;
20
- output = str_new();
21
- str_append_string(output, "v_");
22
- str_append_int(output, g->var_number);
23
- return output;
15
+ static void write_relop(struct generator * g, int relop) {
16
+ // Relational operators are the same as C, save for (in-)equality.
17
+ switch (relop) {
18
+ case c_eq: write_string(g, " === "); break;
19
+ case c_ne: write_string(g, " !== "); break;
20
+ default: write_c_relop(g, relop);
21
+ }
24
22
  }
25
23
 
26
- /* Write routines for items from the syntax tree */
27
-
28
24
  static void write_varname(struct generator * g, struct name * p) {
29
- int ch = "SBIrxg"[p->type];
30
- if (p->type != t_external) {
31
- write_char(g, ch);
25
+ if (p->type == t_external) {
26
+ if (g->options->externals_prefix) {
27
+ write_string(g, g->options->externals_prefix);
28
+ }
29
+ } else {
30
+ // We use the same naming scheme for both global and local variables.
31
+ write_char(g, "SBIrxg"[p->type]);
32
32
  write_char(g, '_');
33
33
  }
34
34
  write_s(g, p->s);
35
35
  }
36
36
 
37
+ /* Reference to variable, e.g. when assigning to or using in an expression. */
37
38
  static void write_varref(struct generator * g, struct name * p) {
39
+ if (p->type != t_grouping && p->local_to == NULL) {
40
+ w(g, "this.#");
41
+ }
38
42
  write_varname(g, p);
39
43
  }
40
44
 
41
45
  static void write_literal_string(struct generator * g, symbol * p) {
42
- write_string(g, "\"");
46
+ write_char(g, '"');
43
47
  for (int i = 0; i < SIZE(p); i++) {
44
48
  int ch = p[i];
45
49
  if (32 <= ch && ch < 127) {
46
- if (ch == '\"' || ch == '\\') write_string(g, "\\");
50
+ if (ch == '\"' || ch == '\\') write_char(g, '\\');
47
51
  write_char(g, ch);
48
52
  } else {
49
53
  write_string(g, "\\u");
50
54
  write_hex4(g, ch);
51
55
  }
52
56
  }
53
- write_string(g, "\"");
54
- }
55
-
56
- static void write_margin(struct generator * g) {
57
- for (int i = 0; i < g->margin; i++) write_string(g, " ");
57
+ write_char(g, '"');
58
58
  }
59
59
 
60
60
  static void write_comment(struct generator * g, struct node * p) {
61
61
  if (!g->options->comments) return;
62
62
  write_margin(g);
63
63
  write_string(g, "// ");
64
- write_comment_content(g, p);
64
+ write_comment_content(g, p, NULL);
65
65
  write_newline(g);
66
66
  }
67
67
 
@@ -77,15 +77,15 @@ static void write_savecursor(struct generator * g, struct node * p,
77
77
  struct str * savevar) {
78
78
  g->B[0] = str_data(savevar);
79
79
  g->S[1] = "";
80
- if (p->mode != m_forward) g->S[1] = "base.limit - ";
81
- writef(g, "~M~C /** number */ ~B0 = ~S1base.cursor;~N", p);
80
+ if (p->mode != m_forward) g->S[1] = "this.limit - ";
81
+ writef(g, "~Mconst /** number */ ~B0 = ~S1this.c;~N", p);
82
82
  }
83
83
 
84
84
  static void append_restore_string(struct node * p, struct str * out, struct str * savevar) {
85
- str_append_string(out, "base.cursor = ");
86
- if (p->mode != m_forward) str_append_string(out, "base.limit - ");
85
+ str_append_string(out, "this.c = ");
86
+ if (p->mode != m_forward) str_append_string(out, "this.limit - ");
87
87
  str_append(out, savevar);
88
- str_append_string(out, ";");
88
+ str_append_ch(out, ';');
89
89
  }
90
90
 
91
91
  static void write_restorecursor(struct generator * g, struct node * p, struct str * savevar) {
@@ -96,12 +96,14 @@ static void write_restorecursor(struct generator * g, struct node * p, struct st
96
96
 
97
97
  static void write_inc_cursor(struct generator * g, struct node * p) {
98
98
  write_margin(g);
99
- write_string(g, p->mode == m_forward ? "base.cursor++;" : "base.cursor--;");
99
+ write_string(g, p->mode == m_forward ? "this.c++;" : "this.c--;");
100
100
  write_newline(g);
101
101
  }
102
102
 
103
103
  static void wsetlab_begin(struct generator * g, int n) {
104
104
  g->I[0] = n;
105
+ // This label may end up unused, but we can't easily tell at this point.
106
+ w(g, "~M// deno-lint-ignore no-unused-labels~N");
105
107
  w(g, "~Mlab~I0: {~N~+");
106
108
  }
107
109
 
@@ -113,53 +115,62 @@ static void wgotol(struct generator * g, int n) {
113
115
  write_margin(g);
114
116
  write_string(g, "break lab");
115
117
  write_int(g, n);
116
- write_string(g, ";");
118
+ write_char(g, ';');
117
119
  write_newline(g);
120
+ g->unreachable = true;
118
121
  }
119
122
 
120
- static void write_failure(struct generator * g) {
123
+ static void write_failure_(struct generator * g, byte after_if) {
124
+ if (!after_if) write_margin(g);
121
125
  if (str_len(g->failure_str) != 0) {
122
- write_margin(g);
126
+ if (after_if) w(g, "{~N~+~M");
123
127
  write_str(g, g->failure_str);
124
128
  write_newline(g);
129
+ write_margin(g);
125
130
  }
126
- write_margin(g);
127
131
  switch (g->failure_label) {
128
132
  case x_return:
129
133
  write_string(g, "return false;");
130
- g->unreachable = true;
131
134
  break;
132
135
  default:
133
136
  write_string(g, "break lab");
134
137
  write_int(g, g->failure_label);
135
- write_string(g, ";");
136
- g->unreachable = true;
138
+ write_char(g, ';');
137
139
  }
138
140
  write_newline(g);
141
+ g->unreachable = true;
142
+ if (after_if && str_len(g->failure_str) != 0) {
143
+ w(g, "~-~M}~N");
144
+ }
145
+ }
146
+
147
+ static void write_failure(struct generator * g) {
148
+ write_failure_(g, false);
149
+ }
150
+
151
+ static void write_failure_after_if(struct generator * g) {
152
+ write_failure_(g, true);
139
153
  }
140
154
 
141
155
  static void write_failure_if(struct generator * g, const char * s, struct node * p) {
142
156
  writef(g, "~Mif (", p);
143
157
  writef(g, s, p);
144
- writef(g, ")~N", p);
145
- write_block_start(g);
146
- write_failure(g);
147
- write_block_end(g);
158
+ writef(g, ") ", p);
159
+ write_failure_after_if(g);
148
160
  g->unreachable = false;
149
161
  }
150
162
 
151
163
  /* if at limit fail */
152
164
  static void write_check_limit(struct generator * g, struct node * p) {
153
165
  if (p->mode == m_forward) {
154
- write_failure_if(g, "base.cursor >= base.limit", p);
166
+ write_failure_if(g, "this.c >= this.limit", p);
155
167
  } else {
156
- write_failure_if(g, "base.cursor <= base.limit_backward", p);
168
+ write_failure_if(g, "this.c <= this.limit_backward", p);
157
169
  }
158
170
  }
159
171
 
160
172
  /* Formatted write. */
161
173
  static void writef(struct generator * g, const char * input, struct node * p) {
162
- (void)p;
163
174
  int i = 0;
164
175
 
165
176
  while (input[i]) {
@@ -171,10 +182,8 @@ static void writef(struct generator * g, const char * input, struct node * p) {
171
182
  ch = input[i++];
172
183
  switch (ch) {
173
184
  case '~': write_char(g, '~'); continue;
174
- case 'f': write_block_start(g);
175
- write_failure(g);
185
+ case 'f': write_failure(g);
176
186
  g->unreachable = false;
177
- write_block_end(g);
178
187
  continue;
179
188
  case 'M': write_margin(g); continue;
180
189
  case 'N': write_newline(g); continue;
@@ -197,6 +206,33 @@ static void writef(struct generator * g, const char * input, struct node * p) {
197
206
  write_s(g, g->B[j]);
198
207
  continue;
199
208
  }
209
+ case 'F': { // Among function dispatcher.
210
+ struct among * x = p->among;
211
+ if (x->function_count == 0) {
212
+ continue;
213
+ }
214
+
215
+ w(g, ", ");
216
+
217
+ if (x->function_count == 1) {
218
+ // Only one different function used in this among.
219
+ struct amongvec * v = x->b;
220
+ for (int j = 0; j < x->literalstring_count; j++) {
221
+ if (v[j].function) {
222
+ write_varref(g, v[j].function);
223
+ goto continue_outer_loop;
224
+ }
225
+ }
226
+ fprintf(stderr, "function_count == 1 but no among functions\n");
227
+ exit(1);
228
+ continue_outer_loop:
229
+ continue;
230
+ }
231
+
232
+ w(g, "this.#af_");
233
+ write_int(g, x->number);
234
+ continue;
235
+ }
200
236
  case 'I': {
201
237
  int j = input[i++] - '0';
202
238
  if (j < 0 || j > (int)(sizeof(g->I) / sizeof(g->I[0])))
@@ -205,43 +241,17 @@ static void writef(struct generator * g, const char * input, struct node * p) {
205
241
  continue;
206
242
  }
207
243
  case 'V':
208
- case 'W': {
209
- int j = input[i++] - '0';
210
- if (j < 0 || j > (int)(sizeof(g->V) / sizeof(g->V[0])))
211
- goto invalid_escape2;
212
- if (ch == 'V')
213
- write_varref(g, g->V[j]);
214
- else
215
- write_varname(g, g->V[j]);
244
+ write_varref(g, p->name);
216
245
  continue;
217
- }
218
- case 'L': {
219
- int j = input[i++] - '0';
220
- if (j < 0 || j > (int)(sizeof(g->L) / sizeof(g->L[0])))
221
- goto invalid_escape2;
222
- write_literal_string(g, g->L[j]);
246
+ case 'W':
247
+ write_varname(g, p->name);
248
+ continue;
249
+ case 'L':
250
+ write_literal_string(g, p->literalstring);
223
251
  continue;
224
- }
225
252
  case '+': g->margin++; continue;
226
253
  case '-': g->margin--; continue;
227
- case 'n': write_string(g, g->options->name); continue;
228
254
  case 'P': write_string(g, g->options->parent_class_name); continue;
229
- case 'C': { // Constant.
230
- if (g->options->js_esm) {
231
- w(g, "const");
232
- } else {
233
- w(g, "/** @const */ var");
234
- }
235
- continue;
236
- }
237
- case 'D': { // Declare variable.
238
- if (g->options->js_esm) {
239
- w(g, "let");
240
- } else {
241
- w(g, "var");
242
- }
243
- continue;
244
- }
245
255
  default:
246
256
  printf("Invalid escape sequence ~%c in writef(g, \"%s\", p)\n",
247
257
  ch, input);
@@ -262,46 +272,56 @@ static void generate_AE(struct generator * g, struct node * p) {
262
272
  const char * s;
263
273
  switch (p->type) {
264
274
  case c_name:
265
- write_varref(g, p->name); break;
275
+ write_varref(g, p->name);
276
+ break;
266
277
  case c_number:
267
- write_int(g, p->number); break;
278
+ write_int(g, p->number);
279
+ break;
268
280
  case c_maxint:
269
- write_string(g, "(-1>>>1)"); break;
281
+ write_string(g, "(-1>>>1)");
282
+ break;
270
283
  case c_minint:
271
- write_string(g, "(~(-1>>>1))"); break;
284
+ write_string(g, "(~(-1>>>1))");
285
+ break;
272
286
  case c_neg:
273
- write_char(g, '-'); generate_AE(g, p->right); break;
287
+ write_char(g, '-');
288
+ generate_AE(g, p->right);
289
+ break;
274
290
  case c_multiply:
275
- s = " * "; goto label0;
276
- case c_plus:
277
- s = " + "; goto label0;
278
- case c_minus:
279
- s = " - ";
280
- label0:
281
- write_char(g, '('); generate_AE(g, p->left);
282
- write_string(g, s); generate_AE(g, p->right); write_char(g, ')'); break;
291
+ s = " * ";
292
+ goto label0;
283
293
  case c_divide:
284
294
  /* Snowball specifies integer division with semantics matching C,
285
295
  * so we need to use `Math.trunc(x/y)` here.
286
296
  */
287
- write_string(g, "Math.trunc(");
297
+ write_string(g, "Math.trunc");
298
+ s = " / ";
299
+ goto label0;
300
+ case c_plus:
301
+ s = " + ";
302
+ goto label0;
303
+ case c_minus:
304
+ s = " - ";
305
+ label0:
306
+ write_char(g, '(');
288
307
  generate_AE(g, p->left);
289
- write_string(g, " / ");
308
+ write_string(g, s);
290
309
  generate_AE(g, p->right);
291
310
  write_char(g, ')');
292
311
  break;
293
312
  case c_cursor:
294
- w(g, "base.cursor"); break;
313
+ w(g, "this.c");
314
+ break;
295
315
  case c_limit:
296
- w(g, p->mode == m_forward ? "base.limit" : "base.limit_backward"); break;
297
- case c_lenof: /* Same as sizeof() for Javascript. */
298
- case c_sizeof:
299
- g->V[0] = p->name;
300
- w(g, "~V0.length");
316
+ w(g, p->mode == m_forward ? "this.limit" : "this.limit_backward");
301
317
  break;
302
318
  case c_len: /* Same as size() for Javascript. */
303
319
  case c_size:
304
- w(g, "base.current.length");
320
+ w(g, "this.current.length");
321
+ break;
322
+ case c_lenof: /* Same as sizeof() for Javascript. */
323
+ case c_sizeof:
324
+ writef(g, "~V.length", p);
305
325
  break;
306
326
  }
307
327
  }
@@ -317,13 +337,15 @@ static void generate_bra(struct generator * g, struct node * p) {
317
337
 
318
338
  static void generate_and(struct generator * g, struct node * p) {
319
339
  struct str * savevar = NULL;
320
- if (K_needed(g, p->left)) {
340
+ if (K_needed_for_and(p->left)) {
321
341
  savevar = vars_newname(g);
322
342
  }
323
343
 
324
344
  write_comment(g, p);
325
345
 
326
- if (savevar) write_savecursor(g, p, savevar);
346
+ if (savevar) {
347
+ write_savecursor(g, p, savevar);
348
+ }
327
349
 
328
350
  p = p->left;
329
351
  while (p) {
@@ -340,7 +362,7 @@ static void generate_and(struct generator * g, struct node * p) {
340
362
 
341
363
  static void generate_or(struct generator * g, struct node * p) {
342
364
  struct str * savevar = NULL;
343
- if (K_needed(g, p->left)) {
365
+ if (K_needed_for_or(p->left)) {
344
366
  savevar = vars_newname(g);
345
367
  }
346
368
 
@@ -348,12 +370,14 @@ static void generate_or(struct generator * g, struct node * p) {
348
370
  struct str * a1 = str_copy(g->failure_str);
349
371
 
350
372
  int out_lab = new_label(g);
351
- int end_unreachable = true;
373
+ bool end_unreachable = true;
352
374
 
353
375
  write_comment(g, p);
354
376
  wsetlab_begin(g, out_lab);
355
377
 
356
- if (savevar) write_savecursor(g, p, savevar);
378
+ if (savevar && K_needed_node_on_f(p)) {
379
+ write_savecursor(g, p, savevar);
380
+ }
357
381
 
358
382
  p = p->left;
359
383
  str_clear(g->failure_str);
@@ -365,7 +389,8 @@ static void generate_or(struct generator * g, struct node * p) {
365
389
  exit(1);
366
390
  }
367
391
  while (p->right != NULL) {
368
- g->failure_label = new_label(g);
392
+ int label = new_label(g);
393
+ g->failure_label = label;
369
394
  wsetlab_begin(g, g->failure_label);
370
395
  generate(g, p);
371
396
  if (!g->unreachable) {
@@ -374,7 +399,9 @@ static void generate_or(struct generator * g, struct node * p) {
374
399
  }
375
400
  wsetlab_end(g);
376
401
  g->unreachable = false;
377
- if (savevar) write_restorecursor(g, p, savevar);
402
+ if (savevar) {
403
+ write_restorecursor(g, p, savevar);
404
+ }
378
405
  p = p->right;
379
406
  }
380
407
 
@@ -395,15 +422,14 @@ static void generate_or(struct generator * g, struct node * p) {
395
422
 
396
423
  static void generate_backwards(struct generator * g, struct node * p) {
397
424
  write_comment(g, p);
398
- writef(g, "~Mbase.limit_backward = base.cursor; base.cursor = base.limit;~N", p);
425
+ writef(g, "~Mthis.limit_backward = this.c; this.c = this.limit;~N", p);
399
426
  generate(g, p->left);
400
- w(g, "~Mbase.cursor = base.limit_backward;~N");
427
+ w(g, "~Mthis.c = this.limit_backward;~N");
401
428
  }
402
429
 
403
-
404
430
  static void generate_not(struct generator * g, struct node * p) {
405
431
  struct str * savevar = NULL;
406
- if (K_needed(g, p->left)) {
432
+ if (K_needed_node_on_f(p->left)) {
407
433
  savevar = vars_newname(g);
408
434
  }
409
435
 
@@ -416,7 +442,8 @@ static void generate_not(struct generator * g, struct node * p) {
416
442
  write_savecursor(g, p, savevar);
417
443
  }
418
444
 
419
- g->failure_label = new_label(g);
445
+ int label = new_label(g);
446
+ g->failure_label = label;
420
447
  str_clear(g->failure_str);
421
448
 
422
449
  wsetlab_begin(g, g->failure_label);
@@ -439,14 +466,14 @@ static void generate_not(struct generator * g, struct node * p) {
439
466
  }
440
467
  }
441
468
 
442
-
443
469
  static void generate_try(struct generator * g, struct node * p) {
444
470
  struct str * savevar = NULL;
445
- if (K_needed(g, p->left)) {
471
+ if (K_needed(p->left)) {
446
472
  savevar = vars_newname(g);
447
473
  }
448
474
 
449
- g->failure_label = new_label(g);
475
+ int label = new_label(g);
476
+ g->failure_label = label;
450
477
  str_clear(g->failure_str);
451
478
 
452
479
  write_comment(g, p);
@@ -467,14 +494,12 @@ static void generate_try(struct generator * g, struct node * p) {
467
494
 
468
495
  static void generate_set(struct generator * g, struct node * p) {
469
496
  write_comment(g, p);
470
- g->V[0] = p->name;
471
- writef(g, "~M~V0 = true;~N", p);
497
+ writef(g, "~M~V = true;~N", p);
472
498
  }
473
499
 
474
500
  static void generate_unset(struct generator * g, struct node * p) {
475
501
  write_comment(g, p);
476
- g->V[0] = p->name;
477
- writef(g, "~M~V0 = false;~N", p);
502
+ writef(g, "~M~V = false;~N", p);
478
503
  }
479
504
 
480
505
  static void generate_fail(struct generator * g, struct node * p) {
@@ -484,10 +509,9 @@ static void generate_fail(struct generator * g, struct node * p) {
484
509
  }
485
510
 
486
511
  /* generate_test() also implements 'reverse' */
487
-
488
512
  static void generate_test(struct generator * g, struct node * p) {
489
513
  struct str * savevar = NULL;
490
- if (K_needed(g, p->left)) {
514
+ if (K_needed(p->left)) {
491
515
  savevar = vars_newname(g);
492
516
  }
493
517
 
@@ -509,20 +533,22 @@ static void generate_test(struct generator * g, struct node * p) {
509
533
 
510
534
  static void generate_do(struct generator * g, struct node * p) {
511
535
  struct str * savevar = NULL;
512
- if (K_needed(g, p->left)) {
536
+ if (K_needed(p->left)) {
513
537
  savevar = vars_newname(g);
514
538
  }
515
539
 
516
540
  write_comment(g, p);
517
- if (savevar) write_savecursor(g, p, savevar);
541
+ if (savevar) {
542
+ write_savecursor(g, p, savevar);
543
+ }
518
544
 
519
545
  if (p->left->type == c_call) {
520
546
  /* Optimise do <call> */
521
547
  write_comment(g, p->left);
522
- g->V[0] = p->left->name;
523
- w(g, "~M~V0();~N");
548
+ writef(g, "~M~V();~N", p->left);
524
549
  } else {
525
- g->failure_label = new_label(g);
550
+ int label = new_label(g);
551
+ g->failure_label = label;
526
552
  str_clear(g->failure_str);
527
553
 
528
554
  wsetlab_begin(g, g->failure_label);
@@ -549,37 +575,40 @@ static void generate_GO_grouping(struct generator * g, struct node * p, int is_g
549
575
  struct grouping * q = p->name->grouping;
550
576
  g->S[0] = p->mode == m_forward ? "" : "_b";
551
577
  g->S[1] = complement ? "in" : "out";
552
- g->V[0] = p->name;
553
578
  g->I[0] = q->smallest_ch;
554
579
  g->I[1] = q->largest_ch;
555
- write_failure_if(g, "!base.go_~S1_grouping~S0(~V0, ~I0, ~I1)", p);
580
+ write_failure_if(g, "!this.go_~S1_grouping~S0(~V, ~I0, ~I1)", p);
556
581
  if (!is_goto) {
557
582
  if (p->mode == m_forward)
558
- w(g, "~Mbase.cursor++;~N");
583
+ w(g, "~Mthis.c++;~N");
559
584
  else
560
- w(g, "~Mbase.cursor--;~N");
585
+ w(g, "~Mthis.c--;~N");
561
586
  }
562
587
  }
563
588
 
564
- static void generate_GO(struct generator * g, struct node * p, int style) {
589
+ static void generate_GO(struct generator * g, struct node * p, int is_goto) {
565
590
  write_comment(g, p);
566
591
 
567
592
  int a0 = g->failure_label;
568
593
  struct str * a1 = str_copy(g->failure_str);
569
594
 
570
- int end_unreachable = false;
595
+ bool end_unreachable = false;
596
+
571
597
  int golab = new_label(g);
572
598
  g->I[0] = golab;
573
- w(g, "~Mgolab~I0: while(true)~N");
599
+ // This label may end up unused, but we can't easily tell at this point.
600
+ w(g, "~M// deno-lint-ignore no-unused-labels~N");
601
+ w(g, "~Mgolab~I0: while (true)~N");
574
602
  w(g, "~{");
575
603
 
576
604
  struct str * savevar = NULL;
577
- if (style == 1 || repeat_restore(g, p->left)) {
605
+ if (is_goto || repeat_restore(p->left)) {
578
606
  savevar = vars_newname(g);
579
607
  write_savecursor(g, p, savevar);
580
608
  }
581
609
 
582
- g->failure_label = new_label(g);
610
+ int label = new_label(g);
611
+ g->failure_label = label;
583
612
  str_clear(g->failure_str);
584
613
  wsetlab_begin(g, g->failure_label);
585
614
  generate(g, p->left);
@@ -590,7 +619,7 @@ static void generate_GO(struct generator * g, struct node * p, int style) {
590
619
  end_unreachable = true;
591
620
  } else {
592
621
  /* include for goto; omit for gopast */
593
- if (style == 1) write_restorecursor(g, p, savevar);
622
+ if (is_goto) write_restorecursor(g, p, savevar);
594
623
  g->I[0] = golab;
595
624
  w(g, "~Mbreak golab~I0;~N");
596
625
  }
@@ -612,10 +641,10 @@ static void generate_GO(struct generator * g, struct node * p, int style) {
612
641
  }
613
642
 
614
643
  static void generate_loop(struct generator * g, struct node * p) {
615
- struct str * loopvar = vars_newname(g);
616
644
  write_comment(g, p);
645
+ struct str * loopvar = vars_newname(g);
617
646
  g->B[0] = str_data(loopvar);
618
- w(g, "~Mfor (~D /** number */ ~B0 = ");
647
+ w(g, "~Mfor (let /** number */ ~B0 = ");
619
648
  generate_AE(g, p->AE);
620
649
  g->B[0] = str_data(loopvar);
621
650
  writef(g, "; ~B0 > 0; ~B0--)~N", p);
@@ -629,15 +658,16 @@ static void generate_loop(struct generator * g, struct node * p) {
629
658
  }
630
659
 
631
660
  static void generate_repeat_or_atleast(struct generator * g, struct node * p, struct str * loopvar) {
632
- writef(g, "~Mwhile(true)~N~{", p);
661
+ writef(g, "~Mwhile (true) {~N~+", p);
633
662
 
634
663
  struct str * savevar = NULL;
635
- if (repeat_restore(g, p->left)) {
664
+ if (repeat_restore(p->left)) {
636
665
  savevar = vars_newname(g);
637
666
  write_savecursor(g, p, savevar);
638
667
  }
639
668
 
640
- g->failure_label = new_label(g);
669
+ int label = new_label(g);
670
+ g->failure_label = label;
641
671
  str_clear(g->failure_str);
642
672
  wsetlab_begin(g, g->failure_label);
643
673
  generate(g, p->left);
@@ -668,12 +698,12 @@ static void generate_repeat(struct generator * g, struct node * p) {
668
698
  }
669
699
 
670
700
  static void generate_atleast(struct generator * g, struct node * p) {
671
- struct str * loopvar = vars_newname(g);
672
-
673
701
  write_comment(g, p);
702
+
674
703
  w(g, "~{");
704
+ struct str * loopvar = vars_newname(g);
675
705
  g->B[0] = str_data(loopvar);
676
- w(g, "~M~D ~B0 = ");
706
+ w(g, "~Mlet ~B0 = ");
677
707
  generate_AE(g, p->AE);
678
708
  w(g, ";~N");
679
709
  {
@@ -692,117 +722,75 @@ static void generate_atleast(struct generator * g, struct node * p) {
692
722
  str_delete(loopvar);
693
723
  }
694
724
 
695
- static void generate_setmark(struct generator * g, struct node * p) {
696
- write_comment(g, p);
697
- g->V[0] = p->name;
698
- writef(g, "~M~V0 = base.cursor;~N", p);
699
- }
700
-
701
725
  static void generate_tomark(struct generator * g, struct node * p) {
702
726
  write_comment(g, p);
703
727
  g->S[0] = p->mode == m_forward ? ">" : "<";
704
728
 
705
- w(g, "~Mif (base.cursor ~S0 "); generate_AE(g, p->AE); w(g, ")~N");
706
- write_block_start(g);
707
- write_failure(g);
708
- write_block_end(g);
709
- g->unreachable = false;
710
- w(g, "~Mbase.cursor = "); generate_AE(g, p->AE); writef(g, ";~N", p);
711
- }
712
-
713
- static void generate_atmark(struct generator * g, struct node * p) {
714
- write_comment(g, p);
715
- w(g, "~Mif (base.cursor != "); generate_AE(g, p->AE); writef(g, ")~N", p);
716
- write_block_start(g);
717
- write_failure(g);
718
- write_block_end(g);
729
+ w(g, "~Mif (this.c ~S0 "); generate_AE(g, p->AE); w(g, ") ");
730
+ write_failure_after_if(g);
719
731
  g->unreachable = false;
732
+ w(g, "~Mthis.c = "); generate_AE(g, p->AE); writef(g, ";~N", p);
720
733
  }
721
734
 
722
735
  static void generate_hop(struct generator * g, struct node * p) {
723
- int c_count = ++g->keep_count;
724
736
  write_comment(g, p);
725
737
  g->S[0] = p->mode == m_forward ? "+" : "-";
726
738
 
727
- g->I[0] = c_count;
728
- w(g, "~{~M~C /** number */ c~I0 = base.cursor ~S0 ");
729
- generate_AE(g, p->AE);
730
- w(g, ";~N");
731
-
732
- g->I[0] = c_count;
733
- g->S[1] = p->mode == m_forward ? "> base.limit" : "< base.limit_backward";
739
+ g->S[1] = p->mode == m_forward ? "> this.limit" : "< this.limit_backward";
734
740
  g->S[2] = p->mode == m_forward ? "<" : ">";
735
741
  if (p->AE->type == c_number) {
736
742
  // Constant distance hop.
737
743
  //
738
744
  // No need to check for negative hop as that's converted to false by
739
745
  // the analyser.
740
- write_failure_if(g, "c~I0 ~S1", p);
746
+ g->I[0] = p->AE->number;
747
+ write_failure_if(g, "this.c ~S0 ~I0 ~S1", p);
748
+ writef(g, "~Mthis.c ~S0= ~I0;~N", p);
741
749
  } else {
742
- write_failure_if(g, "c~I0 ~S1 || c~I0 ~S2 base.cursor", p);
750
+ w(g, "~{~Mconst /** number */ c = this.c ~S0 ");
751
+ generate_AE(g, p->AE);
752
+ w(g, ";~N");
753
+ write_failure_if(g, "c ~S1 || c ~S2 this.c", p);
754
+ writef(g, "~Mthis.c = c;~N", p);
755
+ writef(g, "~}", p);
743
756
  }
744
- writef(g, "~Mbase.cursor = c~I0;~N", p);
745
- writef(g, "~}", p);
746
- }
747
-
748
- static void generate_delete(struct generator * g, struct node * p) {
749
- write_comment(g, p);
750
- writef(g, "~Mif (!base.slice_del())~N"
751
- "~M{~N"
752
- "~+~Mreturn false;~N~-"
753
- "~M}~N", p);
754
757
  }
755
758
 
756
759
  static void generate_tolimit(struct generator * g, struct node * p) {
757
760
  write_comment(g, p);
758
761
  if (p->mode == m_forward) {
759
- writef(g, "~Mbase.cursor = base.limit;~N", p);
762
+ writef(g, "~Mthis.c = this.limit;~N", p);
760
763
  } else {
761
- writef(g, "~Mbase.cursor = base.limit_backward;~N", p);
762
- }
763
- }
764
-
765
- static void generate_atlimit(struct generator * g, struct node * p) {
766
- write_comment(g, p);
767
- if (p->mode == m_forward) {
768
- write_failure_if(g, "base.cursor < base.limit", p);
769
- } else {
770
- write_failure_if(g, "base.cursor > base.limit_backward", p);
764
+ writef(g, "~Mthis.c = this.limit_backward;~N", p);
771
765
  }
772
766
  }
773
767
 
774
768
  static void generate_leftslice(struct generator * g, struct node * p) {
775
769
  write_comment(g, p);
776
770
  if (p->mode == m_forward) {
777
- writef(g, "~Mbase.bra = base.cursor;~N", p);
771
+ writef(g, "~Mthis.bra = this.c;~N", p);
778
772
  } else {
779
- writef(g, "~Mbase.ket = base.cursor;~N", p);
773
+ writef(g, "~Mthis.ket = this.c;~N", p);
780
774
  }
781
775
  }
782
776
 
783
777
  static void generate_rightslice(struct generator * g, struct node * p) {
784
778
  write_comment(g, p);
785
779
  if (p->mode == m_forward) {
786
- writef(g, "~Mbase.ket = base.cursor;~N", p);
780
+ writef(g, "~Mthis.ket = this.c;~N", p);
787
781
  } else {
788
- writef(g, "~Mbase.bra = base.cursor;~N", p);
782
+ writef(g, "~Mthis.bra = this.c;~N", p);
789
783
  }
790
784
  }
791
785
 
792
786
  static void generate_assignto(struct generator * g, struct node * p) {
793
787
  write_comment(g, p);
794
- g->V[0] = p->name;
795
- writef(g, "~M~V0 = base.assign_to();~N", p);
788
+ writef(g, "~M~V = this.current.slice(0, this.limit);~N", p);
796
789
  }
797
790
 
798
791
  static void generate_sliceto(struct generator * g, struct node * p) {
799
792
  write_comment(g, p);
800
- g->V[0] = p->name;
801
- writef(g, "~M~V0 = base.slice_to();~N"
802
- "~Mif (~V0 == '')~N"
803
- "~M{~N"
804
- "~+~Mreturn false;~N~-"
805
- "~M}~N", p);
793
+ writef(g, "~M~V = this.slice_to();~N", p);
806
794
  }
807
795
 
808
796
  static void generate_address(struct generator * g, struct node * p) {
@@ -815,60 +803,59 @@ static void generate_address(struct generator * g, struct node * p) {
815
803
  }
816
804
 
817
805
  static void generate_insert(struct generator * g, struct node * p, int style) {
818
- int c_count;
819
- int keep_c = style == c_attach;
820
806
  write_comment(g, p);
807
+ int keep_c = style == c_attach;
821
808
  if (p->mode == m_backward) keep_c = !keep_c;
822
809
  if (keep_c) {
823
- c_count = ++g->keep_count;
824
- g->I[0] = c_count;
825
- w(g, "~{~M~C /** number */ c~I0 = base.cursor;~N");
810
+ w(g, "~{~Mconst /** number */ c = this.c;~N");
811
+ writef(g, "~Mthis.insert(c, c, ", p);
812
+ } else {
813
+ writef(g, "~Mthis.insert(this.c, this.c, ", p);
826
814
  }
827
- writef(g, "~Mbase.insert(base.cursor, base.cursor, ", p);
828
815
  generate_address(g, p);
829
816
  writef(g, ");~N", p);
830
- if (keep_c) {
831
- g->I[0] = c_count;
832
- w(g, "~Mbase.cursor = c~I0;~N~}");
833
- }
817
+ if (keep_c) w(g, "~Mthis.c = c;~N~}");
834
818
  }
835
819
 
836
- static void generate_assignfrom(struct generator * g, struct node * p) {
837
- int c_count;
838
- int keep_c = p->mode == m_forward; /* like 'attach' */
839
-
820
+ static void generate_stringassign(struct generator * g, struct node * p) {
840
821
  write_comment(g, p);
822
+ int keep_c = p->mode == m_forward; /* like 'attach' */
841
823
  if (keep_c) {
842
- c_count = ++g->keep_count;
843
- g->I[0] = c_count;
844
- w(g, "~{~M~C /** number */ c~I0 = base.cursor;~N");
845
- }
846
- if (p->mode == m_forward) {
847
- writef(g, "~Mbase.insert(base.cursor, base.limit, ", p);
824
+ w(g, "~{~Mconst /** number */ c = this.c;~N");
825
+ if (p->mode == m_forward) {
826
+ writef(g, "~Mthis.insert(c, this.limit, ", p);
827
+ } else {
828
+ writef(g, "~Mthis.insert(this.limit_backward, c, ", p);
829
+ }
848
830
  } else {
849
- writef(g, "~Mbase.insert(base.limit_backward, base.cursor, ", p);
831
+ if (p->mode == m_forward) {
832
+ writef(g, "~Mthis.insert(this.c, this.limit, ", p);
833
+ } else {
834
+ writef(g, "~Mthis.insert(this.limit_backward, this.c, ", p);
835
+ }
850
836
  }
851
837
  generate_address(g, p);
852
838
  writef(g, ");~N", p);
853
839
  if (keep_c) {
854
- g->I[0] = c_count;
855
- w(g, "~Mbase.cursor = c~I0;~N~}");
840
+ w(g, "~Mthis.c = c;~N~}");
856
841
  }
857
842
  }
858
843
 
859
844
  static void generate_slicefrom(struct generator * g, struct node * p) {
860
845
  write_comment(g, p);
861
- w(g, "~Mif (!base.slice_from(");
846
+ if (p->literalstring && SIZE(p->literalstring) == 0) {
847
+ w(g, "~Mthis.slice_del();~N");
848
+ return;
849
+ }
850
+ w(g, "~Mthis.slice_from(");
862
851
  generate_address(g, p);
863
- writef(g, "))~N"
864
- "~M{~N"
865
- "~+~Mreturn false;~N~-"
866
- "~M}~N", p);
852
+ w(g, ");~N");
867
853
  }
868
854
 
869
855
  static void generate_setlimit(struct generator * g, struct node * p) {
870
- struct str * varname = vars_newname(g);
871
856
  write_comment(g, p);
857
+ struct str * varname = vars_newname(g);
858
+
872
859
  if (p->left && p->left->type == c_tomark) {
873
860
  /* Special case for:
874
861
  *
@@ -880,30 +867,32 @@ static void generate_setlimit(struct generator * g, struct node * p) {
880
867
  */
881
868
  struct node * q = p->left;
882
869
  write_comment(g, q);
870
+
883
871
  g->S[0] = q->mode == m_forward ? ">" : "<";
884
- w(g, "~Mif (base.cursor ~S0 "); generate_AE(g, q->AE); w(g, ")~N");
885
- write_block_start(g);
886
- write_failure(g);
887
- write_block_end(g);
872
+ w(g, "~Mif (this.c ~S0 "); generate_AE(g, q->AE); w(g, ") ");
873
+ write_failure_after_if(g);
888
874
  g->unreachable = false;
889
875
 
890
876
  g->B[0] = str_data(varname);
891
- w(g, "~M~C /** number */ ~B0 = ");
892
877
  if (p->mode == m_forward) {
893
- w(g, "base.limit - base.cursor;~N");
894
- w(g, "~Mbase.limit = ");
878
+ w(g, "~Mlet /** number */ ~B0 = this.limit;~N");
879
+ w(g, "~Mthis.limit = ");
880
+ generate_AE(g, q->AE);
881
+ w(g, ";~N");
882
+ w(g, "~M~B0 -= this.limit;~N");
895
883
  } else {
896
- w(g, "base.limit_backward;~N");
897
- w(g, "~Mbase.limit_backward = ");
884
+ w(g, "~Mconst /** number */ ~B0 = this.limit_backward;~N");
885
+ w(g, "~Mthis.limit_backward = ");
886
+ generate_AE(g, q->AE);
887
+ w(g, ";~N");
898
888
  }
899
- generate_AE(g, q->AE); writef(g, ";~N", q);
900
889
 
901
890
  if (p->mode == m_forward) {
902
- str_assign(g->failure_str, "base.limit += ");
891
+ str_assign(g->failure_str, "this.limit += ");
903
892
  str_append(g->failure_str, varname);
904
893
  str_append_ch(g->failure_str, ';');
905
894
  } else {
906
- str_assign(g->failure_str, "base.limit_backward = ");
895
+ str_assign(g->failure_str, "this.limit_backward = ");
907
896
  str_append(g->failure_str, varname);
908
897
  str_append_ch(g->failure_str, ';');
909
898
  }
@@ -915,22 +904,22 @@ static void generate_setlimit(struct generator * g, struct node * p) {
915
904
 
916
905
  if (!g->unreachable) {
917
906
  g->B[0] = str_data(varname);
918
- w(g, "~M~C /** number */ ~B0 = ");
907
+ w(g, "~Mconst /** number */ ~B0 = ");
919
908
  if (p->mode == m_forward) {
920
- w(g, "base.limit - base.cursor;~N");
921
- w(g, "~Mbase.limit = base.cursor;~N");
909
+ w(g, "this.limit - this.c;~N");
910
+ w(g, "~Mthis.limit = this.c;~N");
922
911
  } else {
923
- w(g, "base.limit_backward;~N");
924
- w(g, "~Mbase.limit_backward = base.cursor;~N");
912
+ w(g, "this.limit_backward;~N");
913
+ w(g, "~Mthis.limit_backward = this.c;~N");
925
914
  }
926
915
  write_restorecursor(g, p, savevar);
927
916
 
928
917
  if (p->mode == m_forward) {
929
- str_assign(g->failure_str, "base.limit += ");
918
+ str_assign(g->failure_str, "this.limit += ");
930
919
  str_append(g->failure_str, varname);
931
920
  str_append_ch(g->failure_str, ';');
932
921
  } else {
933
- str_assign(g->failure_str, "base.limit_backward = ");
922
+ str_assign(g->failure_str, "this.limit_backward = ");
934
923
  str_append(g->failure_str, varname);
935
924
  str_append_ch(g->failure_str, ';');
936
925
  }
@@ -955,38 +944,74 @@ static void generate_setlimit(struct generator * g, struct node * p) {
955
944
  static void generate_dollar(struct generator * g, struct node * p) {
956
945
  write_comment(g, p);
957
946
 
947
+ int a0 = g->failure_label;
948
+ struct str * a1 = str_copy(g->failure_str);
949
+ int label = new_label(g);
950
+ g->failure_label = label;
951
+ str_clear(g->failure_str);
952
+
958
953
  struct str * savevar = vars_newname(g);
959
954
  g->B[0] = str_data(savevar);
960
955
  writef(g, "~{~N"
961
- "~M~D /** !Object */ ~B0 = new ~P();~N", p);
962
- writef(g, "~M~B0.copy_from(base);~N", p);
956
+ "~Mconst /** !Object */ ~B0 = new ~P();~N", p);
957
+ writef(g, "~M~B0.copy_from(this);~N", p);
958
+
959
+ writef(g, "~Mthis.current = ~V;~N"
960
+ "~Mthis.c = 0;~N"
961
+ "~Mthis.limit_backward = 0;~N"
962
+ "~Mthis.limit = this.current.length;~N", p);
963
+ if (p->left->possible_signals == -1) {
964
+ /* Assume failure. */
965
+ w(g, "~Mlet /**boolean*/ ~B0_f = true;~N");
966
+ }
967
+
968
+ wsetlab_begin(g, g->failure_label);
963
969
 
964
- ++g->copy_from_count;
965
- str_assign(g->failure_str, "base.copy_from(");
966
- str_append(g->failure_str, savevar);
967
- str_append_string(g->failure_str, ");");
968
- g->V[0] = p->name;
969
- writef(g, "~Mbase.current = ~V0;~N"
970
- "~Mbase.cursor = 0;~N"
971
- "~Mbase.limit_backward = 0;~N"
972
- "~Mbase.limit = base.current.length;~N", p);
973
970
  generate(g, p->left);
974
- if (!g->unreachable) {
975
- g->V[0] = p->name;
976
- writef(g, "~M~V0 = base.current;~N", p);
977
- write_margin(g);
978
- write_str(g, g->failure_str);
979
- write_newline(g);
971
+
972
+ if (p->left->possible_signals == -1) {
973
+ /* Mark success. */
974
+ g->B[0] = str_data(savevar);
975
+ w(g, "~M~B0_f = false;~N");
976
+ }
977
+
978
+ wsetlab_end(g);
979
+
980
+ g->failure_label = a0;
981
+ str_delete(g->failure_str);
982
+ g->failure_str = a1;
983
+
984
+ g->B[0] = str_data(savevar);
985
+ writef(g, "~M~V = this.current;~N"
986
+ "~Mthis.copy_from(~B0);~N", p);
987
+ ++g->copy_from_count;
988
+
989
+ if (p->left->possible_signals == 0) {
990
+ // p->left always signals f.
991
+ write_failure(g);
992
+ } else if (p->left->possible_signals == -1) {
993
+ write_failure_if(g, "~B0_f", p);
980
994
  }
981
995
  w(g, "~}");
996
+
982
997
  str_delete(savevar);
983
998
  }
984
999
 
985
1000
  static void generate_integer_assign(struct generator * g, struct node * p, const char * s) {
986
1001
  write_comment(g, p);
987
- g->V[0] = p->name;
1002
+ if (p->AE->type == c_number && p->AE->number == 1) {
1003
+ if (strcmp(s, "+=") == 0) {
1004
+ // Optimise `+= 1` to increment.
1005
+ writef(g, "~M++~V;~N", p);
1006
+ return;
1007
+ } else if (strcmp(s, "-=") == 0) {
1008
+ // Optimise `-= 1` to decrement.
1009
+ writef(g, "~M--~V;~N", p);
1010
+ return;
1011
+ }
1012
+ }
988
1013
  g->S[0] = s;
989
- w(g, "~M~V0 ~S0 ");
1014
+ writef(g, "~M~V ~S0 ", p);
990
1015
  generate_AE(g, p->AE);
991
1016
  w(g, ";~N");
992
1017
  }
@@ -994,7 +1019,7 @@ static void generate_integer_assign(struct generator * g, struct node * p, const
994
1019
  static void generate_integer_test(struct generator * g, struct node * p) {
995
1020
  write_comment(g, p);
996
1021
  int relop = p->type;
997
- int optimise_to_return = (g->failure_label == x_return && p->right && p->right->type == c_functionend);
1022
+ int optimise_to_return = tailcallable(g, p);
998
1023
  if (optimise_to_return) {
999
1024
  w(g, "~Mreturn ");
1000
1025
  p->right = NULL;
@@ -1004,39 +1029,42 @@ static void generate_integer_test(struct generator * g, struct node * p) {
1004
1029
  relop ^= 1;
1005
1030
  }
1006
1031
  generate_AE(g, p->left);
1007
- // Relational operators are the same as C.
1008
- write_c_relop(g, relop);
1032
+ write_relop(g, relop);
1009
1033
  generate_AE(g, p->AE);
1010
1034
  if (optimise_to_return) {
1011
1035
  w(g, ";~N");
1012
1036
  } else {
1013
- w(g, ")~N");
1014
- write_block_start(g);
1015
- write_failure(g);
1016
- write_block_end(g);
1037
+ w(g, ") ");
1038
+ write_failure_after_if(g);
1017
1039
  g->unreachable = false;
1018
1040
  }
1019
1041
  }
1020
1042
 
1021
1043
  static void generate_call(struct generator * g, struct node * p) {
1022
- int signals = check_possible_signals_list(g, p->name->definition, c_define, 0);
1044
+ int signals = p->name->definition->possible_signals;
1023
1045
  write_comment(g, p);
1024
- g->V[0] = p->name;
1025
- if (g->failure_label == x_return &&
1026
- (signals == 0 || (p->right && p->right->type == c_functionend))) {
1027
- /* Always fails or tail call. */
1028
- writef(g, "~Mreturn ~V0();~N", p);
1046
+ if (tailcallable(g, p)) {
1047
+ /* Tail call. */
1048
+ writef(g, "~Mreturn ~V();~N", p);
1049
+ p->right = NULL;
1050
+ g->unreachable = true;
1051
+ return;
1052
+ }
1053
+ if (just_return_on_fail(g) && signals == 0) {
1054
+ /* Always fails. */
1055
+ writef(g, "~Mreturn ~V();~N", p);
1056
+ g->unreachable = true;
1029
1057
  return;
1030
1058
  }
1031
1059
  if (signals == 1) {
1032
1060
  /* Always succeeds. */
1033
- writef(g, "~M~V0();~N", p);
1061
+ writef(g, "~M~V();~N", p);
1034
1062
  } else if (signals == 0) {
1035
1063
  /* Always fails. */
1036
- writef(g, "~M~V0();~N", p);
1064
+ writef(g, "~M~V();~N", p);
1037
1065
  write_failure(g);
1038
1066
  } else {
1039
- write_failure_if(g, "!~V0()", p);
1067
+ write_failure_if(g, "!~V()", p);
1040
1068
  }
1041
1069
  }
1042
1070
 
@@ -1046,41 +1074,44 @@ static void generate_grouping(struct generator * g, struct node * p, int complem
1046
1074
  struct grouping * q = p->name->grouping;
1047
1075
  g->S[0] = p->mode == m_forward ? "" : "_b";
1048
1076
  g->S[1] = complement ? "out" : "in";
1049
- g->V[0] = p->name;
1050
1077
  g->I[0] = q->smallest_ch;
1051
1078
  g->I[1] = q->largest_ch;
1052
- write_failure_if(g, "!(base.~S1_grouping~S0(~V0, ~I0, ~I1))", p);
1079
+ if (tailcallable(g, p)) {
1080
+ writef(g, "~Mreturn this.~S1_grouping~S0(~V, ~I0, ~I1);~N", p);
1081
+ p->right = NULL;
1082
+ } else {
1083
+ write_failure_if(g, "!(this.~S1_grouping~S0(~V, ~I0, ~I1))", p);
1084
+ }
1053
1085
  }
1054
1086
 
1055
1087
  static void generate_namedstring(struct generator * g, struct node * p) {
1056
1088
  write_comment(g, p);
1057
1089
  g->S[0] = p->mode == m_forward ? "" : "_b";
1058
- g->V[0] = p->name;
1059
- write_failure_if(g, "!(base.eq_s~S0(~V0))", p);
1090
+ if (tailcallable(g, p)) {
1091
+ writef(g, "~Mreturn this.eq_s~S0(~V);~N", p);
1092
+ p->right = NULL;
1093
+ } else {
1094
+ write_failure_if(g, "!(this.eq_s~S0(~V))", p);
1095
+ }
1060
1096
  }
1061
1097
 
1062
1098
  static void generate_literalstring(struct generator * g, struct node * p) {
1063
- symbol * b = p->literalstring;
1064
1099
  write_comment(g, p);
1065
1100
  g->S[0] = p->mode == m_forward ? "" : "_b";
1066
- g->L[0] = b;
1067
- write_failure_if(g, "!(base.eq_s~S0(~L0))", p);
1101
+ if (tailcallable(g, p)) {
1102
+ writef(g, "~Mreturn this.eq_s~S0(~L);~N", p);
1103
+ p->right = NULL;
1104
+ } else {
1105
+ write_failure_if(g, "!(this.eq_s~S0(~L))", p);
1106
+ }
1068
1107
  }
1069
1108
 
1070
1109
  static void generate_define(struct generator * g, struct node * p) {
1071
- struct name * q = p->name;
1072
- if (q->type == t_routine && !q->used) return;
1073
-
1074
1110
  write_newline(g);
1075
1111
  write_comment(g, p);
1076
1112
 
1077
- g->V[0] = q;
1078
- if (q->type == t_routine) {
1079
- w(g, "~M/** @return {boolean} */~N"
1080
- "~Mfunction ~W0() {~+~N");
1081
- } else {
1082
- w(g, "~Mthis.~W0 = /** @return {boolean} */ function() {~+~N");
1083
- }
1113
+ writef(g, "~M/** @return {boolean} */~N"
1114
+ "~M#~W() {~+~N", p);
1084
1115
 
1085
1116
  /* Save output. */
1086
1117
  struct str * saved_output = g->outbuf;
@@ -1091,24 +1122,67 @@ static void generate_define(struct generator * g, struct node * p) {
1091
1122
  g->next_label = 0;
1092
1123
  g->var_number = 0;
1093
1124
 
1094
- if (p->amongvar_needed) {
1095
- w(g, "~M~D /** number */ among_var;~N");
1125
+ struct name * q = p->name;
1126
+
1127
+ if (q->amongvar_needed) {
1128
+ // The "among var" (`a`) is only assigned to, but the initialisation
1129
+ // can be generated in a nested block so it seems hard to declare it as
1130
+ // const and still have it visible when we want to use it.
1131
+ w(g, "~Mlet /** number */ a;~N");
1132
+ }
1133
+
1134
+ /* Declare localised variables. */
1135
+ for (struct name * name = g->analyser->names; name; name = name->next) {
1136
+ if (name->local_to == q) {
1137
+ switch (name->type) {
1138
+ case t_string:
1139
+ w(g, "~Mlet /** string */ ");
1140
+ write_varname(g, name);
1141
+ w(g, ";~N");
1142
+ break;
1143
+ case t_integer:
1144
+ w(g, "~Mlet /** number */ ");
1145
+ write_varname(g, name);
1146
+ w(g, ";~N");
1147
+ break;
1148
+ case t_boolean:
1149
+ w(g, "~Mlet /** boolean */ ");
1150
+ write_varname(g, name);
1151
+ w(g, ";~N");
1152
+ break;
1153
+ }
1154
+ }
1096
1155
  }
1156
+
1097
1157
  str_clear(g->failure_str);
1098
1158
  g->failure_label = x_return;
1099
1159
  g->unreachable = false;
1100
1160
  g->keep_count = 0;
1101
- int signals = check_possible_signals_list(g, p->left, c_define, 0);
1102
1161
 
1103
1162
  /* Generate function body. */
1104
1163
  generate(g, p->left);
1105
1164
  if (p->left->right) {
1106
1165
  assert(p->left->right->type == c_functionend);
1107
- if (signals) {
1166
+ if (p->left->possible_signals) {
1108
1167
  generate(g, p->left->right);
1109
1168
  }
1110
1169
  }
1111
- w(g, "~-~M};~N");
1170
+ w(g, "~-~M}~N");
1171
+
1172
+ if (q->type == t_external) {
1173
+ w(g, "~N");
1174
+ w(g, "~M/**@return{string}*/~N");
1175
+ writef(g, "~M~W(/**string*/input) {~+~N", p);
1176
+ w(g, "~Mthis.setCurrent(input);~N");
1177
+ writef(g, "~Mthis.#~W();~N", p);
1178
+ w(g, "~Mreturn this.getCurrent();~N");
1179
+ w(g, "~-~M}~N");
1180
+
1181
+ if (SIZE(q->s) == 4 && memcmp(q->s, "stem", 4) == 0) {
1182
+ w(g, "~N");
1183
+ w(g, "~MstemWord = this.stem;~N");
1184
+ }
1185
+ }
1112
1186
 
1113
1187
  str_append(saved_output, g->declarations);
1114
1188
  str_append(saved_output, g->outbuf);
@@ -1132,14 +1206,18 @@ static void generate_substring(struct generator * g, struct node * p) {
1132
1206
  g->I[0] = x->number;
1133
1207
 
1134
1208
  if (x->amongvar_needed) {
1135
- writef(g, "~Mamong_var = base.find_among~S0(a_~I0);~N", p);
1209
+ writef(g, "~Ma = this.find_among~S0(a_~I0~F);~N", p);
1136
1210
  if (!x->always_matches) {
1137
- write_failure_if(g, "among_var == 0", p);
1211
+ write_failure_if(g, "a === 0", p);
1138
1212
  }
1139
1213
  } else if (x->always_matches) {
1140
- writef(g, "~Mbase.find_among~S0(a_~I0);~N", p);
1214
+ writef(g, "~Mthis.find_among~S0(a_~I0~F);~N", p);
1215
+ } else if (x->command_count == 0 && tailcallable(g, p)) {
1216
+ writef(g, "~Mreturn this.find_among~S0(a_~I0~F) !== 0;~N", p);
1217
+ x->node->right = NULL;
1218
+ g->unreachable = true;
1141
1219
  } else {
1142
- write_failure_if(g, "base.find_among~S0(a_~I0) == 0", p);
1220
+ write_failure_if(g, "this.find_among~S0(a_~I0~F) === 0", p);
1143
1221
  }
1144
1222
  }
1145
1223
 
@@ -1156,23 +1234,51 @@ static void generate_among(struct generator * g, struct node * p) {
1156
1234
  /* Only one outcome ("no match" already handled). */
1157
1235
  generate(g, x->commands[0]);
1158
1236
  } else if (x->command_count > 0) {
1159
- w(g, "~Mswitch (among_var) {~N~+");
1237
+ if (x->same_action == c_slicefrom && x->command_count > 1) {
1238
+ if (x->nocommand_count > 0) {
1239
+ w(g, "~Mif (a > 0) {~N~+");
1240
+ }
1241
+ write_comment(g, x->commands[0]);
1242
+ g->I[0] = x->number;
1243
+ w(g, "~Mthis.slice_from(as_~I0[a - 1]);~N");
1244
+ if (x->nocommand_count > 0) {
1245
+ write_block_end(g);
1246
+ }
1247
+ g->unreachable = false;
1248
+ return;
1249
+ }
1250
+
1251
+ w(g, "~Mswitch (a) {~N~+");
1160
1252
  for (int i = 1; i <= x->command_count; i++) {
1161
1253
  g->I[0] = i;
1162
- w(g, "~Mcase ~I0:~N~+");
1254
+ w(g, "~Mcase ~I0: {~N~+");
1163
1255
  generate(g, x->commands[i - 1]);
1164
1256
  if (!g->unreachable) w(g, "~Mbreak;~N");
1165
- w(g, "~-");
1257
+ w(g, "~-~M}~N");
1166
1258
  g->unreachable = false;
1167
1259
  }
1168
1260
  write_block_end(g);
1169
1261
  }
1170
1262
  }
1171
1263
 
1172
- static void generate_booltest(struct generator * g, struct node * p) {
1264
+ static void generate_booltest(struct generator * g, struct node * p, int inverted) {
1173
1265
  write_comment(g, p);
1174
- g->V[0] = p->name;
1175
- write_failure_if(g, "!~V0", p);
1266
+ if (tailcallable(g, p)) {
1267
+ // Optimise at end of function.
1268
+ if (inverted) {
1269
+ writef(g, "~Mreturn !~V;~N", p);
1270
+ } else {
1271
+ writef(g, "~Mreturn ~V;~N", p);
1272
+ }
1273
+ p->right = NULL;
1274
+ g->unreachable = true;
1275
+ return;
1276
+ }
1277
+ if (inverted) {
1278
+ write_failure_if(g, "~V", p);
1279
+ } else {
1280
+ write_failure_if(g, "!~V", p);
1281
+ }
1176
1282
  }
1177
1283
 
1178
1284
  static void generate_false(struct generator * g, struct node * p) {
@@ -1184,7 +1290,7 @@ static void generate_debug(struct generator * g, struct node * p) {
1184
1290
  write_comment(g, p);
1185
1291
  g->I[0] = g->debug_count++;
1186
1292
  g->I[1] = p->line_number;
1187
- writef(g, "~Mbase.debug(~I0, ~I1);~N", p);
1293
+ writef(g, "~Mthis.debug(~I0, ~I1);~N", p);
1188
1294
  }
1189
1295
 
1190
1296
  static void generate(struct generator * g, struct node * p) {
@@ -1217,25 +1323,21 @@ static void generate(struct generator * g, struct node * p) {
1217
1323
  case c_repeat: generate_repeat(g, p); break;
1218
1324
  case c_loop: generate_loop(g, p); break;
1219
1325
  case c_atleast: generate_atleast(g, p); break;
1220
- case c_setmark: generate_setmark(g, p); break;
1221
1326
  case c_tomark: generate_tomark(g, p); break;
1222
- case c_atmark: generate_atmark(g, p); break;
1223
1327
  case c_hop: generate_hop(g, p); break;
1224
- case c_delete: generate_delete(g, p); break;
1225
1328
  case c_next: generate_next(g, p); break;
1226
1329
  case c_tolimit: generate_tolimit(g, p); break;
1227
- case c_atlimit: generate_atlimit(g, p); break;
1228
1330
  case c_leftslice: generate_leftslice(g, p); break;
1229
1331
  case c_rightslice: generate_rightslice(g, p); break;
1230
1332
  case c_assignto: generate_assignto(g, p); break;
1231
1333
  case c_sliceto: generate_sliceto(g, p); break;
1232
- case c_assign: generate_assignfrom(g, p); break;
1334
+ case c_stringassign: generate_stringassign(g, p); break;
1233
1335
  case c_insert:
1234
1336
  case c_attach: generate_insert(g, p, p->type); break;
1235
1337
  case c_slicefrom: generate_slicefrom(g, p); break;
1236
1338
  case c_setlimit: generate_setlimit(g, p); break;
1237
1339
  case c_dollar: generate_dollar(g, p); break;
1238
- case c_mathassign: generate_integer_assign(g, p, "="); break;
1340
+ case c_assign: generate_integer_assign(g, p, "="); break;
1239
1341
  case c_plusassign: generate_integer_assign(g, p, "+="); break;
1240
1342
  case c_minusassign: generate_integer_assign(g, p, "-="); break;
1241
1343
  case c_multiplyassign:generate_integer_assign(g, p, "*="); break;
@@ -1243,8 +1345,7 @@ static void generate(struct generator * g, struct node * p) {
1243
1345
  /* Snowball specifies integer division with semantics matching C,
1244
1346
  * so we need to use `Math.trunc(x/y)` here.
1245
1347
  */
1246
- g->V[0] = p->name;
1247
- w(g, "~M~V0 = Math.trunc(~V0 / ");
1348
+ writef(g, "~M~V = Math.trunc(~V / ", p);
1248
1349
  generate_AE(g, p->AE);
1249
1350
  w(g, ");~N");
1250
1351
  break;
@@ -1263,7 +1364,8 @@ static void generate(struct generator * g, struct node * p) {
1263
1364
  case c_literalstring: generate_literalstring(g, p); break;
1264
1365
  case c_among: generate_among(g, p); break;
1265
1366
  case c_substring: generate_substring(g, p); break;
1266
- case c_booltest: generate_booltest(g, p); break;
1367
+ case c_booltest: generate_booltest(g, p, false); break;
1368
+ case c_not_booltest: generate_booltest(g, p, true); break;
1267
1369
  case c_false: generate_false(g, p); break;
1268
1370
  case c_true: break;
1269
1371
  case c_debug: generate_debug(g, p); break;
@@ -1278,40 +1380,15 @@ static void generate(struct generator * g, struct node * p) {
1278
1380
  }
1279
1381
 
1280
1382
  static void generate_class_begin(struct generator * g) {
1281
- if (g->options->js_esm) {
1282
- w(g, "// deno-lint-ignore-file~N"
1283
- "import ~P from './base-stemmer.mjs'~N"
1284
- "~N"
1285
- "/** @typedef {{ stemWord(word: string): string }} Stemmer */~N"
1286
- "~N"
1287
- "/** @type {{ new(): Stemmer }} */~N"
1288
- "~C ~n = function() {~+~N"
1289
- "~M~D base = new ~P();~N");
1290
- } else {
1291
- w(g, "/**@constructor*/~N"
1292
- "~D ~n = function() {~+~N"
1293
- "~M~C ~P = require('./base-stemmer.js');~N"
1294
- "~M~D base = new ~P();~N");
1295
- }
1383
+ w(g, "import ~P from './base-stemmer.js'~N"
1384
+ "~N"
1385
+ "export default class extends ~P {~+~N");
1296
1386
  write_newline(g);
1297
1387
  }
1298
1388
 
1299
1389
  static void generate_class_end(struct generator * g) {
1390
+ w(g, "~-}~N");
1300
1391
  w(g, "~N");
1301
- w(g, "~M/**@return{string}*/~N");
1302
- w(g, "~Mthis['stemWord'] = function(/**string*/word) {~+~N");
1303
- w(g, "~Mbase.setCurrent(word);~N");
1304
- w(g, "~Mthis.stem();~N");
1305
- w(g, "~Mreturn base.getCurrent();~N");
1306
- w(g, "~-~M};~N");
1307
- w(g, "~-};~N");
1308
- if (g->options->js_esm) {
1309
- w(g, "~N"
1310
- "export default ~n~N");
1311
- } else {
1312
- w(g, "~N"
1313
- "if (typeof module === 'object' && module.exports) module.exports = ~n;~N");
1314
- }
1315
1392
  }
1316
1393
 
1317
1394
  static void generate_among_table(struct generator * g, struct among * x) {
@@ -1320,22 +1397,36 @@ static void generate_among_table(struct generator * g, struct among * x) {
1320
1397
  struct amongvec * v = x->b;
1321
1398
 
1322
1399
  g->I[0] = x->number;
1323
- w(g, "~M~C a_~I0 = [~N~+");
1400
+ w(g, "~Mconst a_~I0 = [~N~+");
1324
1401
 
1325
1402
  for (int i = 0; i < x->literalstring_count; i++) {
1326
- g->I[0] = v[i].i;
1327
- g->I[1] = v[i].result;
1328
- g->L[0] = v[i].b;
1329
- g->S[0] = i < x->literalstring_count - 1 ? "," : "";
1403
+ if (i) w(g, ",~N");
1404
+ g->I[0] = v[i].result;
1330
1405
 
1331
- w(g, "~M[~L0, ~I0, ~I1");
1406
+ w(g, "~M[");
1407
+ write_literal_string(g, v[i].b);
1408
+ w(g, ", ~I0");
1409
+ if (v[i].i >= 0 || v[i].function != NULL) {
1410
+ w(g, ", ");
1411
+ write_int(g, v[i].i >= 0 ? i - v[i].i : 0);
1412
+ }
1332
1413
  if (v[i].function != NULL) {
1333
1414
  w(g, ", ");
1334
- write_varname(g, v[i].function);
1415
+ write_int(g, v[i].function_index);
1416
+ }
1417
+ w(g, "]");
1418
+ }
1419
+ w(g, "~N~-~M];~N~N");
1420
+
1421
+ if (x->same_action == c_slicefrom && x->command_count > 1) {
1422
+ g->I[0] = x->number;
1423
+ w(g, "~Mconst /** Array<string> */ as_~I0 = [");
1424
+ for (int i = 1; i <= x->command_count; i++) {
1425
+ if (i > 1) w(g, ", ");
1426
+ write_literal_string(g, x->commands[i - 1]->left->literalstring);
1335
1427
  }
1336
- w(g, "]~S0~N");
1428
+ w(g, "];~N~N");
1337
1429
  }
1338
- w(g, "~-~M];~N~N");
1339
1430
  }
1340
1431
 
1341
1432
  static void generate_amongs(struct generator * g) {
@@ -1344,11 +1435,45 @@ static void generate_amongs(struct generator * g) {
1344
1435
  }
1345
1436
  }
1346
1437
 
1347
- static void set_bit(symbol * b, int i) { b[i/8] |= 1 << i%8; }
1438
+ static void generate_among_dispatcher(struct generator * g, struct among * x) {
1439
+ if (x->function_count <= 1) return;
1440
+
1441
+ struct amongvec * v = x->b;
1442
+
1443
+ write_comment(g, x->node);
1444
+
1445
+ g->I[0] = x->number;
1446
+ w(g, "~N~M/** @return {boolean} */~N");
1447
+ w(g, "~M#af_~I0() {~N~+");
1448
+ w(g, "~Mswitch (this.af) {~N~+");
1449
+ for (int n = 1; n <= x->function_count; n++) {
1450
+ w(g, "~Mcase ");
1451
+ write_int(g, n);
1452
+ w(g, ": return ");
1453
+ for (int i = 0; i < x->literalstring_count; i++) {
1454
+ if (v[i].function_index == n) {
1455
+ write_varref(g, v[i].function);
1456
+ w(g, "();~N");
1457
+ break;
1458
+ }
1459
+ }
1460
+ }
1461
+ w(g, "~-~M}~N");
1462
+ w(g, "~Mreturn false;~N");
1463
+ w(g, "~-~M}~N");
1464
+ }
1465
+
1466
+ static void generate_among_dispatchers(struct generator * g) {
1467
+ for (struct among * x = g->analyser->amongs; x; x = x->next) {
1468
+ generate_among_dispatcher(g, x);
1469
+ }
1470
+ }
1471
+
1472
+ static void set_bit(symbol * b, int i) { b[i >> 3] |= 1 << (i & 7); }
1348
1473
 
1349
1474
  static void generate_grouping_table(struct generator * g, struct grouping * q) {
1350
1475
  int range = q->largest_ch - q->smallest_ch + 1;
1351
- int size = (range + 7)/ 8; /* assume 8 bits per symbol */
1476
+ int size = (range + 7) / 8; /* assume 8 bits per symbol */
1352
1477
  symbol * b = q->b;
1353
1478
  symbol * map = create_b(size);
1354
1479
 
@@ -1356,11 +1481,12 @@ static void generate_grouping_table(struct generator * g, struct grouping * q) {
1356
1481
 
1357
1482
  for (int i = 0; i < SIZE(b); i++) set_bit(map, b[i] - q->smallest_ch);
1358
1483
 
1359
- g->V[0] = q->name;
1360
- w(g, "~M~C /** Array<int> */ ~W0 = [");
1484
+ w(g, "~Mconst /** Array<number> */ ");
1485
+ write_varname(g, q->name);
1486
+ write_string(g, " = [");
1361
1487
  for (int i = 0; i < size; i++) {
1488
+ if (i) w(g, ", ");
1362
1489
  write_int(g, map[i]);
1363
- if (i < size - 1) w(g, ", ");
1364
1490
  }
1365
1491
  w(g, "];~N~N");
1366
1492
 
@@ -1369,27 +1495,32 @@ static void generate_grouping_table(struct generator * g, struct grouping * q) {
1369
1495
 
1370
1496
  static void generate_groupings(struct generator * g) {
1371
1497
  for (struct grouping * q = g->analyser->groupings; q; q = q->next) {
1372
- if (q->name->used)
1373
- generate_grouping_table(g, q);
1498
+ generate_grouping_table(g, q);
1374
1499
  }
1375
1500
  }
1376
1501
 
1377
1502
  static void generate_members(struct generator * g) {
1378
- int wrote_members = false;
1503
+ bool wrote_members = false;
1379
1504
 
1380
1505
  for (struct name * q = g->analyser->names; q; q = q->next) {
1381
- g->V[0] = q;
1506
+ if (q->local_to) continue;
1382
1507
  switch (q->type) {
1383
1508
  case t_string:
1384
- w(g, "~M~D /** string */ ~W0 = '';~N");
1509
+ w(g, "~M#");
1510
+ write_varname(g, q);
1511
+ w(g, "/** string */ = '';~N");
1385
1512
  wrote_members = true;
1386
1513
  break;
1387
1514
  case t_integer:
1388
- w(g, "~M~D /** number */ ~W0 = 0;~N");
1515
+ w(g, "~M#");
1516
+ write_varname(g, q);
1517
+ w(g, "/** number */ = 0;~N");
1389
1518
  wrote_members = true;
1390
1519
  break;
1391
1520
  case t_boolean:
1392
- w(g, "~M~D /** boolean */ ~W0 = false;~N");
1521
+ w(g, "~M#");
1522
+ write_varname(g, q);
1523
+ w(g, "/** boolean */ = false;~N");
1393
1524
  wrote_members = true;
1394
1525
  break;
1395
1526
  }
@@ -1410,12 +1541,30 @@ extern void generate_program_js(struct generator * g) {
1410
1541
 
1411
1542
  write_start_comment(g, "// ", NULL);
1412
1543
 
1413
- generate_class_begin(g);
1544
+ // Disable some deno-lint warnings which the generated code can trigger.
1545
+ w(g, "// deno-lint-ignore-file"
1546
+ // Some of our generated deno-lint-ignore comments may not be used.
1547
+ " ban-unused-ignore"
1548
+ // Expressions in conditionals may be constant.
1549
+ " no-constant-condition"
1550
+ // Empty blocks may be generated in some cases.
1551
+ " no-empty"
1552
+ // Among var `a` is only assigned to once per `among`, but is declared
1553
+ // at the start of the function and may be initialised in a nested
1554
+ // block.
1555
+ //
1556
+ // Some localised variables may only be assigned to once.
1557
+ " prefer-const~N~N");
1414
1558
 
1415
1559
  generate_amongs(g);
1416
1560
  generate_groupings(g);
1417
1561
 
1562
+ generate_class_begin(g);
1563
+
1418
1564
  generate_members(g);
1565
+
1566
+ generate_among_dispatchers(g);
1567
+
1419
1568
  generate_methods(g);
1420
1569
 
1421
1570
  generate_class_end(g);