commit 248e4cc
shrub
·
2026-04-07 17:59:42 +0000 UTC
parent b427801
feat: cond block handling
M
main.c
+29,
-9
1@@ -87,19 +87,23 @@ printwords(const char *name, const struct StrList *list)
2 printf("\n");
3 }
4
5-/*dump ast, TODO make it actually do something useful*/
6-/*also TODO
7- * variable eval
8- * build a proper graph
9- * a lot more
10- */
11 static void
12-dump(const struct Ast *ast)
13+indent(size_t n)
14+{
15+ size_t i;
16+
17+ for (i = 0; i < n; i++)
18+ printf(" ");
19+}
20+
21+static void
22+dumpnodes(const struct Node *v, size_t n, size_t depth)
23 {
24 size_t i, j;
25
26- for (i = 0; i < ast->n; i++) {
27- const struct Node *s = &ast->v[i];
28+ for (i = 0; i < n; i++) {
29+ const struct Node *s = &v[i];
30+ indent(depth);
31 printf("%d-%d ", s->loc.line0, s->loc.line1);
32 switch (s->kind) {
33 case NODE_BLANK:
34@@ -118,6 +122,16 @@ dump(const struct Ast *ast)
35 if (s->data.cond.arg2)
36 printf(" arg2=%s", s->data.cond.arg2);
37 printf("\n");
38+ if (s->data.cond.thenpart.n) {
39+ indent(depth);
40+ puts("then");
41+ dumpnodes(s->data.cond.thenpart.v, s->data.cond.thenpart.n, depth + 1);
42+ }
43+ if (s->data.cond.elsepart.n) {
44+ indent(depth);
45+ puts("else");
46+ dumpnodes(s->data.cond.elsepart.v, s->data.cond.elsepart.n, depth + 1);
47+ }
48 break;
49 case NODE_ASSIGN:
50 printf("assign%s lhs=%s op=%s rhs=%s\n",
51@@ -143,6 +157,12 @@ dump(const struct Ast *ast)
52 }
53 }
54
55+static void
56+dump(const struct Ast *ast)
57+{
58+ dumpnodes(ast->v, ast->n, 0);
59+}
60+
61 int
62 main(int argc, char **argv)
63 {
+173,
-74
1@@ -72,6 +72,13 @@ xstrdup(const char *s)
2 return xstrndup(s, strlen(s));
3 }
4
5+static void
6+addnode(struct NodeList *list, struct Node node)
7+{
8+ list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
9+ list->v[list->n++] = node;
10+}
11+
12 static char *
13 trimdup(const char *s, size_t n)
14 {
15@@ -552,11 +559,6 @@ parsecond(const struct PreLine *line, const char *s)
16 } else if (haskw(s, "ifneq")) {
17 state.data.cond.kind = COND_IFNEQ;
18 p = s + 5;
19- } else if (haskw(s, "else")) {
20- state.data.cond.kind = COND_ELSE;
21- p = s + 4;
22- state.data.cond.arg1 = trimdup(p, strlen(p));
23- return state;
24 } else {
25 state.data.cond.kind = COND_ENDIF;
26 return state;
27@@ -707,22 +709,18 @@ parseline(const struct PreLine *line)
28 return state;
29 }
30
31-/*TODO the ast we build is very flat. we probably want actual nested blocks for conditionals*/
32-int
33-buildast(const char *path, const struct Pre *pre, struct Ast *ast)
34+static int
35+parseblock(const struct Pre *pre, size_t *i, struct NodeList *out)
36 {
37 struct Node state;
38 struct Node *last_rule;
39- size_t i;
40
41- (void)path;
42- memset(ast, 0, sizeof(*ast));
43 last_rule = 0;
44-
45- for (i = 0; i < pre->n; i++) {
46+ while (*i < pre->n) {
47 struct PreLine *line;
48+ char *trim;
49
50- line = &pre->v[i];
51+ line = &pre->v[*i];
52 if (line->isrecipe) {
53 if (last_rule) {
54 last_rule->data.rule.recipes.v = xrealloc(last_rule->data.rule.recipes.v,
55@@ -730,19 +728,75 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
56 last_rule->data.rule.recipes.v[last_rule->data.rule.recipes.n++] =
57 xstrndup(line->text + 1, strlen(line->text + 1));
58 last_rule->loc.line1 = line->line1;
59+ (*i)++;
60 continue;
61 }
62 memmove(line->text, line->text + 1, strlen(line->text + 1) + 1);
63 }
64+ trim = trimdup(line->text, strlen(line->text));
65+ if (haskw(trim, "else") || haskw(trim, "endif")) {
66+ free(trim);
67+ break;
68+ }
69+ if (haskw(trim, "ifeq") || haskw(trim, "ifneq")) {
70+ state = parsecond(line, trim);
71+ free(trim);
72+ (*i)++;
73+ if (parseblock(pre, i, &state.data.cond.thenpart) < 0)
74+ return -1;
75+ if (*i < pre->n) {
76+ struct PreLine *endline;
77+ char *endtrim;
78+
79+ endline = &pre->v[*i];
80+ endtrim = trimdup(endline->text, strlen(endline->text));
81+ if (haskw(endtrim, "else")) {
82+ free(endtrim);
83+ (*i)++;
84+ if (parseblock(pre, i, &state.data.cond.elsepart) < 0)
85+ return -1;
86+ if (*i >= pre->n)
87+ return -1;
88+ endline = &pre->v[*i];
89+ endtrim = trimdup(endline->text, strlen(endline->text));
90+ }
91+ if (!haskw(endtrim, "endif")) {
92+ free(endtrim);
93+ return -1;
94+ }
95+ state.loc.line1 = endline->line1;
96+ free(endtrim);
97+ (*i)++;
98+ } else {
99+ return -1;
100+ }
101+ addnode(out, state);
102+ last_rule = 0;
103+ continue;
104+ }
105+ free(trim);
106
107 state = parseline(line);
108- ast->v = xrealloc(ast->v, (ast->n + 1) * sizeof(ast->v[0]));
109- ast->v[ast->n++] = state;
110+ addnode(out, state);
111 if (state.kind == NODE_RULE)
112- last_rule = &ast->v[ast->n - 1];
113+ last_rule = &out->v[out->n - 1];
114 else if (state.kind != NODE_BLANK && state.kind != NODE_COMMENT)
115 last_rule = 0;
116+ (*i)++;
117 }
118+ return 0;
119+}
120+
121+int
122+buildast(const char *path, const struct Pre *pre, struct Ast *ast)
123+{
124+ size_t i;
125+
126+ (void)path;
127+ memset(ast, 0, sizeof(*ast));
128+ i = 0;
129+ if (parseblock(pre, &i, (struct NodeList *)ast) < 0)
130+ return -1;
131
132 return 0;
133 }
134@@ -910,6 +964,20 @@ seedenv(struct Env *env)
135 setvar(env, "SHELL", xstrdup("/bin/sh"), 1);
136 }
137
138+static void
139+freeenv(struct Env *env)
140+{
141+ size_t i;
142+
143+ for (i = 0; i < env->n; i++) {
144+ free(env->v[i].name);
145+ free(env->v[i].val);
146+ }
147+ free(env->v);
148+ env->v = 0;
149+ env->n = 0;
150+}
151+
152 static void
153 evalassign(struct Env *env, const struct AssignNode *in)
154 {
155@@ -947,6 +1015,22 @@ evalassign(struct Env *env, const struct AssignNode *in)
156 }
157 }
158
159+static int
160+testcond(struct Env *env, const struct CondNode *cond)
161+{
162+ char *a, *b;
163+ int ok;
164+
165+ a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
166+ b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
167+ ok = strcmp(a, b) == 0;
168+ free(a);
169+ free(b);
170+ if (cond->kind == COND_IFNEQ)
171+ ok = !ok;
172+ return ok;
173+}
174+
175 static void
176 copywords(struct StrList *out, const struct StrList *in, struct Env *env)
177 {
178@@ -971,69 +1055,76 @@ copyrecipes(struct RecipeList *out, const struct RecipeList *in, struct Env *env
179 }
180 }
181
182-int
183-eval(const struct Ast *ast, struct Ast *out)
184+static int
185+evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
186 {
187- struct Env env;
188 size_t i;
189
190 memset(out, 0, sizeof(*out));
191- memset(&env, 0, sizeof(env));
192- seedenv(&env);
193- for (i = 0; i < ast->n; i++) {
194- const struct Node *in;
195+ for (i = 0; i < in->n; i++) {
196+ const struct Node *src;
197 struct Node node;
198
199- in = &ast->v[i];
200+ src = &in->v[i];
201 memset(&node, 0, sizeof(node));
202- node.kind = in->kind;
203- node.loc = in->loc;
204- switch (in->kind) {
205+ node.kind = src->kind;
206+ node.loc = src->loc;
207+ switch (src->kind) {
208 case NODE_BLANK:
209 break;
210 case NODE_COMMENT:
211 case NODE_RAW:
212- node.data.raw.text = xstrdup(in->data.raw.text);
213+ node.data.raw.text = xstrdup(src->data.raw.text);
214 break;
215 case NODE_INCLUDE:
216- node.data.include.optional = in->data.include.optional;
217- node.data.include.path = expandstr(&env, in->data.include.path);
218+ node.data.include.optional = src->data.include.optional;
219+ node.data.include.path = expandstr(env, src->data.include.path);
220 break;
221 case NODE_COND:
222- node.data.cond.kind = in->data.cond.kind;
223- node.data.cond.arg1 = in->data.cond.arg1 ? expandstr(&env, in->data.cond.arg1) : 0;
224- node.data.cond.arg2 = in->data.cond.arg2 ? expandstr(&env, in->data.cond.arg2) : 0;
225- node.data.cond.raw = in->data.cond.raw ? expandstr(&env, in->data.cond.raw) : 0;
226- break;
227+ if (testcond(env, &src->data.cond)) {
228+ if (evalnodes(&src->data.cond.thenpart, out, env) < 0)
229+ return -1;
230+ } else {
231+ if (evalnodes(&src->data.cond.elsepart, out, env) < 0)
232+ return -1;
233+ }
234+ continue;
235 case NODE_ASSIGN:
236- node.data.assign.lhs = xstrdup(in->data.assign.lhs);
237- node.data.assign.op = in->data.assign.op;
238- node.data.assign.tspec = in->data.assign.tspec;
239- if (in->data.assign.op == ASSIGN_COLON_EQ || in->data.assign.op == ASSIGN_BANG_EQ)
240- node.data.assign.rhs = expandstr(&env, in->data.assign.rhs);
241+ node.data.assign.lhs = xstrdup(src->data.assign.lhs);
242+ node.data.assign.op = src->data.assign.op;
243+ node.data.assign.tspec = src->data.assign.tspec;
244+ if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
245+ node.data.assign.rhs = expandstr(env, src->data.assign.rhs);
246 else
247- node.data.assign.rhs = xstrdup(in->data.assign.rhs);
248- copywords(&node.data.assign.targets, &in->data.assign.targets, &env);
249- evalassign(&env, &in->data.assign);
250+ node.data.assign.rhs = xstrdup(src->data.assign.rhs);
251+ copywords(&node.data.assign.targets, &src->data.assign.targets, env);
252+ evalassign(env, &src->data.assign);
253 break;
254 case NODE_RULE:
255- copywords(&node.data.rule.targets, &in->data.rule.targets, &env);
256- copywords(&node.data.rule.prereqs, &in->data.rule.prereqs, &env);
257- copywords(&node.data.rule.order_only, &in->data.rule.order_only, &env);
258- copyrecipes(&node.data.rule.recipes, &in->data.rule.recipes, &env);
259+ copywords(&node.data.rule.targets, &src->data.rule.targets, env);
260+ copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
261+ copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
262+ copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes, env);
263 break;
264 }
265- out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
266- out->v[out->n++] = node;
267- }
268- for (i = 0; i < env.n; i++) {
269- free(env.v[i].name);
270- free(env.v[i].val);
271+ addnode(out, node);
272 }
273- free(env.v);
274 return 0;
275 }
276
277+int
278+eval(const struct Ast *ast, struct Ast *out)
279+{
280+ struct Env env;
281+ int rc;
282+
283+ memset(&env, 0, sizeof(env));
284+ seedenv(&env);
285+ rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
286+ freeenv(&env);
287+ return rc;
288+}
289+
290 static void
291 freestrs(struct StrList *list)
292 {
293@@ -1054,41 +1145,49 @@ freerec(struct RecipeList *list)
294 free(list->v);
295 }
296
297-void
298-freeast(struct Ast *ast)
299+static void
300+freenodes(struct NodeList *list)
301 {
302 size_t i;
303
304- for (i = 0; i < ast->n; i++) {
305- switch (ast->v[i].kind) {
306+ for (i = 0; i < list->n; i++) {
307+ switch (list->v[i].kind) {
308 case NODE_COMMENT:
309 case NODE_RAW:
310- free(ast->v[i].data.raw.text);
311+ free(list->v[i].data.raw.text);
312 break;
313 case NODE_ASSIGN:
314- free(ast->v[i].data.assign.lhs);
315- free(ast->v[i].data.assign.rhs);
316- freestrs(&ast->v[i].data.assign.targets);
317+ free(list->v[i].data.assign.lhs);
318+ free(list->v[i].data.assign.rhs);
319+ freestrs(&list->v[i].data.assign.targets);
320 break;
321 case NODE_RULE:
322- freestrs(&ast->v[i].data.rule.targets);
323- freestrs(&ast->v[i].data.rule.prereqs);
324- freestrs(&ast->v[i].data.rule.order_only);
325- freerec(&ast->v[i].data.rule.recipes);
326+ freestrs(&list->v[i].data.rule.targets);
327+ freestrs(&list->v[i].data.rule.prereqs);
328+ freestrs(&list->v[i].data.rule.order_only);
329+ freerec(&list->v[i].data.rule.recipes);
330 break;
331 case NODE_INCLUDE:
332- free(ast->v[i].data.include.path);
333+ free(list->v[i].data.include.path);
334 break;
335 case NODE_COND:
336- free(ast->v[i].data.cond.arg1);
337- free(ast->v[i].data.cond.arg2);
338- free(ast->v[i].data.cond.raw);
339+ free(list->v[i].data.cond.arg1);
340+ free(list->v[i].data.cond.arg2);
341+ free(list->v[i].data.cond.raw);
342+ freenodes(&list->v[i].data.cond.thenpart);
343+ freenodes(&list->v[i].data.cond.elsepart);
344 break;
345 case NODE_BLANK:
346 break;
347 }
348 }
349- free(ast->v);
350- ast->v = 0;
351- ast->n = 0;
352+ free(list->v);
353+ list->v = 0;
354+ list->n = 0;
355+}
356+
357+void
358+freeast(struct Ast *ast)
359+{
360+ freenodes((struct NodeList *)ast);
361 }
+14,
-5
1@@ -8,7 +8,7 @@
2 * PreLine: one preprocessed line with source location
3 * Pre: flattened preprocessed makefile input
4 * Node: one parsed syntax node
5- * Ast: flat list of parsed nodes in source order
6+ * Ast: parsed nodes in source order with nested cond blocks
7 */
8
9 enum NodeKind {
10@@ -84,15 +84,24 @@ struct IncludeNode {
11 char *path;
12 };
13
14+struct RawNode {
15+ char *text;
16+};
17+
18+struct Node;
19+
20+struct NodeList {
21+ struct Node *v;
22+ size_t n;
23+};
24+
25 struct CondNode {
26 enum CondKind kind;
27 char *arg1;
28 char *arg2;
29 char *raw;
30-};
31-
32-struct RawNode {
33- char *text;
34+ struct NodeList thenpart;
35+ struct NodeList elsepart;
36 };
37
38 struct Node {