commit b427801

shrub  ·  2026-04-07 17:31:51 +0000 UTC
parent 133667f
feat: eval pass with simple var expansion
3 files changed,  +292, -1
M main.c
M main.c
+9, -1
 1@@ -149,6 +149,7 @@ main(int argc, char **argv)
 2 	const char *path;
 3 	char *src;
 4 	struct Ast ast;
 5+	struct Ast out;
 6 
 7 	path = argc > 1 ? argv[1] : "Makefile";
 8 	src = readfile(path);
 9@@ -161,7 +162,14 @@ main(int argc, char **argv)
10 		free(src);
11 		return 1;
12 	}
13-	dump(&ast);
14+	if (eval(&ast, &out) < 0) {
15+		fprintf(stderr, "eval error in %s\n", path);
16+		freeast(&ast);
17+		free(src);
18+		return 1;
19+	}
20+	dump(&out);
21+	freeast(&out);
22 	freeast(&ast);
23 	free(src);
24 	return 0;
+282, -0
  1@@ -18,6 +18,17 @@ struct Inc {
  2 	size_t n;
  3 };
  4 
  5+struct Var {
  6+	char *name;
  7+	char *val;
  8+	int simple;
  9+};
 10+
 11+struct Env {
 12+	struct Var *v;
 13+	size_t n;
 14+};
 15+
 16 static void *
 17 xmalloc(size_t n)
 18 {
 19@@ -752,6 +763,277 @@ parse(const char *path, const char *src, struct Ast *ast)
 20 	return rc;
 21 }
 22 
 23+static struct Var *
 24+findvar(struct Env *env, const char *name)
 25+{
 26+	size_t i;
 27+
 28+	for (i = 0; i < env->n; i++) {
 29+		if (strcmp(env->v[i].name, name) == 0)
 30+			return &env->v[i];
 31+	}
 32+	return 0;
 33+}
 34+
 35+static char *
 36+cat2(const char *a, const char *b)
 37+{
 38+	size_t na, nb;
 39+	char *s;
 40+
 41+	na = strlen(a);
 42+	nb = strlen(b);
 43+	s = xmalloc(na + nb + 1);
 44+	memcpy(s, a, na);
 45+	memcpy(s + na, b, nb);
 46+	s[na + nb] = 0;
 47+	return s;
 48+}
 49+
 50+static int
 51+isplainvar(const char *s, size_t n)
 52+{
 53+	size_t i;
 54+
 55+	if (!n)
 56+		return 0;
 57+	for (i = 0; i < n; i++) {
 58+		if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
 59+			return 0;
 60+	}
 61+	return 1;
 62+}
 63+
 64+static char *
 65+expandstr(struct Env *env, const char *s)
 66+{
 67+	size_t i, j, k, n, cap, len, inner, start;
 68+	char close;
 69+	char *out, *name, *val;
 70+	struct Var *v;
 71+
 72+	n = strlen(s);
 73+	cap = n + 1;
 74+	len = 0;
 75+	out = xmalloc(cap);
 76+	for (i = 0; i < n; i++) {
 77+		if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
 78+			if (len + 2 > cap) {
 79+				cap *= 2;
 80+				out = xrealloc(out, cap);
 81+			}
 82+			out[len++] = s[i];
 83+			continue;
 84+		}
 85+		close = s[i + 1] == '(' ? ')' : '}';
 86+		start = i;
 87+		j = i + 2;
 88+		inner = 1;
 89+		while (j < n && inner) {
 90+			if (s[j] == '$' && j + 1 < n && (s[j + 1] == '(' || s[j + 1] == '{')) {
 91+				inner++;
 92+				j += 2;
 93+				continue;
 94+			}
 95+			if (s[j] == close)
 96+				inner--;
 97+			j++;
 98+		}
 99+		if (inner) {
100+			if (len + (n - start) + 1 > cap) {
101+				cap = len + (n - start) + 1;
102+				out = xrealloc(out, cap);
103+			}
104+			memcpy(out + len, s + start, n - start);
105+			len += n - start;
106+			break;
107+		}
108+		if (isplainvar(s + i + 2, j - i - 3)) {
109+			name = xstrndup(s + i + 2, j - i - 3);
110+			v = findvar(env, name);
111+			free(name);
112+			val = v ? expandstr(env, v->val) : xstrdup("");
113+			k = strlen(val);
114+			if (len + k + 1 > cap) {
115+				cap = len + k + 1;
116+				out = xrealloc(out, cap);
117+			}
118+			memcpy(out + len, val, k);
119+			len += k;
120+			free(val);
121+		} else {
122+			k = j - start;
123+			if (len + k + 1 > cap) {
124+				cap = len + k + 1;
125+				out = xrealloc(out, cap);
126+			}
127+			memcpy(out + len, s + start, k);
128+			len += k;
129+		}
130+		i = j - 1;
131+	}
132+	out[len] = 0;
133+	return out;
134+}
135+
136+static void
137+setvar(struct Env *env, const char *name, char *val, int simple)
138+{
139+	struct Var *v;
140+
141+	v = findvar(env, name);
142+	if (v) {
143+		free(v->val);
144+		v->val = val;
145+		v->simple = simple;
146+		return;
147+	}
148+	env->v = xrealloc(env->v, (env->n + 1) * sizeof(env->v[0]));
149+	env->v[env->n].name = xstrdup(name);
150+	env->v[env->n].val = val;
151+	env->v[env->n].simple = simple;
152+	env->n++;
153+}
154+
155+static void
156+seedenv(struct Env *env)
157+{
158+	setvar(env, "CC", xstrdup("cc"), 1);
159+	setvar(env, "CXX", xstrdup("c++"), 1);
160+	setvar(env, "CPP", xstrdup("$(CC) -E"), 0);
161+	setvar(env, "AR", xstrdup("ar"), 1);
162+	setvar(env, "ARFLAGS", xstrdup("-rv"), 1);
163+	setvar(env, "AS", xstrdup("as"), 1);
164+	setvar(env, "LD", xstrdup("ld"), 1);
165+	setvar(env, "LEX", xstrdup("lex"), 1);
166+	setvar(env, "YACC", xstrdup("yacc"), 1);
167+	setvar(env, "SHELL", xstrdup("/bin/sh"), 1);
168+}
169+
170+static void
171+evalassign(struct Env *env, const struct AssignNode *in)
172+{
173+	struct Var *v;
174+	char *rhs, *joined;
175+
176+	if (in->tspec)
177+		return;
178+	switch (in->op) {
179+	case ASSIGN_EQ:
180+		setvar(env, in->lhs, xstrdup(in->rhs), 0);
181+		break;
182+	case ASSIGN_COLON_EQ:
183+		setvar(env, in->lhs, expandstr(env, in->rhs), 1);
184+		break;
185+	case ASSIGN_QMARK_EQ:
186+		if (!findvar(env, in->lhs))
187+			setvar(env, in->lhs, xstrdup(in->rhs), 0);
188+		break;
189+	case ASSIGN_PLUS_EQ:
190+		v = findvar(env, in->lhs);
191+		if (!v) {
192+			setvar(env, in->lhs, xstrdup(in->rhs), 0);
193+			break;
194+		}
195+		rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
196+		joined = cat2(v->val, rhs);
197+		free(rhs);
198+		free(v->val);
199+		v->val = joined;
200+		break;
201+	case ASSIGN_BANG_EQ:
202+		setvar(env, in->lhs, expandstr(env, in->rhs), 1);
203+		break;
204+	}
205+}
206+
207+static void
208+copywords(struct StrList *out, const struct StrList *in, struct Env *env)
209+{
210+	size_t i;
211+
212+	memset(out, 0, sizeof(*out));
213+	for (i = 0; i < in->n; i++) {
214+		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
215+		out->v[out->n++] = expandstr(env, in->v[i]);
216+	}
217+}
218+
219+static void
220+copyrecipes(struct RecipeList *out, const struct RecipeList *in, struct Env *env)
221+{
222+	size_t i;
223+
224+	memset(out, 0, sizeof(*out));
225+	for (i = 0; i < in->n; i++) {
226+		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
227+		out->v[out->n++] = expandstr(env, in->v[i]);
228+	}
229+}
230+
231+int
232+eval(const struct Ast *ast, struct Ast *out)
233+{
234+	struct Env env;
235+	size_t i;
236+
237+	memset(out, 0, sizeof(*out));
238+	memset(&env, 0, sizeof(env));
239+	seedenv(&env);
240+	for (i = 0; i < ast->n; i++) {
241+		const struct Node *in;
242+		struct Node node;
243+
244+		in = &ast->v[i];
245+		memset(&node, 0, sizeof(node));
246+		node.kind = in->kind;
247+		node.loc = in->loc;
248+		switch (in->kind) {
249+		case NODE_BLANK:
250+			break;
251+		case NODE_COMMENT:
252+		case NODE_RAW:
253+			node.data.raw.text = xstrdup(in->data.raw.text);
254+			break;
255+		case NODE_INCLUDE:
256+			node.data.include.optional = in->data.include.optional;
257+			node.data.include.path = expandstr(&env, in->data.include.path);
258+			break;
259+		case NODE_COND:
260+			node.data.cond.kind = in->data.cond.kind;
261+			node.data.cond.arg1 = in->data.cond.arg1 ? expandstr(&env, in->data.cond.arg1) : 0;
262+			node.data.cond.arg2 = in->data.cond.arg2 ? expandstr(&env, in->data.cond.arg2) : 0;
263+			node.data.cond.raw = in->data.cond.raw ? expandstr(&env, in->data.cond.raw) : 0;
264+			break;
265+		case NODE_ASSIGN:
266+			node.data.assign.lhs = xstrdup(in->data.assign.lhs);
267+			node.data.assign.op = in->data.assign.op;
268+			node.data.assign.tspec = in->data.assign.tspec;
269+			if (in->data.assign.op == ASSIGN_COLON_EQ || in->data.assign.op == ASSIGN_BANG_EQ)
270+				node.data.assign.rhs = expandstr(&env, in->data.assign.rhs);
271+			else
272+				node.data.assign.rhs = xstrdup(in->data.assign.rhs);
273+			copywords(&node.data.assign.targets, &in->data.assign.targets, &env);
274+			evalassign(&env, &in->data.assign);
275+			break;
276+		case NODE_RULE:
277+			copywords(&node.data.rule.targets, &in->data.rule.targets, &env);
278+			copywords(&node.data.rule.prereqs, &in->data.rule.prereqs, &env);
279+			copywords(&node.data.rule.order_only, &in->data.rule.order_only, &env);
280+			copyrecipes(&node.data.rule.recipes, &in->data.rule.recipes, &env);
281+			break;
282+		}
283+		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
284+		out->v[out->n++] = node;
285+	}
286+	for (i = 0; i < env.n; i++) {
287+		free(env.v[i].name);
288+		free(env.v[i].val);
289+	}
290+	free(env.v);
291+	return 0;
292+}
293+
294 static void
295 freestrs(struct StrList *list)
296 {
+1, -0
1@@ -115,6 +115,7 @@ struct Ast {
2 int preproc(const char *path, struct Pre *pre);
3 void freepre(struct Pre *pre);
4 int buildast(const char *path, const struct Pre *pre, struct Ast *ast);
5+int eval(const struct Ast *ast, struct Ast *out);
6 int parse(const char *path, const char *src, struct Ast *ast);
7 void freeast(struct Ast *ast);
8