commit 67e2bc0

shrub  ·  2026-04-17 20:54:08 +0000 UTC
parent 23ba302
implement call and eval
9 files changed,  +273, -73
+1, -2
 1@@ -50,8 +50,7 @@ static int
 2 issource(const char *path)
 3 {
 4 	static const char *const exts[] = {
 5-		".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm", 0
 6-	};
 7+	    ".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm", 0};
 8 	size_t i;
 9 
10 	for (i = 0; exts[i]; i++) {
+32, -32
 1@@ -257,38 +257,38 @@ main(int argc, char **argv)
 2 			usage(stdout, argv[0]);
 3 			return 0;
 4 		} else if (strcmp(argv[i], "-C") == 0) {
 5-		  if (i + 1 >= argc) {
 6-		    fprintf(stderr, "specify a directory\n\n");
 7-		    usage(stderr, argv[0]);
 8-		    return 1;
 9-		  }
10-		  ++i;
11+			if (i + 1 >= argc) {
12+				fprintf(stderr, "specify a directory\n\n");
13+				usage(stderr, argv[0]);
14+				return 1;
15+			}
16+			++i;
17 
18-		  if (chdir(argv[i]) != 0) {
19-		    fprintf(stderr, "failed to chdir to %s", argv[i]);
20-		    return 1;
21-		  }
22+			if (chdir(argv[i]) != 0) {
23+				fprintf(stderr, "failed to chdir to %s", argv[i]);
24+				return 1;
25+			}
26 		} else if (strcmp(argv[i], "-f") == 0) {
27-		  if (i + 1 >= argc) {
28-		    fprintf(stderr, "specify a file\n\n");
29-		    usage(stderr, argv[0]);
30-		    return 1;
31-		  }
32-		  ++i;
33-		  path = argv[i];
34+			if (i + 1 >= argc) {
35+				fprintf(stderr, "specify a file\n\n");
36+				usage(stderr, argv[0]);
37+				return 1;
38+			}
39+			++i;
40+			path = argv[i];
41 		} else if (isvarassign(argv[i])) {
42-		  assigns = realloc(assigns, (nassigns + 1) * sizeof(assigns[0]));
43-		  if (!assigns) {
44-		    fprintf(stderr, "out of memory\n");
45-		    return 1;
46-		  }
47-		  assigns[nassigns++] = argv[i];
48+			assigns = realloc(assigns, (nassigns + 1) * sizeof(assigns[0]));
49+			if (!assigns) {
50+				fprintf(stderr, "out of memory\n");
51+				return 1;
52+			}
53+			assigns[nassigns++] = argv[i];
54 		} else if (argv[i][0] == '-') {
55 			usage(stderr, argv[0]);
56 			return 1;
57 		} else {
58-		  usage(stderr, argv[0]);
59-		  return 1;
60+			usage(stderr, argv[0]);
61+			return 1;
62 		}
63 	}
64 	if (path) {
65@@ -299,10 +299,10 @@ main(int argc, char **argv)
66 		}
67 	} else {
68 		static const char *const defaults[] = {
69-			"GNUmakefile",
70-			"makefile",
71-			"Makefile",
72-			0,
73+		    "GNUmakefile",
74+		    "makefile",
75+		    "Makefile",
76+		    0,
77 		};
78 		size_t mk;
79 
80@@ -320,9 +320,9 @@ main(int argc, char **argv)
81 	}
82 	src = appendassigns(src, assigns, nassigns);
83 	if (!src) {
84-	  free(assigns);
85-	  fprintf(stderr, "out of memory\n");
86-	  return 1;
87+		free(assigns);
88+		fprintf(stderr, "out of memory\n");
89+		return 1;
90 	}
91 	if (parse(path, src, &ast) < 0) {
92 		fprintf(stderr, "parse error in %s\n", path);
+66, -23
  1@@ -232,6 +232,32 @@ expandvarref(struct EvalCtx *ctx, const char *s, size_t n)
  2 	char *name, *val;
  3 	struct Var *v;
  4 
  5+	if (n > 0 && ctx->call) {
  6+		size_t i;
  7+		int numeric;
  8+
  9+		numeric = 1;
 10+		for (i = 0; i < n; i++) {
 11+			if (!isdigit((unsigned char)s[i])) {
 12+				numeric = 0;
 13+				break;
 14+			}
 15+		}
 16+		if (numeric) {
 17+			unsigned long idx;
 18+			char *end, *tmp;
 19+
 20+			tmp = xstrndup(s, n);
 21+			idx = strtoul(tmp, &end, 10);
 22+			if (*end == 0 && idx < ctx->call->nargs) {
 23+				val = expandstr(ctx, ctx->call->args[idx]);
 24+				free(tmp);
 25+				return val;
 26+			}
 27+			free(tmp);
 28+		}
 29+	}
 30+
 31 	name = xstrndup(s, n);
 32 	v = findvar(ctx->env, name);
 33 	free(name);
 34@@ -275,38 +301,49 @@ expandsubstref(struct EvalCtx *ctx, const char *s, size_t colon, size_t eq, size
 35 typedef char *(*fn1_t)(const char *);
 36 typedef char *(*fn2_t)(const char *, const char *);
 37 typedef char *(*fn3_t)(const char *, const char *, const char *);
 38+typedef char *(*ctxfn_t)(struct EvalCtx *, const char *);
 39+
 40+enum FuncMode {
 41+	FNEXP1,
 42+	FNEXP2,
 43+	FNEXP3,
 44+	FNCTX,
 45+};
 46 
 47 struct func {
 48 	const char *name;
 49-	int arity;
 50+	enum FuncMode mode;
 51 	union {
 52 		fn1_t f1;
 53 		fn2_t f2;
 54 		fn3_t f3;
 55+		ctxfn_t ctx;
 56 	} fn;
 57 };
 58 
 59 static const struct func funcs[] = {
 60-	{"wildcard",   1, {.f1 = fnwildcard}},
 61-	{"shell",      1, {.f1 = fnshell}},
 62-	{"sort",       1, {.f1 = fnsort}},
 63-	{"info",       1, {.f1 = fninfo}},
 64-	{"notdir",     1, {.f1 = fnnotdir}},
 65-	{"dir",        1, {.f1 = fndir}},
 66-	{"basename",   1, {.f1 = fnbasename}},
 67-	{"filter-out", 2, {.f2 = fnfilterout}},
 68-	{"filter",     2, {.f2 = fnfilter}},
 69-	{"addprefix",  2, {.f2 = fnaddprefix}},
 70-	{"addsuffix",  2, {.f2 = fnaddsuffix}},
 71-	{"subst",      3, {.f3 = fnsubst}},
 72-	{"patsubst",   3, {.f3 = fnpatsubst}},
 73-	{"if",         3, {.f3 = fnif}},
 74-	{"words",      1, {.f1 = fnwords}},
 75-	{"word",       2, {.f2 = fnword}},
 76-	{"wordlist",   3, {.f3 = fnwordlist}},
 77-	{"firstword",  1, {.f1 = fnfirstword}},
 78-	{"lastword",   1, {.f1 = fnlastword}},
 79-	{0, 0, {.f1 = 0}},
 80+    {"wildcard", FNEXP1, {.f1 = fnwildcard}},
 81+    {"shell", FNEXP1, {.f1 = fnshell}},
 82+    {"sort", FNEXP1, {.f1 = fnsort}},
 83+    {"info", FNEXP1, {.f1 = fninfo}},
 84+    {"notdir", FNEXP1, {.f1 = fnnotdir}},
 85+    {"dir", FNEXP1, {.f1 = fndir}},
 86+    {"basename", FNEXP1, {.f1 = fnbasename}},
 87+    {"filter-out", FNEXP2, {.f2 = fnfilterout}},
 88+    {"filter", FNEXP2, {.f2 = fnfilter}},
 89+    {"addprefix", FNEXP2, {.f2 = fnaddprefix}},
 90+    {"addsuffix", FNEXP2, {.f2 = fnaddsuffix}},
 91+    {"subst", FNEXP3, {.f3 = fnsubst}},
 92+    {"patsubst", FNEXP3, {.f3 = fnpatsubst}},
 93+    {"if", FNEXP3, {.f3 = fnif}},
 94+    {"call", FNCTX, {.ctx = fncall}},
 95+    {"eval", FNCTX, {.ctx = fneval}},
 96+    {"words", FNEXP1, {.f1 = fnwords}},
 97+    {"word", FNEXP2, {.f2 = fnword}},
 98+    {"wordlist", FNEXP3, {.f3 = fnwordlist}},
 99+    {"firstword", FNEXP1, {.f1 = fnfirstword}},
100+    {"lastword", FNEXP1, {.f1 = fnlastword}},
101+    {0, FNEXP1, {.f1 = 0}},
102 };
103 
104 static char *
105@@ -324,7 +361,13 @@ funcref(struct EvalCtx *ctx, const char *s, size_t n)
106 		start = namelen;
107 		while (start < n && isspace((unsigned char)s[start]))
108 			start++;
109-		if (f->arity == 1) {
110+		if (f->mode == FNCTX) {
111+			char *args;
112+
113+			args = xstrndup(s + start, n - start);
114+			val = f->fn.ctx(ctx, args);
115+			free(args);
116+		} else if (f->mode == FNEXP1) {
117 			char *raw, *exp;
118 
119 			raw = xstrndup(s + start, n - start);
120@@ -332,7 +375,7 @@ funcref(struct EvalCtx *ctx, const char *s, size_t n)
121 			free(raw);
122 			val = f->fn.f1(exp);
123 			free(exp);
124-		} else if (f->arity == 2) {
125+		} else if (f->mode == FNEXP2) {
126 			char *args, *lhs_raw, *rhs_raw, *lhs_exp, *rhs_exp, *detail;
127 			ptrdiff_t comma;
128 
+14, -0
 1@@ -235,6 +235,19 @@ evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
 2 	return 0;
 3 }
 4 
 5+int
 6+evalsnippet(struct EvalCtx *ctx, const char *path, const char *src)
 7+{
 8+	struct Ast ast;
 9+	int rc;
10+
11+	if (parse(path, src, &ast) < 0)
12+		return -1;
13+	rc = evalnodes((const struct NodeList *)&ast, ctx->out, ctx);
14+	freeast(&ast);
15+	return rc;
16+}
17+
18 int
19 eval(const struct Ast *ast, struct RuleSet *out)
20 {
21@@ -247,6 +260,7 @@ eval(const struct Ast *ast, struct RuleSet *out)
22 	memset(&ctx, 0, sizeof(ctx));
23 	seedenv(&env);
24 	ctx.env = &env;
25+	ctx.out = out;
26 	rc = evalnodes((const struct NodeList *)ast, out, &ctx);
27 	freeenv(&env);
28 	if (rc == 0 && ctx.errors)
+127, -0
  1@@ -835,3 +835,130 @@ fnlastword(const char *list)
  2 	}
  3 	return last ? xstrndup(last, lastlen) : xstrdup("");
  4 }
  5+
  6+static char **
  7+splitargsraw(const char *s, size_t *out_n)
  8+{
  9+	size_t i, start, depth, argc, n;
 10+	char **argv;
 11+
 12+	while (*s && isspace((unsigned char)*s))
 13+		s++;
 14+	n = strlen(s);
 15+	argv = 0;
 16+	argc = 0;
 17+	start = 0;
 18+	depth = 0;
 19+	for (i = 0; i < n; i++) {
 20+		if (s[i] == '$' && i + 1 < n && (s[i + 1] == '(' || s[i + 1] == '{')) {
 21+			depth++;
 22+			i++;
 23+			continue;
 24+		}
 25+		if ((s[i] == ')' || s[i] == '}') && depth > 0) {
 26+			depth--;
 27+			continue;
 28+		}
 29+		if (s[i] != ',' || depth != 0)
 30+			continue;
 31+		argv = xrealloc(argv, (argc + 1) * sizeof(argv[0]));
 32+		argv[argc++] = xstrndup(s + start, i - start);
 33+		start = i + 1;
 34+	}
 35+	argv = xrealloc(argv, (argc + 1) * sizeof(argv[0]));
 36+	argv[argc++] = xstrndup(s + start, n - start);
 37+	*out_n = argc;
 38+	return argv;
 39+}
 40+
 41+static void
 42+freeargsraw(char **argv, size_t argc)
 43+{
 44+	size_t i;
 45+
 46+	for (i = 0; i < argc; i++)
 47+		free(argv[i]);
 48+	free(argv);
 49+}
 50+
 51+char *
 52+fncall(struct EvalCtx *ctx, const char *args)
 53+{
 54+	char **raw, **exp;
 55+	char *name, *val;
 56+	size_t argc, i;
 57+	struct Var *v;
 58+	struct CallFrame frame;
 59+
 60+	raw = splitargsraw(args, &argc);
 61+	if (argc == 0) {
 62+		freeargsraw(raw, argc);
 63+		return xstrdup("");
 64+	}
 65+
 66+	name = expandstr(ctx, raw[0]);
 67+	exp = xrealloc(0, argc * sizeof(exp[0]));
 68+	exp[0] = xstrdup(name);
 69+	for (i = 1; i < argc; i++)
 70+		exp[i] = expandstr(ctx, raw[i]);
 71+	freeargsraw(raw, argc);
 72+
 73+	memset(&frame, 0, sizeof(frame));
 74+	frame.args = exp;
 75+	frame.nargs = argc;
 76+	frame.next = ctx->call;
 77+	ctx->call = &frame;
 78+
 79+	v = findvar(ctx->env, name);
 80+	if (v) {
 81+		val = expandstr(ctx, v->val);
 82+	} else {
 83+		size_t need, pos;
 84+		char *inv;
 85+
 86+		need = 2 + strlen(name) + 2;
 87+		for (i = 1; i < argc; i++)
 88+			need += strlen(exp[i]) + 1;
 89+		inv = xmalloc(need);
 90+		pos = 0;
 91+		inv[pos++] = '$';
 92+		inv[pos++] = '(';
 93+		memcpy(inv + pos, name, strlen(name));
 94+		pos += strlen(name);
 95+		if (argc > 1)
 96+			inv[pos++] = ' ';
 97+		for (i = 1; i < argc; i++) {
 98+			size_t len = strlen(exp[i]);
 99+
100+			memcpy(inv + pos, exp[i], len);
101+			pos += len;
102+			if (i + 1 < argc)
103+				inv[pos++] = ',';
104+		}
105+		inv[pos++] = ')';
106+		inv[pos] = 0;
107+		val = expandstr(ctx, inv);
108+		free(inv);
109+	}
110+	ctx->call = frame.next;
111+
112+	for (i = 0; i < argc; i++)
113+		free(exp[i]);
114+	free(exp);
115+	free(name);
116+	return val;
117+}
118+
119+char *
120+fneval(struct EvalCtx *ctx, const char *args)
121+{
122+	char *exp;
123+
124+	while (*args && isspace((unsigned char)*args))
125+		args++;
126+	exp = expandstr(ctx, args);
127+	if (evalsnippet(ctx, "*eval*", exp) < 0)
128+		ctx->errors++;
129+	free(exp);
130+	return xstrdup("");
131+}
+12, -12
 1@@ -289,19 +289,19 @@ buildgraph(const struct RuleSet *ruleset, struct Graph *graph)
 2 					ctx.errors += targetctx.errors;
 3 			}
 4 			nprereqs = t->prereqs.n;
 5-				for (j = 0; j < nprereqs; j++) {
 6-					const char *prereq = t->prereqs.v[j];
 7-
 8-					if (!findtarget(graph, prereq)) {
 9-						struct Target *nt;
10-
11-						graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
12-						nt = &graph->v[graph->n++];
13-						memset(nt, 0, sizeof(*nt));
14-						nt->name = xstrdup(prereq);
15-						t = &graph->v[i];
16-					}
17+			for (j = 0; j < nprereqs; j++) {
18+				const char *prereq = t->prereqs.v[j];
19+
20+				if (!findtarget(graph, prereq)) {
21+					struct Target *nt;
22+
23+					graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
24+					nt = &graph->v[graph->n++];
25+					memset(nt, 0, sizeof(*nt));
26+					nt->name = xstrdup(prereq);
27+					t = &graph->v[i];
28 				}
29+			}
30 		}
31 		start = current_n;
32 	}
+12, -0
 1@@ -15,8 +15,16 @@ struct Env {
 2 	size_t n;
 3 };
 4 
 5+struct CallFrame {
 6+	char **args;
 7+	size_t nargs;
 8+	struct CallFrame *next;
 9+};
