mittens 0.3.1 → 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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +1 -0
  4. data/README.md +2 -2
  5. data/Rakefile +10 -3
  6. data/lib/mittens/version.rb +1 -1
  7. data/vendor/snowball/.github/workflows/ci.yml +144 -17
  8. data/vendor/snowball/.github/workflows/coverage.yml +117 -0
  9. data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
  10. data/vendor/snowball/.gitignore +33 -7
  11. data/vendor/snowball/CONTRIBUTING.rst +7 -0
  12. data/vendor/snowball/COPYING +1 -1
  13. data/vendor/snowball/GNUmakefile +736 -298
  14. data/vendor/snowball/NEWS +1394 -7
  15. data/vendor/snowball/README.rst +8 -8
  16. data/vendor/snowball/ada/generate/generate.adb +5 -5
  17. data/vendor/snowball/ada/src/stemmer.adb +177 -188
  18. data/vendor/snowball/ada/src/stemmer.ads +86 -89
  19. data/vendor/snowball/ada/src/stemwords.adb +21 -5
  20. data/vendor/snowball/algorithms/czech.sbl +255 -0
  21. data/vendor/snowball/algorithms/danish.sbl +22 -10
  22. data/vendor/snowball/algorithms/english.sbl +7 -4
  23. data/vendor/snowball/algorithms/esperanto.sbl +6 -7
  24. data/vendor/snowball/algorithms/estonian.sbl +9 -4
  25. data/vendor/snowball/algorithms/finnish.sbl +33 -21
  26. data/vendor/snowball/algorithms/german.sbl +5 -0
  27. data/vendor/snowball/algorithms/indonesian.sbl +115 -96
  28. data/vendor/snowball/algorithms/italian.sbl +26 -1
  29. data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
  30. data/vendor/snowball/algorithms/norwegian.sbl +17 -6
  31. data/vendor/snowball/algorithms/persian.sbl +387 -0
  32. data/vendor/snowball/algorithms/polish.sbl +177 -0
  33. data/vendor/snowball/algorithms/sesotho.sbl +115 -0
  34. data/vendor/snowball/algorithms/turkish.sbl +4 -4
  35. data/vendor/snowball/compiler/analyser.c +2002 -711
  36. data/vendor/snowball/compiler/driver.c +460 -298
  37. data/vendor/snowball/compiler/generator.c +439 -1965
  38. data/vendor/snowball/compiler/generator_ada.c +605 -430
  39. data/vendor/snowball/compiler/generator_c.c +2227 -0
  40. data/vendor/snowball/compiler/generator_csharp.c +365 -274
  41. data/vendor/snowball/compiler/generator_dart.c +1476 -0
  42. data/vendor/snowball/compiler/generator_go.c +348 -270
  43. data/vendor/snowball/compiler/generator_java.c +330 -233
  44. data/vendor/snowball/compiler/generator_js.c +534 -385
  45. data/vendor/snowball/compiler/generator_pascal.c +361 -320
  46. data/vendor/snowball/compiler/generator_php.c +1445 -0
  47. data/vendor/snowball/compiler/generator_python.c +399 -299
  48. data/vendor/snowball/compiler/generator_rust.c +370 -261
  49. data/vendor/snowball/compiler/generator_zig.c +1474 -0
  50. data/vendor/snowball/compiler/header.h +153 -86
  51. data/vendor/snowball/compiler/space.c +121 -63
  52. data/vendor/snowball/compiler/tokeniser.c +286 -182
  53. data/vendor/snowball/compiler/tokens.h +71 -0
  54. data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
  55. data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
  56. data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
  57. data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
  58. data/vendor/snowball/cxx/.gitignore +4 -0
  59. data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
  60. data/vendor/snowball/cxx/stemmer.h +12 -0
  61. data/vendor/snowball/cxx/stemwords.cxx +176 -0
  62. data/vendor/snowball/cxx/utilities.cxx +2 -0
  63. data/vendor/snowball/dart/.gitignore +4 -0
  64. data/vendor/snowball/dart/analysis_options.yaml +1 -0
  65. data/vendor/snowball/dart/example/test_app.dart +65 -0
  66. data/vendor/snowball/dart/generate_algorithms.pl +21 -0
  67. data/vendor/snowball/dart/lib/snowball.dart +18 -0
  68. data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
  69. data/vendor/snowball/dart/pubspec.yaml +17 -0
  70. data/vendor/snowball/doc/libstemmer_dart_README +37 -0
  71. data/vendor/snowball/doc/libstemmer_js_README +2 -2
  72. data/vendor/snowball/doc/libstemmer_php_README +38 -0
  73. data/vendor/snowball/doc/libstemmer_python_README +3 -3
  74. data/vendor/snowball/examples/stemwords.c +18 -3
  75. data/vendor/snowball/go/README.md +12 -4
  76. data/vendor/snowball/go/env.go +12 -14
  77. data/vendor/snowball/go/stemwords/main.go +2 -1
  78. data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
  79. data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
  80. data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
  81. data/vendor/snowball/javascript/base-stemmer.js +219 -252
  82. data/vendor/snowball/javascript/stemwords.js +86 -58
  83. data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
  84. data/vendor/snowball/libstemmer/modules.txt +4 -0
  85. data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
  86. data/vendor/snowball/php/base-stemmer.php +453 -0
  87. data/vendor/snowball/php/stemwords.php +25 -0
  88. data/vendor/snowball/python/create_init.py +16 -21
  89. data/vendor/snowball/python/pyproject.toml +3 -0
  90. data/vendor/snowball/python/setup.py +5 -7
  91. data/vendor/snowball/python/snowballstemmer/among.py +1 -2
  92. data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
  93. data/vendor/snowball/python/stemwords.py +72 -63
  94. data/vendor/snowball/runtime/api.c +11 -42
  95. data/vendor/snowball/runtime/api.h +11 -9
  96. data/vendor/snowball/runtime/snowball_runtime.h +110 -0
  97. data/vendor/snowball/runtime/utilities.c +282 -106
  98. data/vendor/snowball/rust/src/main.rs +2 -2
  99. data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
  100. data/vendor/snowball/tests/compilertest +62 -0
  101. data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
  102. data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
  103. data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
  104. data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
  105. data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
  106. data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
  107. data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
  108. data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
  109. data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
  110. data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
  111. data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
  112. data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
  113. data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
  114. data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
  115. data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
  116. data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
  117. data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
  118. data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
  119. data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
  120. data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
  121. data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
  122. data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
  123. data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
  124. data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
  125. data/vendor/snowball/tests/runtime/among.sbl +19 -0
  126. data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
  127. data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
  128. data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
  129. data/vendor/snowball/tests/runtime/externals.sbl +22 -0
  130. data/vendor/snowball/tests/runtime/hop.sbl +12 -0
  131. data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
  132. data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
  133. data/vendor/snowball/tests/runtime/naming.sbl +20 -0
  134. data/vendor/snowball/tests/runtime/not.sbl +30 -0
  135. data/vendor/snowball/tests/runtime/or.sbl +17 -0
  136. data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
  137. data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
  138. data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
  139. data/vendor/snowball/tests/runtime/slice.sbl +27 -0
  140. data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
  141. data/vendor/snowball/tests/runtime/strings.sbl +66 -0
  142. data/vendor/snowball/tests/runtime/test.sbl +19 -0
  143. data/vendor/snowball/tests/stemtest.c +41 -1
  144. data/vendor/snowball/tests/syntax/canon.sbl +32 -0
  145. data/vendor/snowball/tests/syntax/canon.stderr +0 -0
  146. data/vendor/snowball/tests/syntax/canon.syntax +57 -0
  147. data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
  148. data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
  149. data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
  150. data/vendor/snowball/tests/syntax/inline.sbl +144 -0
  151. data/vendor/snowball/tests/syntax/inline.stderr +0 -0
  152. data/vendor/snowball/tests/syntax/inline.syntax +121 -0
  153. data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
  154. data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
  155. data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
  156. data/vendor/snowball/tests/syntax/loops.sbl +14 -0
  157. data/vendor/snowball/tests/syntax/loops.stderr +9 -0
  158. data/vendor/snowball/tests/syntax/loops.syntax +29 -0
  159. data/vendor/snowball/tests/syntax/noops.sbl +76 -0
  160. data/vendor/snowball/tests/syntax/noops.stderr +27 -0
  161. data/vendor/snowball/tests/syntax/noops.syntax +135 -0
  162. data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
  163. data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
  164. data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
  165. data/vendor/snowball/tests/syntax/unused.sbl +18 -0
  166. data/vendor/snowball/tests/syntax/unused.stderr +7 -0
  167. data/vendor/snowball/tests/syntax/unused.syntax +3 -0
  168. data/vendor/snowball/zig/env.zig +599 -0
  169. data/vendor/snowball/zig/generate_algorithms.pl +19 -0
  170. data/vendor/snowball/zig/stemwords.zig +123 -0
  171. metadata +102 -4
  172. data/vendor/snowball/compiler/syswords.h +0 -86
  173. data/vendor/snowball/runtime/header.h +0 -62
@@ -1,42 +1,18 @@
1
1
  #include <assert.h>
2
+ #include <ctype.h>
2
3
  #include <limits.h> /* for INT_MAX */
3
4
  #include <stdio.h> /* printf etc */
4
5
  #include <stdlib.h> /* exit */
5
6
  #include <string.h> /* memmove */
6
7
  #include "header.h"
7
8
 
8
- typedef enum {
9
- e_token_omitted = 0,
10
- e_unexpected_token = 1,
11
- e_string_omitted = 2,
12
- e_unexpected_token_in_among = 3,
13
- /* For codes above here, report "after " t->previous_token after the error. */
14
- e_unresolved_substring = 14,
15
- e_not_allowed_inside_reverse = 15,
16
- e_empty_grouping = 16,
17
- e_already_backwards = 17,
18
- e_empty_among = 18,
19
- e_adjacent_bracketed_in_among = 19,
20
- e_substring_preceded_by_substring = 20,
21
- /* For codes below here, tokeniser->s is printed before the error. */
22
- e_redeclared = 30,
23
- e_undeclared = 31,
24
- e_declared_as_different_mode = 32,
25
- e_not_of_type_x = 33,
26
- e_not_of_type_string_or_integer = 34,
27
- e_misplaced = 35,
28
- e_redefined = 36,
29
- e_misused = 37
30
- } error_code;
31
-
32
9
  /* recursive usage: */
33
10
 
34
11
  static void read_program_(struct analyser * a, int terminator);
35
12
  static struct node * read_C(struct analyser * a);
36
- static struct node * C_style(struct analyser * a, const char * s, int token);
37
-
13
+ static struct node * new_string_command(struct analyser * a, int token);
38
14
 
