[master] f279df5 Add support for optional arguments to VMOD functions.

Poul-Henning Kamp phk at FreeBSD.org
Fri Mar 2 14:16:08 UTC 2018


commit f279df5536424d97a24ee1c9839909a90184c430
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Fri Mar 2 14:14:09 2018 +0000

    Add support for optional arguments to VMOD functions.

diff --git a/lib/libvcc/vcc_expr.c b/lib/libvcc/vcc_expr.c
index 2191d4b..ddb1376 100644
--- a/lib/libvcc/vcc_expr.c
+++ b/lib/libvcc/vcc_expr.c
@@ -406,6 +406,8 @@ struct func_arg {
 	const char		*name;
 	const char		*val;
 	struct expr		*result;
+	int			avail;
+	int			optional;
 	VTAILQ_ENTRY(func_arg)	list;
 };
 
@@ -449,6 +451,7 @@ vcc_do_arg(struct vcc *tl, struct func_arg *fa)
 		assert(e2->fmt == fa->type);
 		fa->result = e2;
 	}
+	fa->avail = 1;
 }
 
 static void
@@ -462,6 +465,9 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv,
 	VTAILQ_HEAD(,func_arg) head;
 	struct token *t1;
 	const struct vjsn_val *vv, *vvp;
+	const char *sa;
+	char ssa[64];
+	char ssa2[64];
 
 	CAST_OBJ_NOTNULL(vv, priv, VJSN_VAL_MAGIC);
 	assert(vv->type == VJSN_ARRAY);
@@ -471,6 +477,15 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv,
 	vv = VTAILQ_NEXT(vv, list);
 	cfunc = vv->value;
 	vv = VTAILQ_NEXT(vv, list);
+	sa = vv->value;
+	if (*sa == '\0') {
+		sa = NULL;
+	} else {
+		bprintf(ssa, "args_%u", tl->unique++);
+		VSB_printf(tl->curproc->prologue, "  %s %s;\n", sa, ssa);
+		sa = ssa;
+	}
+	vv = VTAILQ_NEXT(vv, list);
 	SkipToken(tl, '(');
 	if (extra == NULL)
 		extra = "";
@@ -504,6 +519,10 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv,
 				}
 			}
 		}
+		if (sa != NULL && vvp != NULL && vvp->type == VJSN_TRUE) {
+			fa->optional = 1;
+			vvp = VTAILQ_NEXT(vvp, list);
+		}
 		AZ(vvp);
 	}
 
@@ -551,23 +570,37 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv,
 		SkipToken(tl, ',');
 	}
 
-	e1 = vcc_mk_expr(rfmt, "%s(ctx%s\v+", cfunc, extra);
+	if (sa != NULL)
+		e1 = vcc_mk_expr(rfmt, "%s(ctx%s,\v+(\n", cfunc, extra);
+	else
+		e1 = vcc_mk_expr(rfmt, "%s(ctx%s\v+", cfunc, extra);
 	VTAILQ_FOREACH_SAFE(fa, &head, list, fa2) {
+		if (fa->optional)
+			VSB_printf(tl->curproc->prologue,
+			    "  %s.valid_%s = %d;\n", sa, fa->name, fa->avail);
 		if (fa->result == NULL && fa->type == ENUM && fa->val != NULL)
 			vcc_do_enum(tl, fa, strlen(fa->val), fa->val);
 		if (fa->result == NULL && fa->val != NULL)
 			fa->result = vcc_mk_expr(fa->type, "%s", fa->val);
-		if (fa->result != NULL)
+		if (fa->result != NULL && sa != NULL) {
+			bprintf(ssa2, "\v1%s.%s = \v2,\n", sa, fa->name);
+			e1 = vcc_expr_edit(tl, e1->fmt, ssa2, e1, fa->result);
+		} else if (fa->result != NULL) {
 			e1 = vcc_expr_edit(tl, e1->fmt, "\v1,\n\v2",
 			    e1, fa->result);
-		else {
+		} else if (!fa->optional) {
 			VSB_printf(tl->sb, "Argument '%s' missing\n",
 			    fa->name);
 			vcc_ErrWhere(tl, tl->t);
 		}
 		free(fa);
 	}
-	*e = vcc_expr_edit(tl, e1->fmt, "\v1\n)\v-", e1, NULL);
+	if (sa != NULL) {
+		bprintf(ssa2, "\v1&%s\v-\n))", sa);
+		*e = vcc_expr_edit(tl, e1->fmt, ssa2, e1, NULL);
+	} else {
+		*e = vcc_expr_edit(tl, e1->fmt, "\v1\n)\v-", e1, NULL);
+	}
 	SkipToken(tl, ')');
 }
 
diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py
index aee9756..853abb7 100755
--- a/lib/libvcc/vmodtool.py
+++ b/lib/libvcc/vmodtool.py
@@ -199,6 +199,7 @@ class ctype(object):
         self.nm = None
         self.defval = None
         self.spec = None
+        self.opt = False
 
         self.vt = wl.pop(0)
         self.ct = ctypes.get(self.vt)
@@ -287,6 +288,8 @@ class arg(ctype):
 
     def json(self, jl):
         jl.append([self.vt, self.nm, self.defval, self.spec])
+        if self.opt:
+                jl[-1].append(True)
         while jl[-1][-1] is None:
             jl[-1].pop(-1)
 
@@ -313,7 +316,7 @@ def lex(l):
         if s == 0 and c in (' ', '\t', '\n', '\r'):
             continue
 
