mittens 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -1
- data/README.md +2 -2
- data/Rakefile +12 -5
- data/ext/mittens/extconf.rb +3 -1
- data/lib/mittens/version.rb +1 -1
- data/vendor/snowball/.github/workflows/ci.yml +144 -17
- data/vendor/snowball/.github/workflows/coverage.yml +117 -0
- data/vendor/snowball/.github/workflows/runtime-tests.yml +26 -0
- data/vendor/snowball/.gitignore +33 -7
- data/vendor/snowball/CONTRIBUTING.rst +7 -0
- data/vendor/snowball/COPYING +1 -1
- data/vendor/snowball/GNUmakefile +736 -298
- data/vendor/snowball/NEWS +1394 -7
- data/vendor/snowball/README.rst +8 -8
- data/vendor/snowball/ada/generate/generate.adb +5 -5
- data/vendor/snowball/ada/src/stemmer.adb +177 -188
- data/vendor/snowball/ada/src/stemmer.ads +86 -89
- data/vendor/snowball/ada/src/stemwords.adb +21 -5
- data/vendor/snowball/algorithms/czech.sbl +255 -0
- data/vendor/snowball/algorithms/danish.sbl +22 -10
- data/vendor/snowball/algorithms/english.sbl +7 -4
- data/vendor/snowball/algorithms/esperanto.sbl +6 -7
- data/vendor/snowball/algorithms/estonian.sbl +9 -4
- data/vendor/snowball/algorithms/finnish.sbl +33 -21
- data/vendor/snowball/algorithms/german.sbl +5 -0
- data/vendor/snowball/algorithms/indonesian.sbl +115 -96
- data/vendor/snowball/algorithms/italian.sbl +26 -1
- data/vendor/snowball/algorithms/lithuanian.sbl +13 -13
- data/vendor/snowball/algorithms/norwegian.sbl +17 -6
- data/vendor/snowball/algorithms/persian.sbl +387 -0
- data/vendor/snowball/algorithms/polish.sbl +177 -0
- data/vendor/snowball/algorithms/sesotho.sbl +115 -0
- data/vendor/snowball/algorithms/turkish.sbl +4 -4
- data/vendor/snowball/compiler/analyser.c +2002 -711
- data/vendor/snowball/compiler/driver.c +460 -298
- data/vendor/snowball/compiler/generator.c +439 -1965
- data/vendor/snowball/compiler/generator_ada.c +605 -430
- data/vendor/snowball/compiler/generator_c.c +2227 -0
- data/vendor/snowball/compiler/generator_csharp.c +365 -274
- data/vendor/snowball/compiler/generator_dart.c +1476 -0
- data/vendor/snowball/compiler/generator_go.c +348 -270
- data/vendor/snowball/compiler/generator_java.c +330 -233
- data/vendor/snowball/compiler/generator_js.c +534 -385
- data/vendor/snowball/compiler/generator_pascal.c +361 -320
- data/vendor/snowball/compiler/generator_php.c +1445 -0
- data/vendor/snowball/compiler/generator_python.c +399 -299
- data/vendor/snowball/compiler/generator_rust.c +370 -261
- data/vendor/snowball/compiler/generator_zig.c +1474 -0
- data/vendor/snowball/compiler/header.h +153 -86
- data/vendor/snowball/compiler/space.c +121 -63
- data/vendor/snowball/compiler/tokeniser.c +286 -182
- data/vendor/snowball/compiler/tokens.h +71 -0
- data/vendor/snowball/csharp/Snowball/Among.cs +14 -26
- data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +3 -3
- data/vendor/snowball/csharp/Snowball/Stemmer.cs +41 -45
- data/vendor/snowball/csharp/Stemwords/Program.cs +20 -14
- data/vendor/snowball/cxx/.gitignore +4 -0
- data/vendor/snowball/cxx/generate_algorithms.pl +87 -0
- data/vendor/snowball/cxx/stemmer.h +12 -0
- data/vendor/snowball/cxx/stemwords.cxx +176 -0
- data/vendor/snowball/cxx/utilities.cxx +2 -0
- data/vendor/snowball/dart/.gitignore +4 -0
- data/vendor/snowball/dart/analysis_options.yaml +1 -0
- data/vendor/snowball/dart/example/test_app.dart +65 -0
- data/vendor/snowball/dart/generate_algorithms.pl +21 -0
- data/vendor/snowball/dart/lib/snowball.dart +18 -0
- data/vendor/snowball/dart/lib/src/snowball.dart +339 -0
- data/vendor/snowball/dart/pubspec.yaml +17 -0
- data/vendor/snowball/doc/libstemmer_dart_README +37 -0
- data/vendor/snowball/doc/libstemmer_js_README +2 -2
- data/vendor/snowball/doc/libstemmer_php_README +38 -0
- data/vendor/snowball/doc/libstemmer_python_README +3 -3
- data/vendor/snowball/examples/stemwords.c +18 -3
- data/vendor/snowball/go/README.md +12 -4
- data/vendor/snowball/go/env.go +12 -14
- data/vendor/snowball/go/stemwords/main.go +2 -1
- data/vendor/snowball/java/org/tartarus/snowball/CharArraySequence.java +36 -0
- data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +17 -34
- data/vendor/snowball/java/org/tartarus/snowball/SnowballStemmer.java +0 -2
- data/vendor/snowball/javascript/base-stemmer.js +219 -252
- data/vendor/snowball/javascript/stemwords.js +86 -58
- data/vendor/snowball/libstemmer/mkmodules.pl +1 -1
- data/vendor/snowball/libstemmer/modules.txt +4 -0
- data/vendor/snowball/pascal/SnowballProgram.pas +18 -31
- data/vendor/snowball/php/base-stemmer.php +453 -0
- data/vendor/snowball/php/stemwords.php +25 -0
- data/vendor/snowball/python/create_init.py +16 -21
- data/vendor/snowball/python/pyproject.toml +3 -0
- data/vendor/snowball/python/setup.py +5 -7
- data/vendor/snowball/python/snowballstemmer/among.py +1 -2
- data/vendor/snowball/python/snowballstemmer/basestemmer.py +24 -33
- data/vendor/snowball/python/stemwords.py +72 -63
- data/vendor/snowball/runtime/api.c +11 -42
- data/vendor/snowball/runtime/api.h +11 -9
- data/vendor/snowball/runtime/snowball_runtime.h +110 -0
- data/vendor/snowball/runtime/utilities.c +282 -106
- data/vendor/snowball/rust/src/main.rs +2 -2
- data/vendor/snowball/rust/src/snowball/snowball_env.rs +7 -9
- data/vendor/snowball/tests/compilertest +62 -0
- data/vendor/snowball/tests/errors/ae-errors.sbl +53 -0
- data/vendor/snowball/tests/errors/ae-errors.stderr +8 -0
- data/vendor/snowball/tests/errors/bad-dollar.sbl +14 -0
- data/vendor/snowball/tests/errors/bad-dollar.stderr +4 -0
- data/vendor/snowball/tests/errors/bad-grouping-definition.sbl +9 -0
- data/vendor/snowball/tests/errors/bad-grouping-definition.stderr +6 -0
- data/vendor/snowball/tests/errors/missing-bra.sbl +6 -0
- data/vendor/snowball/tests/errors/missing-bra.stderr +3 -0
- data/vendor/snowball/tests/errors/missing-command.sbl +9 -0
- data/vendor/snowball/tests/errors/missing-command.stderr +1 -0
- data/vendor/snowball/tests/errors/missing-ket-backwardmode.sbl +3 -0
- data/vendor/snowball/tests/errors/missing-ket-backwardmode.stderr +1 -0
- data/vendor/snowball/tests/errors/missing-ket.sbl +7 -0
- data/vendor/snowball/tests/errors/missing-ket.stderr +6 -0
- data/vendor/snowball/tests/errors/notdefined.sbl +4 -0
- data/vendor/snowball/tests/errors/notdefined.stderr +2 -0
- data/vendor/snowball/tests/errors/string-omitted.sbl +5 -0
- data/vendor/snowball/tests/errors/string-omitted.stderr +1 -0
- data/vendor/snowball/tests/errors/undeclared.sbl +20 -0
- data/vendor/snowball/tests/errors/undeclared.stderr +9 -0
- data/vendor/snowball/tests/errors/unexpected-token.sbl +24 -0
- data/vendor/snowball/tests/errors/unexpected-token.stderr +17 -0
- data/vendor/snowball/tests/errors/wrongdirection.sbl +14 -0
- data/vendor/snowball/tests/errors/wrongdirection.stderr +1 -0
- data/vendor/snowball/tests/runtime/among.sbl +19 -0
- data/vendor/snowball/tests/runtime/arithmeticexpr.sbl +86 -0
- data/vendor/snowball/tests/runtime/attachinsert.sbl +47 -0
- data/vendor/snowball/tests/runtime/booleans.sbl +19 -0
- data/vendor/snowball/tests/runtime/externals.sbl +22 -0
- data/vendor/snowball/tests/runtime/hop.sbl +12 -0
- data/vendor/snowball/tests/runtime/integertests.sbl +60 -0
- data/vendor/snowball/tests/runtime/intlimits.sbl +25 -0
- data/vendor/snowball/tests/runtime/naming.sbl +20 -0
- data/vendor/snowball/tests/runtime/not.sbl +30 -0
- data/vendor/snowball/tests/runtime/or.sbl +17 -0
- data/vendor/snowball/tests/runtime/repeat.sbl +16 -0
- data/vendor/snowball/tests/runtime/setlimit.sbl +24 -0
- data/vendor/snowball/tests/runtime/sizelen.sbl +56 -0
- data/vendor/snowball/tests/runtime/slice.sbl +27 -0
- data/vendor/snowball/tests/runtime/stringdollar.sbl +39 -0
- data/vendor/snowball/tests/runtime/strings.sbl +66 -0
- data/vendor/snowball/tests/runtime/test.sbl +19 -0
- data/vendor/snowball/tests/stemtest.c +41 -1
- data/vendor/snowball/tests/syntax/canon.sbl +32 -0
- data/vendor/snowball/tests/syntax/canon.stderr +0 -0
- data/vendor/snowball/tests/syntax/canon.syntax +57 -0
- data/vendor/snowball/tests/syntax/emptyprogram.sbl +2 -0
- data/vendor/snowball/tests/syntax/emptyprogram.syntax +0 -0
- data/vendor/snowball/tests/syntax/groupings.sbl +11 -0
- data/vendor/snowball/tests/syntax/inline.sbl +144 -0
- data/vendor/snowball/tests/syntax/inline.stderr +0 -0
- data/vendor/snowball/tests/syntax/inline.syntax +121 -0
- data/vendor/snowball/tests/syntax/legacy.sbl +17 -0
- data/vendor/snowball/tests/syntax/legacy.stderr +5 -0
- data/vendor/snowball/tests/syntax/legacy.syntax +19 -0
- data/vendor/snowball/tests/syntax/loops.sbl +14 -0
- data/vendor/snowball/tests/syntax/loops.stderr +9 -0
- data/vendor/snowball/tests/syntax/loops.syntax +29 -0
- data/vendor/snowball/tests/syntax/noops.sbl +76 -0
- data/vendor/snowball/tests/syntax/noops.stderr +27 -0
- data/vendor/snowball/tests/syntax/noops.syntax +135 -0
- data/vendor/snowball/tests/syntax/simplifyae.sbl +21 -0
- data/vendor/snowball/tests/syntax/simplifyae.stderr +0 -0
- data/vendor/snowball/tests/syntax/simplifyae.syntax +39 -0
- data/vendor/snowball/tests/syntax/unused.sbl +18 -0
- data/vendor/snowball/tests/syntax/unused.stderr +7 -0
- data/vendor/snowball/tests/syntax/unused.syntax +3 -0
- data/vendor/snowball/zig/env.zig +599 -0
- data/vendor/snowball/zig/generate_algorithms.pl +19 -0
- data/vendor/snowball/zig/stemwords.zig +123 -0
- metadata +102 -4
- data/vendor/snowball/compiler/syswords.h +0 -86
- 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 *
|
|
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 *
|
|
39
|
+
static struct node * new_node_at_line(struct analyser * a, int type, int line) {
|
|
64
40
|
NEW(node, p);
|
|
65
|
-
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 =
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
196
|
-
|
|
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) {
|
|
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
|
-
|
|
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->
|
|
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)
|
|
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)
|
|
235
|
-
|
|
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
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
* variables
|
|
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
|
-
|
|
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:
|
|
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:
|
|
310
|
+
case c_le:
|
|
311
|
+
return t->token;
|
|
342
312
|
default:
|
|
343
|
-
|
|
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 =
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
448
|
-
|
|
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
|
-
|
|
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 &&
|
|
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 =
|
|
446
|
+
q = p;
|
|
478
447
|
switch (token) {
|
|
479
448
|
case c_plus:
|
|
480
|
-
q->number
|
|
449
|
+
q->number += r->number;
|
|
481
450
|
break;
|
|
482
451
|
case c_minus:
|
|
483
|
-
q->number
|
|
452
|
+
q->number -= r->number;
|
|
484
453
|
break;
|
|
485
454
|
case c_multiply:
|
|
486
|
-
q->number
|
|
455
|
+
q->number *= r->number;
|
|
487
456
|
break;
|
|
488
457
|
case c_divide:
|
|
489
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
586
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
604
|
-
|
|
605
|
-
p
|
|
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
|
-
|
|
609
|
-
|
|
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
|
-
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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 *
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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->
|
|
703
|
+
return p->string_index - q->string_index;
|
|
694
704
|
}
|
|
695
705
|
|
|
696
|
-
#define
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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
|
|
712
|
-
if (p->mode != q->mode) return
|
|
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
|
|
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
|
-
|
|
725
|
-
if (p->AE)
|
|
726
|
-
|
|
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
|
-
|
|
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
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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->
|
|
780
|
+
++function->uses_in_among;
|
|
802
781
|
check_routine_mode(a, function, direction);
|
|
803
|
-
|
|
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 &&
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (
|
|
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,
|
|
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++;
|
|
1055
|
+
p->number++;
|
|
1056
|
+
break;
|
|
958
1057
|
case c_bra:
|
|
959
|
-
if (previous_token == c_bra)
|
|
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
|
-
|
|
1071
|
+
unexpected_token_error(a, "among(...)");
|
|
970
1072
|
previous_token = token;
|
|
971
1073
|
continue;
|
|
972
1074
|
case c_ket:
|
|
973
|
-
if (p->number == 0)
|
|
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)
|
|
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)
|
|
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
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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
|
|
1127
|
-
struct node * n =
|
|
1128
|
-
|
|
1129
|
-
if (
|
|
1130
|
-
|
|
1131
|
-
|
|
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
|
|
1144
|
-
struct node * n =
|
|
1145
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
1195
|
-
if (
|
|
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 =
|
|
1212
|
-
if (n->name)
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
1291
|
-
|
|
1292
|
-
|
|
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
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
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
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
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
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
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
|
-
|
|
1398
|
-
return
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1453
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
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
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
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
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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
|
-
|
|
1496
|
-
{
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
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->
|
|
1916
|
+
a->current_routine = q;
|
|
1515
1917
|
if (q) {
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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
|
|
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
|
-
|
|
1939
|
+
substring_without_among_error(a);
|
|
1537
1940
|
a->substring = NULL;
|
|
1538
1941
|
}
|
|
1539
|
-
|
|
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
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2004
|
+
if (token == terminator) return;
|
|
2005
|
+
unexpected_token_error(a, 0);
|
|
2006
|
+
break;
|
|
1599
2007
|
case -1:
|
|
1600
|
-
if (terminator
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
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
|
-
|
|
2821
|
+
line_num = q->grouping->line_number;
|
|
1673
2822
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
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
|
-
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
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
|
-
|
|
1706
|
-
q->s);
|
|
2837
|
+
name_of_type(q->type),
|
|
2838
|
+
SIZE(q->s), q->s);
|
|
1707
2839
|
} else if (!q->value_used) {
|
|
1708
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
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
|
|