commit 757e0ed
shrub
·
2026-04-08 16:58:02 +0000 UTC
parent 33a080f
fail-fast and add wildcard support
6 files changed,
+206,
-5
M
Makefile
+1,
-1
1@@ -1,5 +1,5 @@
2 BIN = shin
3-SRCS = cli/main.c src/parse.c src/eval.c src/graph.c backends/ninja.c src/util.c
4+SRCS = cli/main.c src/parse.c src/eval.c src/graph.c backends/ninja.c src/util.c src/functions.c
5 FMTSRCS = $(SRCS) src/shinobi.h src/internal.h backends/ninja.h
6 OBJS = $(SRCS:.c=.o)
7
+35,
-0
1@@ -0,0 +1,35 @@
2+build all: phony shin
3+
4+rule r1
5+ command = clang -static -o $out $in
6+
7+build shin: r1 cli/main.o src/parse.o src/eval.o src/graph.o backends/ninja.o src/util.o src/functions.o
8+
9+rule r2
10+ command = clang -O2 -Wall -Wextra -pedantic -Isrc -Ibackends -c -o $out $in
11+
12+build cli/main.o: r2 cli/main.c
13+
14+build src/parse.o: r2 src/parse.c
15+
16+build src/eval.o: r2 src/eval.c
17+
18+build src/graph.o: r2 src/graph.c
19+
20+build backends/ninja.o: r2 backends/ninja.c
21+
22+build src/util.o: r2 src/util.c
23+
24+build src/functions.o: r2 src/functions.c
25+
26+rule r3
27+ command = clang-format -i cli/main.c src/parse.c src/eval.c src/graph.c backends/ninja.c src/util.c src/functions.c src/shinobi.h src/internal.h backends/ninja.h
28+
29+build fmt: r3
30+
31+rule r4
32+ command = rm -f shin cli/main.o src/parse.o src/eval.o src/graph.o backends/ninja.o src/util.o src/functions.o
33+
34+build clean: r4
35+
36+default all
+43,
-1
1@@ -28,6 +28,18 @@ struct Env {
2 size_t n;
3 };
4
5+static int evalerrs;
6+
7+static void
8+evalerr(const char *msg, const char *detail)
9+{
10+ if (detail)
11+ fprintf(stderr, "eval: unsupported: %s: %s\n", msg, detail);
12+ else
13+ fprintf(stderr, "eval: unsupported: %s\n", msg);
14+ evalerrs++;
15+}
16+
17 static struct Var *
18 findvar(struct Env *env, const char *name)
19 {
20@@ -247,7 +259,32 @@ expandstr(struct Env *env, const char *s)
21 memcpy(out + len, val, k);
22 len += k;
23 free(val);
24+ } else if (j - i - 3 >= 10 &&
25+ memcmp(s + i + 2, "wildcard", 8) == 0 &&
26+ isspace((unsigned char)s[i + 10])) {
27+ char *pat_raw, *pat_exp, *val;
28+ size_t pstart;
29+
30+ pstart = i + 11;
31+ pat_raw = xstrndup(s + pstart, j - 1 - pstart);
32+ pat_exp = expandstr(env, pat_raw);
33+ free(pat_raw);
34+ val = fnwildcard(pat_exp);
35+ free(pat_exp);
36+ k = strlen(val);
37+ if (len + k + 1 > cap) {
38+ cap = len + k + 1;
39+ out = xrealloc(out, cap);
40+ }
41+ memcpy(out + len, val, k);
42+ len += k;
43+ free(val);
44 } else {
45+ char *unsup;
46+
47+ unsup = xstrndup(s + start, j - start);
48+ evalerr("variable reference or function", unsup);
49+ free(unsup);
50 k = j - start;
51 if (len + k + 1 > cap) {
52 cap = len + k + 1;
53@@ -316,8 +353,10 @@ evalassign(struct Env *env, const struct AssignNode *in)
54 struct Var *v;
55 char *rhs, *joined;
56
57- if (in->tspec)
58+ if (in->tspec) {
59+ evalerr("target-specific assignment", in->lhs);
60 return;
61+ }
62 switch (in->op) {
63 case ASSIGN_EQ:
64 setvar(env, in->lhs, xstrdup(in->rhs), 0);
65@@ -464,8 +503,11 @@ eval(const struct Ast *ast, struct Ast *out)
66
67 memset(&env, 0, sizeof(env));
68 seedenv(&env);
69+ evalerrs = 0;
70 rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
71 freeenv(&env);
72+ if (rc == 0 && evalerrs)
73+ return -1;
74 return rc;
75 }
76
+64,
-0
1@@ -0,0 +1,64 @@
2+#include "internal.h"
3+
4+#include <ctype.h>
5+#include <glob.h>
6+#include <stddef.h>
7+#include <stdlib.h>
8+#include <string.h>
9+
10+/* implementations of gnu make builtin functions*/
11+
12+char *
13+fnwildcard(const char *patterns)
14+{
15+ char *out;
16+ size_t len, cap;
17+ const char *p;
18+
19+ cap = 64;
20+ len = 0;
21+ out = xmalloc(cap);
22+ out[0] = 0;
23+
24+ p = patterns;
25+ while (*p) {
26+ glob_t g;
27+ size_t i;
28+ const char *start;
29+ char *pat;
30+ int rc;
31+
32+ while (*p && isspace((unsigned char)*p))
33+ p++;
34+ if (!*p)
35+ break;
36+ start = p;
37+ while (*p && !isspace((unsigned char)*p))
38+ p++;
39+ pat = xstrndup(start, (size_t)(p - start));
40+
41+ memset(&g, 0, sizeof(g));
42+ rc = glob(pat, 0, 0, &g);
43+ free(pat);
44+ if (rc == 0) {
45+ for (i = 0; i < g.gl_pathc; i++) {
46+ size_t plen, need;
47+
48+ plen = strlen(g.gl_pathv[i]);
49+ need = len + plen + 2;
50+ if (need > cap) {
51+ while (cap < need)
52+ cap *= 2;
53+ out = xrealloc(out, cap);
54+ }
55+ if (len)
56+ out[len++] = ' ';
57+ memcpy(out + len, g.gl_pathv[i], plen);
58+ len += plen;
59+ out[len] = 0;
60+ }
61+ }
62+ globfree(&g);
63+ }
64+ return out;
65+}
+2,
-0
1@@ -13,4 +13,6 @@ char *cat3(const char *a, const char *b, const char *c);
2 void freestrs(struct StrList *list);
3 void freerecipes(struct RecipeList *list);
4
5+char *fnwildcard(const char *patterns);
6+
7 #endif
+61,
-3
1@@ -29,6 +29,25 @@ struct Inc {
2 size_t n;
3 };
4
5+static int parseerrs;
6+
7+static void
8+parseerr(const struct PreLine *line, const char *msg, const char *detail)
9+{
10+ if (detail)
11+ fprintf(stderr, "%s:%d: unsupported: %s: %s\n",
12+ line->path, line->line0, msg, detail);
13+ else
14+ fprintf(stderr, "%s:%d: unsupported: %s\n",
15+ line->path, line->line0, msg);
16+ parseerrs++;
17+}
18+
19+static const char *const unsupported_kws[] = {
20+ "define", "endef", "override", "export", "unexport",
21+ "undefine", "vpath", "private", "load", 0
22+};
23+
24 static char *
25 trimdup(const char *s, size_t n)
26 {
27@@ -548,8 +567,14 @@ parsecond(const struct PreLine *line, const char *s)
28 free(state.data.cond.arg2);
29 state.data.cond.arg1 = trimdup(p + 1, mid);
30 state.data.cond.arg2 = trimdup(p + 2 + mid, n - 3 - mid);
31+ } else {
32+ parseerr(line, "malformed ifeq/ifneq arguments", p);
33 }
34 }
35+ } else {
36+ parseerr(line, "malformed ifeq/ifneq arguments", p);
37+ state.data.cond.arg1 = xstrndup("", 0);
38+ state.data.cond.arg2 = xstrndup("", 0);
39 }
40 return state;
41 }
42@@ -598,17 +623,29 @@ parserule(const struct PreLine *line, const char *s, size_t n, size_t colon)
43 return state;
44 }
45
46-/* unclassified line */
47+/* unclassified line, treat as unsupported syntax */
48 static struct Node
49 parseraw(const struct PreLine *line, const char *s)
50 {
51 struct Node state;
52
53+ parseerr(line, "unrecognized line", s);
54 memset(&state, 0, sizeof(state));
55- state.kind = NODE_RAW;
56+ state.kind = NODE_BLANK;
57+ state.loc.line0 = line->line0;
58+ state.loc.line1 = line->line1;
59+ return state;
60+}
61+
62+static struct Node
63+blanknode(const struct PreLine *line)
64+{
65+ struct Node state;
66+
67+ memset(&state, 0, sizeof(state));
68+ state.kind = NODE_BLANK;
69 state.loc.line0 = line->line0;
70 state.loc.line1 = line->line1;
71- state.data.raw.text = xstrndup(s, strlen(s));
72 return state;
73 }
74
75@@ -652,6 +689,18 @@ parseline(const struct PreLine *line)
76 free(trim);
77 return state;
78 }
79+ {
80+ size_t k;
81+
82+ for (k = 0; unsupported_kws[k]; k++) {
83+ if (haskw(trim, unsupported_kws[k])) {
84+ parseerr(line, "directive", unsupported_kws[k]);
85+ state = blanknode(line);
86+ free(trim);
87+ return state;
88+ }
89+ }
90+ }
91
92 colon = findtop(trim, n, ':');
93 as = findassign(trim, n, 0);
94@@ -660,6 +709,12 @@ parseline(const struct PreLine *line)
95 free(trim);
96 return state;
97 }
98+ if (colon >= 0 && (size_t)colon + 1 < n && trim[colon + 1] == ':') {
99+ parseerr(line, "double-colon rule", trim);
100+ state = blanknode(line);
101+ free(trim);
102+ return state;
103+ }
104 if (colon >= 0) {
105 state = parserule(line, trim, n, (size_t)colon);
106 free(trim);
107@@ -761,9 +816,12 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
108
109 (void)path;
110 memset(ast, 0, sizeof(*ast));
111+ parseerrs = 0;
112 i = 0;
113 if (parseblock(pre, &i, (struct NodeList *)ast) < 0)
114 return -1;
115+ if (parseerrs)
116+ return -1;
117
118 return 0;
119 }