commit 427bbfc

shrub  ·  2026-04-16 16:40:49 +0000 UTC
parent d34ce1a
make eval produce a ruleset instead of another ast to simplify graph construction
6 files changed,  +150, -212
+2, -1
 1@@ -1,3 +1,4 @@
 2+build.dot
 3 config.mak
 4 test-musl.mk
 5 *.o
 6@@ -5,4 +6,4 @@ shin
 7 tests/work
 8 build.ninja
 9 .ninja_log
10-.ninja_deps
11+.ninja_deps
+0, -58
 1@@ -1,58 +0,0 @@
 2-digraph shin {
 3-  rankdir=LR;
 4-  node [fontname="monospace"];
 5-  n0 [label="all", style=dashed];
 6-  n1 [label="shin", shape=box];
 7-  n2 [label="cli/main.o", shape=box];
 8-  n3 [label="src/parse.o", shape=box];
 9-  n4 [label="src/eval/eval.o", shape=box];
10-  n5 [label="src/eval/dollar.o", shape=box];
11-  n6 [label="src/graph.o", shape=box];
12-  n7 [label="src/posix.o", shape=box];
13-  n8 [label="src/gnu/pattern.o", shape=box];
14-  n9 [label="backends/ninja.o", shape=box];
15-  n10 [label="backends/graphviz.o", shape=box];
16-  n11 [label="src/util.o", shape=box];
17-  n12 [label="src/gnu/functions.o", shape=box];
18-  n13 [label="fmt", shape=box];
19-  n14 [label="test", shape=box];
20-  n15 [label="install", shape=box];
21-  n16 [label="clean", shape=box];
22-  n17 [label="cli/main.c"];
23-  n18 [label="src/parse.c"];
24-  n19 [label="src/eval/eval.c"];
25-  n20 [label="src/eval/dollar.c"];
26-  n21 [label="src/graph.c"];
27-  n22 [label="src/posix.c"];
28-  n23 [label="src/gnu/pattern.c"];
29-  n24 [label="backends/ninja.c"];
30-  n25 [label="backends/graphviz.c"];
31-  n26 [label="src/util.c"];
32-  n27 [label="src/gnu/functions.c"];
33-
34-  n1 -> n0;
35-  n2 -> n1;
36-  n3 -> n1;
37-  n4 -> n1;
38-  n5 -> n1;
39-  n6 -> n1;
40-  n7 -> n1;
41-  n8 -> n1;
42-  n9 -> n1;
43-  n10 -> n1;
44-  n11 -> n1;
45-  n12 -> n1;
46-  n17 -> n2;
47-  n18 -> n3;
48-  n19 -> n4;
49-  n20 -> n5;
50-  n21 -> n6;
51-  n22 -> n7;
52-  n23 -> n8;
53-  n24 -> n9;
54-  n25 -> n10;
55-  n26 -> n11;
56-  n27 -> n12;
57-  n1 -> n14;
58-  n1 -> n15;
59-}
+30, -87
  1@@ -111,26 +111,6 @@ assignopname(enum AssignOp op)
  2 	return "?";
  3 }
  4 
  5-static const char *
  6-condkindname(enum CondKind kind)
  7-{
  8-	switch (kind) {
  9-	case COND_IFEQ:
 10-		return "ifeq";
 11-	case COND_IFNEQ:
 12-		return "ifneq";
 13-	case COND_IFDEF:
 14-		return "ifdef";
 15-	case COND_IFNDEF:
 16-		return "ifndef";
 17-	case COND_ELSE:
 18-		return "else";
 19-	case COND_ENDIF:
 20-		return "endif";
 21-	}
 22-	return "?";
 23-}
 24-
 25 static void
 26 printindent(int depth)
 27 {
 28@@ -165,76 +145,39 @@ dumprecipes(const struct RecipeList *list, int depth)
 29 	}
 30 }
 31 
 32-static void dumpnodes(const struct Node *nodes, size_t n, int depth);
 33-
 34 static void
 35-dumpnode(const struct Node *node, int depth)
 36+dumpassign(const struct AssignNode *assign, int depth)
 37 {
 38 	printindent(depth);
 39-	printf("%d-%d ", node->loc.line0, node->loc.line1);
 40-	switch (node->kind) {
 41-	case NODE_BLANK:
 42-		printf("blank\n");
 43-		break;
 44-	case NODE_COMMENT:
 45-		printf("comment %s\n", node->data.raw.text);
 46-		break;
 47-	case NODE_RAW:
 48-		printf("raw %s\n", node->data.raw.text);
 49-		break;
 50-	case NODE_INCLUDE:
 51-		printf("include%s %s\n",
 52-		       node->data.include.optional ? " optional" : "",
 53-		       node->data.include.path);
 54-		break;
 55-	case NODE_ASSIGN:
 56-		printf("assign %s %s %s\n",
 57-		       node->data.assign.lhs,
 58-		       assignopname(node->data.assign.op),
 59-		       node->data.assign.rhs);
 60-		if (node->data.assign.tspec)
 61-			dumpstrlist("targets", &node->data.assign.targets, depth + 1);
 62-		break;
 63-	case NODE_RULE:
 64-		printf("rule\n");
 65-		dumpstrlist("targets", &node->data.rule.targets, depth + 1);
 66-		dumpstrlist("prereqs", &node->data.rule.prereqs, depth + 1);
 67-		dumpstrlist("order-only", &node->data.rule.order_only, depth + 1);
 68-		dumprecipes(&node->data.rule.recipes, depth + 1);
 69-		break;
 70-	case NODE_COND:
 71-		printf("cond %s", condkindname(node->data.cond.kind));
 72-		if (node->data.cond.arg1)
 73-			printf(" arg1=%s", node->data.cond.arg1);
 74-		if (node->data.cond.arg2)
 75-			printf(" arg2=%s", node->data.cond.arg2);
 76-		putchar('\n');
 77-		printindent(depth + 1);
 78-		printf("then:\n");
 79-		dumpnodes(node->data.cond.thenpart.v, node->data.cond.thenpart.n, depth + 2);
 80-		if (node->data.cond.elsepart.n) {
 81-			printindent(depth + 1);
 82-			printf("else:\n");
 83-			dumpnodes(node->data.cond.elsepart.v, node->data.cond.elsepart.n, depth + 2);
 84-		}
 85-		break;
 86-	}
 87+	printf("assign %s %s %s\n", assign->lhs, assignopname(assign->op), assign->rhs);
 88+	if (assign->tspec)
 89+		dumpstrlist("targets", &assign->targets, depth + 1);
 90 }
 91 
 92 static void
 93-dumpnodes(const struct Node *nodes, size_t n, int depth)
 94+dumprule(const struct RuleNode *rule, int depth)
 95 {
 96-	size_t i;
 97-
 98-	for (i = 0; i < n; i++)
 99-		dumpnode(&nodes[i], depth);
100+	printindent(depth);
101+	printf("rule\n");
102+	dumpstrlist("targets", &rule->targets, depth + 1);
103+	dumpstrlist("prereqs", &rule->prereqs, depth + 1);
104+	dumpstrlist("order-only", &rule->order_only, depth + 1);
105+	dumprecipes(&rule->recipes, depth + 1);
106 }
107 
108 static void
109-dumpast(const struct Ast *ast)
110+dumpruleset(const struct RuleSet *ruleset)
111 {
112-	printf("ast (%zu nodes)\n", ast->n);
113-	dumpnodes(ast->v, ast->n, 1);
114+	size_t i;
115+
116+	printf("ruleset (%zu vars, %zu target vars, %zu rules)\n",
117+	       ruleset->nvars, ruleset->ntvars, ruleset->nrules);
118+	for (i = 0; i < ruleset->nvars; i++)
119+		dumpassign(&ruleset->vars[i], 1);
120+	for (i = 0; i < ruleset->ntvars; i++)
121+		dumpassign(&ruleset->tvars[i], 1);
122+	for (i = 0; i < ruleset->nrules; i++)
123+		dumprule(&ruleset->rules[i], 1);
124 }
125 
126 static void
127@@ -266,7 +209,7 @@ main(int argc, char **argv)
128 	char **assigns;
129 	size_t nassigns;
130 	struct Ast ast;
131-	struct Ast out;
132+	struct RuleSet rs;
133 	struct Graph graph;
134 
135 	path = "Makefile";
136@@ -356,17 +299,17 @@ main(int argc, char **argv)
137 		return 1;
138 		
139 	}
140-	if (eval(&ast, &out) < 0) {
141+	if (eval(&ast, &rs) < 0) {
142 		fprintf(stderr, "eval error in %s\n", path);
143 		freeast(&ast);
144 		free(src);
145 		return 1;
146 	}
147 	if (dump_ast)
148-		dumpast(&out);
149-	if (buildgraph(&out, &graph) < 0) {
150+		dumpruleset(&rs);
151+	if (buildgraph(&rs, &graph) < 0) {
152 		fprintf(stderr, "graph error in %s\n", path);
153-		freeast(&out);
154+		freeruleset(&rs);
155 		freeast(&ast);
156 		free(src);
157 		return 1;
158@@ -377,7 +320,7 @@ main(int argc, char **argv)
159 		if (gengraphviz(&graph, "build.dot") < 0) {
160 			fprintf(stderr, "graphviz generation error\n");
161 			freegraph(&graph);
162-			freeast(&out);
163+			freeruleset(&rs);
164 			freeast(&ast);
165 			free(src);
166 			return 1;
167@@ -386,13 +329,13 @@ main(int argc, char **argv)
168 	if (genninja(&graph, "build.ninja") < 0) {
169 		fprintf(stderr, "ninja generation error\n");
170 		freegraph(&graph);
171-		freeast(&out);
172+		freeruleset(&rs);
173 		freeast(&ast);
174 		free(src);
175 		return 1;
176 	}
177 	freegraph(&graph);
178-	freeast(&out);
179+	freeruleset(&rs);
180 	freeast(&ast);
181 	free(src);
182 	return 0;
+81, -28
  1@@ -144,36 +144,60 @@ copyrecipes(struct RecipeList *out, const struct RecipeList *in)
  2 	}
  3 }
  4 
  5+static void
  6+addrulesetassign(struct AssignNode **vec, size_t *n, const struct Node *src, struct EvalCtx *ctx)
  7+{
  8+	struct AssignNode *dst;
  9+
 10+	*vec = xrealloc(*vec, (*n + 1) * sizeof((*vec)[0]));
 11+	dst = &(*vec)[(*n)++];
 12+	memset(dst, 0, sizeof(*dst));
 13+	dst->lhs = xstrdup(src->data.assign.lhs);
 14+	dst->op = src->data.assign.op;
 15+	dst->tspec = src->data.assign.tspec;
 16+	if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
 17+		dst->rhs = expandstr(ctx, src->data.assign.rhs);
 18+	else
 19+		dst->rhs = xstrdup(src->data.assign.rhs);
 20+	copywords(&dst->targets, &src->data.assign.targets, ctx);
 21+}
 22+
 23+static void
 24+addrulesetrule(struct RuleSet *out, const struct RuleNode *src, struct EvalCtx *ctx)
 25+{
 26+	struct RuleNode *dst;
 27+
 28+	out->rules = xrealloc(out->rules, (out->nrules + 1) * sizeof(out->rules[0]));
 29+	dst = &out->rules[out->nrules++];
 30+	memset(dst, 0, sizeof(*dst));
 31+	dst->dcolon = src->dcolon;
 32+	copywords(&dst->targets, &src->targets, ctx);
 33+	copywords(&dst->prereqs, &src->prereqs, ctx);
 34+	copywords(&dst->order_only, &src->order_only, ctx);
 35+	copyrecipes(&dst->recipes, &src->recipes);
 36+}
 37+
 38 static int
 39-evalnodes(const struct NodeList *in, struct NodeList *out, struct EvalCtx *ctx)
 40+evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
 41 {
 42 	size_t i;
 43 
 44 	for (i = 0; i < in->n; i++) {
 45 		const struct Node *src;
 46-		struct Node node;
 47 
 48 		src = &in->v[i];
 49-		memset(&node, 0, sizeof(node));
 50-		node.kind = src->kind;
 51-		node.loc = src->loc;
 52 		switch (src->kind) {
 53 		case NODE_BLANK:
 54 			break;
 55-		case NODE_COMMENT:
 56-			node.data.raw.text = xstrdup(src->data.raw.text);
 57-			break;
 58 		case NODE_RAW: {
 59 			char *exp;
 60 
 61 			exp = expandstr(ctx, src->data.raw.text);
 62 			free(exp);
 63-			node.kind = NODE_BLANK;
 64 			break;
 65 		}
 66+		case NODE_COMMENT:
 67 		case NODE_INCLUDE:
 68-			node.data.include.optional = src->data.include.optional;
 69-			node.data.include.path = expandstr(ctx, src->data.include.path);
 70 			break;
 71 		case NODE_COND:
 72 			if (testcond(ctx, &src->data.cond)) {
 73@@ -185,32 +209,23 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct EvalCtx *ctx)
 74 			}
 75 			continue;
 76 		case NODE_ASSIGN:
 77-			node.data.assign.lhs = xstrdup(src->data.assign.lhs);
 78-			node.data.assign.op = src->data.assign.op;
 79-			node.data.assign.tspec = src->data.assign.tspec;
 80-			if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
 81-				node.data.assign.rhs = expandstr(ctx, src->data.assign.rhs);
 82-			else
 83-				node.data.assign.rhs = xstrdup(src->data.assign.rhs);
 84-			copywords(&node.data.assign.targets, &src->data.assign.targets, ctx);
 85-			if (!src->data.assign.tspec)
 86+			if (src->data.assign.tspec) {
 87+				addrulesetassign(&out->tvars, &out->ntvars, src, ctx);
 88+			} else {
 89+				addrulesetassign(&out->vars, &out->nvars, src, ctx);
 90 				evalassign(ctx, &src->data.assign);
 91+			}
 92 			break;
 93 		case NODE_RULE:
 94-			node.data.rule.dcolon = src->data.rule.dcolon;
 95-			copywords(&node.data.rule.targets, &src->data.rule.targets, ctx);
 96-			copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, ctx);
 97-			copywords(&node.data.rule.order_only, &src->data.rule.order_only, ctx);
 98-			copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes);
 99+			addrulesetrule(out, &src->data.rule, ctx);
100 			break;
101 		}
102-		addnode(out, node);
103 	}
104 	return 0;
105 }
106 
107 int
108-eval(const struct Ast *ast, struct Ast *out)
109+eval(const struct Ast *ast, struct RuleSet *out)
110 {
111 	struct Env env;
112 	struct EvalCtx ctx;
113@@ -221,13 +236,51 @@ eval(const struct Ast *ast, struct Ast *out)
114 	memset(&ctx, 0, sizeof(ctx));
115 	seedenv(&env);
116 	ctx.env = &env;
117-	rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &ctx);
118+	rc = evalnodes((const struct NodeList *)ast, out, &ctx);
119 	freeenv(&env);
120 	if (rc == 0 && ctx.errors)
121 		return -1;
122 	return rc;
123 }
124 
125+static void
126+freeassigns(struct AssignNode *v, size_t n)
127+{
128+	size_t i;
129+
130+	for (i = 0; i < n; i++) {
131+		free(v[i].lhs);
132+		free(v[i].rhs);
133+		freestrs(&v[i].targets);
134+	}
135+	free(v);
136+}
137+
138+static void
139+freerules(struct RuleNode *v, size_t n)
140+{
141+	size_t i;
142+
143+	for (i = 0; i < n; i++) {
144+		freestrs(&v[i].targets);
145+		freestrs(&v[i].prereqs);
146+		freestrs(&v[i].order_only);
147+		freerecipes(&v[i].recipes);
148+	}
149+	free(v);
150+}
151+
152+void
153+freeruleset(struct RuleSet *ruleset)
154+{
155+	if (!ruleset)
156+		return;
157+	freeassigns(ruleset->vars, ruleset->nvars);
158+	freeassigns(ruleset->tvars, ruleset->ntvars);
159+	freerules(ruleset->rules, ruleset->nrules);
160+	memset(ruleset, 0, sizeof(*ruleset));
161+}
162+
163 static void
164 freenodes(struct NodeList *list)
165 {
+25, -36
  1@@ -19,7 +19,7 @@ struct TAssign {
  2 struct GraphState {
  3 	struct Graph *graph;
  4 	struct Env env;
  5-	const struct RuleNode **rules;
  6+	const struct RuleNode *rules;
  7 	size_t nrules;
  8 	struct PatRules patterns;
  9 	struct SufRules sufs;
 10@@ -88,13 +88,6 @@ seedsufs(struct SufRules *rules, const struct RuleNode *rule)
 11 	free(to);
 12 }
 13 
 14-static void
 15-collectrule(struct GraphState *gs, const struct RuleNode *rule)
 16-{
 17-	gs->rules = xrealloc(gs->rules, (gs->nrules + 1) * sizeof(gs->rules[0]));
 18-	gs->rules[gs->nrules++] = rule;
 19-}
 20-
 21 static int
 22 targetmatches(const char *pat, const char *name)
 23 {
 24@@ -211,12 +204,12 @@ addtassign(struct GraphState *gs, const struct AssignNode *assign)
 25 	ta->op = assign->op;
 26 }
 27 
 28-/*generate the graph from the evaluated ast
 29+/*generate the graph from the evaluated rule set
 30  * get the env and target specific assignments,
 31  * then expand placeholder targets until all prereqs
 32  * we can find are in the graph. */
 33 int
 34-buildgraph(const struct Ast *ast, struct Graph *graph)
 35+buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
 36 {
 37 	size_t i, j, start;
 38 	struct GraphState gs;
 39@@ -227,44 +220,41 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
 40 	memset(&ctx, 0, sizeof(ctx));
 41 	gs.graph = graph;
 42 	seedenv(&gs.env);
 43+	gs.rules = ruleset->rules;
 44+	gs.nrules = ruleset->nrules;
 45 	ctx.env = &gs.env;
 46 	imprules(&gs.sufs);
 47 
 48-	/* get the state, env vars in gs.env, target-specific var in gs.tas,
 49-	 * and rules in gs.rules */
 50-	for (i = 0; i < ast->n; i++) {
 51-		if (ast->v[i].kind == NODE_ASSIGN) {
 52-			if (ast->v[i].data.assign.tspec)
 53-				addtassign(&gs, &ast->v[i].data.assign);
 54-			else
 55-				evalassign(&ctx, &ast->v[i].data.assign);
 56-		} else if (ast->v[i].kind == NODE_RULE) {
 57-			if (issufrule(&ast->v[i].data.rule)) {
 58-				gs.saw_suffixes = 1;
 59-				if (ast->v[i].data.rule.prereqs.n == 0)
 60-					clearsufs(&gs.sufs.active);
 61-				else
 62-					addsufs(&gs.sufs.active, &ast->v[i].data.rule.prereqs);
 63-				continue;
 64-			}
 65-			collectrule(&gs, &ast->v[i].data.rule);
 66-		}
 67+	for (i = 0; i < ruleset->nvars; i++)
 68+		evalassign(&ctx, &ruleset->vars[i]);
 69+
 70+	for (i = 0; i < ruleset->ntvars; i++)
 71+		addtassign(&gs, &ruleset->tvars[i]);
 72+
 73+	for (i = 0; i < gs.nrules; i++) {
 74+		if (!issufrule(&gs.rules[i]))
 75+			continue;
 76+		gs.saw_suffixes = 1;
 77+		if (gs.rules[i].prereqs.n == 0)
 78+			clearsufs(&gs.sufs.active);
 79+		else
 80+			addsufs(&gs.sufs.active, &gs.rules[i].prereqs);
 81 	}
 82 
 83 	if (!gs.saw_suffixes) {
 84 		for (i = 0; i < gs.nrules; i++)
 85-			seedsufs(&gs.sufs, gs.rules[i]);
 86+			seedsufs(&gs.sufs, &gs.rules[i]);
 87 	}
 88 
 89 	for (i = 0; i < gs.nrules; i++) {
 90 		size_t k;
 91 
 92-		if (collectsufrule(&gs.sufs, gs.rules[i]))
 93+		if (collectsufrule(&gs.sufs, &gs.rules[i]))
 94 			continue;
 95-		collectpat(&gs.patterns, gs.rules[i]);
 96-		for (k = 0; k < gs.rules[i]->targets.n; k++) {
 97-			if (!ispat(gs.rules[i]->targets.v[k]))
 98-				addrule(&gs, gs.rules[i]->targets.v[k], gs.rules[i]);
 99+		collectpat(&gs.patterns, &gs.rules[i]);
100+		for (k = 0; k < gs.rules[i].targets.n; k++) {
101+			if (!ispat(gs.rules[i].targets.v[k]))
102+				addrule(&gs, gs.rules[i].targets.v[k], &gs.rules[i]);
103 		}
104 	}
105 
106@@ -320,7 +310,6 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
107 	free(gs.tas);
108 	freepatrule(&gs.patterns);
109 	freesufrules(&gs.sufs);
110-	free(gs.rules);
111 	freeenv(&gs.env);
112 
113 	return ctx.errors ? -1 : 0;
+12, -2
 1@@ -124,6 +124,15 @@ struct Ast {
 2 	size_t n;
 3 };
 4 
 5+struct RuleSet {
 6+	struct AssignNode *vars;
 7+	size_t nvars;
 8+	struct AssignNode *tvars;
 9+	size_t ntvars;
10+	struct RuleNode *rules;
11+	size_t nrules;
12+};
13+
14 struct Target {
15 	char *name;
16 	struct StrList prereqs;
17@@ -141,10 +150,11 @@ struct Graph {
18 int preproc(const char *path, struct Pre *pre);
19 void freepre(struct Pre *pre);
20 int buildast(const char *path, const struct Pre *pre, struct Ast *ast);
21-int eval(const struct Ast *ast, struct Ast *out);
22+int eval(const struct Ast *ast, struct RuleSet *out);
23 int parse(const char *path, const char *src, struct Ast *ast);
24 void freeast(struct Ast *ast);
25-int buildgraph(const struct Ast *ast, struct Graph *graph);
26+void freeruleset(struct RuleSet *ruleset);
27+int buildgraph(const struct RuleSet *ruleset, struct Graph *graph);
28 void freegraph(struct Graph *graph);
29 
30 #endif