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