10+
11 struct EvalCtx {
12 	struct Env *env;
13+	struct RuleSet *out;
14+	struct CallFrame *call;
15 	int errors;
16 };
17 
18@@ -63,4 +71,8 @@ char *fnwordlist(const char *s, const char *e, const char *list);
19 char *fnfirstword(const char *list);
20 char *fnlastword(const char *list);
21 
22+char *fncall(struct EvalCtx *ctx, const char *args);
23+char *fneval(struct EvalCtx *ctx, const char *args);
24+int evalsnippet(struct EvalCtx *ctx, const char *path, const char *src);
25+
26 #endif
+6, -1
 1@@ -296,7 +296,12 @@ imprules(struct SufRules *rules)
 2 	    "mv lex.yy.c $@",
 3 	};
 4 	static const char *const suffixes[] = {
 5-	    ".o", ".c", ".f", ".y", ".l", ".sh",
 6+	    ".o",
 7+	    ".c",
 8+	    ".f",
 9+	    ".y",
10+	    ".l",
11+	    ".sh",
12 	};
13 	struct StrList list;
14 
+3, -3
 1@@ -37,9 +37,9 @@ void seedenv(struct Env *env);
 2 void imprules(struct SufRules *rules);
 3 int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
 4 int instsufrule(const struct SufRules *rules,
 5-            const struct Graph *graph,
 6-            struct Target *t,
 7-            struct EvalCtx *ctx);
 8+                const struct Graph *graph,
 9+                struct Target *t,
10+                struct EvalCtx *ctx);
11 int issufrule(const struct RuleNode *rule);
12 void addsufs(struct SuffixList *list, const struct StrList *sufs);
13 void clearsufs(struct SuffixList *list);