| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#ifndef LITAVIS_AST_H |
|
2
|
|
|
|
|
|
|
#define LITAVIS_AST_H |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
#include |
|
5
|
|
|
|
|
|
|
#include |
|
6
|
|
|
|
|
|
|
#include |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
/* ── Error handling (overridable by XS layer) ──────────────── */ |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#ifndef LITAVIS_FATAL |
|
11
|
|
|
|
|
|
|
#define LITAVIS_FATAL(msg) do { fprintf(stderr, "litavis: %s\n", (msg)); abort(); } while(0) |
|
12
|
|
|
|
|
|
|
#endif |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
/* ── Forward declarations ─────────────────────────────────── */ |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
typedef struct LitavisProp LitavisProp; |
|
17
|
|
|
|
|
|
|
typedef struct LitavisRule LitavisRule; |
|
18
|
|
|
|
|
|
|
typedef struct LitavisAST LitavisAST; |
|
19
|
|
|
|
|
|
|
typedef struct LitavisBucket LitavisBucket; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
/* ── Property (key-value pair, ordered) ───────────────────── */ |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
struct LitavisProp { |
|
24
|
|
|
|
|
|
|
char *key; /* property name, e.g. "color" */ |
|
25
|
|
|
|
|
|
|
char *value; /* property value, e.g. "red" */ |
|
26
|
|
|
|
|
|
|
}; |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
/* ── Rule (selector + ordered properties + optional children) */ |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
struct LitavisRule { |
|
31
|
|
|
|
|
|
|
char *selector; /* e.g. ".card", ".card:hover" */ |
|
32
|
|
|
|
|
|
|
LitavisProp *props; /* ordered array of properties */ |
|
33
|
|
|
|
|
|
|
int prop_count; |
|
34
|
|
|
|
|
|
|
int prop_cap; |
|
35
|
|
|
|
|
|
|
LitavisRule *children; /* nested rules (before flattening) */ |
|
36
|
|
|
|
|
|
|
int child_count; |
|
37
|
|
|
|
|
|
|
int child_cap; |
|
38
|
|
|
|
|
|
|
int is_at_rule; /* 1 if @media, @keyframes, etc. */ |
|
39
|
|
|
|
|
|
|
char *at_prelude; /* e.g. "(max-width: 768px)" */ |
|
40
|
|
|
|
|
|
|
char *source_file; /* origin file for error reporting */ |
|
41
|
|
|
|
|
|
|
int source_line; |
|
42
|
|
|
|
|
|
|
}; |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
/* ── Hash bucket for O(1) selector lookup ─────────────────── */ |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
struct LitavisBucket { |
|
47
|
|
|
|
|
|
|
char *key; |
|
48
|
|
|
|
|
|
|
int index; /* index into rules[] array */ |
|
49
|
|
|
|
|
|
|
LitavisBucket *next; /* chaining for collisions */ |
|
50
|
|
|
|
|
|
|
}; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
/* ── AST (top-level ordered collection of rules) ──────────── */ |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
struct LitavisAST { |
|
55
|
|
|
|
|
|
|
LitavisRule *rules; /* ordered array — insertion order */ |
|
56
|
|
|
|
|
|
|
int count; |
|
57
|
|
|
|
|
|
|
int capacity; |
|
58
|
|
|
|
|
|
|
LitavisBucket **buckets; /* hash table for lookup by selector */ |
|
59
|
|
|
|
|
|
|
int bucket_count; |
|
60
|
|
|
|
|
|
|
}; |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
/* ── Internal: hash function ──────────────────────────────── */ |
|
63
|
|
|
|
|
|
|
|
|
64
|
10366
|
|
|
|
|
|
static unsigned int litavis_hash(const char *key, int bucket_count) { |
|
65
|
10366
|
|
|
|
|
|
unsigned int h = 5381; |
|
66
|
111787
|
100
|
|
|
|
|
while (*key) |
|
67
|
101421
|
|
|
|
|
|
h = ((h << 5) + h) + (unsigned char)*key++; |
|
68
|
10366
|
|
|
|
|
|
return h % (unsigned int)bucket_count; |
|
69
|
|
|
|
|
|
|
} |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
/* ── Internal: strdup portable ────────────────────────────── */ |
|
72
|
|
|
|
|
|
|
|
|
73
|
19557
|
|
|
|
|
|
static char* litavis_strdup(const char *s) { |
|
74
|
19557
|
|
|
|
|
|
size_t len = strlen(s); |
|
75
|
19557
|
|
|
|
|
|
char *dup = (char*)malloc(len + 1); |
|
76
|
19557
|
50
|
|
|
|
|
if (!dup) LITAVIS_FATAL("out of memory"); |
|
77
|
19557
|
|
|
|
|
|
memcpy(dup, s, len + 1); |
|
78
|
19557
|
|
|
|
|
|
return dup; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
/* ── Internal: initialise a rule struct ───────────────────── */ |
|
82
|
|
|
|
|
|
|
|
|
83
|
3441
|
|
|
|
|
|
static void litavis_rule_init(LitavisRule *rule) { |
|
84
|
3441
|
|
|
|
|
|
rule->selector = NULL; |
|
85
|
3441
|
|
|
|
|
|
rule->props = NULL; |
|
86
|
3441
|
|
|
|
|
|
rule->prop_count = 0; |
|
87
|
3441
|
|
|
|
|
|
rule->prop_cap = 0; |
|
88
|
3441
|
|
|
|
|
|
rule->children = NULL; |
|
89
|
3441
|
|
|
|
|
|
rule->child_count = 0; |
|
90
|
3441
|
|
|
|
|
|
rule->child_cap = 0; |
|
91
|
3441
|
|
|
|
|
|
rule->is_at_rule = 0; |
|
92
|
3441
|
|
|
|
|
|
rule->at_prelude = NULL; |
|
93
|
3441
|
|
|
|
|
|
rule->source_file = NULL; |
|
94
|
3441
|
|
|
|
|
|
rule->source_line = 0; |
|
95
|
3441
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
/* ── Internal: free a rule's contents (not the rule itself) ── */ |
|
98
|
|
|
|
|
|
|
|
|
99
|
3441
|
|
|
|
|
|
static void litavis_rule_cleanup(LitavisRule *rule) { |
|
100
|
|
|
|
|
|
|
int i; |
|
101
|
3441
|
50
|
|
|
|
|
if (rule->selector) free(rule->selector); |
|
102
|
3441
|
100
|
|
|
|
|
if (rule->at_prelude) free(rule->at_prelude); |
|
103
|
3441
|
100
|
|
|
|
|
if (rule->source_file) free(rule->source_file); |
|
104
|
7388
|
100
|
|
|
|
|
for (i = 0; i < rule->prop_count; i++) { |
|
105
|
3947
|
|
|
|
|
|
free(rule->props[i].key); |
|
106
|
3947
|
|
|
|
|
|
free(rule->props[i].value); |
|
107
|
|
|
|
|
|
|
} |
|
108
|
3441
|
100
|
|
|
|
|
if (rule->props) free(rule->props); |
|
109
|
3525
|
100
|
|
|
|
|
for (i = 0; i < rule->child_count; i++) { |
|
110
|
84
|
|
|
|
|
|
litavis_rule_cleanup(&rule->children[i]); |
|
111
|
|
|
|
|
|
|
} |
|
112
|
3441
|
100
|
|
|
|
|
if (rule->children) free(rule->children); |
|
113
|
3441
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
/* ── Internal: free all hash buckets ─────────────────────── */ |
|
116
|
|
|
|
|
|
|
|
|
117
|
1943
|
|
|
|
|
|
static void litavis_ast_free_buckets(LitavisAST *ast) { |
|
118
|
|
|
|
|
|
|
int i; |
|
119
|
66511
|
100
|
|
|
|
|
for (i = 0; i < ast->bucket_count; i++) { |
|
120
|
64568
|
|
|
|
|
|
LitavisBucket *b = ast->buckets[i]; |
|
121
|
70772
|
100
|
|
|
|
|
while (b) { |
|
122
|
6204
|
|
|
|
|
|
LitavisBucket *next = b->next; |
|
123
|
6204
|
|
|
|
|
|
free(b->key); |
|
124
|
6204
|
|
|
|
|
|
free(b); |
|
125
|
6204
|
|
|
|
|
|
b = next; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
} |
|
128
|
1943
|
|
|
|
|
|
free(ast->buckets); |
|
129
|
1943
|
|
|
|
|
|
ast->buckets = NULL; |
|
130
|
1943
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
/* ── Internal: insert into hash table ────────────────────── */ |
|
133
|
|
|
|
|
|
|
|
|
134
|
6204
|
|
|
|
|
|
static void litavis_ast_hash_insert(LitavisAST *ast, const char *key, int index) { |
|
135
|
6204
|
|
|
|
|
|
unsigned int h = litavis_hash(key, ast->bucket_count); |
|
136
|
6204
|
|
|
|
|
|
LitavisBucket *b = (LitavisBucket*)malloc(sizeof(LitavisBucket)); |
|
137
|
6204
|
50
|
|
|
|
|
if (!b) LITAVIS_FATAL("out of memory"); |
|
138
|
6204
|
|
|
|
|
|
b->key = litavis_strdup(key); |
|
139
|
6204
|
|
|
|
|
|
b->index = index; |
|
140
|
6204
|
|
|
|
|
|
b->next = ast->buckets[h]; |
|
141
|
6204
|
|
|
|
|
|
ast->buckets[h] = b; |
|
142
|
6204
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
/* ── Internal: rehash when load factor exceeds 0.75 ──────── */ |
|
145
|
|
|
|
|
|
|
|
|
146
|
16
|
|
|
|
|
|
static void litavis_ast_rehash(LitavisAST *ast) { |
|
147
|
16
|
|
|
|
|
|
int new_bc = ast->bucket_count * 2; |
|
148
|
16
|
|
|
|
|
|
LitavisBucket **new_buckets = (LitavisBucket**)calloc((size_t)new_bc, sizeof(LitavisBucket*)); |
|
149
|
|
|
|
|
|
|
int i; |
|
150
|
16
|
50
|
|
|
|
|
if (!new_buckets) LITAVIS_FATAL("out of memory"); |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
/* Re-insert all existing entries */ |
|
153
|
1520
|
100
|
|
|
|
|
for (i = 0; i < ast->bucket_count; i++) { |
|
154
|
1504
|
|
|
|
|
|
LitavisBucket *b = ast->buckets[i]; |
|
155
|
2648
|
100
|
|
|
|
|
while (b) { |
|
156
|
1144
|
|
|
|
|
|
LitavisBucket *next = b->next; |
|
157
|
1144
|
|
|
|
|
|
unsigned int h = litavis_hash(b->key, new_bc); |
|
158
|
1144
|
|
|
|
|
|
b->next = new_buckets[h]; |
|
159
|
1144
|
|
|
|
|
|
new_buckets[h] = b; |
|
160
|
1144
|
|
|
|
|
|
b = next; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
} |
|
163
|
16
|
|
|
|
|
|
free(ast->buckets); |
|
164
|
16
|
|
|
|
|
|
ast->buckets = new_buckets; |
|
165
|
16
|
|
|
|
|
|
ast->bucket_count = new_bc; |
|
166
|
16
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
/* ── AST lifecycle ────────────────────────────────────────── */ |
|
169
|
|
|
|
|
|
|
|
|
170
|
1686
|
|
|
|
|
|
static LitavisAST* litavis_ast_new(int initial_capacity) { |
|
171
|
1686
|
|
|
|
|
|
LitavisAST *ast = (LitavisAST*)malloc(sizeof(LitavisAST)); |
|
172
|
1686
|
50
|
|
|
|
|
if (!ast) LITAVIS_FATAL("out of memory"); |
|
173
|
|
|
|
|
|
|
|
|
174
|
1686
|
100
|
|
|
|
|
if (initial_capacity < 8) initial_capacity = 8; |
|
175
|
1686
|
|
|
|
|
|
ast->rules = (LitavisRule*)malloc(sizeof(LitavisRule) * (size_t)initial_capacity); |
|
176
|
1686
|
|
|
|
|
|
ast->count = 0; |
|
177
|
1686
|
|
|
|
|
|
ast->capacity = initial_capacity; |
|
178
|
|
|
|
|
|
|
|
|
179
|
1686
|
|
|
|
|
|
ast->bucket_count = initial_capacity * 2; |
|
180
|
1686
|
|
|
|
|
|
ast->buckets = (LitavisBucket**)calloc((size_t)ast->bucket_count, sizeof(LitavisBucket*)); |
|
181
|
|
|
|
|
|
|
|
|
182
|
1686
|
50
|
|
|
|
|
if (!ast->rules || !ast->buckets) LITAVIS_FATAL("out of memory"); |
|
|
|
50
|
|
|
|
|
|
|
183
|
1686
|
|
|
|
|
|
return ast; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
1686
|
|
|
|
|
|
static void litavis_ast_free(LitavisAST *ast) { |
|
187
|
|
|
|
|
|
|
int i; |
|
188
|
1686
|
50
|
|
|
|
|
if (!ast) return; |
|
189
|
4872
|
100
|
|
|
|
|
for (i = 0; i < ast->count; i++) { |
|
190
|
3186
|
|
|
|
|
|
litavis_rule_cleanup(&ast->rules[i]); |
|
191
|
|
|
|
|
|
|
} |
|
192
|
1686
|
|
|
|
|
|
free(ast->rules); |
|
193
|
1686
|
|
|
|
|
|
litavis_ast_free_buckets(ast); |
|
194
|
1686
|
|
|
|
|
|
free(ast); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
/* ── Deep clone ───────────────────────────────────────────── */ |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
static void litavis_rule_clone_into(LitavisRule *dst, const LitavisRule *src); |
|
200
|
|
|
|
|
|
|
|
|
201
|
590
|
|
|
|
|
|
static void litavis_rule_clone_into(LitavisRule *dst, const LitavisRule *src) { |
|
202
|
|
|
|
|
|
|
int i; |
|
203
|
590
|
|
|
|
|
|
litavis_rule_init(dst); |
|
204
|
590
|
50
|
|
|
|
|
if (src->selector) dst->selector = litavis_strdup(src->selector); |
|
205
|
590
|
100
|
|
|
|
|
if (src->at_prelude) dst->at_prelude = litavis_strdup(src->at_prelude); |
|
206
|
590
|
100
|
|
|
|
|
if (src->source_file) dst->source_file = litavis_strdup(src->source_file); |
|
207
|
590
|
|
|
|
|
|
dst->is_at_rule = src->is_at_rule; |
|
208
|
590
|
|
|
|
|
|
dst->source_line = src->source_line; |
|
209
|
|
|
|
|
|
|
|
|
210
|
590
|
100
|
|
|
|
|
if (src->prop_count > 0) { |
|
211
|
579
|
|
|
|
|
|
dst->prop_cap = src->prop_count; |
|
212
|
579
|
|
|
|
|
|
dst->prop_count = src->prop_count; |
|
213
|
579
|
|
|
|
|
|
dst->props = (LitavisProp*)malloc(sizeof(LitavisProp) * (size_t)dst->prop_cap); |
|
214
|
579
|
50
|
|
|
|
|
if (!dst->props) LITAVIS_FATAL("out of memory"); |
|
215
|
1270
|
100
|
|
|
|
|
for (i = 0; i < src->prop_count; i++) { |
|
216
|
691
|
|
|
|
|
|
dst->props[i].key = litavis_strdup(src->props[i].key); |
|
217
|
691
|
|
|
|
|
|
dst->props[i].value = litavis_strdup(src->props[i].value); |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
590
|
100
|
|
|
|
|
if (src->child_count > 0) { |
|
222
|
8
|
|
|
|
|
|
dst->child_cap = src->child_count; |
|
223
|
8
|
|
|
|
|
|
dst->child_count = src->child_count; |
|
224
|
8
|
|
|
|
|
|
dst->children = (LitavisRule*)malloc(sizeof(LitavisRule) * (size_t)dst->child_cap); |
|
225
|
8
|
50
|
|
|
|
|
if (!dst->children) LITAVIS_FATAL("out of memory"); |
|
226
|
18
|
100
|
|
|
|
|
for (i = 0; i < src->child_count; i++) { |
|
227
|
10
|
|
|
|
|
|
litavis_rule_clone_into(&dst->children[i], &src->children[i]); |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
} |
|
230
|
590
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
177
|
|
|
|
|
|
static LitavisAST* litavis_ast_clone(LitavisAST *ast) { |
|
233
|
|
|
|
|
|
|
int i; |
|
234
|
|
|
|
|
|
|
LitavisAST *clone; |
|
235
|
177
|
50
|
|
|
|
|
if (!ast) return NULL; |
|
236
|
177
|
|
|
|
|
|
clone = litavis_ast_new(ast->capacity); |
|
237
|
757
|
100
|
|
|
|
|
for (i = 0; i < ast->count; i++) { |
|
238
|
580
|
|
|
|
|
|
litavis_rule_clone_into(&clone->rules[i], &ast->rules[i]); |
|
239
|
580
|
|
|
|
|
|
litavis_ast_hash_insert(clone, ast->rules[i].selector, i); |
|
240
|
|
|
|
|
|
|
} |
|
241
|
177
|
|
|
|
|
|
clone->count = ast->count; |
|
242
|
177
|
|
|
|
|
|
return clone; |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
/* ── Rule operations ──────────────────────────────────────── */ |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
/* O(1) lookup by selector */ |
|
248
|
3018
|
|
|
|
|
|
static LitavisRule* litavis_ast_get_rule(LitavisAST *ast, const char *selector) { |
|
249
|
3018
|
|
|
|
|
|
unsigned int h = litavis_hash(selector, ast->bucket_count); |
|
250
|
3018
|
|
|
|
|
|
LitavisBucket *b = ast->buckets[h]; |
|
251
|
3329
|
100
|
|
|
|
|
while (b) { |
|
252
|
534
|
100
|
|
|
|
|
if (strcmp(b->key, selector) == 0) |
|
253
|
223
|
|
|
|
|
|
return &ast->rules[b->index]; |
|
254
|
311
|
|
|
|
|
|
b = b->next; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
2795
|
|
|
|
|
|
return NULL; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
/* Check existence */ |
|
260
|
38
|
|
|
|
|
|
static int litavis_ast_has_rule(LitavisAST *ast, const char *selector) { |
|
261
|
38
|
|
|
|
|
|
return litavis_ast_get_rule(ast, selector) != NULL; |
|
262
|
|
|
|
|
|
|
} |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
/* Append a new rule or return existing if selector matches */ |
|
265
|
2810
|
|
|
|
|
|
static LitavisRule* litavis_ast_add_rule(LitavisAST *ast, const char *selector) { |
|
266
|
2810
|
|
|
|
|
|
LitavisRule *existing = litavis_ast_get_rule(ast, selector); |
|
267
|
2810
|
100
|
|
|
|
|
if (existing) return existing; |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
/* Grow array if needed */ |
|
270
|
2777
|
100
|
|
|
|
|
if (ast->count >= ast->capacity) { |
|
271
|
16
|
|
|
|
|
|
int new_cap = ast->capacity * 2; |
|
272
|
16
|
|
|
|
|
|
LitavisRule *new_rules = (LitavisRule*)realloc(ast->rules, sizeof(LitavisRule) * (size_t)new_cap); |
|
273
|
16
|
50
|
|
|
|
|
if (!new_rules) LITAVIS_FATAL("out of memory"); |
|
274
|
16
|
|
|
|
|
|
ast->rules = new_rules; |
|
275
|
16
|
|
|
|
|
|
ast->capacity = new_cap; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
/* Rehash if load factor > 0.75 */ |
|
279
|
2777
|
100
|
|
|
|
|
if (ast->count * 4 > ast->bucket_count * 3) { |
|
280
|
16
|
|
|
|
|
|
litavis_ast_rehash(ast); |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
/* Init new rule */ |
|
284
|
2777
|
|
|
|
|
|
litavis_rule_init(&ast->rules[ast->count]); |
|
285
|
2777
|
|
|
|
|
|
ast->rules[ast->count].selector = litavis_strdup(selector); |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
/* Insert into hash */ |
|
288
|
2777
|
|
|
|
|
|
litavis_ast_hash_insert(ast, selector, ast->count); |
|
289
|
|
|
|
|
|
|
|
|
290
|
2777
|
|
|
|
|
|
return &ast->rules[ast->count++]; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
/* Remove rule, shift remaining to preserve order */ |
|
294
|
171
|
|
|
|
|
|
static void litavis_ast_remove_rule(LitavisAST *ast, int index) { |
|
295
|
|
|
|
|
|
|
int i; |
|
296
|
171
|
50
|
|
|
|
|
if (index < 0 || index >= ast->count) return; |
|
|
|
50
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
/* Clean up the rule being removed */ |
|
299
|
171
|
|
|
|
|
|
litavis_rule_cleanup(&ast->rules[index]); |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
/* Shift remaining rules down */ |
|
302
|
1510
|
100
|
|
|
|
|
for (i = index; i < ast->count - 1; i++) { |
|
303
|
1339
|
|
|
|
|
|
ast->rules[i] = ast->rules[i + 1]; |
|
304
|
|
|
|
|
|
|
} |
|
305
|
171
|
|
|
|
|
|
ast->count--; |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
/* Rebuild hash table (indices changed) */ |
|
308
|
171
|
|
|
|
|
|
litavis_ast_free_buckets(ast); |
|
309
|
171
|
|
|
|
|
|
ast->bucket_count = (ast->capacity > 8 ? ast->capacity : 8) * 2; |
|
310
|
171
|
|
|
|
|
|
ast->buckets = (LitavisBucket**)calloc((size_t)ast->bucket_count, sizeof(LitavisBucket*)); |
|
311
|
171
|
50
|
|
|
|
|
if (!ast->buckets) LITAVIS_FATAL("out of memory"); |
|
312
|
1613
|
100
|
|
|
|
|
for (i = 0; i < ast->count; i++) { |
|
313
|
1442
|
|
|
|
|
|
litavis_ast_hash_insert(ast, ast->rules[i].selector, i); |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
/* Rename a rule's selector (for dedup merging) */ |
|
318
|
86
|
|
|
|
|
|
static void litavis_ast_rename_rule(LitavisAST *ast, int index, const char *new_selector) { |
|
319
|
|
|
|
|
|
|
int i; |
|
320
|
86
|
50
|
|
|
|
|
if (index < 0 || index >= ast->count) return; |
|
|
|
50
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
|
|
322
|
86
|
|
|
|
|
|
free(ast->rules[index].selector); |
|
323
|
86
|
|
|
|
|
|
ast->rules[index].selector = litavis_strdup(new_selector); |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
/* Rebuild hash table */ |
|
326
|
86
|
|
|
|
|
|
litavis_ast_free_buckets(ast); |
|
327
|
86
|
|
|
|
|
|
ast->bucket_count = (ast->capacity > 8 ? ast->capacity : 8) * 2; |
|
328
|
86
|
|
|
|
|
|
ast->buckets = (LitavisBucket**)calloc((size_t)ast->bucket_count, sizeof(LitavisBucket*)); |
|
329
|
86
|
50
|
|
|
|
|
if (!ast->buckets) LITAVIS_FATAL("out of memory"); |
|
330
|
1491
|
100
|
|
|
|
|
for (i = 0; i < ast->count; i++) { |
|
331
|
1405
|
|
|
|
|
|
litavis_ast_hash_insert(ast, ast->rules[i].selector, i); |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
/* ── Property operations on a rule ────────────────────────── */ |
|
336
|
|
|
|
|
|
|
|
|
337
|
3255
|
|
|
|
|
|
static void litavis_rule_add_prop(LitavisRule *rule, const char *key, const char *value) { |
|
338
|
|
|
|
|
|
|
int i; |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
/* Update existing property if key matches */ |
|
341
|
4554
|
100
|
|
|
|
|
for (i = 0; i < rule->prop_count; i++) { |
|
342
|
1323
|
100
|
|
|
|
|
if (strcmp(rule->props[i].key, key) == 0) { |
|
343
|
24
|
|
|
|
|
|
free(rule->props[i].value); |
|
344
|
24
|
|
|
|
|
|
rule->props[i].value = litavis_strdup(value); |
|
345
|
24
|
|
|
|
|
|
return; |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
/* Grow array if needed */ |
|
350
|
3231
|
100
|
|
|
|
|
if (rule->prop_count >= rule->prop_cap) { |
|
351
|
2583
|
100
|
|
|
|
|
int new_cap = rule->prop_cap < 4 ? 4 : rule->prop_cap * 2; |
|
352
|
2583
|
|
|
|
|
|
LitavisProp *new_props = (LitavisProp*)realloc(rule->props, sizeof(LitavisProp) * (size_t)new_cap); |
|
353
|
2583
|
50
|
|
|
|
|
if (!new_props) LITAVIS_FATAL("out of memory"); |
|
354
|
2583
|
|
|
|
|
|
rule->props = new_props; |
|
355
|
2583
|
|
|
|
|
|
rule->prop_cap = new_cap; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
3231
|
|
|
|
|
|
rule->props[rule->prop_count].key = litavis_strdup(key); |
|
359
|
3231
|
|
|
|
|
|
rule->props[rule->prop_count].value = litavis_strdup(value); |
|
360
|
3231
|
|
|
|
|
|
rule->prop_count++; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
135
|
|
|
|
|
|
static char* litavis_rule_get_prop(LitavisRule *rule, const char *key) { |
|
364
|
|
|
|
|
|
|
int i; |
|
365
|
199
|
100
|
|
|
|
|
for (i = 0; i < rule->prop_count; i++) { |
|
366
|
196
|
100
|
|
|
|
|
if (strcmp(rule->props[i].key, key) == 0) |
|
367
|
132
|
|
|
|
|
|
return rule->props[i].value; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
3
|
|
|
|
|
|
return NULL; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
3
|
|
|
|
|
|
static int litavis_rule_has_prop(LitavisRule *rule, const char *key) { |
|
373
|
3
|
|
|
|
|
|
return litavis_rule_get_prop(rule, key) != NULL; |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
/* ── Child rule operations ────────────────────────────────── */ |
|
377
|
|
|
|
|
|
|
|
|
378
|
74
|
|
|
|
|
|
static LitavisRule* litavis_rule_add_child(LitavisRule *rule, const char *selector) { |
|
379
|
74
|
100
|
|
|
|
|
if (rule->child_count >= rule->child_cap) { |
|
380
|
63
|
50
|
|
|
|
|
int new_cap = rule->child_cap < 4 ? 4 : rule->child_cap * 2; |
|
381
|
63
|
|
|
|
|
|
LitavisRule *new_children = (LitavisRule*)realloc(rule->children, sizeof(LitavisRule) * (size_t)new_cap); |
|
382
|
63
|
50
|
|
|
|
|
if (!new_children) LITAVIS_FATAL("out of memory"); |
|
383
|
63
|
|
|
|
|
|
rule->children = new_children; |
|
384
|
63
|
|
|
|
|
|
rule->child_cap = new_cap; |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
74
|
|
|
|
|
|
litavis_rule_init(&rule->children[rule->child_count]); |
|
388
|
74
|
|
|
|
|
|
rule->children[rule->child_count].selector = litavis_strdup(selector); |
|
389
|
74
|
|
|
|
|
|
return &rule->children[rule->child_count++]; |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
/* ── Utility ──────────────────────────────────────────────── */ |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
/* Deep compare two rules' properties (same keys, same values, same order) */ |
|
395
|
2
|
|
|
|
|
|
static int litavis_rules_props_equal(LitavisRule *a, LitavisRule *b) { |
|
396
|
|
|
|
|
|
|
int i; |
|
397
|
2
|
100
|
|
|
|
|
if (a->prop_count != b->prop_count) return 0; |
|
398
|
3
|
100
|
|
|
|
|
for (i = 0; i < a->prop_count; i++) { |
|
399
|
2
|
50
|
|
|
|
|
if (strcmp(a->props[i].key, b->props[i].key) != 0) return 0; |
|
400
|
2
|
50
|
|
|
|
|
if (strcmp(a->props[i].value, b->props[i].value) != 0) return 0; |
|
401
|
|
|
|
|
|
|
} |
|
402
|
1
|
|
|
|
|
|
return 1; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
/* Merge properties from src into dst (src wins on conflict) */ |
|
406
|
887
|
|
|
|
|
|
static void litavis_rule_merge_props(LitavisRule *dst, LitavisRule *src) { |
|
407
|
|
|
|
|
|
|
int i; |
|
408
|
1979
|
100
|
|
|
|
|
for (i = 0; i < src->prop_count; i++) { |
|
409
|
1092
|
|
|
|
|
|
litavis_rule_add_prop(dst, src->props[i].key, src->props[i].value); |
|
410
|
|
|
|
|
|
|
} |
|
411
|
887
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
#endif /* LITAVIS_AST_H */ |