39
- static void print_node_(struct node * p, int n, const char * s) {
15
+ static void print_node_(const struct node * p, int n, const char * s) {
40
16
  printf("%*s%s", n * 2, s, name_of_token(p->type));
41
17
  if (p->name) {
42
18
  putchar(' ');
@@ -57,24 +33,24 @@ static void print_node_(struct node * p, int n, const char * s) {
57
33
  }
58
34
 
59
35
  extern void print_program(struct analyser * a) {
60
- print_node_(a->program, 0, "");
36
+ if (a->program) print_node_(a->program, 0, "");
61
37
  }
62
38
 
63
- static struct node * new_node(struct analyser * a, int type) {
39
+ static struct node * new_node_at_line(struct analyser * a, int type, int line) {
64
40
  NEW(node, p);
65
- p->next = a->nodes; a->nodes = p;
66
- p->left = NULL;
67
- p->right = NULL;
68
- p->aux = NULL;
69
- p->AE = NULL;
70
- p->name = NULL;
71
- p->literalstring = NULL;
41
+ *p = (struct node){0};
72
42
  p->mode = a->mode;
73
- p->line_number = a->tokeniser->line_number;
43
+ p->line_number = line;
74
44
  p->type = type;
45
+ p->next = a->nodes;
46
+ a->nodes = p;
75
47
  return p;
76
48
  }
77
49
 
50
+ static struct node * new_node(struct analyser * a, int type) {
51
+ return new_node_at_line(a, type, a->tokeniser->line_number);
52
+ }
53
+
78
54
  static const char * name_of_mode(int n) {
79
55
  switch (n) {
80
56
  case m_backward: return "string backward";
@@ -84,20 +60,7 @@ static const char * name_of_mode(int n) {
84
60
  exit(1);
85
61
  }
86
62
 
87
- static const char * name_of_type(int n) {
88
- switch (n) {
89
- case 'b': return "boolean";
90
- case 's': return "string";
91
- case 'i': return "integer";
92
- case 'r': return "routine";
93
- case 'R': return "routine or grouping";
94
- case 'g': return "grouping";
95
- }
96
- fprintf(stderr, "Invalid type %d in name_of_type()\n", n);
97
- exit(1);
98
- }
99
-
100
- static const char * name_of_name_type(int code) {
63
+ static const char * name_of_type(int code) {
101
64
  switch (code) {
102
65
  case t_string: return "string";
103
66
  case t_boolean: return "boolean";
@@ -106,7 +69,7 @@ static const char * name_of_name_type(int code) {
106
69
  case t_external: return "external";
107
70
  case t_grouping: return "grouping";
108
71
  }
109
- fprintf(stderr, "Invalid type code %d in name_of_name_type()\n", code);
72
+ fprintf(stderr, "Invalid type code %d in name_of_type()\n", code);
110
73
  exit(1);
111
74
  }
112
75
 
@@ -116,108 +79,99 @@ static void count_error(struct analyser * a) {
116
79
  t->error_count++;
117
80
  }
118
81
 
119
- static void error2(struct analyser * a, error_code n, int x) {
82
+ static void report_error_location_line(struct analyser * a, int line) {
120
83
  struct tokeniser * t = a->tokeniser;
121
- if (n == e_unexpected_token && t->token_reported_as_unexpected) {
122
- // Avoid duplicate errors if this token was already reported as
123
- // unexpected and then held.
124
- return;
125
- }
126
84
  count_error(a);
127
- fprintf(stderr, "%s:%d: ", t->file, t->line_number);
128
- if ((int)n >= (int)e_redeclared) report_s(stderr, t->s);
129
- switch (n) {
130
- case e_token_omitted:
131
- fprintf(stderr, "%s omitted", name_of_token(t->omission)); break;
132
- case e_unexpected_token_in_among:
133
- fprintf(stderr, "in among(...), ");
134
- /* fall through */
135
- case e_unexpected_token:
136
- t->token_reported_as_unexpected = true;
137
- fprintf(stderr, "unexpected %s", name_of_token(t->token));
138
- if (t->token == c_number) fprintf(stderr, " %d", t->number);
139
- if (t->token == c_name) {
140
- t->s[SIZE(t->s)] = 0;
141
- fprintf(stderr, " %s", t->s);
142
- }
143
- break;
144
- case e_string_omitted:
145
- fprintf(stderr, "string omitted"); break;
146
-
147
- case e_unresolved_substring:
148
- fprintf(stderr, "unresolved substring on line %d", x); break;
149
- case e_not_allowed_inside_reverse:
150
- fprintf(stderr, "%s not allowed inside reverse(...)", name_of_token(t->token)); break;
151
- case e_empty_grouping:
152
- fprintf(stderr, "empty grouping"); break;
153
- case e_already_backwards:
154
- fprintf(stderr, "backwards used when already in this mode"); break;
155
- case e_empty_among:
156
- fprintf(stderr, "empty among(...)"); break;
157
- case e_adjacent_bracketed_in_among:
158
- fprintf(stderr, "two adjacent bracketed expressions in among(...)"); break;
159
- case e_substring_preceded_by_substring:
160
- fprintf(stderr, "substring preceded by another substring on line %d", x); break;
161
-
162
- case e_redeclared:
163
- fprintf(stderr, " re-declared"); break;
164
- case e_undeclared:
165
- fprintf(stderr, " undeclared"); break;
166
- case e_declared_as_different_mode:
167
- fprintf(stderr, " declared as %s mode; used as %s mode",
168
- name_of_mode(a->mode), name_of_mode(x)); break;
169
- case e_not_of_type_x:
170
- fprintf(stderr, " not of type %s", name_of_type(x)); break;
171
- case e_not_of_type_string_or_integer:
172
- fprintf(stderr, " not of type string or integer"); break;
173
- case e_misplaced:
174
- fprintf(stderr, " misplaced"); break;
175
- case e_redefined:
176
- fprintf(stderr, " redefined"); break;
177
- case e_misused:
178
- fprintf(stderr, " mis-used as %s mode",
179
- name_of_mode(x)); break;
180
- }
181
- if ((int)n < (int)e_unresolved_substring && t->previous_token > 0)
182
- fprintf(stderr, " after %s", name_of_token(t->previous_token));
183
- fprintf(stderr, "\n");
85
+ fprintf(stderr, "%s:%d: ", t->file, line);
184
86
  }
185
87
 
186
- static void error(struct analyser * a, error_code n) { error2(a, n, 0); }
88
+ static void report_error_location(struct analyser * a) {
89
+ struct tokeniser * t = a->tokeniser;
90
+ report_error_location_line(a, t->line_number);
91
+ }
187
92
 
188
- static void error4(struct analyser * a, struct name * q) {
189
- count_error(a);
190
- q->s[SIZE(q->s)] = 0;
191
- fprintf(stderr, "%s:%d: %s undefined\n", a->tokeniser->file, q->used->line_number, q->s);
93
+ static void report_error_after(struct analyser * a) {
94
+ struct tokeniser * t = a->tokeniser;
95
+ if (t->previous_token > 0)
96
+ fprintf(stderr, " after %s", name_of_token(t->previous_token));
192
97
  }
193
98
 
194
99
  static void omission_error(struct analyser * a, int n) {
195
- a->tokeniser->omission = n;
196
- error(a, e_token_omitted);
100
+ report_error_location(a);
101
+ fprintf(stderr, "%s omitted", name_of_token(n));
102
+ report_error_after(a);
103
+ putc('\n', stderr);
104
+ }
105
+
106
+ static void unexpected_token_error(struct analyser * a,
107
+ const char * context) {
108
+ struct tokeniser * t = a->tokeniser;
109
+ if (t->token_reported_as_unexpected) {
110
+ // Avoid duplicate errors if this token was already reported as
111
+ // unexpected and then held.
112
+ return;
113
+ }
114
+ report_error_location(a);
115
+ t->token_reported_as_unexpected = true;
116
+ fprintf(stderr, "unexpected %s", name_of_token(t->token));
117
+ if (t->token == c_number) fprintf(stderr, " %d", t->number);
118
+ if (t->token == c_name) {
119
+ fprintf(stderr, " %.*s", SIZE(t->s), t->s);
120
+ }
121
+ if (context) {
122
+ fprintf(stderr, " in %s", context);
123
+ }
124
+ report_error_after(a);
125
+ putc('\n', stderr);
126
+ // If the token is `)` then always hold it as the actual problem is almost
127
+ // certainly another token missing before it.
128
+ if (t->token == c_ket) hold_token(t);
129
+ }
130
+
131
+ static void substring_without_among_error(struct analyser * a) {
132
+ count_error(a);
133
+ fprintf(stderr, "%s:%d: 'substring' with no matching 'among'\n",
134
+ a->tokeniser->file, a->substring->line_number);
197
135
  }
198
136
 
199
137
  static int check_token(struct analyser * a, int code) {
200
138
  struct tokeniser * t = a->tokeniser;
201
- if (t->token != code) { omission_error(a, code); return false; }
139
+ if (t->token != code) {
140
+ omission_error(a, code);
141
+ hold_token(t);
142
+ return false;
143
+ }
202
144
  return true;
203
145
  }
204
146
 
147
+ static void hold_token_if_toplevel(struct tokeniser * t) {
148
+ // Hold token if it starts a top-level construct.
149
+ switch (t->token) {
150
+ case c_backwardmode:
151
+ case c_booleans:
152
+ case c_define:
153
+ case c_externals:
154
+ case c_groupings:
155
+ case c_integers:
156
+ case c_routines:
157
+ case c_strings:
158
+ hold_token(t);
159
+ }
160
+ }
161
+
205
162
  static int get_token(struct analyser * a, int code) {
206
163
  struct tokeniser * t = a->tokeniser;
207
164
  read_token(t);
208
- int x = check_token(a, code);
209
- if (!x) hold_token(t);
210
- return x;
165
+ return check_token(a, code);
211
166
  }
212
167
 
213
168
  static struct name * look_for_name(struct analyser * a) {
214
169
  const byte * q = a->tokeniser->s;
215
- struct name * p;
216
- for (p = a->names; p; p = p->next) {
170
+ for (struct name * p = a->names; p; p = p->next) {
217
171
  byte * b = p->s;
218
172
  int n = SIZE(b);
219
173
  if (n == SIZE(q) && memcmp(q, b, n) == 0) {
220
- p->referenced = true;
174
+ ++p->references;
221
175
  return p;
222
176
  }
223
177
  }
@@ -226,37 +180,34 @@ static struct name * look_for_name(struct analyser * a) {
226
180
 
227
181
  static struct name * find_name(struct analyser * a) {
228
182
  struct name * p = look_for_name(a);
229
- if (p == NULL) error(a, e_undeclared);
183
+ if (p == NULL) {
184
+ report_error_location(a);
185
+ byte * s = a->tokeniser->s;
186
+ fprintf(stderr, "'%.*s' undeclared\n", SIZE(s), s);
187
+ }
230
188
  return p;
231
189
  }
232
190
 
233
191
  static void check_routine_mode(struct analyser * a, struct name * p, int mode) {
234
- if (p->mode == m_unknown) p->mode = mode; else
235
- if (p->mode != mode) error2(a, e_misused, mode);
192
+ if (p->mode == m_unknown) {
193
+ p->mode = mode;
194
+ } else if (p->mode != mode) {
195
+ report_error_location(a);
196
+ fprintf(stderr, "%s '%.*s' mis-used in %s mode\n",
197
+ name_of_type(p->type),
198
+ SIZE(p->s), p->s,
199
+ name_of_mode(mode));
200
+ }
236
201
  }
237
202
 
238
- static void check_name_type(struct analyser * a, struct name * p, int type) {
239
- switch (type) {
240
- case 's':
241
- if (p->type == t_string) return;
242
- break;
243
- case 'i':
244
- if (p->type == t_integer) return;
245
- break;
246
- case 'b':
247
- if (p->type == t_boolean) return;
248
- break;
249
- case 'R':
250
- if (p->type == t_grouping) return;
251
- /* FALLTHRU */
252
- case 'r':
253
- if (p->type == t_routine || p->type == t_external) return;
254
- break;
255
- case 'g':
256
- if (p->type == t_grouping) return;
257
- break;
258
- }
259
- error2(a, e_not_of_type_x, type);
203
+ static int check_name_type(struct analyser * a, struct name * p, int type) {
204
+ if (p->type == type) return true;
205
+ if (type == t_routine && p->type == t_external) return true;
206
+ report_error_location(a);
207
+ fprintf(stderr, "'%.*s' not of type %s\n",
208
+ SIZE(p->s), p->s,
209
+ type == t_routine ? "routine or external" : name_of_type(type));
210
+ return false;
260
211
  }
261
212
 
262
213
  static void read_names(struct analyser * a, int type) {
@@ -270,7 +221,7 @@ static void read_names(struct analyser * a, int type) {
270
221
  * its special meaning, for compatibility with older versions
271
222
  * of snowball.
272
223
  */
273
- SIZE(t->s) = 0;
224
+ SET_SIZE(t->s, 0);
274
225
  t->s = add_literal_to_s(t->s, "len");
275
226
  goto handle_as_name;
276
227
  }
@@ -279,39 +230,57 @@ static void read_names(struct analyser * a, int type) {
279
230
  * its special meaning, for compatibility with older versions
280
231
  * of snowball.
281
232
  */
282
- SIZE(t->s) = 0;
233
+ SET_SIZE(t->s, 0);
283
234
  t->s = add_literal_to_s(t->s, "lenof");
284
235
  goto handle_as_name;
285
236
  }
286
237
  case c_name:
287
238
  handle_as_name:
288
- if (look_for_name(a) != NULL) error(a, e_redeclared); else {
239
+ if (token != c_name) {
240
+ disable_token(t, token);
241
+ }
242
+ if (look_for_name(a) != NULL) {
243
+ report_error_location(a);
244
+ fprintf(stderr, "'%.*s' re-declared\n", SIZE(t->s), t->s);
245
+ } else {
289
246
  NEW(name, p);
247
+ *p = (struct name){0};
248
+ p->mode = m_unknown; /* used for routines, externals */
290
249
  p->s = copy_s(t->s);
291
250
  p->type = type;
292
- p->mode = m_unknown; /* used for routines, externals */
293
- /* We defer assigning counts until after we've eliminated
294
- * variables whose values are never used. */
251
+ /* Delay assigning counts until after we've eliminated
252
+ * variables whose values are never used and checked for
253
+ * variables which can be localised.
254
+ */
295
255
  p->count = -1;
296
- p->referenced = false;
297
- p->used_in_among = false;
298
- p->used = NULL;
299
- p->value_used = false;
300
- p->initialised = false;
301
- p->used_in_definition = false;
302
- p->local_to = NULL;
303
- p->grouping = NULL;
304
- p->definition = NULL;
305
256
  p->declaration_line_number = t->line_number;
257
+ // Check if any existing names of the same type differ
258
+ // only by case - if we find one we set a flag so we know
259
+ // to mangle this name for languages with case-insensitive
260
+ // identifiers. (Note that the first declared name of any
261
+ // group of colliding names collision doesn't get this flag
262
+ // set so won't get mangled.)
263
+ for (struct name * q = a->names; q; q = q->next) {
264
+ if (q->type != type) continue;
265
+ byte * b = q->s;
266
+ int n = SIZE(b);
267
+ if (n != SIZE(p->s)) continue;
268
+ for (int i = 0; i < n; ++i) {
269
+ if (tolower(p->s[i]) != tolower(b[i]))
270
+ goto next_name;
271
+ }
272
+ p->case_collision = true;
273
+ goto done_case_check;
274
+
275
+ next_name: ;
276
+ }
277
+ done_case_check:
306
278
  p->next = a->names;
307
279
  a->names = p;
308
- if (token != c_name) {
309
- disable_token(t, token);
310
- }
311
280
  }
312
281
  break;
313
282
  default:
314
- if (!check_token(a, c_ket)) hold_token(t);
283
+ check_token(a, c_ket);
315
284
  return;
316
285
  }
317
286
  }
@@ -328,7 +297,7 @@ static symbol * new_literalstring(struct analyser * a) {
328
297
  static int read_AE_test(struct analyser * a) {
329
298
  struct tokeniser * t = a->tokeniser;
330
299
  switch (read_token(t)) {
331
- case c_assign: return c_mathassign;
300
+ case c_assign:
332
301
  case c_plusassign:
333
302
  case c_minusassign:
334
303
  case c_multiplyassign:
@@ -338,9 +307,10 @@ static int read_AE_test(struct analyser * a) {
338
307
  case c_gt:
339
308
  case c_ge:
340
309
  case c_lt:
341
- case c_le: return t->token;
310
+ case c_le:
311
+ return t->token;
342
312
  default:
343
- error(a, e_unexpected_token);
313
+ unexpected_token_error(a, "integer test expression");
344
314
  hold_token(t);
345
315
  return c_eq;
346
316
  }
@@ -355,14 +325,9 @@ static int binding(int t) {
355
325
  }
356
326
 
357
327
  static void mark_used_in(struct analyser * a, struct name * q, struct node * p) {
328
+ (void)a;
358
329
  if (!q->used) {
359
330
  q->used = p;
360
- q->local_to = a->program_end->name;
361
- } else if (q->local_to) {
362
- if (q->local_to != a->program_end->name) {
363
- /* Used in more than one routine/external. */
364
- q->local_to = NULL;
365
- }
366
331
  }
367
332
  }
368
333
 
@@ -380,7 +345,9 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
380
345
  struct node * p;
381
346
  struct node * q;
382
347
  switch (read_token(t)) {
383
- case c_minus: /* monadic */
348
+ case c_minus: { /* monadic */
349
+ // Note current line number so c_neg node reports the right line.
350
+ int neg_line = a->tokeniser->line_number;
384
351
  q = read_AE(a, assigned_to, 100);
385
352
  if (q->type == c_neg) {
386
353
  /* Optimise away double negation, which avoids generators
@@ -397,16 +364,17 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
397
364
  p = q;
398
365
  break;
399
366
  }
400
- p = new_node(a, c_neg);
367
+ p = new_node_at_line(a, c_neg, neg_line);
401
368
  p->right = q;
402
369
  break;
370
+ }
403
371
  case c_bra:
404
372
  p = read_AE(a, assigned_to, 0);
405
373
  get_token(a, c_ket);
406
374
  break;
407
375
  case c_name:
408
376
  p = new_node(a, c_name);
409
- name_to_node(a, p, 'i');
377
+ name_to_node(a, p, t_integer);
410
378
  if (p->name) {
411
379
  // $x = x + 1 shouldn't count as a use of x.
412
380
  p->name->value_used = (p->name != assigned_to);
@@ -430,7 +398,7 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
430
398
  case c_lenof:
431
399
  case c_sizeof: {
432
400
  int token = t->token;
433
- p = C_style(a, "S", token);
401
+ p = new_string_command(a, token);
434
402
  if (!p->literalstring) {
435
403
  if (p->name) p->name->value_used = true;
436
404
  break;
@@ -439,15 +407,12 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
439
407
  /* Replace lenof or sizeof on a literal string with a numeric
440
408
  * constant.
441
409
  */
442
- int result;
410
+ int result = 0;
443
411
  if (token == c_lenof && t->encoding == ENC_UTF8) {
444
412
  // UTF-8.
445
- int i = 0;
446
413
  symbol * b = p->literalstring;
447
- result = 0;
448
- while (i < SIZE(b)) {
449
- int dummy;
450
- i += get_utf8(b + i, &dummy);
414
+ int dummy;
415
+ for (int i = 0; i < SIZE(b); i += get_utf8(b + i, &dummy)) {
451
416
  ++result;
452
417
  }
453
418
  } else {
@@ -460,46 +425,44 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
460
425
  break;
461
426
  }
462
427
  default:
463
- error(a, e_unexpected_token);
428
+ unexpected_token_error(a, "integer expression");
464
429
  hold_token(t);
465
430
  return NULL;
466
431
  }
467
432
  while (true) {
468
433
  int token = read_token(t);
434
+ int op_line = t->line_number;
469
435
  int b = binding(token);
470
436
  if (binding(token) <= B) {
471
437
  hold_token(t);
472
438
  return p;
473
439
  }
474
440
  struct node * r = read_AE(a, assigned_to, b);
475
- if (p->type == c_number && r->type == c_number) {
441
+ if (p->type == c_number &&
442
+ r->type == c_number &&
443
+ // Can't evaluate division by zero.
444
+ !(token == c_divide && r->number == 0)) {
476
445
  // Evaluate constant sub-expression.
477
- q = new_node(a, c_number);
446
+ q = p;
478
447
  switch (token) {
479
448
  case c_plus:
480
- q->number = p->number + r->number;
449
+ q->number += r->number;
481
450
  break;
482
451
  case c_minus:
483
- q->number = p->number - r->number;
452
+ q->number -= r->number;
484
453
  break;
485
454
  case c_multiply:
486
- q->number = p->number * r->number;
455
+ q->number *= r->number;
487
456
  break;
488
457
  case c_divide:
489
- if (r->number == 0) {
490
- fprintf(stderr, "%s:%d: Division by zero\n",
491
- t->file, t->line_number);
492
- exit(1);
493
- }
494
- q->number = p->number / r->number;
458
+ q->number /= r->number;
495
459
  break;
496
460
  default:
497
461
  fprintf(stderr, "Unexpected AE operator %s\n",
498
462
  name_of_token(token));
499
463
  exit(1);
500
464
  }
501
- q->fixed_constant = p->fixed_constant && r->fixed_constant;
502
- q->line_number = p->line_number;
465
+ q->fixed_constant = q->fixed_constant && r->fixed_constant;
503
466
  } else {
504
467
  // Check for specific constant or no-op cases.
505
468
  q = NULL;
@@ -519,7 +482,7 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
519
482
  case c_minus:
520
483
  // 0 - r is -r
521
484
  if (p->type == c_number && p->number == 0) {
522
- q = new_node(a, c_neg);
485
+ q = new_node_at_line(a, c_neg, op_line);
523
486
  q->right = r;
524
487
  break;
525
488
  }
@@ -538,27 +501,23 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
538
501
  // p * 0 is 0
539
502
  if (r->type == c_number && r->number == 0) {
540
503
  q = r;
541
- q->line_number = p->line_number;
542
504
  break;
543
505
  }
544
506
  // -1 * r is -r
545
507
  if (p->type == c_number && p->number == -1) {
546
- q = new_node(a, c_neg);
508
+ q = new_node_at_line(a, c_neg, p->line_number);
547
509
  q->right = r;
548
- q->line_number = p->line_number;
549
510
  break;
550
511
  }
551
512
  // p * -1 is -p
552
513
  if (r->type == c_number && r->number == -1) {
553
- q = new_node(a, c_neg);
514
+ q = new_node_at_line(a, c_neg, r->line_number);
554
515
  q->right = p;
555
- q->line_number = p->line_number;
556
516
  break;
557
517
  }
558
518
  // 1 * r is r
559
519
  if (p->type == c_number && p->number == 1) {
560
520
  q = r;
561
- q->line_number = p->line_number;
562
521
  break;
563
522
  }
564
523
  // p * 1 is p
@@ -575,21 +534,19 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
575
534
  }
576
535
  // p / -1 is -p
577
536
  if (r->type == c_number && r->number == -1) {
578
- q = new_node(a, c_neg);
537
+ q = new_node_at_line(a, c_neg, r->line_number);
579
538
  q->right = p;
580
- q->line_number = p->line_number;
581
539
  break;
582
540
  }
583
541
  // p / 0 is an error!
584
542
  if (r->type == c_number && r->number == 0) {
585
- fprintf(stderr, "%s:%d: Division by zero\n",
586
- t->file, t->line_number);
587
- exit(1);
543
+ report_error_location_line(a, op_line);
544
+ fprintf(stderr, "Division by zero\n");
588
545
  }
589
546
  break;
590
547
  }
591
548
  if (!q) {
592
- q = new_node(a, token);
549
+ q = new_node_at_line(a, token, op_line);
593
550
  q->left = p;
594
551
  q->right = r;
595
552
  }
@@ -598,17 +555,81 @@ static struct node * read_AE(struct analyser * a, struct name * assigned_to, int
598
555
  }
599
556
  }
600
557
 
601
- static struct node * read_C_connection(struct analyser * a, struct node * q, int op) {
558
+ static int
559
+ is_just_false(struct node * q)
560
+ {
561
+ if (!q) return 1;
562
+ if (q->type == c_false) return 1;
563
+ if (q->type != c_bra) return 0;
564
+ return is_just_false(q->left);
565
+ }
566
+
567
+ static struct node * read_or(struct analyser * a, struct node * n) {
602
568
  struct tokeniser * t = a->tokeniser;
603
- struct node * p = new_node(a, op);
604
- struct node * p_end = q;
605
- p->left = q;
569
+ // Note current line number so c_or node reports the right line.
570
+ int or_line = t->line_number;
571
+ struct node * p = is_just_false(n) ? NULL : n;
572
+ struct node * p_end = p;
606
573
  do {
607
- q = read_C(a);
608
- p_end->right = q; p_end = q;
609
- } while (read_token(t) == op);
574
+ struct node * q = read_C(a);
575
+ // Discard `false` nodes in an `or` chain.
576
+ if (!is_just_false(q)) {
577
+ if (p_end) {
578
+ p_end->right = q;
579
+ } else {
580
+ p = q;
581
+ }
582
+ p_end = q;
583
+ }
584
+ } while (read_token(t) == c_or);
610
585
  hold_token(t);
611
- return p;
586
+ if (p == NULL) {
587
+ // All sub-nodes are `false` so return the first.
588
+ return n;
589
+ } else if (p->right == NULL) {
590
+ return p;
591
+ }
592
+ n = new_node_at_line(a, c_or, or_line);
593
+ n->left = p;
594
+ return n;
595
+ }
596
+
597
+ static int
598
+ is_just_true(struct node * q)
599
+ {
600
+ if (!q) return 1;
601
+ if (q->type != c_bra && q->type != c_true) return 0;
602
+ return is_just_true(q->left) && is_just_true(q->right);
603
+ }
604
+
605
+ static struct node * read_and(struct analyser * a, struct node * n) {
606
+ struct tokeniser * t = a->tokeniser;
607
+ // Note current line number so c_and node reports the right line.
608
+ int and_line = t->line_number;
609
+ struct node * p = is_just_true(n) ? NULL : n;
610
+ struct node * p_end = p;
611
+ do {
612
+ struct node * q = read_C(a);
613
+ // Discard nodes equivalent to `true` in an `and` chain.
614
+ if (!is_just_true(q)) {
615
+ if (p_end) {
616
+ p_end->right = q;
617
+ } else {
618
+ p = q;
619
+ }
620
+ p_end = q;
621
+ }
622
+ } while (read_token(t) == c_and);
623
+ hold_token(t);
624
+ if (p == NULL) {
625
+ // Note: is_just_true(n).
626
+ return n;
627
+ } else if (p->right == NULL) {
628
+ return p;
629
+ }
630
+ n = new_node_at_line(a, c_and, and_line);
631
+ n->left = p;
632
+ return n;
612
633
  }
613
634
 
614
635
  static struct node * read_C_list(struct analyser * a) {
@@ -620,47 +641,37 @@ static struct node * read_C_list(struct analyser * a) {
620
641
  if (token == c_ket) return p;
621
642
  if (token < 0) { omission_error(a, c_ket); return p; }
622
643
  hold_token(t);
623
- {
624
- struct node * q = read_C(a);
625
- while (true) {
626
- token = read_token(t);
627
- if (token != c_and && token != c_or) {
628
- hold_token(t);
629
- break;
630
- }
631
- q = read_C_connection(a, q, token);
644
+
645
+ struct node * q = read_C(a);
646
+ while (true) {
647
+ token = read_token(t);
648
+ if (token == c_or) {
649
+ q = read_or(a, q);
650
+ } else if (token == c_and) {
651
+ q = read_and(a, q);
652
+ } else {
653
+ hold_token(t);
654
+ break;
632
655
  }
633
- if (p_end == NULL) p->left = q; else p_end->right = q;
634
- p_end = q;
635
656
  }
657
+ if (p_end == NULL) p->left = q; else p_end->right = q;
658
+ p_end = q;
636
659
  }
637
660
  }
638
661
 
639
- static struct node * C_style(struct analyser * a, const char * s, int token) {
640
- int i;
662
+ static struct node * new_string_command(struct analyser * a, int token) {
641
663
  struct node * p = new_node(a, token);
642
- for (i = 0; s[i] != 0; i++) switch (s[i]) {
643
- case 'C':
644
- p->left = read_C(a); continue;
645
- case 'D':
646
- p->aux = read_C(a); continue;
647
- case 'A':
648
- p->AE = read_AE(a, NULL, 0); continue;
649
- case 'f':
650
- get_token(a, c_for); continue;
651
- case 'S':
652
- {
653
- int str_token = read_token(a->tokeniser);
654
- if (str_token == c_name) name_to_node(a, p, 's'); else
655
- if (str_token == c_literalstring) p->literalstring = new_literalstring(a);
656
- else error(a, e_string_omitted);
657
- }
658
- continue;
659
- case 'b':
660
- case 's':
661
- case 'i':
662
- if (get_token(a, c_name)) name_to_node(a, p, s[i]);
663
- continue;
664
+ int str_token = read_token(a->tokeniser);
665
+ if (str_token == c_literalstring) {
666
+ p->literalstring = new_literalstring(a);
667
+ } else if (str_token == c_name) {
668
+ name_to_node(a, p, t_string);
669
+ } else {
670
+ report_error_location(a);
671
+ fprintf(stderr, "string omitted");
672
+ report_error_after(a);
673
+ putc('\n', stderr);
674
+ hold_token(a->tokeniser);
664
675
  }
665
676
  return p;
666
677
  }
@@ -685,76 +696,44 @@ static int compare_amongvec(const void *pv, const void *qv) {
685
696
  symbol * b_p = p->b; int p_size = p->size;
686
697
  symbol * b_q = q->b; int q_size = q->size;
687
698
  int smaller_size = p_size < q_size ? p_size : q_size;
688
- int i;
689
- for (i = 0; i < smaller_size; i++)
699
+ for (int i = 0; i < smaller_size; i++)
690
700
  if (b_p[i] != b_q[i]) return b_p[i] - b_q[i];
691
701
  if (p_size - q_size)
692
702
  return p_size - q_size;
693
- return p->line_number - q->line_number;
703
+ return p->string_index - q->string_index;
694
704
  }
695
705
 
696
- #define PTR_NULL_CHECK(P, Q) do {\
697
- if ((Q) == NULL) {\
698
- if ((P) != NULL) return 1;\
699
- } else {\
700
- if ((P) == NULL) return -1;\
701
- }\
702
- } while (0)
703
-
704
- static int compare_node(const struct node *p, const struct node *q) {
705
- PTR_NULL_CHECK(p, q);
706
- if (q == NULL) {
707
- /* p must be NULL too. */
708
- return 0;
709
- }
706
+ #define nodes_equivalent(P, Q) \
707
+ ((P) == (Q) || ((P) && (Q) && nodes_equivalent_((P), (Q))))
708
+
709
+ static int nodes_equivalent_(const struct node *p, const struct node *q) {
710
+ if (p == q) return true;
711
+ if (p == NULL || q == NULL) return false;
710
712
 
711
- if (p->type != q->type) return p->type > q->type ? 1 : -1;
712
- if (p->mode != q->mode) return p->mode > q->mode ? 1 : -1;
713
+ if (p->type != q->type) return false;
714
+ if (p->mode != q->mode) return false;
713
715
  if (p->type == c_number) {
714
716
  if (p->number != q->number)
715
- return p->number > q->number ? 1 : -1;
716
- }
717
-
718
- PTR_NULL_CHECK(p->left, q->left);
719
- if (p->left) {
720
- int r = compare_node(p->left, q->left);
721
- if (r != 0) return r;
717
+ return false;
722
718
  }
723
719
 
724
- PTR_NULL_CHECK(p->AE, q->AE);
725
- if (p->AE) {
726
- int r = compare_node(p->AE, q->AE);
727
- if (r != 0) return r;
728
- }
729
-
730
- PTR_NULL_CHECK(p->aux, q->aux);
731
- if (p->aux) {
732
- int r = compare_node(p->aux, q->aux);
733
- if (r != 0) return r;
734
- }
720
+ if (!nodes_equivalent(p->left, q->left)) return false;
721
+ if (!nodes_equivalent(p->AE, q->AE)) return false;
722
+ if (!nodes_equivalent(p->aux, q->aux)) return false;
735
723
 
736
- PTR_NULL_CHECK(p->name, q->name);
737
- if (p->name) {
738
- int r;
739
- if (SIZE(p->name->s) != SIZE(q->name->s)) {
740
- return SIZE(p->name->s) - SIZE(q->name->s);
741
- }
742
- r = memcmp(p->name->s, q->name->s, SIZE(p->name->s));
743
- if (r != 0) return r;
744
- }
724
+ if (p->name != q->name) return false;
745
725
 
746
- PTR_NULL_CHECK(p->literalstring, q->literalstring);
747
- if (p->literalstring) {
748
- int r;
749
- if (SIZE(p->literalstring) != SIZE(q->literalstring)) {
750
- return SIZE(p->literalstring) - SIZE(q->literalstring);
726
+ if (p->literalstring != q->literalstring) {
727
+ if (!p->literalstring ||
728
+ !q->literalstring ||
729
+ SIZE(p->literalstring) != SIZE(q->literalstring) ||
730
+ memcmp(p->literalstring, q->literalstring,
731
+ SIZE(p->literalstring) * sizeof(symbol)) != 0) {
732
+ return false;
751
733
  }
752
- r = memcmp(p->literalstring, q->literalstring,
753
- SIZE(p->literalstring) * sizeof(symbol));
754
- if (r != 0) return r;
755
734
  }
756
735
 
757
- return compare_node(p->right, q->right);
736
+ return nodes_equivalent(p->right, q->right);
758
737
  }
759
738
 
760
739
  static struct node * make_among(struct analyser * a, struct node * p, struct node * substring) {
@@ -769,40 +748,44 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
769
748
  int direction = substring != NULL ? substring->mode : p->mode;
770
749
  int backward = direction == m_backward;
771
750
 
772
- if (a->amongs == NULL) a->amongs = x; else a->amongs_end->next = x;
773
- a->amongs_end = x;
774
- x->next = NULL;
751
+ *x = (struct among){0};
775
752
  x->node = p;
776
753
  x->b = v;
777
- x->number = a->among_count++;
778
- x->function_count = 0;
779
- x->nocommand_count = 0;
780
- x->amongvar_needed = false;
781
- x->always_matches = false;
782
754
  x->shortest_size = INT_MAX;
755
+ x->in_routine = a->current_routine;
783
756
 
784
757
  if (q->type == c_bra) {
758
+ fprintf(stderr,
759
+ "%s:%d: warning: among starter is a legacy feature - put "
760
+ "starter code between `substring` and `among` instead\n",
761
+ a->tokeniser->file, q->line_number);
785
762
  starter = q;
786
763
  p->left = q = q->right;
787
764
  }
788
765
 
766
+ int string_index = 0;
789
767
  while (q) {
790
768
  if (q->type == c_literalstring) {
791
769
  symbol * b = q->literalstring;
792
770
  w1->b = b; /* pointer to case string */
793
- w1->action = NULL; /* action gets filled in below */
771
+ w1->action = NULL; /* action gets filled in later */
794
772
  w1->line_number = q->line_number;
795
773
  w1->size = SIZE(b); /* number of characters in string */
796
774
  w1->i = -1; /* index of longest substring */
797
775
  w1->result = -1; /* number of corresponding case expression */
776
+ w1->string_index = string_index++;
798
777
  if (q->left) {
799
778
  struct name * function = q->left->name;
800
779
  w1->function = function;
801
- function->used_in_among = true;
780
+ ++function->uses_in_among;
802
781
  check_routine_mode(a, function, direction);
803
- x->function_count++;
782
+ if (function->among_index == 0) {
783
+ function->among_index = ++x->function_count;
784
+ }
785
+ w1->function_index = function->among_index;
804
786
  } else {
805
787
  w1->function = NULL;
788
+ w1->function_index = 0;
806
789
  if (w1->size == 0) {
807
790
  // This among contains the empty string without a gating
808
791
  // function so it will always match.
@@ -818,13 +801,15 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
818
801
  * the same action code if we find one.
819
802
  */
820
803
  int among_result = -1;
804
+ struct node * action = q;
821
805
  struct amongvec * w;
822
806
  for (w = v; w < w0; ++w) {
823
- if (w->action && compare_node(w->action->left, q->left) == 0) {
807
+ if (w->action && nodes_equivalent(w->action->left, q->left)) {
824
808
  if (w->result <= 0) {
825
809
  printf("Among code %d isn't positive\n", w->result);
826
810
  exit(1);
827
811
  }
812
+ action = w->action;
828
813
  among_result = w->result;
829
814
  break;
830
815
  }
@@ -834,7 +819,7 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
834
819
  }
835
820
 
836
821
  while (w0 != w1) {
837
- w0->action = q;
822
+ w0->action = action;
838
823
  w0->result = among_result;
839
824
  w0++;
840
825
  }
@@ -845,6 +830,7 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
845
830
  x->command_count = result - 1;
846
831
  {
847
832
  NEWVEC(node*, commands, x->command_count);
833
+ x->same_action = -2;
848
834
  for (int i = 0; i != x->command_count; ++i)
849
835
  commands[i] = NULL;
850
836
  for (w0 = v; w0 < w1; w0++) {
@@ -854,8 +840,25 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
854
840
  fprintf(stderr, "More among codes than expected\n");
855
841
  exit(1);
856
842
  }
857
- if (!commands[w0->result - 1])
843
+ if (!commands[w0->result - 1]) {
858
844
  commands[w0->result - 1] = w0->action;
845
+ // Check if all actions are a single command of the same
846
+ // type with a literalstring argument.
847
+ if (x->same_action > -1) {
848
+ if (w0->action->left->right ||
849
+ !w0->action->left->literalstring ||
850
+ x->same_action != w0->action->left->type) {
851
+ x->same_action = -1;
852
+ }
853
+ } else if (x->same_action == -2) {
854
+ if (w0->action->left->right ||
855
+ !w0->action->left->literalstring) {
856
+ x->same_action = -1;
857
+ } else {
858
+ x->same_action = w0->action->left->type;
859
+ }
860
+ }
861
+ }
859
862
  } else {
860
863
  ++x->nocommand_count;
861
864
  }
@@ -871,7 +874,10 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
871
874
  int size = w0->size;
872
875
  struct amongvec * w;
873
876
 
874
- if (size && size < x->shortest_size) x->shortest_size = size;
877
+ if (size) {
878
+ if (size < x->shortest_size) x->shortest_size = size;
879
+ if (size > x->longest_size) x->longest_size = size;
880
+ }
875
881
 
876
882
  for (w = w0 - 1; w >= v; w--) {
877
883
  if (w->size < size && memcmp(w->b, b, w->size * sizeof(symbol)) == 0) {
@@ -898,39 +904,131 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
898
904
  x->literalstring_count = p->number;
899
905
  p->among = x;
900
906
 
901
- if (x->command_count > 1 ||
902
- (x->command_count == 1 && x->nocommand_count > 0)) {
903
- /* We need to set among_var rather than just checking if find_among*()
904
- * returns zero or not.
905
- */
906
- x->amongvar_needed = a->amongvar_needed = true;
907
- }
908
907
  if (starter) {
909
908
  starter->right = p;
909
+ p = new_node_at_line(a, c_bra, starter->line_number);
910
910
  if (substring) {
911
- p = starter;
911
+ p->left = starter;
912
912
  } else {
913
- substring = new_node(a, c_substring);
913
+ substring = new_node_at_line(a, c_substring, starter->line_number);
914
914
  substring->right = starter;
915
- p = substring;
915
+ p->left = substring;
916
+ }
917
+ }
918
+
919
+ // Clear any among_index values we set so we correctly handle a function
920
+ // used in more than one among.
921
+ for (int i = 0; i < x->literalstring_count; i++) {
922
+ if (v[i].function) {
923
+ v[i].function->among_index = 0;
924
+ }
925
+ }
926
+
927
+ if (x->literalstring_count == 1) {
928
+ // Eliminate single-case amongs. Sometimes it's the natural way to
929
+ // express a single rule in Snowball code as it can show commonality
930
+ // with rulesets with multiple rules, but it's silly to actually
931
+ // generate as an among.
932
+ //
933
+ // We handle an `among` which only has the empty string here - this is
934
+ // syntactically valid but is not a useful construct so we warn about
935
+ // it.
936
+ if (substring) {
937
+ substring->among = NULL;
938
+ if (SIZE(v[0].b) == 0) {
939
+ // substring ... among ( '' (C) )
940
+ //
941
+ // becomes:
942
+ //
943
+ // ... (C)
944
+ fprintf(stderr, "%s:%d: warning: `among` with only empty string always matches\n",
945
+ a->tokeniser->file, p->line_number);
946
+ substring->type = c_true;
947
+ } else {
948
+ substring->type = c_literalstring;
949
+ substring->literalstring = v[0].b;
950
+ }
951
+ if (v[0].action) {
952
+ // substring ... among ( S (C) )
953
+ //
954
+ // becomes:
955
+ //
956
+ // S ... (C)
957
+ p = v[0].action;
958
+ } else {
959
+ // substring ... among ( S )
960
+ //
961
+ // becomes:
962
+ //
963
+ // S ... true
964
+ p = new_node_at_line(a, c_true, v[0].line_number);
965
+ }
966
+ } else {
967
+ if (v[0].action) {
968
+ // among ( S (C) )
969
+ //
970
+ // becomes:
971
+ //
972
+ // (S C)
973
+ p = v[0].action;
974
+ assert(p->type == c_bra);
975
+ if (SIZE(v[0].b) == 0) {
976
+ fprintf(stderr, "%s:%d: warning: `among` with only empty string always matches\n",
977
+ a->tokeniser->file, p->line_number);
978
+ } else {
979
+ // Insert a c_literalstring node at the start of (C)
980
+ struct node * literalstring = new_node(a, c_literalstring);
981
+ literalstring->literalstring = v[0].b;
982
+ literalstring->right = p->left;
983
+ p->left = literalstring;
984
+ }
985
+ } else {
986
+ // among ( S )
987
+ //
988
+ // becomes:
989
+ //
990
+ // S
991
+ if (SIZE(v[0].b) == 0) {
992
+ fprintf(stderr, "%s:%d: warning: `among` with only empty string always matches\n",
993
+ a->tokeniser->file, p->line_number);
994
+ p->type = c_true;
995
+ } else {
996
+ p->type = c_literalstring;
997
+ p->literalstring = v[0].b;
998
+ }
999
+ p->left = NULL;
1000
+ }
916
1001
  }
1002
+ if (v[0].function) {
1003
+ // If there's an among function, convert the action to:
1004
+ //
1005
+ // FUNC and C
1006
+ struct node * and_node = new_node(a, c_and);
1007
+ and_node->left = new_node(a, c_call);
1008
+ and_node->left->name = v[0].function;
1009
+ and_node->left->right = p;
1010
+ p = and_node;
1011
+ --v[0].function->uses_in_among;
1012
+ }
1013
+ FREE(x->commands);
1014
+ FREE(x);
1015
+ FREE(v);
1016
+ return p;
1017
+ }
1018
+
1019
+ if (x->function_count) {
1020
+ if (a->current_routine) a->current_routine->among_with_function = true;
917
1021
  }
1022
+
918
1023
  x->substring = substring;
919
1024
  if (substring != NULL) substring->among = x;
920
1025
 
921
- if (x->function_count > 0) ++a->among_with_function_count;
1026
+ if (a->amongs == NULL) a->amongs = x; else a->amongs_end->next = x;
1027
+ a->amongs_end = x;
922
1028
 
923
1029
  return p;
924
1030
  }
925
1031
 
926
- static int
927
- is_just_true(struct node * q)
928
- {
929
- if (!q) return 1;
930
- if (q->type != c_bra && q->type != c_true) return 0;
931
- return is_just_true(q->left) && is_just_true(q->right);
932
- }
933
-
934
1032
  static struct node * read_among(struct analyser * a) {
935
1033
  struct tokeniser * t = a->tokeniser;
936
1034
  struct node * p = new_node(a, c_among);
@@ -949,14 +1047,18 @@ static struct node * read_among(struct analyser * a) {
949
1047
  q = read_literalstring(a);
950
1048
  if (read_token(t) == c_name) {
951
1049
  struct node * r = new_node(a, c_name);
952
- name_to_node(a, r, 'r');
1050
+ name_to_node(a, r, t_routine);
953
1051
  q->left = r;
954
1052
  } else {
955
1053
  hold_token(t);
956
1054
  }
957
- p->number++; break;
1055
+ p->number++;
1056
+ break;
958
1057
  case c_bra:
959
- if (previous_token == c_bra) error(a, e_adjacent_bracketed_in_among);
1058
+ if (previous_token == c_bra) {
1059
+ report_error_location(a);
1060
+ fprintf(stderr, "two adjacent bracketed expressions in among(...)\n");
1061
+ }
960
1062
  q = read_C_list(a);
961
1063
  if (is_just_true(q->left)) {
962
1064
  /* Convert anything equivalent to () to () so we handle it
@@ -966,11 +1068,14 @@ static struct node * read_among(struct analyser * a) {
966
1068
  }
967
1069
  break;
968
1070
  default:
969
- error(a, e_unexpected_token_in_among);
1071
+ unexpected_token_error(a, "among(...)");
970
1072
  previous_token = token;
971
1073
  continue;
972
1074
  case c_ket:
973
- if (p->number == 0) error(a, e_empty_among);
1075
+ if (p->number == 0) {
1076
+ report_error_location(a);
1077
+ fprintf(stderr, "empty among(...)\n");
1078
+ }
974
1079
  if (t->error_count == 0) p = make_among(a, p, substring);
975
1080
  return p;
976
1081
  }
@@ -982,13 +1087,20 @@ static struct node * read_among(struct analyser * a) {
982
1087
 
983
1088
  static struct node * read_substring(struct analyser * a) {
984
1089
  struct node * p = new_node(a, c_substring);
985
- if (a->substring != NULL) error2(a, e_substring_preceded_by_substring, a->substring->line_number);
1090
+ if (a->substring != NULL) {
1091
+ substring_without_among_error(a);
1092
+ }
986
1093
  a->substring = p;
987
1094
  return p;
988
1095
  }
989
1096
 
990
1097
  static void check_modifyable(struct analyser * a) {
991
- if (!a->modifyable) error(a, e_not_allowed_inside_reverse);
1098
+ if (!a->modifyable) {
1099
+ struct tokeniser * t = a->tokeniser;
1100
+ report_error_location(a);
1101
+ fprintf(stderr, "%s not allowed inside reverse(...)\n",
1102
+ name_of_token(t->token));
1103
+ }
992
1104
  }
993
1105
 
994
1106
  static int ae_uses_name(struct node * p, struct name * q) {
@@ -1031,35 +1143,89 @@ static struct node * read_C(struct analyser * a) {
1031
1143
  }
1032
1144
  return p;
1033
1145
  }
1034
- case c_backwards:
1035
- {
1036
- int mode = a->mode;
1037
- if (a->mode == m_backward) error(a, e_already_backwards); else a->mode = m_backward;
1038
- { struct node * p = C_style(a, "C", token);
1039
- a->mode = mode;
1040
- return p;
1041
- }
1146
+ case c_backwards: {
1147
+ int mode = a->mode;
1148
+ if (a->mode == m_backward) {
1149
+ report_error_location(a);
1150
+ fprintf(stderr, "'backwards' used when already in this mode\n");
1042
1151
  }
1043
- case c_reverse:
1044
- {
1045
- int mode = a->mode;
1046
- int modifyable = a->modifyable;
1047
- a->modifyable = false;
1048
- a->mode = mode == m_forward ? m_backward : m_forward;
1049
- {
1050
- struct node * p = C_style(a, "C", token);
1051
- a->mode = mode;
1052
- a->modifyable = modifyable;
1053
- return p;
1152
+ a->mode = m_backward;
1153
+ struct node * p = new_node(a, token);
1154
+ p->left = read_C(a);
1155
+ a->mode = mode;
1156
+ return p;
1157
+ }
1158
+ case c_reverse: {
1159
+ int mode = a->mode;
1160
+ int modifyable = a->modifyable;
1161
+ a->modifyable = false;
1162
+ a->mode = (mode == m_forward) ? m_backward : m_forward;
1163
+ struct node * p = new_node(a, token);
1164
+ p->left = read_C(a);
1165
+ a->mode = mode;
1166
+ a->modifyable = modifyable;
1167
+ return p;
1168
+ }
1169
+ case c_not: {
1170
+ struct node * subcommand = read_C(a);
1171
+ switch (subcommand->type) {
1172
+ case c_booltest:
1173
+ /* We synthesise a special command for "not" applied to
1174
+ * testing a boolean variable.
1175
+ */
1176
+ subcommand->type = c_not_booltest;
1177
+ return subcommand;
1178
+ case c_next: {
1179
+ // `not next` -> compare `cursor` and `limit`.
1180
+ int mode = a->mode;
1181
+ struct node * n = new_node(a, mode == m_forward ? c_ge : c_le);
1182
+ n->left = subcommand;
1183
+ n->left->type = c_cursor;
1184
+ n->AE = new_node_at_line(a, c_limit, n->left->line_number);
1185
+ return n;
1054
1186
  }
1187
+ case c_eq:
1188
+ case c_ge:
1189
+ case c_gt:
1190
+ case c_le:
1191
+ case c_lt:
1192
+ case c_ne:
1193
+ // Flip the sense of the test.
1194
+ subcommand->type ^= 1;
1195
+ return subcommand;
1055
1196
  }
1056
- case c_not:
1197
+ struct node * p = new_node(a, token);
1198
+ p->left = subcommand;
1199
+ return p;
1200
+ }
1057
1201
  case c_try:
1058
- case c_fail:
1059
- case c_test:
1060
1202
  case c_do:
1061
- case c_repeat:
1062
- return C_style(a, "C", token);
1203
+ case c_repeat: {
1204
+ struct node * p = new_node(a, token);
1205
+ p->left = read_C(a);
1206
+ return p;
1207
+ }
1208
+ case c_test: {
1209
+ struct node * p = new_node(a, token);
1210
+ p->left = read_C(a);
1211
+ if (p->left->type == c_next) {
1212
+ // `test next` -> compare `cursor` and `limit`.
1213
+ int mode = a->mode;
1214
+ p->type = (mode == m_forward ? c_lt : c_gt);
1215
+ p->left->type = c_cursor;
1216
+ p->AE = new_node_at_line(a, c_limit, p->left->line_number);
1217
+ }
1218
+ return p;
1219
+ }
1220
+ case c_fail: {
1221
+ struct node * p = new_node(a, token);
1222
+ p->left = read_C(a);
1223
+ if (!p->left || is_just_true(p->left)) {
1224
+ p->type = c_false;
1225
+ p->left = NULL;
1226
+ }
1227
+ return p;
1228
+ }
1063
1229
  case c_goto:
1064
1230
  case c_gopast: {
1065
1231
  struct node * subcommand = read_C(a);
@@ -1097,59 +1263,88 @@ static struct node * read_C(struct analyser * a) {
1097
1263
  p->left = subcommand;
1098
1264
  return p;
1099
1265
  }
1100
- case c_loop: {
1101
- struct node * n = C_style(a, "AC", token);
1266
+ case c_loop:
1267
+ case c_atleast: {
1268
+ struct node * n = new_node(a, token);
1269
+ n->AE = read_AE(a, NULL, 0);
1270
+ n->left = read_C(a);
1271
+
1102
1272
  // n->AE is NULL after a syntax error, e.g. `loop next`.
1103
1273
  if (n->AE && n->AE->type == c_number) {
1104
1274
  if (n->AE->number <= 0) {
1105
- // `loop N C`, where N <= 0 is a no-op.
1106
- if (n->AE->fixed_constant) {
1107
- fprintf(stderr,
1108
- "%s:%d: warning: loop %d C is a no-op\n",
1109
- t->file, n->AE->line_number, n->AE->number);
1275
+ if (token == c_loop) {
1276
+ // `loop N C`, where N <= 0 is a no-op.
1277
+ if (n->AE->fixed_constant) {
1278
+ fprintf(stderr,
1279
+ "%s:%d: warning: `loop %d C` is a no-op\n",
1280
+ t->file, n->AE->line_number, n->AE->number);
1281
+ }
1282
+ n->AE = NULL;
1283
+ n->left = NULL;
1284
+ n->type = c_true;
1285
+ } else {
1286
+ // `atleast N C` where N <= 0 -> `repeat C`.
1287
+ if (n->AE->fixed_constant) {
1288
+ fprintf(stderr,
1289
+ "%s:%d: warning: atleast %d C is just repeat C\n",
1290
+ t->file, n->AE->line_number, n->AE->number);
1291
+ }
1292
+ n->AE = NULL;
1293
+ n->type = c_repeat;
1110
1294
  }
1111
- n->AE = NULL;
1112
- n->left = NULL;
1113
- n->type = c_true;
1114
1295
  } else if (n->AE->number == 1) {
1115
- // `loop 1 C` -> `C`.
1116
- if (n->AE->fixed_constant) {
1117
- fprintf(stderr,
1118
- "%s:%d: warning: loop 1 C is just C\n",
1119
- t->file, n->AE->line_number);
1296
+ if (token == c_loop) {
1297
+ // `loop 1 C` -> `C`.
1298
+ if (n->AE->fixed_constant) {
1299
+ fprintf(stderr,
1300
+ "%s:%d: warning: loop 1 C is just C\n",
1301
+ t->file, n->AE->line_number);
1302
+ }
1303
+ n = n->left;
1120
1304
  }
1121
- n = n->left;
1122
1305
  }
1123
1306
  }
1124
1307
  return n;
1125
1308
  }
1126
- case c_atleast: {
1127
- struct node * n = C_style(a, "AC", token);
1128
- // n->AE is NULL after a syntax error, e.g. `loop next`.
1129
- if (n->AE && n->AE->type == c_number) {
1130
- if (n->AE->number <= 0) {
1131
- // `atleast N C` where N <= 0 -> `repeat C`.
1132
- if (n->AE->fixed_constant) {
1133
- fprintf(stderr,
1134
- "%s:%d: warning: atleast %d C is just repeat C\n",
1135
- t->file, n->AE->line_number, n->AE->number);
1136
- }
1137
- n->AE = NULL;
1138
- n->type = c_repeat;
1139
- }
1309
+ case c_setmark: {
1310
+ struct node * n = new_node(a, c_assign);
1311
+ n->AE = new_node(a, c_cursor);
1312
+ if (get_token(a, c_name)) {
1313
+ name_to_node(a, n, t_integer);
1314
+ if (n->name) n->name->initialised = true;
1140
1315
  }
1141
1316
  return n;
1142
1317
  }
1143
- case c_setmark: {
1144
- struct node * n = C_style(a, "i", token);
1145
- if (n->name) n->name->initialised = true;
1318
+ case c_atmark: {
1319
+ struct node * n = new_node(a, c_eq);
1320
+ struct node * AE = read_AE(a, NULL, 0);
1321
+ if (AE->type == c_cursor) {
1322
+ fprintf(stderr,
1323
+ "%s:%d: warning: `atmark cursor` is always true\n",
1324
+ t->file, n->line_number);
1325
+ n->type = c_true;
1326
+ } else {
1327
+ n->left = new_node_at_line(a, c_cursor, n->line_number);
1328
+ n->AE = AE;
1329
+ }
1330
+ return n;
1331
+ }
1332
+ case c_tomark: {
1333
+ struct node * n = new_node(a, token);
1334
+ struct node * AE = read_AE(a, NULL, 0);
1335
+ if (AE->type == c_cursor) {
1336
+ fprintf(stderr,
1337
+ "%s:%d: warning: `tomark cursor` is a no-op\n",
1338
+ t->file, n->line_number);
1339
+ n->type = c_true;
1340
+ } else {
1341
+ n->AE = AE;
1342
+ }
1146
1343
  return n;
1147
1344
  }
1148
- case c_tomark:
1149
- case c_atmark:
1150
- return C_style(a, "A", token);
1151
1345
  case c_hop: {
1152
- struct node * n = C_style(a, "A", token);
1346
+ struct node * n = new_node(a, token);
1347
+ n->AE = read_AE(a, NULL, 0);
1153
1348
  // n->AE is NULL after a syntax error, e.g. `hop hop`.
1154
1349
  if (n->AE && n->AE->type == c_number) {
1155
1350
  if (n->AE->number == 1) {
@@ -1159,7 +1354,7 @@ static struct node * read_C(struct analyser * a) {
1159
1354
  } else if (n->AE->number == 0) {
1160
1355
  if (n->AE->fixed_constant) {
1161
1356
  fprintf(stderr,
1162
- "%s:%d: warning: hop 0 is a no-op\n",
1357
+ "%s:%d: warning: `hop 0` is a no-op\n",
1163
1358
  t->file, n->AE->line_number);
1164
1359
  }
1165
1360
  n->AE = NULL;
@@ -1176,23 +1371,42 @@ static struct node * read_C(struct analyser * a) {
1176
1371
  }
1177
1372
  return n;
1178
1373
  }
1179
- case c_delete:
1374
+ case c_delete: {
1180
1375
  check_modifyable(a);
1181
- /* fall through */
1376
+ // Canonicalise `delete` to `<-''`.
1377
+ struct node * n = new_node(a, c_slicefrom);
1378
+ NEW(literalstring, p);
1379
+ p->b = create_b(0);
1380
+ p->next = a->literalstrings;
1381
+ a->literalstrings = p;
1382
+ n->literalstring = p->b;
1383
+ return n;
1384
+ }
1182
1385
  case c_next:
1183
1386
  case c_tolimit:
1184
- case c_atlimit:
1185
1387
  case c_leftslice:
1186
1388
  case c_rightslice:
1187
1389
  case c_true:
1188
1390
  case c_false:
1391
+ return new_node(a, token);
1392
+ case c_atlimit: {
1393
+ int mode = a->mode;
1394
+ struct node * n = new_node(a, mode == m_forward ? c_ge : c_le);
1395
+ n->left = new_node_at_line(a, c_cursor, n->line_number);
1396
+ n->AE = new_node_at_line(a, c_limit, n->line_number);
1397
+ return n;
1398
+ }
1189
1399
  case c_debug:
1400
+ a->debug_used = true;
1190
1401
  return new_node(a, token);
1191
1402
  case c_assignto:
1192
1403
  case c_sliceto: {
1193
1404
  check_modifyable(a);
1194
- struct node *n = C_style(a, "s", token);
1195
- if (n->name) n->name->initialised = true;
1405
+ struct node * n = new_node(a, token);
1406
+ if (get_token(a, c_name)) {
1407
+ name_to_node(a, n, t_string);
1408
+ if (n->name) n->name->initialised = true;
1409
+ }
1196
1410
  if (token == c_assignto) {
1197
1411
  fprintf(stderr,
1198
1412
  "%s:%d: warning: Use of `=>` is not recommended, "
@@ -1203,47 +1417,106 @@ static struct node * read_C(struct analyser * a) {
1203
1417
  return n;
1204
1418
  }
1205
1419
  case c_assign:
1420
+ token = c_stringassign;
1421
+ /* FALLTHRU */
1206
1422
  case c_insert:
1207
1423
  case c_attach:
1208
1424
  case c_slicefrom: {
1209
- struct node *n;
1210
1425
  check_modifyable(a);
1211
- n = C_style(a, "S", token);
1212
- if (n->name) n->name->value_used = true;
1213
- return n;
1214
- }
1215
- case c_setlimit:
1216
- return C_style(a, "CfD", token);
1217
- case c_set:
1218
- case c_unset: {
1219
- struct node * n = C_style(a, "b", token);
1220
- if (n->name) n->name->initialised = true;
1426
+ struct node * n = new_string_command(a, token);
1427
+ if (n->name) {
1428
+ n->name->value_used = true;
1429
+ } else if (n->literalstring == NULL || SIZE(n->literalstring) == 0) {
1430
+ switch (token) {
1431
+ case c_insert:
1432
+ case c_attach:
1433
+ fprintf(stderr,
1434
+ "%s:%d: warning: `%s ''` is a no-op\n",
1435
+ t->file, n->line_number, name_of_token(token));
1436
+ n->type = c_true;
1437
+ n->literalstring = NULL;
1438
+ break;
1439
+ }
1440
+ }
1441
+ return n;
1442
+ }
1443
+ case c_setlimit: {
1444
+ struct node * n = new_node(a, token);
1445
+ n->left = read_C(a);
1446
+ get_token(a, c_for);
1447
+ n->aux = read_C(a);
1448
+ if (n->left->type == c_tomark && n->left->AE->type == c_limit) {
1449
+ fprintf(stderr,
1450
+ "%s:%d: warning: `setlimit tomark limit` is a no-op\n",
1451
+ t->file, n->line_number);
1452
+ return n->aux;
1453
+ }
1454
+ return n;
1455
+ }
1456
+ case c_set:
1457
+ case c_unset: {
1458
+ struct node * n = new_node(a, token);
1459
+ if (get_token(a, c_name)) {
1460
+ name_to_node(a, n, t_boolean);
1461
+ if (n->name) n->name->initialised = true;
1462
+ }
1221
1463
  return n;
1222
1464
  }
1223
1465
  case c_dollar: {
1466
+ int dollar_line = t->line_number;
1224
1467
  read_token(t);
1225
1468
  if (t->token == c_bra) {
1226
1469
  /* Handle newer $(AE REL_OP AE) syntax. */
1227
1470
  struct node * n = read_AE(a, NULL, 0);
1228
1471
  read_token(t);
1229
1472
  token = t->token;
1473
+ bool eval_constant_expr = false;
1230
1474
  switch (token) {
1231
1475
  case c_assign:
1232
- count_error(a);
1233
- fprintf(stderr, "%s:%d: Expected relational operator (did you mean '=='?)\n",
1234
- t->file, t->line_number);
1235
- /* Assume it was == to try to avoid an error avalanche. */
1476
+ // Assume `==` was meant to try to avoid an error avalanche.
1477
+ token = c_eq;
1478
+ report_assumed_rel_op_error:
1479
+ report_error_location(a);
1480
+ fprintf(stderr, "Expected relational operator, got '%s' (did you mean '%s'?)\n",
1481
+ name_of_token(t->token), name_of_token(token));
1482
+ goto handle_rel_op;
1483
+ case c_assignto:
1484
+ // Assume `>=` was meant to try to avoid an error avalanche.
1485
+ // (`=>` instead of `>=` is a possible typo.)
1486
+ token = c_ge;
1487
+ goto report_assumed_rel_op_error;
1488
+ case c_divideassign:
1489
+ // Assume `!=` was meant to try to avoid an error avalanche.
1490
+ // (Ada, Erlang, Fortran90, etc use `/=` for not-equal.)
1491
+ token = c_ne;
1492
+ goto report_assumed_rel_op_error;
1493
+ case c_minusassign:
1494
+ case c_multiplyassign:
1495
+ case c_plusassign:
1496
+ // Give a better error if any other assignment operator
1497
+ // is used in this context.
1498
+ report_error_location(a);
1499
+ fprintf(stderr, "Expected relational operator, got '%s'\n",
1500
+ name_of_token(token));
1501
+ // Assume `==` was meant to try to avoid an error avalanche.
1236
1502
  token = c_eq;
1237
- /* FALLTHRU */
1503
+ goto handle_rel_op;
1238
1504
  case c_eq:
1239
1505
  case c_ne:
1240
1506
  case c_gt:
1241
1507
  case c_ge:
1242
1508
  case c_lt:
1243
1509
  case c_le: {
1510
+ // Only evaluate constant expressions if we got a valid
1511
+ // relational operator to avoid spurious unreachable
1512
+ // code warnings after an error.
1513
+ eval_constant_expr = true;
1514
+ handle_rel_op: ;
1244
1515
  struct node * lhs = n;
1245
1516
  struct node * rhs = read_AE(a, NULL, 0);
1246
- if (lhs->type == c_number && rhs->type == c_number) {
1517
+ if (eval_constant_expr &&
1518
+ lhs->type == c_number &&
1519
+ rhs->type == c_number) {
1247
1520
  // Evaluate constant numeric test expression.
1248
1521
  int result;
1249
1522
  switch (token) {
@@ -1280,125 +1553,205 @@ static struct node * read_C(struct analyser * a) {
1280
1553
  break;
1281
1554
  }
1282
1555
  default:
1283
- error(a, e_unexpected_token);
1556
+ unexpected_token_error(a, "integer test expression");
1284
1557
  hold_token(t);
1558
+ (void)read_AE(a, NULL, 0);
1559
+ get_token(a, c_ket);
1285
1560
  break;
1286
1561
  }
1287
1562
  return n;
1288
1563
  }
1289
1564
 
1290
- if (t->token == c_name) {
1291
- struct node * p;
1292
- struct name * q = find_name(a);
1565
+ if (t->token != c_name) {
1566
+ unexpected_token_error(a, "integer test expression");
1567
+ hold_token(t);
1568
+ return new_node_at_line(a, c_dollar, dollar_line);
1569
+ }
1570
+
1571
+ struct name * q = find_name(a);
1572
+ if (q && q->type == t_string) {
1573
+ /* Assume for now that $ on string both initialises and uses
1574
+ * the string variable. FIXME: Can we do better?
1575
+ */
1576
+ q->initialised = true;
1577
+ q->value_used = true;
1578
+ struct node * p = new_node_at_line(a, c_dollar, dollar_line);
1293
1579
  int mode = a->mode;
1294
1580
  int modifyable = a->modifyable;
1295
- if (q && q->type == t_string) {
1296
- /* Assume for now that $ on string both initialises and
1297
- * uses the string variable. FIXME: Can we do better?
1298
- */
1299
- q->initialised = true;
1300
- q->value_used = true;
1301
- a->mode = m_forward;
1302
- a->modifyable = true;
1303
- p = new_node(a, c_dollar);
1304
- p->left = read_C(a);
1305
- p->name = q;
1306
- } else {
1307
- if (q && q->type != t_integer) {
1308
- /* If $ is used on an unknown name or a name which
1309
- * isn't a string or an integer then we assume the
1310
- * unknown name is an integer as $ is used more often
1311
- * on integers than strings, so hopefully this it less
1312
- * likely to cause an error avalanche.
1313
- *
1314
- * For an unknown name, we'll already have reported an
1315
- * error.
1316
- */
1317
- error(a, e_not_of_type_string_or_integer);
1318
- q = NULL;
1319
- }
1320
- p = new_node(a, read_AE_test(a));
1321
- switch (p->type) {
1322
- case c_eq:
1323
- case c_ne:
1324
- case c_gt:
1325
- case c_ge:
1326
- case c_lt:
1327
- case c_le:
1328
- p->left = new_node(a, c_name);
1329
- p->left->name = q;
1330
- if (q) {
1331
- q->value_used = true;
1332
- }
1333
- p->AE = read_AE(a, NULL, 0);
1334
- break;
1335
- default:
1336
- /* +=, etc don't "initialise" as they only
1337
- * amend an existing value. Similarly, they
1338
- * don't count as using the value.
1339
- */
1340
- p->name = q;
1341
- p->AE = read_AE(a, q, 0);
1342
- if (p->type == c_mathassign && q) {
1343
- /* $x = x + 1 doesn't initialise x. */
1344
- q->initialised = !ae_uses_name(p->AE, q);
1345
- }
1346
- break;
1347
- }
1348
- }
1349
- if (q) mark_used_in(a, q, p);
1581
+ a->mode = m_forward;
1582
+ a->modifyable = true;
1583
+ p->left = read_C(a);
1350
1584
  a->mode = mode;
1351
1585
  a->modifyable = modifyable;
1586
+ p->name = q;
1587
+ mark_used_in(a, q, p);
1352
1588
  return p;
1353
1589
  }
1354
1590
 
1355
- error(a, e_unexpected_token);
1356
- hold_token(t);
1357
- return new_node(a, c_dollar);
1358
- }
1359
- case c_name:
1360
- {
1361
- struct name * q = find_name(a);
1362
- struct node * p = new_node(a, c_name);
1363
- if (q) {
1364
- mark_used_in(a, q, p);
1365
- switch (q->type) {
1366
- case t_boolean:
1367
- p->type = c_booltest;
1368
- q->value_used = true;
1369
- break;
1370
- case t_integer:
1371
- error(a, e_misplaced); /* integer name misplaced */
1372
- break;
1373
- case t_string:
1374
- q->value_used = true;
1375
- break;
1376
- case t_routine:
1377
- case t_external:
1378
- p->type = c_call;
1379
- check_routine_mode(a, q, a->mode);
1380
- break;
1381
- case t_grouping:
1382
- p->type = c_grouping; break;
1591
+ if (q && q->type != t_integer) {
1592
+ /* If $ is used on an unknown name or a name which isn't a
1593
+ * string or an integer then we assume the unknown name is an
1594
+ * integer as $ is used more often on integers than strings, so
1595
+ * hopefully this it less likely to cause an error avalanche.
1596
+ *
1597
+ * For an unknown name, we'll already have reported an error.
1598
+ */
1599
+ report_error_location(a);
1600
+ fprintf(stderr, "'%.*s' not of type integer or string\n",
1601
+ SIZE(q->s), q->s);
1602
+ q = NULL;
1603
+ }
1604
+ struct node * p = new_node(a, read_AE_test(a));
1605
+ switch (p->type) {
1606
+ case c_eq:
1607
+ case c_ne:
1608
+ case c_gt:
1609
+ case c_ge:
1610
+ case c_lt:
1611
+ case c_le:
1612
+ p->left = new_node(a, c_name);
1613
+ p->left->name = q;
1614
+ p->AE = read_AE(a, NULL, 0);
1615
+ if (q) {
1616
+ q->value_used = true;
1617
+ mark_used_in(a, q, p);
1383
1618
  }
1619
+ return p;
1620
+ }
1621
+
1622
+ /* +=, etc don't "initialise" as they only amend an existing value.
1623
+ * Similarly, they don't count as using the value.
1624
+ */
1625
+ p->name = q;
1626
+ p->AE = read_AE(a, q, 0);
1627
+ if (p->AE && p->AE->type == c_number) {
1628
+ switch (p->type) {
1629
+ case c_plusassign:
1630
+ case c_minusassign:
1631
+ if (p->AE->number == 0) {
1632
+ // `$x+=0` and `$x-=0` are no-ops.
1633
+ p->type = c_true;
1634
+ p->name = NULL;
1635
+ p->AE = NULL;
1636
+ } else if (p->AE->number < 0) {
1637
+ // `$x+=-N` -> `$x-=N`, etc as
1638
+ // this may result in slightly
1639
+ // shorter target language code.
1640
+ p->type ^= (c_plusassign ^ c_minusassign);
1641
+ p->AE->number = -p->AE->number;
1642
+ }
1643
+ break;
1644
+ case c_multiplyassign:
1645
+ case c_divideassign:
1646
+ if (p->AE->number == 1) {
1647
+ // `$x*=1` and `$x/=1` are no-ops.
1648
+ p->type = c_true;
1649
+ p->name = NULL;
1650
+ p->AE = NULL;
1651
+ } else if (p->AE->number == 0) {
1652
+ if (p->type == c_divideassign) {
1653
+ report_error_location_line(a, p->line_number);
1654
+ fprintf(stderr, "Division by zero\n");
1655
+ // Set the largest possible value.
1656
+ p->type = c_maxint;
1657
+ p->name = NULL;
1658
+ p->AE = NULL;
1659
+ } else {
1660
+ // `$x*=0` -> `$x=0`
1661
+ p->type = c_assign;
1662
+ }
1663
+ } else if (p->AE->number == -1) {
1664
+ // `$x/=-1` -> `$x=-x`
1665
+ // `$x*=-1` -> `$x=-x`
1666
+ p->type = c_assign;
1667
+ p->AE->number = 0;
1668
+ p->AE->type = c_neg;
1669
+ p->AE->right = new_node_at_line(a, c_name,
1670
+ p->AE->line_number);
1671
+ p->AE->right->name = p->name;
1672
+ }
1673
+ break;
1384
1674
  }
1385
- p->name = q;
1386
- return p;
1387
1675
  }
1388
- case c_non:
1389
- {
1390
- struct node * p = new_node(a, token);
1391
- read_token(t);
1392
- if (t->token == c_minus) read_token(t);
1393
- if (!check_token(a, c_name)) { omission_error(a, c_name); return p; }
1394
- name_to_node(a, p, 'g');
1676
+ if (p->type == c_assign && q) {
1677
+ if (ae_uses_name(p->AE, q)) {
1678
+ if (p->AE->type == c_name) {
1679
+ fprintf(stderr,
1680
+ "%s:%d: warning: `$",
1681
+ t->file, p->line_number);
1682
+ report_s(stderr, p->AE->name->s);
1683
+ fprintf(stderr, " = ");
1684
+ report_s(stderr, p->AE->name->s);
1685
+ fprintf(stderr, "` is a no-op\n");
1686
+ p->AE = NULL;
1687
+ p->type = c_true;
1688
+ q = NULL;
1689
+ }
1690
+ } else {
1691
+ // `$x = <AE>` initialises `x` unless AE references `x`.
1692
+ q->initialised = true;
1693
+ }
1694
+ }
1695
+ if (q) mark_used_in(a, q, p);
1696
+ return p;
1697
+ }
1698
+ case c_name: {
1699
+ struct name * q = find_name(a);
1700
+ struct node * p = new_node(a, c_name);
1701
+ if (q) {
1702
+ mark_used_in(a, q, p);
1703
+ switch (q->type) {
1704
+ case t_boolean:
1705
+ p->type = c_booltest;
1706
+ q->value_used = true;
1707
+ break;
1708
+ case t_integer:
1709
+ report_error_location(a);
1710
+ fprintf(stderr, "integer name '%.*s' misplaced\n",
1711
+ SIZE(t->s), t->s);
1712
+ break;
1713
+ case t_string:
1714
+ q->value_used = true;
1715
+ break;
1716
+ case t_routine:
1717
+ case t_external:
1718
+ p->type = c_call;
1719
+ check_routine_mode(a, q, a->mode);
1720
+ break;
1721
+ case t_grouping:
1722
+ p->type = c_grouping;
1723
+ break;
1724
+ }
1725
+ }
1726
+ p->name = q;
1727
+ return p;
1728
+ }
1729
+ case c_non: {
1730
+ struct node * p = new_node(a, token);
1731
+ read_token(t);
1732
+ if (t->token == c_minus) read_token(t);
1733
+ if (!check_token(a, c_name)) {
1395
1734
  return p;
1396
1735
  }
1397
- case c_literalstring:
1398
- return read_literalstring(a);
1736
+ name_to_node(a, p, t_grouping);
1737
+ return p;
1738
+ }
1739
+ case c_literalstring: {
1740
+ struct node * p = read_literalstring(a);
1741
+ if (SIZE(p->literalstring) == 0) {
1742
+ fprintf(stderr,
1743
+ "%s:%d: warning: empty literal string is a no-op\n",
1744
+ t->file, p->line_number);
1745
+ p->type = c_true;
1746
+ p->literalstring = NULL;
1747
+ }
1748
+ return p;
1749
+ }
1399
1750
  case c_among: return read_among(a);
1400
1751
  case c_substring: return read_substring(a);
1401
- default: error(a, e_unexpected_token); return NULL;
1752
+ default:
1753
+ unexpected_token_error(a, 0);
1754
+ return NULL;
1402
1755
  }
1403
1756
  }
1404
1757
 
@@ -1416,22 +1769,21 @@ static int next_symbol(symbol * p, symbol * W, int utf8) {
1416
1769
 
1417
1770
  static symbol * alter_grouping(symbol * p, symbol * q, int style, int utf8) {
1418
1771
  int j = 0;
1419
- symbol W;
1420
- int width;
1421
1772
  if (style == c_plus) {
1422
1773
  while (j < SIZE(q)) {
1423
- width = next_symbol(q + j, &W, utf8);
1774
+ symbol W;
1775
+ int width = next_symbol(q + j, &W, utf8);
1424
1776
  p = add_symbol_to_b(p, W);
1425
1777
  j += width;
1426
1778
  }
1427
1779
  } else {
1428
1780
  while (j < SIZE(q)) {
1429
- int i;
1430
- width = next_symbol(q + j, &W, utf8);
1431
- for (i = 0; i < SIZE(p); i++) {
1781
+ symbol W;
1782
+ int width = next_symbol(q + j, &W, utf8);
1783
+ for (int i = 0; i < SIZE(p); i++) {
1432
1784
  if (p[i] == W) {
1433
1785
  memmove(p + i, p + i + 1, (SIZE(p) - i - 1) * sizeof(symbol));
1434
- SIZE(p)--;
1786
+ ADD_TO_SIZE(p, -1);
1435
1787
  }
1436
1788
  }
1437
1789
  j += width;
@@ -1440,70 +1792,120 @@ static symbol * alter_grouping(symbol * p, symbol * q, int style, int utf8) {
1440
1792
  return p;
1441
1793
  }
1442
1794
 
1795
+ static int compare_symbol(const void *pv, const void *qv) {
1796
+ const symbol * p = (const symbol*)pv;
1797
+ const symbol * q = (const symbol*)qv;
1798
+ return *p - *q;
1799
+ }
1800
+
1801
+ static int finalise_grouping(struct grouping * p) {
1802
+ if (SIZE(p->b) == 0) {
1803
+ // Empty grouping - leave things in a non-surprising state.
1804
+ p->smallest_ch = p->largest_ch = 0;
1805
+ return false;
1806
+ }
1807
+
1808
+ qsort(p->b, SIZE(p->b), sizeof(symbol), compare_symbol);
1809
+ p->smallest_ch = p->b[0];
1810
+ p->largest_ch = p->b[SIZE(p->b) - 1];
1811
+
1812
+ // Eliminate duplicates.
1813
+ symbol ch = p->b[0];
1814
+ int j = 1;
1815
+ for (int i = 1; i < SIZE(p->b); i++) {
1816
+ if (p->b[i] != ch) {
1817
+ ch = p->b[j++] = p->b[i];
1818
+ }
1819
+ }
1820
+ SET_SIZE(p->b, j);
1821
+ return true;
1822
+ }
1823
+
1443
1824
  static void read_define_grouping(struct analyser * a, struct name * q) {
1444
1825
  struct tokeniser * t = a->tokeniser;
1445
1826
  int style = c_plus;
1827
+ bool check_nonempty = true;
1446
1828
  {
1447
1829
  NEW(grouping, p);
1830
+ *p = (struct grouping){0};
1448
1831
  if (a->groupings == NULL) a->groupings = p; else a->groupings_end->next = p;
1449
1832
  a->groupings_end = p;
1450
1833
  if (q) {
1451
1834
  if (q->grouping != NULL) {
1452
- error(a, e_redefined);
1453
- FREE(q->grouping);
1835
+ report_error_location(a);
1836
+ fprintf(stderr, "'%.*s' redefined\n", SIZE(t->s), t->s);
1837
+ q->grouping->name = NULL;
1454
1838
  }
1455
1839
  q->grouping = p;
1456
1840
  }
1457
- p->next = NULL;
1458
1841
  p->name = q;
1459
1842
  p->line_number = t->line_number;
1460
1843
  p->b = create_b(0);
1461
- while (true) {
1844
+ do {
1462
1845
  switch (read_token(t)) {
1463
1846
  case c_name: {
1464
1847
  struct name * r = find_name(a);
1465
1848
  if (!r) break;
1466
1849
 
1467
- check_name_type(a, r, 'g');
1468
1850
  if (r == q) {
1469
1851
  count_error(a);
1470
- r->s[SIZE(r->s)] = 0;
1471
- fprintf(stderr, "%s:%d: %s defined in terms of itself\n",
1472
- t->file, t->line_number, r->s);
1852
+ fprintf(stderr, "%s:%d: %.*s defined in terms of itself\n",
1853
+ t->file, t->line_number, SIZE(r->s), r->s);
1854
+ check_nonempty = false;
1473
1855
  } else if (!r->grouping) {
1474
- count_error(a);
1475
- r->s[SIZE(r->s)] = 0;
1476
- fprintf(stderr, "%s:%d: %s undefined\n",
1477
- t->file, t->line_number, r->s);
1856
+ if (check_name_type(a, r, t_grouping)) {
1857
+ count_error(a);
1858
+ fprintf(stderr, "%s:%d: %.*s undefined\n",
1859
+ t->file, t->line_number, SIZE(r->s), r->s);
1860
+ }
1861
+ check_nonempty = false;
1478
1862
  } else {
1479
1863
  p->b = alter_grouping(p->b, r->grouping->b, style, false);
1480
1864
  }
1481
1865
  r->used_in_definition = true;
1482
1866
  break;
1483
1867
  }
1484
- case c_literalstring:
1868
+ case c_literalstring: {
1869
+ int utf8 = (a->encoding == ENC_UTF8);
1870
+ int i = 0;
1871
+ while (i < SIZE(t->b)) {
1872
+ symbol ch_i;
1873
+ int width_i = next_symbol(t->b + i, &ch_i, utf8);
1874
+ int j = 0;
1875
+ while (j < i) {
1876
+ symbol ch_j;
1877
+ int width_j = next_symbol(t->b + j, &ch_j, utf8);
1878
+ if (ch_i == ch_j) {
1879
+ fprintf(stderr, "%s:%d: warning: Duplicate "
1880
+ "character in grouping: ",
1881
+ t->file, t->line_number);
1882
+ if (ch_i >= 32 && ch_i < 127) {
1883
+ fprintf(stderr, "'%c'\n", ch_i);
1884
+ } else {
1885
+ fprintf(stderr, "U+%04X\n", ch_i);
1886
+ }
1887
+ }
1888
+ j += width_j;
1889
+ }
1890
+ i += width_i;
1891
+ }
1485
1892
  p->b = alter_grouping(p->b, t->b, style, (a->encoding == ENC_UTF8));
1486
1893
  break;
1487
- default: error(a, e_unexpected_token); return;
1488
- }
1489
- switch (read_token(t)) {
1490
- case c_plus:
1491
- case c_minus: style = t->token; break;
1492
- default: goto label0;
1894
+ }
1895
+ default:
1896
+ unexpected_token_error(a, "grouping definition");
1897
+ hold_token_if_toplevel(t);
1898
+ // Don't report an error for an empty grouping as well.
1899
+ (void)finalise_grouping(p);
1900
+ return;
1493
1901
  }
1494
- }
1495
- label0:
1496
- {
1497
- int i;
1498
- int max = 0;
1499
- int min = 1<<16;
1500
- for (i = 0; i < SIZE(p->b); i++) {
1501
- if (p->b[i] > max) max = p->b[i];
1502
- if (p->b[i] < min) min = p->b[i];
1902
+ style = read_token(t);
1903
+ } while (style == c_plus || style == c_minus);
1904
+ if (!finalise_grouping(p)) {
1905
+ if (check_nonempty) {
1906
+ report_error_location_line(a, p->line_number);
1907
+ fprintf(stderr, "empty grouping\n");
1503
1908
  }
1504
- p->largest_ch = max;
1505
- p->smallest_ch = min;
1506
- if (min == 1<<16) error(a, e_empty_grouping);
1507
1909
  }
1508
1910
  hold_token(t);
1509
1911
  }
@@ -1511,62 +1913,69 @@ static void read_define_grouping(struct analyser * a, struct name * q) {
1511
1913
 
1512
1914
  static void read_define_routine(struct analyser * a, struct name * q) {
1513
1915
  struct node * p = new_node(a, c_define);
1514
- a->amongvar_needed = false;
1916
+ a->current_routine = q;
1515
1917
  if (q) {
1516
- check_name_type(a, q, 'R');
1517
- if (q->definition != NULL) error(a, e_redefined);
1518
- if (q->mode == m_unknown) q->mode = a->mode; else
1519
- if (q->mode != a->mode) error2(a, e_declared_as_different_mode, q->mode);
1918
+ if (q->definition != NULL) {
1919
+ report_error_location(a);
1920
+ fprintf(stderr, "'%.*s' redefined\n", SIZE(q->s), q->s);
1921
+ }
1922
+ if (q->mode == m_unknown) {
1923
+ q->mode = a->mode;
1924
+ } else if (q->mode != a->mode) {
1925
+ report_error_location(a);
1926
+ fprintf(stderr, "'%.*s' declared as %s mode; used as %s mode",
1927
+ SIZE(q->s), q->s,
1928
+ name_of_mode(a->mode), name_of_mode(q->mode));
1929
+ }
1520
1930
  }
1521
1931
  p->name = q;
1522
1932
  if (a->program == NULL) a->program = p; else a->program_end->right = p;
1523
1933
  a->program_end = p;
1524
1934
  get_token(a, c_as);
1525
1935
  p->left = read_C(a);
1526
- if (q) q->definition = p->left;
1527
- /* We should get a node with a NULL right pointer from read_C() for the
1528
- * routine's code. We synthesise a "functionend" node there so
1529
- * optimisations such as dead code elimination and tail call optimisation
1530
- * can easily see where the function ends.
1531
- */
1532
- assert(p->left->right == NULL);
1533
- p->left->right = new_node(a, c_functionend);
1936
+ if (q) q->definition = p;
1534
1937
 
1535
1938
  if (a->substring != NULL) {
1536
- error2(a, e_unresolved_substring, a->substring->line_number);
1939
+ substring_without_among_error(a);
1537
1940
  a->substring = NULL;
1538
1941
  }
1539
- p->amongvar_needed = a->amongvar_needed;
1942
+ a->current_routine = NULL;
1540
1943
  }
1541
1944
 
1542
1945
  static void read_define(struct analyser * a) {
1543
- if (get_token(a, c_name)) {
1544
- struct name * q = find_name(a);
1545
- int type;
1546
- if (q) {
1547
- type = q->type;
1548
- } else {
1549
- /* No declaration so sniff next token - if it is a string or name
1550
- * we parse as a grouping, otherwise we parse as a routine. This
1551
- * avoids an avalanche of further errors if `as` is missing from a
1552
- * routine definition.
1553
- */
1554
- switch (peek_token(a->tokeniser)) {
1555
- case c_literalstring:
1556
- case c_name:
1557
- type = t_grouping;
1558
- break;
1559
- default:
1560
- type = t_routine;
1561
- }
1562
- }
1946
+ if (!get_token(a, c_name)) return;
1563
1947
 
1564
- if (type == t_grouping) {
1565
- read_define_grouping(a, q);
1566
- } else {
1567
- read_define_routine(a, q);
1948
+ struct name * q = find_name(a);
1949
+ int type;
1950
+ if (q) {
1951
+ type = q->type;
1952
+ if (type != t_grouping && type != t_routine && type != t_external) {
1953
+ // If integer, boolean, or string name then generate error and
1954
+ // parse based on the next token.
1955
+ type = (peek_token(a->tokeniser) == c_as) ? t_routine : t_grouping;
1956
+ check_name_type(a, q, type);
1957
+ }
1958
+ } else {
1959
+ /* No declaration so sniff next token - if it is a string or name
1960
+ * we parse as a grouping, otherwise we parse as a routine. This
1961
+ * avoids an avalanche of further errors if `as` is missing from a
1962
+ * routine definition.
1963
+ */
1964
+ switch (peek_token(a->tokeniser)) {
1965
+ case c_literalstring:
1966
+ case c_name:
1967
+ type = t_grouping;
1968
+ break;
1969
+ default:
1970
+ type = t_routine;
1568
1971
  }
1569
1972
  }
1973
+
1974
+ if (type == t_grouping) {
1975
+ read_define_grouping(a, q);
1976
+ } else {
1977
+ read_define_routine(a, q);
1978
+ }
1570
1979
  }
1571
1980
 
1572
1981
  static void read_backwardmode(struct analyser * a) {
@@ -1574,7 +1983,6 @@ static void read_backwardmode(struct analyser * a) {
1574
1983
  a->mode = m_backward;
1575
1984
  if (get_token(a, c_bra)) {
1576
1985
  read_program_(a, c_ket);
1577
- check_token(a, c_ket);
1578
1986
  }
1579
1987
  a->mode = mode;
1580
1988
  }
@@ -1582,7 +1990,8 @@ static void read_backwardmode(struct analyser * a) {
1582
1990
  static void read_program_(struct analyser * a, int terminator) {
1583
1991
  struct tokeniser * t = a->tokeniser;
1584
1992
  while (true) {
1585
- switch (read_token(t)) {
1993
+ int token = read_token(t);
1994
+ switch (token) {
1586
1995
  case c_strings: read_names(a, t_string); break;
1587
1996
  case c_booleans: read_names(a, t_boolean); break;
1588
1997
  case c_integers: read_names(a, t_integer); break;
@@ -1591,13 +2000,13 @@ static void read_program_(struct analyser * a, int terminator) {
1591
2000
  case c_groupings: read_names(a, t_grouping); break;
1592
2001
  case c_define: read_define(a); break;
1593
2002
  case c_backwardmode:read_backwardmode(a); break;
1594
- case c_ket:
1595
- if (terminator == c_ket) return;
1596
- /* fall through */
1597
2003
  default:
1598
- error(a, e_unexpected_token); break;
2004
+ if (token == terminator) return;
2005
+ unexpected_token_error(a, 0);
2006
+ break;
1599
2007
  case -1:
1600
- if (terminator >= 0) omission_error(a, c_ket);
2008
+ if (terminator != -1)
2009
+ omission_error(a, terminator);
1601
2010
  return;
1602
2011
  }
1603
2012
  }
@@ -1608,18 +2017,18 @@ static void remove_dead_assignments(struct node * p, struct name * q) {
1608
2017
  switch (p->type) {
1609
2018
  case c_assignto:
1610
2019
  case c_sliceto:
1611
- case c_mathassign:
2020
+ case c_assign:
1612
2021
  case c_plusassign:
1613
2022
  case c_minusassign:
1614
2023
  case c_multiplyassign:
1615
2024
  case c_divideassign:
1616
- case c_setmark:
1617
2025
  case c_set:
1618
2026
  case c_unset:
1619
2027
  case c_dollar:
1620
2028
  /* c_true is a no-op. */
1621
2029
  p->type = c_true;
1622
2030
  p->AE = NULL;
2031
+ p->name = NULL;
1623
2032
  break;
1624
2033
  default:
1625
2034
  /* There are no read accesses to this variable, so any
@@ -1636,119 +2045,1001 @@ static void remove_dead_assignments(struct node * p, struct name * q) {
1636
2045
  if (p->right) remove_dead_assignments(p->right, q);
1637
2046
  }
1638
2047
 
1639
- extern void read_program(struct analyser * a) {
2048
+ enum {
2049
+ // Not set on at least one code path leading to a use.
2050
+ USE_BEFORE_SET,
2051
+ // Need to keep checking.
2052
+ UNKNOWN,
2053
+ // Set on any code path leading to a use.
2054
+ SET_BEFORE_ANY_USE
2055
+ };
2056
+
2057
+ /* Find out if every codepath in the command with node p to a use of variable v
2058
+ * sets v first.
2059
+ *
2060
+ * The checks err towards being too conservative and may report that v can't be
2061
+ * safely localised when it can, but they allow localising all variables which
2062
+ * can be trivially made local in existing stemmers.
2063
+ *
2064
+ * p: the node of the command to check.
2065
+ * func: the c_define of the routine/external this code is in.
2066
+ * v: the variable to check.
2067
+ */
2068
+ static int always_set_before_use_(struct node * p, struct node * func,
2069
+ struct name * v) {
2070
+ assert(p);
2071
+ switch (p->type) {
2072
+ case c_call: {
2073
+ if (p->name->definition == func) {
2074
+ /* We've recursed into the function we're considering
2075
+ * localising this variable into, which means we can't
2076
+ * localise it because then changes to the variable in
2077
+ * the nested call won't be reflected after it returns.
2078
+ */
2079
+ return USE_BEFORE_SET;
2080
+ }
2081
+ // We know v is only referenced in the function we are checking.
2082
+ return UNKNOWN;
2083
+ }
2084
+ case c_among: {
2085
+ bool all_pass = true;
2086
+ struct among * x = p->among;
2087
+ for (int i = 1; i <= x->command_count; i++) {
2088
+ int r = always_set_before_use_(x->commands[i - 1], func, v);
2089
+ if (r == USE_BEFORE_SET) return r;
2090
+ all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
2091
+ }
2092
+ if (all_pass) return SET_BEFORE_ANY_USE;
2093
+ return UNKNOWN;
2094
+ }
2095
+ case c_or: {
2096
+ struct node * q = p->left;
2097
+ bool all_pass = true;
2098
+ while (q) {
2099
+ int r = always_set_before_use_(q, func, v);
2100
+ if (r == USE_BEFORE_SET) return r;
2101
+ all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
2102
+ q = q->right;
2103
+ }
2104
+ if (all_pass) return SET_BEFORE_ANY_USE;
2105
+ return UNKNOWN;
2106
+ }
2107
+ case c_and:
2108
+ case c_bra: {
2109
+ struct node * q = p->left;
2110
+ while (q) {
2111
+ int r = always_set_before_use_(q, func, v);
2112
+ if (r != UNKNOWN) return r;
2113
+ q = q->right;
2114
+ }
2115
+ return UNKNOWN;
2116
+ }
2117
+ case c_backwards:
2118
+ case c_not:
2119
+ case c_reverse:
2120
+ case c_test:
2121
+ return always_set_before_use_(p->left, func, v);
2122
+ case c_do:
2123
+ case c_fail:
2124
+ case c_gopast:
2125
+ case c_goto:
2126
+ case c_try:
2127
+ case c_repeat: {
2128
+ if (always_set_before_use_(p->left, func, v) == USE_BEFORE_SET)
2129
+ return USE_BEFORE_SET;
2130
+ return UNKNOWN;
2131
+ }
2132
+ case c_atleast:
2133
+ case c_loop:
2134
+ if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET)
2135
+ return USE_BEFORE_SET;
2136
+ return always_set_before_use_(p->left, func, v);
2137
+ case c_assign:
2138
+ // Check AE first: `x = x + 1` uses `x` before it sets it.
2139
+ if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET)
2140
+ return USE_BEFORE_SET;
2141
+ if (p->name == v)
2142
+ return SET_BEFORE_ANY_USE;
2143
+ return UNKNOWN;
2144
+ case c_assignto:
2145
+ case c_set:
2146
+ case c_sliceto:
2147
+ case c_unset:
2148
+ if (p->name == v)
2149
+ return SET_BEFORE_ANY_USE;
2150
+ return UNKNOWN;
2151
+ case c_grouping:
2152
+ case c_leftslice:
2153
+ case c_literalstring:
2154
+ case c_next:
2155
+ case c_non:
2156
+ case c_number:
2157
+ case c_rightslice:
2158
+ case c_debug:
2159
+ case c_substring:
2160
+ case c_tolimit:
2161
+ case c_false:
2162
+ case c_true:
2163
+ case c_goto_grouping:
2164
+ case c_gopast_grouping:
2165
+ case c_goto_non:
2166
+ case c_gopast_non:
2167
+ return UNKNOWN;
2168
+ case c_hop:
2169
+ case c_tomark:
2170
+ if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET)
2171
+ return USE_BEFORE_SET;
2172
+ return UNKNOWN;
2173
+ case c_stringassign:
2174
+ case c_attach:
2175
+ case c_booltest:
2176
+ case c_insert:
2177
+ case c_name:
2178
+ case c_not_booltest:
2179
+ case c_slicefrom:
2180
+ if (p->name == v) {
2181
+ return USE_BEFORE_SET;
2182
+ }
2183
+ return UNKNOWN;
2184
+ case c_functionend:
2185
+ return SET_BEFORE_ANY_USE;
2186
+ case c_divide:
2187
+ case c_minus:
2188
+ case c_multiply:
2189
+ case c_plus: {
2190
+ int r = always_set_before_use_(p->left, func, v);
2191
+ if (r != UNKNOWN) return r;
2192
+ return always_set_before_use_(p->right, func, v);
2193
+ }
2194
+ case c_eq:
2195
+ case c_ne:
2196
+ case c_gt:
2197
+ case c_ge:
2198
+ case c_lt:
2199
+ case c_le: {
2200
+ int r = always_set_before_use_(p->left, func, v);
2201
+ if (r != UNKNOWN) return r;
2202
+ return always_set_before_use_(p->AE, func, v);
2203
+ }
2204
+ case c_neg:
2205
+ return always_set_before_use_(p->right, func, v);
2206
+ case c_lenof:
2207
+ case c_sizeof:
2208
+ if (p->name == v) {
2209
+ return USE_BEFORE_SET;
2210
+ }
2211
+ return UNKNOWN;
2212
+ case c_cursor:
2213
+ case c_len:
2214
+ case c_limit:
2215
+ case c_maxint:
2216
+ case c_minint:
2217
+ case c_size:
2218
+ return UNKNOWN;
2219
+ case c_setlimit: {
2220
+ int r = always_set_before_use_(p->aux, func, v);
2221
+ if (r != UNKNOWN) return r;
2222
+ return always_set_before_use_(p->left, func, v);
2223
+ }
2224
+ case c_divideassign:
2225
+ case c_minusassign:
2226
+ case c_multiplyassign:
2227
+ case c_plusassign:
2228
+ if (p->name == v) {
2229
+ return USE_BEFORE_SET;
2230
+ }
2231
+ if (always_set_before_use_(p->AE, func, v) == USE_BEFORE_SET) {
2232
+ return USE_BEFORE_SET;
2233
+ }
2234
+ return UNKNOWN;
2235
+ case c_dollar:
2236
+ if (p->name != v) {
2237
+ return UNKNOWN;
2238
+ }
2239
+ #if 0
2240
+ // This check is valid, but currently it's better to not treat
2241
+ // initialising uses of string-$ as definitely setting the string
2242
+ // variable because for some target languages that means we need to
2243
+ // initialise to an empty string at the start of the function and
2244
+ // we would incur overhead from doing so.
2245
+ if (p->left->type == c_stringassign) {
2246
+ // Special-case `$x = S` because it's easy to handle.
2247
+ return SET_BEFORE_ANY_USE;
2248
+ }
2249
+ #endif
2250
+ // Otherwise, for now we assume that `$x C` might use `x` before
2251
+ // setting it. If string-$ sees wider use we can do better here.
2252
+ return USE_BEFORE_SET;
2253
+ case c_backwardmode:
2254
+ case c_define: // We always start from c_define's ->left.
2255
+ case c_booleans:
2256
+ case c_externals:
2257
+ case c_groupings:
2258
+ case c_integers:
2259
+ case c_routines:
2260
+ case c_strings:
2261
+ // Allowing these would allow checking the whole program.
2262
+ assert(0);
2263
+ return UNKNOWN;
2264
+ case c_comment1:
2265
+ case c_comment2:
2266
+ case c_decimal:
2267
+ case c_get:
2268
+ case c_hex:
2269
+ case c_stringdef:
2270
+ case c_stringescapes:
2271
+ // These are only use in the tokeniser.
2272
+ assert(0);
2273
+ break;
2274
+ case c_atlimit:
2275
+ case c_atmark:
2276
+ case c_setmark:
2277
+ // These are only use in the tokeniser and analyser.
2278
+ assert(0);
2279
+ break;
2280
+ case c_as:
2281
+ case c_for:
2282
+ case c_ket:
2283
+ // These shouldn't occur in this context.
2284
+ assert(0);
2285
+ break;
2286
+ }
2287
+ /* Pessimistic assumption for cases we don't handle yet. */
2288
+ printf("Assuming the worst about '%s' (%d)\n", name_of_token(p->type), p->type);
2289
+ return USE_BEFORE_SET;
2290
+ }
2291
+
2292
+ static int always_set_before_use(struct node * p, struct node * func,
2293
+ struct name * v) {
2294
+ return always_set_before_use_(p, func, v) != USE_BEFORE_SET;
2295
+ }
2296
+
2297
+ static void remove_routine(struct analyser * a, struct name * q) {
2298
+ struct node ** ptr = &(a->program);
2299
+ while (*ptr) {
2300
+ if ((*ptr)->name == q) {
2301
+ *ptr = (*ptr)->right;
2302
+ } else {
2303
+ ptr = &((*ptr)->right);
2304
+ }
2305
+ }
2306
+ }
2307
+
2308
+ // Return 0 for always f.
2309
+ // Return 1 for always t.
2310
+ // Return -1 for don't know (or can raise t or f).
2311
+ static int check_possible_signals(struct analyser * a, struct node * p) {
2312
+ switch (p->type) {
2313
+ case c_fail:
2314
+ case c_false:
2315
+ /* Always gives signal f. */
2316
+ return 0;
2317
+ case c_stringassign:
2318
+ case c_attach:
2319
+ case c_debug:
2320
+ case c_do:
2321
+ case c_insert:
2322
+ case c_leftslice:
2323
+ case c_rightslice:
2324
+ case c_set:
2325
+ case c_slicefrom:
2326
+ case c_sliceto:
2327
+ case c_tolimit:
2328
+ case c_tomark:
2329
+ case c_true:
2330
+ case c_try:
2331
+ case c_unset:
2332
+ case c_assign:
2333
+ case c_plusassign:
2334
+ case c_minusassign:
2335
+ case c_multiplyassign:
2336
+ case c_divideassign:
2337
+ case c_functionend:
2338
+ /* Always gives signal t. */
2339
+ return 1;
2340
+ case c_repeat: {
2341
+ int possible_signals = p->left->possible_signals;
2342
+ if (possible_signals == 1) {
2343
+ fprintf(stderr, "%s:%d: warning: infinite loop: body of 'repeat' always signals 't'\n",
2344
+ a->tokeniser->file, p->line_number);
2345
+ // Any code after this is unreachable.
2346
+ // FIXME: This only prunes the rest of this command list - we
2347
+ // want to prune anything that's only reachable from here.
2348
+ p->right = NULL;
2349
+ } else if (possible_signals == 0) {
2350
+ fprintf(stderr, "%s:%d: warning: body of 'repeat' always signals 'f'\n",
2351
+ a->tokeniser->file, p->line_number);
2352
+ p->type = c_do;
2353
+ }
2354
+ /* Always gives signal t. */
2355
+ return 1;
2356
+ }
2357
+ case c_not: {
2358
+ // `not` signals the opposite to the command it is applied to.
2359
+ int res = p->left->possible_signals;
2360
+ if (res < 0) {
2361
+ // `not` applied to command which can signal `t` or `f`.
2362
+ return res;
2363
+ }
2364
+ if (res == 0) {
2365
+ fprintf(stderr, "%s:%d: warning: 'not' applied to command which always signals f\n",
2366
+ a->tokeniser->file, p->line_number);
2367
+ // Handling the failure will restore the cursor, so equivalent to `do`.
2368
+ p->type = c_do;
2369
+ return 1;
2370
+ }
2371
+ fprintf(stderr, "%s:%d: warning: 'not' applied to command which always signals t\n",
2372
+ a->tokeniser->file, p->line_number);
2373
+ // This `not` is equivalent to `fail`.
2374
+ p->type = c_fail;
2375
+ return 0;
2376
+ }
2377
+ case c_setlimit: {
2378
+ /* If either always signals f, setlimit does too. */
2379
+ int res = p->left->possible_signals;
2380
+ int res2 = p->aux->possible_signals;
2381
+ if (res == 0 || res2 == 0) {
2382
+ return 0;
2383
+ }
2384
+ // If both always signal t, setlimit does too. Otherwise we know at
2385
+ // least one is unknown and that means setlimit's signal is unknown.
2386
+ // We can achieve that with a simple bitwise or.
2387
+ return res | res2;
2388
+ }
2389
+ case c_and:
2390
+ case c_bra: {
2391
+ struct node * q = p->left;
2392
+ int r = 1;
2393
+ while (q) {
2394
+ int res = q->possible_signals;
2395
+ if (res == 0) {
2396
+ // If any command always signals f, then the list always
2397
+ // signals f.
2398
+ if (q->right) {
2399
+ if (q->right->type != c_functionend) {
2400
+ fprintf(stderr, "%s:%d: warning: command always signals f here so rest of %s is unreachable\n",
2401
+ a->tokeniser->file, q->line_number,
2402
+ (p->type == c_and ? "'and'" : "command list"));
2403
+ }
2404
+ q->right = NULL;
2405
+ }
2406
+ return res;
2407
+ }
2408
+ if (res < 0) r = res;
2409
+ q = q->right;
2410
+ }
2411
+ return r;
2412
+ }
2413
+ case c_backwards:
2414
+ case c_dollar:
2415
+ case c_loop:
2416
+ case c_reverse:
2417
+ case c_test:
2418
+ /* Give same signal as p->left. */
2419
+ return p->left->possible_signals;
2420
+ case c_atleast: {
2421
+ int possible_signals = p->left->possible_signals;
2422
+ if (possible_signals == 1) {
2423
+ fprintf(stderr, "%s:%d: warning: infinite loop: body of 'atleast' always signals 't'\n",
2424
+ a->tokeniser->file, p->line_number);
2425
+ // Any code after this is unreachable.
2426
+ // FIXME: This only prunes the rest of this command list - we
2427
+ // want to prune anything that's only reachable from here.
2428
+ p->right = NULL;
2429
+ } else if (possible_signals == 0) {
2430
+ fprintf(stderr, "%s:%d: warning: body of 'atleast' always signals 'f'\n",
2431
+ a->tokeniser->file, p->line_number);
2432
+ p->type = c_bra;
2433
+ p->AE = NULL;
2434
+ }
2435
+ /* Give same signal as p->left. */
2436
+ return possible_signals;
2437
+ }
2438
+ case c_call:
2439
+ // If the call recurses back into the current routine then this
2440
+ // will still be -1.
2441
+ return p->name->definition->possible_signals;
2442
+ case c_gopast:
2443
+ case c_goto:
2444
+ case c_goto_grouping:
2445
+ case c_gopast_grouping:
2446
+ case c_goto_non:
2447
+ case c_gopast_non:
2448
+ /* FIXME: unless we can prove that c is either definitely atlimit
2449
+ * or definitely not atlimit... */
2450
+ return -1;
2451
+ case c_booltest:
2452
+ case c_not_booltest:
2453
+ case c_hop:
2454
+ case c_literalstring:
2455
+ case c_next:
2456
+ case c_eq:
2457
+ case c_ne:
2458
+ case c_gt:
2459
+ case c_ge:
2460
+ case c_lt:
2461
+ case c_le:
2462
+ case c_grouping:
2463
+ case c_non:
2464
+ case c_name:
2465
+ /* FIXME: unless we can prove... */
2466
+ return -1;
2467
+ case c_substring: {
2468
+ struct among * x = p->among;
2469
+ if (x->always_matches) {
2470
+ return 1;
2471
+ }
2472
+ return -1;
2473
+ }
2474
+ case c_among: {
2475
+ struct among * x = p->among;
2476
+ int r = 1;
2477
+
2478
+ if (x->substring == NULL) {
2479
+ if (!x->always_matches) {
2480
+ r = -1;
2481
+ }
2482
+ }
2483
+
2484
+ if (x->command_count > 0) {
2485
+ bool trues = (x->nocommand_count > 0);
2486
+ bool falses = false;
2487
+ for (int i = 1; i <= x->command_count; i++) {
2488
+ int res = x->commands[i - 1]->possible_signals;
2489
+ if (res == 0) {
2490
+ falses = true;
2491
+ } else if (res > 0) {
2492
+ trues = true;
2493
+ } else {
2494
+ falses = trues = true;
2495
+ }
2496
+ if (falses && trues) break;
2497
+ }
2498
+ if (!trues) {
2499
+ // All commands in among always fail.
2500
+ return 0;
2501
+ }
2502
+ if (falses) {
2503
+ // Commands in among can succeed or fail.
2504
+ return -1;
2505
+ }
2506
+ }
2507
+ return r;
2508
+ }
2509
+ case c_or: {
2510
+ int r = 0;
2511
+ for (struct node * q = p->left; q; q = q->right) {
2512
+ // Just check this node - q->right is a separate clause of
2513
+ // the OR.
2514
+ int res = q->possible_signals;
2515
+ if (res > 0) {
2516
+ // If any clause of the OR always signals t, then the OR
2517
+ // always signals t.
2518
+ if (q->right) {
2519
+ if (q->right->type != c_functionend) {
2520
+ fprintf(stderr, "%s:%d: warning: command always signals t here so rest of 'or' is unreachable\n",
2521
+ a->tokeniser->file,
2522
+ q->line_number);
2523
+ }
2524
+ q->right = NULL;
2525
+ }
2526
+ return 1;
2527
+ }
2528
+ if (res < 0) {
2529
+ r = res;
2530
+ }
2531
+ }
2532
+ return r;
2533
+ }
2534
+ default:
2535
+ return -1;
2536
+ }
2537
+ }
2538
+
2539
+ static void visit_routine(struct analyser * a, struct name * n);
2540
+
2541
+ static void visit_node(struct analyser * a, struct node * p, struct name * func) {
2542
+ while (p) {
2543
+ if (p->name) {
2544
+ if (p->name->count != -2) {
2545
+ // First use of this name that visit_node() has seen.
2546
+ p->name->used = p;
2547
+ p->name->local_to = func;
2548
+ if (p->type == c_call) {
2549
+ visit_routine(a, p->name);
2550
+ } else {
2551
+ // Mark as reachable.
2552
+ p->name->count = -2;
2553
+ }
2554
+ } else if (p->name->local_to != func) {
2555
+ // Used in more than one routine/external.
2556
+ p->name->local_to = NULL;
2557
+ }
2558
+ } else if (p->type == c_among) {
2559
+ struct among * x = p->among;
2560
+ x->used = true;
2561
+ for (int i = 0; i < x->literalstring_count; ++i) {
2562
+ if (x->b[i].function)
2563
+ visit_routine(a, x->b[i].function);
2564
+ }
2565
+ for (int i = 0; i < x->command_count; ++i) {
2566
+ visit_node(a, x->commands[i], func);
2567
+ }
2568
+ }
2569
+ if (p->left) {
2570
+ visit_node(a, p->left, func);
2571
+ }
2572
+ if (p->aux) {
2573
+ visit_node(a, p->aux, func);
2574
+ }
2575
+ if (p->AE) {
2576
+ visit_node(a, p->AE, func);
2577
+ }
2578
+
2579
+ p->possible_signals = check_possible_signals(a, p);
2580
+
2581
+ if ((p->type == c_and || p->type == c_or) && !p->left->right) {
2582
+ // Pruning of unreachable code can leave single-entry c_and and
2583
+ // c_or nodes. These can lead to unused variables in the generated
2584
+ // code and may also hinder further optimisations.
2585
+ //
2586
+ // We want to replace these with their subnode. It's fiddly to do
2587
+ // an actual replacement as we'd need to update the location we got
2588
+ // the current value of `p` from, so instead we swap the contents
2589
+ // of the two nodes. Note that we must not swap the existing ->next
2590
+ // pointers as that could break the chain of all allocated nodes we
2591
+ // have from analyser->nodes.
2592
+ struct node * p_left = p->left;
2593
+ struct node * p_right = p->right;
2594
+ struct node * p_next = p->next;
2595
+ struct node tmp = *p;
2596
+ tmp.next = p_left->next;
2597
+ *p = *p_left;
2598
+ p->right = p_right;
2599
+ p->next = p_next;
2600
+ *p_left = tmp;
2601
+ if (p->type == c_among) {
2602
+ // Update struct among's node pointer.
2603
+ for (struct among * q = a->amongs; q; q = q->next) {
2604
+ if (q->node == p_left) {
2605
+ q->node = p;
2606
+ break;
2607
+ }
2608
+ }
2609
+ }
2610
+ }
2611
+
2612
+ p = p->right;
2613
+ }
2614
+ }
2615
+
2616
+ static void visit_routine(struct analyser * a, struct name * n) {
2617
+ if (n->count == -2) {
2618
+ // Already visited. We set n->count before walking the definition so
2619
+ // this also prevents the walk from reentering a routine via recursive
2620
+ // calls.
2621
+ return;
2622
+ }
2623
+ n->count = -2;
2624
+
2625
+ struct node * p = n->definition;
2626
+
2627
+ // Recursive functions are valid in the Snowball language, but aren't
2628
+ // actually used in typical snowball programs so we take a simple
2629
+ // approach and handle them by setting pessimistic assumptions here which
2630
+ // will be used if a function calls itself (directly or indirectly).
2631
+ p->possible_signals = -1; // Assume it could signal t or f.
2632
+
2633
+ visit_node(a, p->left, n);
2634
+
2635
+ // Update with calculated value.
2636
+ p->possible_signals = p->left->possible_signals;
2637
+ }
2638
+
2639
+ extern void read_program(struct analyser * a, unsigned localise_mask) {
1640
2640
  read_program_(a, -1);
1641
- {
1642
- struct name * q = a->names;
1643
- while (q) {
1644
- switch (q->type) {
1645
- case t_external: case t_routine:
1646
- if (q->used && q->definition == NULL) error4(a, q);
1647
- break;
1648
- case t_grouping:
1649
- if (q->used && q->grouping == NULL) error4(a, q);
2641
+ for (struct name * q = a->names; q; q = q->next) {
2642
+ // Declaring but not defining is only an error if used. We'll issue
2643
+ // a warning later on if there are no errors.
2644
+ if (!q->used) continue;
2645
+
2646
+ bool error = false;
2647
+ switch (q->type) {
2648
+ case t_external: case t_routine:
2649
+ error = (q->definition == NULL);
2650
+ break;
2651
+ case t_grouping:
2652
+ error = (q->grouping == NULL);
2653
+ break;
2654
+ }
2655
+ if (error) {
2656
+ count_error(a);
2657
+ fprintf(stderr, "%s:%d: %s '%.*s' declared but not defined\n",
2658
+ a->tokeniser->file, q->used->line_number,
2659
+ name_of_type(q->type),
2660
+ SIZE(q->s), q->s);
2661
+ }
2662
+ }
2663
+
2664
+ // Skip name warning checks if there are errors.
2665
+ if (a->tokeniser->error_count)
2666
+ return;
2667
+
2668
+ for (struct name ** n_ptr = &(a->names); *n_ptr; ) {
2669
+ struct name * n = *n_ptr;
2670
+ // Inline some routines.
2671
+ //
2672
+ // Currently we inline routines which are "simple" and only used once.
2673
+ // Probably we should inline "simple" *OR* only used once, but to do
2674
+ // that we probably need to clone the nodes for routines used more
2675
+ // than once, and we need to handle updating things like
2676
+ // amongvar_needed (which hasn't been set at this point).
2677
+ //
2678
+ // Note: the definition of a routine is counted as a reference to its
2679
+ // name, so a routine only called once has 2 references.
2680
+ if (n->type == t_routine &&
2681
+ n->references == 2 &&
2682
+ n->uses_in_among == 0 &&
2683
+ !n->definition->left->right) {
2684
+ bool inline_routine = false;
2685
+ switch (n->definition->left->type) {
2686
+ case c_eq:
2687
+ case c_ge:
2688
+ case c_gt:
2689
+ case c_le:
2690
+ case c_lt:
2691
+ case c_ne:
2692
+ case c_booltest:
2693
+ case c_not_booltest:
2694
+ case c_grouping:
2695
+ case c_non:
2696
+ case c_goto_grouping:
2697
+ case c_gopast_grouping:
2698
+ case c_goto_non:
2699
+ case c_gopast_non:
2700
+ case c_literalstring:
2701
+ case c_name:
2702
+ case c_true:
2703
+ case c_false:
2704
+ case c_fail:
2705
+ case c_set:
2706
+ case c_unset:
2707
+ case c_assign:
2708
+ case c_plusassign:
2709
+ case c_minusassign:
2710
+ case c_multiplyassign:
2711
+ case c_divideassign:
2712
+ case c_tomark:
2713
+ case c_tolimit:
2714
+ case c_hop:
2715
+ case c_next:
2716
+ case c_insert:
2717
+ case c_attach:
2718
+ case c_leftslice:
2719
+ case c_rightslice:
2720
+ case c_sliceto:
2721
+ case c_stringassign:
2722
+ case c_slicefrom:
2723
+ // Inline "simple" single-command routines.
2724
+ inline_routine = true;
1650
2725
  break;
1651
2726
  }
1652
- q = q->next;
2727
+
2728
+ if (inline_routine) {
2729
+ #if 0
2730
+ fprintf(stderr, "%s:%d: info: Inlining %.*s\n",
2731
+ a->tokeniser->file,
2732
+ n->used->line_number,
2733
+ SIZE(n->s), n->s);
2734
+ #endif
2735
+ struct node * call_site = n->used;
2736
+ call_site->type = c_bra;
2737
+ call_site->name = NULL;
2738
+ call_site->left = n->definition->left;
2739
+ remove_routine(a, n);
2740
+ *n_ptr = n->next;
2741
+ lose_s(n->s);
2742
+ FREE(n);
2743
+ continue;
2744
+ }
1653
2745
  }
2746
+
2747
+ n_ptr = &(n->next);
1654
2748
  }
1655
2749
 
1656
- if (a->tokeniser->error_count == 0) {
1657
- struct name * q = a->names;
1658
- struct name ** ptr = &(a->names);
1659
- while (q) {
1660
- if (!q->referenced) {
1661
- q->s[SIZE(q->s)] = 0;
1662
- fprintf(stderr, "%s:%d: warning: %s '%s' ",
1663
- a->tokeniser->file,
1664
- q->declaration_line_number,
1665
- name_of_name_type(q->type),
1666
- q->s);
1667
- if (q->type == t_routine ||
1668
- q->type == t_external ||
1669
- q->type == t_grouping) {
1670
- fprintf(stderr, "declared but not defined\n");
2750
+ // Add "functionend" nodes there so optimisations such as dead code
2751
+ // elimination and tail call optimisation can easily see where the function
2752
+ // ends.
2753
+ for (struct name * name = a->names; name; name = name->next) {
2754
+ if (name->type != t_routine && name->type != t_external) continue;
2755
+
2756
+ struct node * p = name->definition;
2757
+ if (!p) {
2758
+ // No definition - we'll report this later.
2759
+ continue;
2760
+ }
2761
+ assert(p->type == c_define);
2762
+ assert(p->left->right == NULL);
2763
+ if (p->left->type == c_bra) {
2764
+ /* Put the "functionend" node at the end of the command list. */
2765
+ struct node * e = p->left->left;
2766
+ if (e) {
2767
+ while (e->right) e = e->right;
2768
+ e->right = new_node(a, c_functionend);
2769
+ } else {
2770
+ p->left = new_node(a, c_functionend);
2771
+ }
2772
+ } else {
2773
+ /* Put the "functionend" node after the single command. */
2774
+ p->left->right = new_node(a, c_functionend);
2775
+ }
2776
+ }
2777
+
2778
+ // Trace possible control flow starting from externals to discover any
2779
+ // unreachable code. This also fills in possible_signals for each
2780
+ // reachable node.
2781
+ for (struct name * n = a->names; n; n = n->next) {
2782
+ if (n->type == t_external) {
2783
+ if (!n->used) {
2784
+ // Externals can be called from outside of Snowball, so if they
2785
+ // aren't already marked as used we set the `used` field to
2786
+ // point to the definition so we can just check this field
2787
+ // later.
2788
+ n->used = n->definition;
2789
+ }
2790
+ visit_routine(a, n);
2791
+ }
2792
+ }
2793
+
2794
+ for (struct name * q = a->names; q; q = q->next) {
2795
+ if (q->references == 0) {
2796
+ fprintf(stderr, "%s:%d: warning: %s '%.*s' ",
2797
+ a->tokeniser->file,
2798
+ q->declaration_line_number,
2799
+ name_of_type(q->type),
2800
+ SIZE(q->s), q->s);
2801
+ if (q->type == t_routine ||
2802
+ q->type == t_external ||
2803
+ q->type == t_grouping) {
2804
+ fprintf(stderr, "declared but not defined\n");
2805
+ } else {
2806
+ fprintf(stderr, "declared but not used\n");
2807
+ }
2808
+ q->used = NULL;
2809
+ continue;
2810
+ }
2811
+
2812
+ if (q->type == t_routine || q->type == t_grouping) {
2813
+ /* It's OK to define a grouping but only use it to define other
2814
+ * groupings.
2815
+ */
2816
+ if (!q->used && !q->used_in_definition) {
2817
+ int line_num;
2818
+ if (q->type == t_routine) {
2819
+ line_num = q->definition->line_number;
1671
2820
  } else {
1672
- fprintf(stderr, "defined but not used\n");
2821
+ line_num = q->grouping->line_number;
1673
2822
  }
1674
- q = q->next;
1675
- *ptr = q;
2823
+ fprintf(stderr, "%s:%d: warning: %s '%.*s' defined but not used\n",
2824
+ a->tokeniser->file,
2825
+ line_num,
2826
+ name_of_type(q->type),
2827
+ SIZE(q->s), q->s);
1676
2828
  continue;
1677
- } else if (q->type == t_routine || q->type == t_grouping) {
1678
- /* It's OK to define a grouping but only use it to define other
1679
- * groupings.
1680
- */
1681
- if (!q->used && !q->used_in_definition) {
1682
- int line_num;
1683
- if (q->type == t_routine) {
1684
- line_num = q->definition->line_number;
1685
- } else {
1686
- line_num = q->grouping->line_number;
1687
- }
1688
- q->s[SIZE(q->s)] = 0;
1689
- fprintf(stderr, "%s:%d: warning: %s '%s' defined but not used\n",
1690
- a->tokeniser->file,
1691
- line_num,
1692
- name_of_name_type(q->type),
1693
- q->s);
1694
- q = q->next;
1695
- *ptr = q;
1696
- continue;
1697
- }
1698
- } else if (q->type == t_external) {
1699
- /* Unused is OK. */
1700
- } else if (!q->initialised) {
1701
- q->s[SIZE(q->s)] = 0;
1702
- fprintf(stderr, "%s:%d: warning: %s '%s' is never initialised\n",
2829
+ }
2830
+ }
2831
+
2832
+ if (q->type < t_routine) {
2833
+ if (!q->initialised) {
2834
+ fprintf(stderr, "%s:%d: warning: %s '%.*s' is never initialised\n",
1703
2835
  a->tokeniser->file,
1704
2836
  q->declaration_line_number,
1705
- name_of_name_type(q->type),
1706
- q->s);
2837
+ name_of_type(q->type),
2838
+ SIZE(q->s), q->s);
1707
2839
  } else if (!q->value_used) {
1708
- q->s[SIZE(q->s)] = 0;
1709
- fprintf(stderr, "%s:%d: warning: %s '%s' is set but never used\n",
2840
+ fprintf(stderr, "%s:%d: warning: %s '%.*s' is set but never used\n",
1710
2841
  a->tokeniser->file,
1711
2842
  q->declaration_line_number,
1712
- name_of_name_type(q->type),
1713
- q->s);
2843
+ name_of_type(q->type),
2844
+ SIZE(q->s), q->s);
1714
2845
  remove_dead_assignments(a->program, q);
1715
- q = q->next;
1716
- *ptr = q;
2846
+ q->used = NULL;
1717
2847
  continue;
1718
2848
  }
1719
- ptr = &(q->next);
1720
- q = q->next;
1721
2849
  }
1722
2850
 
1723
- {
1724
- /* Now we've eliminated variables whose values are never used we
1725
- * can number the variables, which is used by some generators.
1726
- */
1727
- int * name_count = a->name_count;
1728
- struct name * n;
1729
- for (n = a->names; n; n = n->next) {
1730
- n->count = name_count[n->type]++;
2851
+ if (q->count == -1) {
2852
+ // Used but use is not reachable by calling any externals so
2853
+ // suppress all code generation for this name.
2854
+ //
2855
+ // We only issue a warning about unreachability for routines here
2856
+ // to avoid excess diagnostics, since other types must be used in a
2857
+ // routine which is not reachable (or will have been warned about as
2858
+ // unused by the check above).
2859
+ if (q->type == t_routine) {
2860
+ fprintf(stderr, "%s:%d: warning: %s '%.*s' not reachable from any externals\n",
2861
+ a->tokeniser->file,
2862
+ q->declaration_line_number,
2863
+ name_of_type(q->type),
2864
+ SIZE(q->s), q->s);
2865
+ remove_routine(a, q);
2866
+ }
2867
+ q->used = NULL;
2868
+ }
2869
+ }
2870
+
2871
+ /* We've now identified variables whose values are never used and
2872
+ * names which are unreachable, and cleared "used" for them, so go
2873
+ * through and unlink the unused ones.
2874
+ */
2875
+ struct name * n = a->names;
2876
+ struct name ** n_ptr = &(a->names);
2877
+ while (n) {
2878
+ if (!n->used) {
2879
+ if (n->grouping) {
2880
+ // Clear the name field then loop through and remove from
2881
+ // the groupings list just below.
2882
+ n->grouping->name = NULL;
2883
+ } else if (n->definition) {
2884
+ remove_routine(a, n);
2885
+ }
2886
+ struct name * n_next = n->next;
2887
+ lose_s(n->s);
2888
+ FREE(n);
2889
+ n = n_next;
2890
+ *n_ptr = n;
2891
+ continue;
2892
+ }
2893
+ n_ptr = &(n->next);
2894
+ n = n->next;
2895
+ }
2896
+
2897
+ // Remove groupings which aren't used.
2898
+ struct grouping * g = a->groupings;
2899
+ struct grouping ** g_ptr = &(a->groupings);
2900
+ while (g) {
2901
+ if (!g->name) {
2902
+ struct grouping * g_next = g->next;
2903
+ lose_b(g->b);
2904
+ FREE(g);
2905
+ g = g_next;
2906
+ *g_ptr = g;
2907
+ continue;
2908
+ }
2909
+ g_ptr = &(g->next);
2910
+ g = g->next;
2911
+ }
2912
+
2913
+ // Remove amongs which are in unreachable routines from the list
2914
+ // and number the others.
2915
+ {
2916
+ int among_count = 0;
2917
+ struct among ** a_ptr = &(a->amongs);
2918
+ while (*a_ptr) {
2919
+ struct among * x = *a_ptr;
2920
+ if (!x->used) {
2921
+ *a_ptr = x->next;
2922
+ continue;
2923
+ }
2924
+
2925
+ x->number = among_count++;
2926
+ if (x->function_count > 0) ++a->among_with_function_count;
2927
+
2928
+ for (int i = 1; i <= x->command_count; i++) {
2929
+ int merge_with = 0;
2930
+ struct node * command = x->commands[i - 1];
2931
+ assert(command->type == c_bra);
2932
+ if (!command->left || is_just_true(command->left)) {
2933
+ // Optimisation has turned this action into a no-op.
2934
+ command->left = NULL;
2935
+ merge_with = -1;
2936
+ } else {
2937
+ for (int k = 1; k < i; ++k) {
2938
+ if (nodes_equivalent(command->left, x->commands[k - 1]->left)) {
2939
+ // Optimisation has made this action equivalent
2940
+ // to an earlier one.
2941
+ merge_with = k;
2942
+ break;
2943
+ }
2944
+ }
2945
+ }
2946
+ if (!merge_with) continue;
2947
+
2948
+ // Update references to this command index to be `merge_with`
2949
+ // and subtract one from references to command indexes after
2950
+ // this one.
2951
+ for (int j = 0; j < x->literalstring_count; ++j) {
2952
+ int diff = (x->b[j].result - i);
2953
+ if (diff == 0) {
2954
+ x->b[j].result = merge_with;
2955
+ if (merge_with == 0) {
2956
+ assert(x->b[j].action->type == c_bra);
2957
+ x->b[j].action->left = NULL;
2958
+ }
2959
+ } else if (diff > 0) {
2960
+ --x->b[j].result;
2961
+ }
2962
+ }
2963
+ memmove(x->commands + (i - 1), x->commands + i,
2964
+ sizeof(x->commands[0]) * (x->command_count - i));
2965
+ --x->command_count;
2966
+ ++x->nocommand_count;
2967
+ --i;
2968
+ }
2969
+
2970
+ if (x->command_count > 1 ||
2971
+ (x->command_count == 1 && x->nocommand_count > 0)) {
2972
+ /* We need to set among_var rather than just checking if
2973
+ * find_among*() returns zero or not.
2974
+ */
2975
+ x->amongvar_needed = true;
2976
+ if (x->in_routine)
2977
+ x->in_routine->amongvar_needed = true;
1731
2978
  }
2979
+
2980
+ a_ptr = &(x->next);
2981
+ }
2982
+ }
2983
+
2984
+ /* Localise variables.
2985
+ *
2986
+ * We localise variables which are only referenced in a single function
2987
+ * (routine or external) and which are always set before being read within
2988
+ * that function (since a function could rely on a variable's previous
2989
+ * value surviving).
2990
+ *
2991
+ * We could potentially localise variables referenced in multiple functions
2992
+ * provided that they are always set before use in every function they are
2993
+ * referenced in, and that these functions don't call one another, but that
2994
+ * situation doesn't occur in any of the stemmers we currently ship.
2995
+ */
2996
+ memset(a->name_count, 0, sizeof(a->name_count));
2997
+ for (struct name * name = a->names; name; name = name->next) {
2998
+ if (name->local_to != NULL) {
2999
+ if (localise_mask & (1 << name->type)) {
3000
+ struct node * func = name->local_to->definition;
3001
+ if (!always_set_before_use(func->left, func, name)) {
3002
+ fprintf(stderr,
3003
+ "%s:%d: info: Could not localise %s `%.*s` to routine `%.*s`\n",
3004
+ a->tokeniser->file, func->line_number,
3005
+ name_of_type(name->type),
3006
+ SIZE(name->s), name->s,
3007
+ SIZE(func->name->s), func->name->s);
3008
+ report_s(stderr, name->s);
3009
+ fprintf(stderr, "\n");
3010
+ name->local_to = NULL;
3011
+ }
3012
+ } else {
3013
+ name->local_to = NULL;
3014
+ }
3015
+ }
3016
+ if (name->local_to == NULL) {
3017
+ name->count = a->name_count[name->type]++;
3018
+ }
3019
+ }
3020
+ a->variable_count = a->name_count[t_string] +
3021
+ a->name_count[t_boolean] +
3022
+ a->name_count[t_integer];
3023
+
3024
+ // Now number the locals (which e.g. Ada and Pascal use to avoid clashes
3025
+ // from case-insensitive variable names). We use a copy of the counters
3026
+ // to do this so that a->name_count[] reflects the number of non-localised
3027
+ // variables of each type.
3028
+ int name_count[t_size];
3029
+ memcpy(name_count, a->name_count, sizeof(name_count));
3030
+ for (struct name * name = a->names; name; name = name->next) {
3031
+ if (name->count < 0) {
3032
+ name->count = name_count[name->type]++;
1732
3033
  }
1733
3034
  }
1734
3035
  }
1735
3036
 
1736
3037
  extern struct analyser * create_analyser(struct tokeniser * t) {
1737
3038
  NEW(analyser, a);
3039
+ *a = (struct analyser){0};
1738
3040
  a->tokeniser = t;
1739
- a->nodes = NULL;
1740
- a->names = NULL;
1741
- a->literalstrings = NULL;
1742
- a->program = NULL;
1743
- a->amongs = NULL;
1744
- a->among_count = 0;
1745
- a->among_with_function_count = 0;
1746
- a->groupings = NULL;
1747
3041
  a->mode = m_forward;
1748
3042
  a->modifyable = true;
1749
- { int i; for (i = 0; i < t_size; i++) a->name_count[i] = 0; }
1750
- a->substring = NULL;
1751
- a->int_limits_used = false;
1752
3043
  return a;
1753
3044
  }
1754
3045