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);