-        if s == 0 and c in ('(', '{', '}', ')', ',', '='):
+        if s == 0 and c in ('[', '(', '{', '}', ')', ']', ',', '='):
             wl.append(c)
         elif s == 0 and c in ('"', "'"):
             sep = c
@@ -330,10 +333,10 @@ def lex(l):
             wl[-1] += c
             s = 1
         else:
-            err("Syntax error at char", i, "'%s'" % c, warn=False)
+            err("Syntax error at char %d '%s'" % (i, c), warn=False)
 
     if s != 0:
-        err("Syntax error at char", i, "'%s'" % c, warn=False)
+        err("Syntax error at char %d '%s'" % (i, c), warn=False)
     return wl
 
 #######################################################################
@@ -344,6 +347,7 @@ class prototype(object):
         self.st = st
         self.obj = None
         self.args = []
+        self.argstruct = False
         wl = lex(st.line[1])
 
         if retval:
@@ -371,13 +375,30 @@ class prototype(object):
         wl[-1] = ','
 
         names = {}
+        n = 0
         while len(wl) > 0:
+            n += 1
             x = wl.pop(0)
             if x != ',':
                 err("Expected ',' found '%s'" % x, warn=False)
             if len(wl) == 0:
                 break
-            t = arg(wl, names, st.vcc.enums, ',')
+            if wl[0] == '[':
+                    wl.pop(0)
+                    t = arg(wl, names, st.vcc.enums, ']')
+                    if t.nm is None:
+                        err("Optional arguments must have names", warn=False)
+                    t.opt = True
+                    x = wl.pop(0)
+                    if x != ']':
+                        err("Expected ']' found '%s'" % x, warn=False)
+                    self.argstruct = True
+            else:
+                    t = arg(wl, names, st.vcc.enums, ',')
+            if t.nm is None:
+                t.nm2 = "arg%d" % n
+            else:
+                t.nm2 = t.nm
             self.args.append(t)
 
     def vcl_proto(self, short, pfx=""):
@@ -406,6 +427,8 @@ class prototype(object):
                     t += " " + i.nm
                 if i.defval is not None:
                     t += "=" + i.defval
+            if i.opt:
+                t = "[" + t + "]"
             ll.append(t)
         t = ",@".join(ll)
         if len(s + t) > 68 and not short:
@@ -440,8 +463,11 @@ class prototype(object):
     def proto(self, args, name):
         s = self.retval.ct + " " + name + '('
         ll = args
-        for i in self.args:
-            ll.append(i.ct)
+        if self.argstruct:
+                ll.append(self.argstructname() + "*")
+        else:
+                for i in self.args:
+                    ll.append(i.ct)
         s += ", ".join(ll)
         return s + ');'
 
@@ -449,21 +475,49 @@ class prototype(object):
         tn = 'td_' + self.st.vcc.modname + '_' + self.cname()
         return "typedef " + self.proto(args, name=tn)
 
+    def argstructname(self):
+        return "struct %s_arg" % self.cname(True)
+
+    def argstructure(self):
+        s = "\n" + self.argstructname() + " {\n"
+        for i in self.args:
+                if i.opt:
+                        assert i.nm is not None
+                        s += "\tchar\t\t\tvalid_%s;\n" % i.nm
+        for i in self.args:
+                s += "\t" + i.ct
+                if len(i.ct) < 8:
+                        s += "\t"
+                if len(i.ct) < 16:
+                        s += "\t"
+                s += "\t" + i.nm2 + ";\n"
+        s += "};\n"
+        return s
+
     def cstuff(self, args, where):
+        s = ""
         if where == 'h':
-                s = self.proto(args, self.cname(True))
+                if self.argstruct:
+                        s += self.argstructure()
+                s += lwrap(self.proto(args, self.cname(True)))
         elif where == 'c':
-                s = self.typedef(args)
+                s += lwrap(self.typedef(args))
         elif where == 'o':
-                s = self.typedef(args)
+                if self.argstruct:
+                        s += self.argstructure()
+                s += lwrap(self.typedef(args))
         else:
             assert False
-        return lwrap(s)
+        return s
 
     def json(self, jl, cfunc):
         ll = []
         self.retval.json(ll)
         ll.append('Vmod_%s_Func.%s' % (self.st.vcc.modname, cfunc))
+        if self.argstruct:
+                ll.append(self.argstructname())
+        else:
+                ll.append("")
         for i in self.args:
             i.json(ll)
         jl.append(ll)
@@ -632,8 +686,7 @@ class s_function(stanza):
         self.vcc.contents.append(self)
 
     def cstuff(self, fo, where):
-        if where in ('h', 'c'):
-            fo.write(self.proto.cstuff(['VRT_CTX'], where))
+        fo.write(self.proto.cstuff(['VRT_CTX'], where))
 
     def cstruct(self, fo, define):
         if define:
@@ -948,13 +1001,13 @@ class vcc(object):
         for i in self.contents:
             if type(i) == s_object:
                 i.cstuff(fo, 'c')
-                i.cstuff(fx, 'c')
+                i.cstuff(fx, 'o')
 
         fx.write("/* Functions */\n")
         for i in self.contents:
             if type(i) == s_function:
                 i.cstuff(fo, 'c')
-                i.cstuff(fx, 'c')
+                i.cstuff(fx, 'o')
 
         csn = "Vmod_%s_Func" % self.modname
         scsn = "struct " + csn


More information about the varnish-commit mailing list