| 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 | 5534 | State *find_state(const Syntax *syn, const char *name) | |
| 14 | { | ||
| 15 | 5534 | 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 | 6092 | static void visit(State *s) | |
| 28 | { | ||
| 29 |
2/2✓ Branch 0 (2→3) taken 2320 times.
✓ Branch 1 (2→10) taken 3772 times.
|
6092 | if (s->visited) { |
| 30 | return; | ||
| 31 | } | ||
| 32 | 2320 | s->visited = true; | |
| 33 |
2/2✓ Branch 0 (7→4) taken 4114 times.
✓ Branch 1 (7→8) taken 2320 times.
|
6434 | for (size_t i = 0, n = s->conds.count; i < n; i++) { |
| 34 | 4114 | const Condition *cond = s->conds.ptrs[i]; | |
| 35 |
2/2✓ Branch 0 (4→5) taken 3788 times.
✓ Branch 1 (4→6) taken 326 times.
|
4114 | if (cond->a.destination) { |
| 36 | 3788 | 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 | 4117 | static void free_condition(Condition *cond) | |
| 45 | { | ||
| 46 | 4117 | free(cond); | |
| 47 | 4117 | } | |
| 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 | 4246 | 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 1158 times.
|
4246 | return a->emit_name ? a->emit_name : a->destination->emit_name; |
| 159 | } | ||
| 160 | |||
| 161 | 4246 | static void update_action_style(const Syntax *syn, Action *a, const StyleMap *styles) | |
| 162 | { | ||
| 163 | 4246 | const char *name = get_effective_emit_name(a); | |
| 164 | 4246 | char full[256]; | |
| 165 | 4246 | xsnprintf(full, sizeof full, "%s.%s", syn->name, name); | |
| 166 | 4246 | a->emit_style = find_style(styles, full); | |
| 167 |
2/2✓ Branch 0 (4→5) taken 655 times.
✓ Branch 1 (4→10) taken 3591 times.
|
4246 | if (a->emit_style) { |
| 168 | 3928 | 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 2764 times.
✓ Branch 1 (5→6) taken 1482 times.
|
4246 | for (size_t i = 0, n = s->conds.count; i < n; i++) { |
| 183 | 2764 | Condition *c = s->conds.ptrs[i]; | |
| 184 | 2764 | 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 | 1 | 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 1 times.
|
135 | for (HashMapIter it = hashmap_iter(syntaxes); hashmap_next(&it); ) { |
| 203 | 134 | update_syntax_styles(it.entry->value, styles); | |
| 204 | } | ||
| 205 | 1 | } | |
| 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 |