commit 757e0ed

shrub  ·  2026-04-08 16:58:02 +0000 UTC
parent 33a080f
fail-fast and add wildcard support
6 files changed,  +206, -5
+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 }