Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <stdlib.h> | ||
2 | #include <string.h> | ||
3 | #include "syntax.h" | ||
4 | #include "util/str-util.h" | ||
5 | #include "util/xmalloc.h" | ||
6 | #include "util/xsnprintf.h" | ||
7 | |||
8 | 199 | StringList *find_string_list(const Syntax *syn, const char *name) | |
9 | { | ||
10 | 199 | return hashmap_get(&syn->string_lists, name); | |
11 | } | ||
12 | |||
13 | 5486 | State *find_state(const Syntax *syn, const char *name) | |
14 | { | ||
15 | 5486 | return hashmap_get(&syn->states, name); | |
16 | } | ||
17 | |||
18 | 596 | Syntax *find_any_syntax(const HashMap *syntaxes, const char *name) | |
19 | { | ||
20 | 596 | return hashmap_get(syntaxes, name); | |
21 | } | ||
22 | |||
23 | // Each State is visited at most once (when reachable) and recursion | ||
24 | // is bounded by the longest chain of `Action::destination` pointers | ||
25 | // (typically not more than 20 or so) | ||
26 | // NOLINTNEXTLINE(misc-no-recursion) | ||
27 | 6037 | static void visit(State *s) | |
28 | { | ||
29 |
2/2✓ Branch 0 (2→3) taken 2300 times.
✓ Branch 1 (2→10) taken 3737 times.
|
6037 | if (s->visited) { |
30 | return; | ||
31 | } | ||
32 | 2300 | s->visited = true; | |
33 |
2/2✓ Branch 0 (7→4) taken 4086 times.
✓ Branch 1 (7→8) taken 2300 times.
|
6386 | for (size_t i = 0, n = s->conds.count; i < n; i++) { |
34 | 4086 | const Condition *cond = s->conds.ptrs[i]; | |
35 |
2/2✓ Branch 0 (4→5) taken 3762 times.
✓ Branch 1 (4→6) taken 324 times.
|
4086 | if (cond->a.destination) { |
36 | 3762 | visit(cond->a.destination); | |
37 | } | ||
38 | } | ||
39 |
2/2✓ Branch 0 (8→9) taken 2140 times.
✓ Branch 1 (8→10) taken 160 times.
|
2300 | if (s->default_action.destination) { |
40 | 2140 | visit(s->default_action.destination); | |
41 | } | ||
42 | } | ||
43 | |||
44 | 4086 | static void free_condition(Condition *cond) | |
45 | { | ||
46 | 4086 | free(cond); | |
47 | 4086 | } | |
48 | |||
49 | ✗ | static void free_heredoc_state(HeredocState *s) | |
50 | { | ||
51 | ✗ | free(s); | |
52 | ✗ | } | |
53 | |||
54 | 2300 | static void free_state(State *s) | |
55 | { | ||
56 | 2300 | ptr_array_free_cb(&s->conds, FREE_FUNC(free_condition)); | |
57 | 2300 | ptr_array_free_cb(&s->heredoc.states, FREE_FUNC(free_heredoc_state)); | |
58 | 2300 | free(s); | |
59 | 2300 | } | |
60 | |||
61 | 98 | static void free_string_list(StringList *list) | |
62 | { | ||
63 | 98 | hashset_free(&list->strings); | |
64 | 98 | free(list); | |
65 | 98 | } | |
66 | |||
67 | 135 | static void free_syntax_contents(Syntax *syn) | |
68 | { | ||
69 | 135 | hashmap_free(&syn->states, FREE_FUNC(free_state)); | |
70 | 135 | hashmap_free(&syn->string_lists, FREE_FUNC(free_string_list)); | |
71 | 135 | hashmap_free(&syn->default_styles, NULL); | |
72 | 135 | } | |
73 | |||
74 | ✗ | static void free_syntax(Syntax *syn) | |
75 | { | ||
76 | ✗ | free_syntax_contents(syn); | |
77 | ✗ | free(syn->name); | |
78 | ✗ | free(syn); | |
79 | ✗ | } | |
80 | |||
81 | 135 | static void free_syntax_cb(Syntax *syn) | |
82 | { | ||
83 | 135 | free_syntax_contents(syn); | |
84 | 135 | free(syn); | |
85 | 135 | } | |
86 | |||
87 | 9 | void free_syntaxes(HashMap *syntaxes) | |
88 | { | ||
89 | 9 | hashmap_free(syntaxes, FREE_FUNC(free_syntax_cb)); | |
90 | 9 | } | |
91 | |||
92 | 135 | void finalize_syntax ( | |
93 | HashMap *syntaxes, | ||
94 | Syntax *syn, | ||
95 | ErrorBuffer *ebuf, | ||
96 | unsigned int saved_nr_errors | ||
97 | ) { | ||
98 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 135 times.
|
135 | if (syn->states.count == 0) { |
99 | ✗ | error_msg(ebuf, "Empty syntax"); | |
100 | } | ||
101 | |||
102 |
2/2✓ Branch 0 (9→5) taken 2300 times.
✓ Branch 1 (9→10) taken 135 times.
|
2570 | for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) { |
103 | 2300 | const State *s = it.entry->value; | |
104 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→8) taken 2300 times.
|
2300 | if (!s->defined) { |
105 | // This state has been referenced but not defined | ||
106 | ✗ | error_msg(ebuf, "No such state %s", it.entry->key); | |
107 | } | ||
108 | } | ||
109 |
2/2✓ Branch 0 (15→11) taken 98 times.
✓ Branch 1 (15→16) taken 135 times.
|
368 | for (HashMapIter it = hashmap_iter(&syn->string_lists); hashmap_next(&it); ) { |
110 | 98 | const StringList *list = it.entry->value; | |
111 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→14) taken 98 times.
|
98 | if (!list->defined) { |
112 | ✗ | error_msg(ebuf, "No such list %s", it.entry->key); | |
113 | } | ||
114 | } | ||
115 | |||
116 |
3/4✓ Branch 0 (16→17) taken 9 times.
✓ Branch 1 (16→19) taken 126 times.
✗ Branch 2 (17→18) not taken.
✓ Branch 3 (17→19) taken 9 times.
|
135 | if (syn->heredoc && !is_subsyntax(syn)) { |
117 | ✗ | error_msg(ebuf, "heredocend can be used only in subsyntaxes"); | |
118 | } | ||
119 | |||
120 |
1/2✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 135 times.
|
135 | if (find_any_syntax(syntaxes, syn->name)) { |
121 | ✗ | error_msg(ebuf, "Syntax %s already exists", syn->name); | |
122 | } | ||
123 | |||
124 |
1/2✗ Branch 0 (22→23) not taken.
✓ Branch 1 (22→25) taken 135 times.
|
135 | if (ebuf->nr_errors != saved_nr_errors) { |
125 | ✗ | free_syntax(syn); | |
126 | ✗ | return; | |
127 | } | ||
128 | |||
129 | // Unused states and lists cause warnings only (to make loading WIP | ||
130 | // syntax files less annoying) | ||
131 | 135 | visit(syn->start_state); | |
132 |
2/2✓ Branch 0 (32→27) taken 2300 times.
✓ Branch 1 (32→33) taken 135 times.
|
2435 | for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) { |
133 | 2300 | const State *s = it.entry->value; | |
134 |
1/4✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→30) taken 2300 times.
✗ Branch 2 (28→29) not taken.
✗ Branch 3 (28→30) not taken.
|
2300 | if (!s->visited && !s->copied) { |
135 | ✗ | error_msg(ebuf, "State %s is unreachable", it.entry->key); | |
136 | } | ||
137 | } | ||
138 |
2/2✓ Branch 0 (38→34) taken 98 times.
✓ Branch 1 (38→39) taken 135 times.
|
368 | for (HashMapIter it = hashmap_iter(&syn->string_lists); hashmap_next(&it); ) { |
139 | 98 | const StringList *list = it.entry->value; | |
140 |
1/2✗ Branch 0 (34→35) not taken.
✓ Branch 1 (34→37) taken 98 times.
|
98 | if (!list->used) { |
141 | ✗ | error_msg(ebuf, "List %s never used", it.entry->key); | |
142 | } | ||
143 | } | ||
144 | |||
145 | 135 | hashmap_insert(syntaxes, syn->name, syn); | |
146 | } | ||
147 | |||
148 | 275 | Syntax *find_syntax(const HashMap *syntaxes, const char *name) | |
149 | { | ||
150 | 275 | Syntax *syn = find_any_syntax(syntaxes, name); | |
151 |
3/4✓ Branch 0 (3→4) taken 114 times.
✓ Branch 1 (3→6) taken 161 times.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 114 times.
|
275 | if (syn && is_subsyntax(syn)) { |
152 | ✗ | return NULL; | |
153 | } | ||
154 | return syn; | ||
155 | } | ||
156 | |||
157 | 633 | static const char *find_default_style(const Syntax *syn, const char *name) | |
158 | { | ||
159 | 633 | return hashmap_get(&syn->default_styles, name); | |
160 | } | ||
161 | |||
162 | 4209 | static const char *get_effective_emit_name(const Action *a) | |
163 | { | ||
164 |
2/2✓ Branch 0 (2→3) taken 3062 times.
✓ Branch 1 (2→4) taken 1147 times.
|
4209 | return a->emit_name ? a->emit_name : a->destination->emit_name; |
165 | } | ||
166 | |||
167 | 4209 | static void update_action_style(const Syntax *syn, Action *a, const StyleMap *styles) | |
168 | { | ||
169 | 4209 | const char *name = get_effective_emit_name(a); | |
170 | 4209 | char full[256]; | |
171 | 4209 | xsnprintf(full, sizeof full, "%s.%s", syn->name, name); | |
172 | 4209 | a->emit_style = find_style(styles, full); | |
173 |
2/2✓ Branch 0 (4→5) taken 633 times.
✓ Branch 1 (4→10) taken 3576 times.
|
4209 | if (a->emit_style) { |
174 | 3894 | return; | |
175 | } | ||
176 | |||
177 | 633 | const char *def = find_default_style(syn, name); | |
178 |
2/2✓ Branch 0 (6→7) taken 315 times.
✓ Branch 1 (6→10) taken 318 times.
|
633 | if (!def) { |
179 | return; | ||
180 | } | ||
181 | |||
182 | 315 | xsnprintf(full, sizeof full, "%s.%s", syn->name, def); | |
183 | 315 | a->emit_style = find_style(styles, full); | |
184 | } | ||
185 | |||
186 | 1471 | void update_state_styles(const Syntax *syn, State *s, const StyleMap *styles) | |
187 | { | ||
188 |
2/2✓ Branch 0 (5→3) taken 2738 times.
✓ Branch 1 (5→6) taken 1471 times.
|
4209 | for (size_t i = 0, n = s->conds.count; i < n; i++) { |
189 | 2738 | Condition *c = s->conds.ptrs[i]; | |
190 | 2738 | update_action_style(syn, &c->a, styles); | |
191 | } | ||
192 | 1471 | update_action_style(syn, &s->default_action, styles); | |
193 | 1471 | } | |
194 | |||
195 | 132 | void update_syntax_styles(Syntax *syn, const StyleMap *styles) | |
196 | { | ||
197 |
2/2✓ Branch 0 (2→3) taken 54 times.
✓ Branch 1 (2→8) taken 78 times.
|
132 | if (is_subsyntax(syn)) { |
198 | // No point in updating styles of a sub-syntax | ||
199 | return; | ||
200 | } | ||
201 |
2/2✓ Branch 0 (6→4) taken 1471 times.
✓ Branch 1 (6→7) taken 54 times.
|
1525 | for (HashMapIter it = hashmap_iter(&syn->states); hashmap_next(&it); ) { |
202 | 1471 | update_state_styles(syn, it.entry->value, styles); | |
203 | } | ||
204 | } | ||
205 | |||
206 | 8 | void update_all_syntax_styles(const HashMap *syntaxes, const StyleMap *styles) | |
207 | { | ||
208 |
2/2✓ Branch 0 (5→3) taken 132 times.
✓ Branch 1 (5→6) taken 8 times.
|
140 | for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) { |
209 | 132 | update_syntax_styles(it.entry->value, styles); | |
210 | } | ||
211 | 8 | } | |
212 | |||
213 | 55 | void find_unused_subsyntaxes(const HashMap *syntaxes, ErrorBuffer *ebuf) | |
214 | { | ||
215 |
2/2✓ Branch 0 (10→3) taken 3548 times.
✓ Branch 1 (10→11) taken 55 times.
|
3603 | for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) { |
216 | 3548 | Syntax *s = it.entry->value; | |
217 |
4/6✓ Branch 0 (3→4) taken 1486 times.
✓ Branch 1 (3→8) taken 2062 times.
✓ Branch 2 (4→5) taken 1486 times.
✗ Branch 3 (4→8) not taken.
✗ Branch 4 (5→6) not taken.
✓ Branch 5 (5→8) taken 1486 times.
|
3548 | if (!s->used && !s->warned_unused_subsyntax && is_subsyntax(s)) { |
218 | ✗ | error_msg(ebuf, "Subsyntax %s is unused", s->name); | |
219 | // Don't complain multiple times about the same unused subsyntaxes | ||
220 | ✗ | s->warned_unused_subsyntax = true; | |
221 | } | ||
222 | } | ||
223 | 55 | } | |
224 | |||
225 | ✗ | void collect_syntax_emit_names ( | |
226 | const Syntax *syntax, | ||
227 | PointerArray *a, | ||
228 | const char *prefix | ||
229 | ) { | ||
230 | ✗ | size_t prefix_len = strlen(prefix); | |
231 | ✗ | HashSet set; | |
232 | ✗ | hashset_init(&set, 16, false); | |
233 | |||
234 | // Insert all `Action::emit_name` strings beginning with `prefix` into | ||
235 | // a HashSet (to avoid duplicates) | ||
236 | ✗ | for (HashMapIter it = hashmap_iter(&syntax->states); hashmap_next(&it); ) { | |
237 | ✗ | const State *s = it.entry->value; | |
238 | ✗ | const char *emit = get_effective_emit_name(&s->default_action); | |
239 | ✗ | if (str_has_strn_prefix(emit, prefix, prefix_len)) { | |
240 | ✗ | hashset_insert(&set, emit, strlen(emit)); | |
241 | } | ||
242 | ✗ | for (size_t i = 0, n = s->conds.count; i < n; i++) { | |
243 | ✗ | const Condition *cond = s->conds.ptrs[i]; | |
244 | ✗ | emit = get_effective_emit_name(&cond->a); | |
245 | ✗ | if (str_has_strn_prefix(emit, prefix, prefix_len)) { | |
246 | ✗ | hashset_insert(&set, emit, strlen(emit)); | |
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | ✗ | const char *ft = syntax->name; | |
252 | ✗ | size_t ftlen = strlen(ft); | |
253 | |||
254 | // Append the collected strings to the PointerArray | ||
255 | ✗ | for (HashSetIter iter = hashset_iter(&set); hashset_next(&iter); ) { | |
256 | ✗ | const HashSetEntry *h = iter.entry; | |
257 | ✗ | char *str = xmemjoin3(ft, ftlen, STRN("."), h->str, h->str_len + 1); | |
258 | ✗ | ptr_array_append(a, str); | |
259 | } | ||
260 | |||
261 | ✗ | hashset_free(&set); | |
262 | ✗ | } | |
263 |