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