1 #include <stdio.h>
2 #include "syntax.h"
3 #include "state.h"
4 #include "../error.h"
5 #include "../util/ascii.h"
6 #include "../util/str-util.h"
7 #include "../util/xmalloc.h"
8
9 static PointerArray syntaxes = PTR_ARRAY_INIT;
10
find_string_list(const Syntax * syn,const char * name)11 StringList *find_string_list(const Syntax *syn, const char *name)
12 {
13 for (size_t i = 0, n = syn->string_lists.count; i < n; i++) {
14 StringList *list = syn->string_lists.ptrs[i];
15 if (streq(list->name, name)) {
16 return list;
17 }
18 }
19 return NULL;
20 }
21
find_state(const Syntax * syn,const char * name)22 State *find_state(const Syntax *syn, const char *name)
23 {
24 for (size_t i = 0, n = syn->states.count; i < n; i++) {
25 State *s = syn->states.ptrs[i];
26 if (streq(s->name, name)) {
27 return s;
28 }
29 }
30 return NULL;
31 }
32
has_destination(ConditionType type)33 static bool has_destination(ConditionType type)
34 {
35 switch (type) {
36 case COND_RECOLOR:
37 case COND_RECOLOR_BUFFER:
38 return false;
39 default:
40 return true;
41 }
42 }
43
find_any_syntax(const char * name)44 Syntax *find_any_syntax(const char *name)
45 {
46 for (size_t i = 0; i < syntaxes.count; i++) {
47 Syntax *syn = syntaxes.ptrs[i];
48 if (streq(syn->name, name)) {
49 return syn;
50 }
51 }
52 return NULL;
53 }
54
fix_name(const char * name,const char * prefix)55 static const char *fix_name(const char *name, const char *prefix)
56 {
57 static char buf[64];
58 snprintf(buf, sizeof(buf), "%s%s", prefix, name);
59 return buf;
60 }
61
fix_action(Syntax * syn,Action * a,const char * prefix)62 static void fix_action(Syntax *syn, Action *a, const char *prefix)
63 {
64 if (a->destination) {
65 const char *name = fix_name(a->destination->name, prefix);
66 a->destination = find_state(syn, name);
67 }
68 if (a->emit_name) {
69 a->emit_name = xstrdup(a->emit_name);
70 }
71 }
72
fix_conditions(Syntax * syn,State * s,SyntaxMerge * m,const char * prefix)73 static void fix_conditions (
74 Syntax *syn,
75 State *s,
76 SyntaxMerge *m,
77 const char *prefix
78 ) {
79 for (size_t i = 0, n = s->conds.count; i < n; i++) {
80 Condition *c = s->conds.ptrs[i];
81 fix_action(syn, &c->a, prefix);
82 if (c->a.destination == NULL && has_destination(c->type)) {
83 c->a.destination = m->return_state;
84 }
85
86 if (m->delim && c->type == COND_HEREDOCEND) {
87 c->u.cond_heredocend.str = xmemdup(m->delim, m->delim_len);
88 c->u.cond_heredocend.len = m->delim_len;
89 }
90 }
91
92 fix_action(syn, &s->a, prefix);
93 if (s->a.destination == NULL) {
94 s->a.destination = m->return_state;
95 }
96 }
97
get_prefix(void)98 static const char *get_prefix(void)
99 {
100 static int counter;
101 static char prefix[32];
102 snprintf(prefix, sizeof(prefix), "%d-", counter++);
103 return prefix;
104 }
105
106 static void update_state_colors(Syntax *syn, State *s);
107
merge_syntax(Syntax * syn,SyntaxMerge * m)108 State *merge_syntax(Syntax *syn, SyntaxMerge *m)
109 {
110 // NOTE: string_lists is owned by Syntax so there's no need to
111 // copy it. Freeing Condition does not free any string lists.
112 const char *prefix = get_prefix();
113 PointerArray *states = &syn->states;
114 size_t old_count = states->count;
115
116 states->count += m->subsyn->states.count;
117 if (states->count > states->alloc) {
118 states->alloc = states->count;
119 xrenew(states->ptrs, states->alloc);
120 }
121 memcpy (
122 states->ptrs + old_count,
123 m->subsyn->states.ptrs,
124 sizeof(*states->ptrs) * m->subsyn->states.count
125 );
126
127 for (size_t i = old_count; i < states->count; i++) {
128 State *s = xmemdup(states->ptrs[i], sizeof(State));
129
130 states->ptrs[i] = s;
131 s->name = xstrdup(fix_name(s->name, prefix));
132 s->emit_name = xstrdup(s->emit_name);
133 if (s->conds.count > 0) {
134 s->conds.ptrs = xmemdup (
135 s->conds.ptrs,
136 sizeof(void *) * s->conds.alloc
137 );
138 for (size_t j = 0; j < s->conds.count; j++) {
139 s->conds.ptrs[j] = xmemdup(s->conds.ptrs[j], sizeof(Condition));
140 }
141 }
142
143 // Mark unvisited so that state that is used only as a return
144 // state gets visited.
145 s->visited = false;
146
147 // Don't complain about unvisited copied states.
148 s->copied = true;
149 }
150
151 for (size_t i = old_count; i < states->count; i++) {
152 fix_conditions(syn, states->ptrs[i], m, prefix);
153 if (m->delim) {
154 update_state_colors(syn, states->ptrs[i]);
155 }
156 }
157
158 m->subsyn->used = true;
159 return states->ptrs[old_count];
160 }
161
visit(State * s)162 static void visit(State *s)
163 {
164 if (s->visited) {
165 return;
166 }
167 s->visited = true;
168 for (size_t i = 0, n = s->conds.count; i < n; i++) {
169 Condition *cond = s->conds.ptrs[i];
170 if (cond->a.destination) {
171 visit(cond->a.destination);
172 }
173 }
174 if (s->a.destination) {
175 visit(s->a.destination);
176 }
177 }
178
free_condition(Condition * cond)179 static void free_condition(Condition *cond)
180 {
181 free(cond->a.emit_name);
182 free(cond);
183 }
184
free_state(State * s)185 static void free_state(State *s)
186 {
187 free(s->name);
188 free(s->emit_name);
189 ptr_array_free_cb(&s->conds, FREE_FUNC(free_condition));
190 free(s->a.emit_name);
191 free(s);
192 }
193
free_string_list(StringList * list)194 static void free_string_list(StringList *list)
195 {
196 hashset_free(&list->strings);
197 free(list->name);
198 free(list);
199 }
200
free_syntax(Syntax * syn)201 static void free_syntax(Syntax *syn)
202 {
203 ptr_array_free_cb(&syn->states, FREE_FUNC(free_state));
204 ptr_array_free_cb(&syn->string_lists, FREE_FUNC(free_string_list));
205 ptr_array_free_cb(&syn->default_colors, FREE_FUNC(free_string_array));
206
207 free(syn->name);
208 free(syn);
209 }
210
finalize_syntax(Syntax * syn,unsigned int saved_nr_errors)211 void finalize_syntax(Syntax *syn, unsigned int saved_nr_errors)
212 {
213 if (syn->states.count == 0) {
214 error_msg("Empty syntax");
215 }
216
217 for (size_t i = 0, n = syn->states.count; i < n; i++) {
218 State *s = syn->states.ptrs[i];
219 if (!s->defined) {
220 // This state has been referenced but not defined
221 error_msg("No such state %s", s->name);
222 }
223 }
224 for (size_t i = 0, n = syn->string_lists.count; i < n; i++) {
225 StringList *list = syn->string_lists.ptrs[i];
226 if (!list->defined) {
227 error_msg("No such list %s", list->name);
228 }
229 }
230
231 if (syn->heredoc && !is_subsyntax(syn)) {
232 error_msg("heredocend can be used only in subsyntaxes");
233 }
234
235 if (find_any_syntax(syn->name)) {
236 error_msg("Syntax %s already exists", syn->name);
237 }
238
239 if (get_nr_errors() != saved_nr_errors) {
240 free_syntax(syn);
241 return;
242 }
243
244 // Unused states and lists cause warning only
245 visit(syn->states.ptrs[0]);
246 for (size_t i = 0, n = syn->states.count; i < n; i++) {
247 State *s = syn->states.ptrs[i];
248 if (!s->visited && !s->copied) {
249 error_msg("State %s is unreachable", s->name);
250 }
251 }
252 for (size_t i = 0, n = syn->string_lists.count; i < n; i++) {
253 StringList *list = syn->string_lists.ptrs[i];
254 if (!list->used) {
255 error_msg("List %s never used", list->name);
256 }
257 }
258
259 ptr_array_add(&syntaxes, syn);
260 }
261
find_syntax(const char * name)262 Syntax *find_syntax(const char *name)
263 {
264 Syntax *syn = find_any_syntax(name);
265 if (syn && is_subsyntax(syn)) {
266 return NULL;
267 }
268 return syn;
269 }
270
find_default_color(Syntax * syn,const char * name)271 static const char *find_default_color(Syntax *syn, const char *name)
272 {
273 for (size_t i = 0, n = syn->default_colors.count; i < n; i++) {
274 char **strs = syn->default_colors.ptrs[i];
275 for (size_t j = 1; strs[j]; j++) {
276 if (streq(strs[j], name)) {
277 return strs[0];
278 }
279 }
280 }
281 return NULL;
282 }
283
update_action_color(Syntax * syn,Action * a)284 static void update_action_color(Syntax *syn, Action *a)
285 {
286 const char *name = a->emit_name;
287 const char *def;
288 char full[64];
289
290 if (!name) {
291 name = a->destination->emit_name;
292 }
293
294 snprintf(full, sizeof(full), "%s.%s", syn->name, name);
295 a->emit_color = find_color(full);
296 if (a->emit_color) {
297 return;
298 }
299
300 def = find_default_color(syn, name);
301 if (!def) {
302 return;
303 }
304
305 snprintf(full, sizeof(full), "%s.%s", syn->name, def);
306 a->emit_color = find_color(full);
307 }
308
update_state_colors(Syntax * syn,State * s)309 static void update_state_colors(Syntax *syn, State *s)
310 {
311 for (size_t i = 0, n = s->conds.count; i < n; i++) {
312 Condition *c = s->conds.ptrs[i];
313 update_action_color(syn, &c->a);
314 }
315 update_action_color(syn, &s->a);
316 }
317
update_syntax_colors(Syntax * syn)318 void update_syntax_colors(Syntax *syn)
319 {
320 if (is_subsyntax(syn)) {
321 // No point to update colors of a sub-syntax
322 return;
323 }
324 for (size_t i = 0, n = syn->states.count; i < n; i++) {
325 update_state_colors(syn, syn->states.ptrs[i]);
326 }
327 }
328
update_all_syntax_colors(void)329 void update_all_syntax_colors(void)
330 {
331 for (size_t i = 0; i < syntaxes.count; i++) {
332 update_syntax_colors(syntaxes.ptrs[i]);
333 }
334 }
335
find_unused_subsyntaxes(void)336 void find_unused_subsyntaxes(void)
337 {
338 // Don't complain multiple times about same unused subsyntaxes
339 static size_t i;
340
341 for (; i < syntaxes.count; i++) {
342 Syntax *s = syntaxes.ptrs[i];
343 if (!s->used && is_subsyntax(s)) {
344 error_msg("Subsyntax %s is unused", s->name);
345 }
346 }
347 }
348