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