commit b427801
shrub
·
2026-04-07 17:31:51 +0000 UTC
parent 133667f
feat: eval pass with simple var expansion
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