[master] a78efad I set out to straighten out a number of hacks and inconsistencies in the string spec we pass to the VCC, to make future VCL/VMOD/VCC work easier.

Poul-Henning Kamp phk at FreeBSD.org
Wed Jun 1 16:57:08 CEST 2016


commit a78efad8002895e6097aeb6b1daeac0f6108b9a9
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Wed Jun 1 14:41:07 2016 +0000

    I set out to straighten out a number of hacks and inconsistencies in the
    string spec we pass to the VCC, to make future VCL/VMOD/VCC work easier.
    
    Instead I ended up rewriting the entire .VCC file compiler, which was
    so much easier than the first time, because now I know what it should do.
    
    There should be no incompatibilities, but as always: Please test and report.

diff --git a/lib/libvcc/vcc_action.c b/lib/libvcc/vcc_action.c
index 2e1a2fb..aa78adb 100644
--- a/lib/libvcc/vcc_action.c
+++ b/lib/libvcc/vcc_action.c
@@ -196,23 +196,22 @@ parse_new(struct vcc *tl)
 	/*lint -save -e448 */
 	/* Split the first three args */
 	p = sy2->args;
+
 	s_obj = p;
 	p += strlen(p) + 1;
-	s_init = p;
-	/*
-	 * Check for the end marked (\0\0) followed by s(truct) to avoid
-	 * matching an ENUM half-way through and generating illegal C code.
-	 */
-	while (p[0] != '\0' || p[1] != '\0' || p[2] != 's')
-		p++;
-	p += 2;
-	AZ(strncmp(p, "struct vmod_", 12));
+
 	s_struct = p;
 	p += strlen(p) + 1;
-	s_fini = p + strlen(p) + 1;
-	while (p[0] != '\0' || p[1] != '\0')
+
+	s_init = p;
+	while (p[0] != '\0' || p[1] != '\0' || p[2] != '\0')
+		p++;
+	p += 3;
+
+	s_fini = p;
+	while (p[0] != '\0' || p[1] != '\0' || p[2] != '\0')
 		p++;
-	p += 2;
+	p += 3;
 
 	Fh(tl, 0, "static %s *vo_%s;\n\n", s_struct, sy1->name);
 
@@ -238,20 +237,9 @@ parse_new(struct vcc *tl)
 
 		sy3->args = p;
 		sy3->extra = TlDup(tl, buf1);
-		while (p[0] != '\0' || p[1] != '\0') {
-			if (!memcmp(p, "ENUM\0", 5)) {
-				/* XXX: Special case for ENUM that has
-				   it's own \0\0 end marker. Not exactly
-				   elegant, we should consider
-				   alternatives here. Maybe runlength
-				   encode the entire block? */
-				p += strlen(p) + 1;
-				while (p[0] != '\0' || p[1] != '\0')
-					p++;
-			}
+		while (p[0] != '\0' || p[1] != '\0' || p[2] != '\0')
 			p++;
-		}
-		p += 2;
+		p += 3;
 	}
 	sy1->def_e = tl->t;
 	/*lint -restore */
diff --git a/lib/libvcc/vcc_expr.c b/lib/libvcc/vcc_expr.c
index 7ad85ee..8284618 100644
--- a/lib/libvcc/vcc_expr.c
+++ b/lib/libvcc/vcc_expr.c
@@ -662,20 +662,23 @@ vcc_func(struct vcc *tl, struct expr **e, const char *cfunc,
 			p += strlen(p) + 1;
 			continue;
 		}
-		if (fa->type == ENUM) {
-			fa->enum_bits = p;
-			while (*p != '\0')
+		if (*p == '\1') {
+			fa->enum_bits = ++p;
+			while (*p != '\1')
 				p += strlen(p) + 1;
-			p += strlen(p) + 1;
+			p++;
+			assert(*p == '\0');
+			p++;
 		}
-		if (*p == '\1') {
+		if (*p == '\2') {
 			fa->name = p + 1;
-			p = strchr(p, '\0') + 1;
-			if (*p == '\2') {
-				fa->val = p + 1;
-				p = strchr(p, '\0') + 1;
-			}
+			p += strlen(p) + 1;
+		}
+		if (*p == '\3') {
+			fa->val = p + 1;
+			p += strlen(p) + 1;
 		}
+		assert(*p == 0 || *p > ' ');
 	}
 
 	VTAILQ_FOREACH(fa, &head, list) {
diff --git a/lib/libvcc/vcc_vmod.c b/lib/libvcc/vcc_vmod.c
index 5d36bd3..06c2b92 100644
--- a/lib/libvcc/vcc_vmod.c
+++ b/lib/libvcc/vcc_vmod.c
@@ -235,7 +235,8 @@ vcc_ParseImport(struct vcc *tl)
 			    "\t\t    VCL_EVENT_DISCARD);\n", p, PF(mod));
 			VSB_printf(ifp->event, "\t%s(ctx, &vmod_priv_%.*s, ev)",
 			    p, PF(mod));
-		} else {
+		} else if (!strcmp(p, "$FUNC")) {
+			p += strlen(p) + 1;
 			sym = VCC_AddSymbolStr(tl, p, SYM_FUNC);
 			ERRCHK(tl);
 			AN(sym);
@@ -245,6 +246,10 @@ vcc_ParseImport(struct vcc *tl)
 			p += strlen(p) + 1;
 			sym->args = p;
 			sym->fmt = VCC_arg_type(&p);
+		} else {
+			VSB_printf(tl->sb, "Internal spec error (%s)\n", p);
+			vcc_ErrWhere(tl, mod);
+			return;
 		}
 	}
 
diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py
index 4ee6341..570a908 100755
--- a/lib/libvcc/vmodtool.py
+++ b/lib/libvcc/vmodtool.py
@@ -36,6 +36,7 @@ Read the vmod.vcc file (inputvcc) and produce:
 # This script should work with both Python 2 and Python 3.
 from __future__ import print_function
 
+import os
 import sys
 import re
 import optparse
@@ -46,6 +47,8 @@ from os.path import dirname, exists, join, realpath
 from pprint import pprint, pformat
 from tempfile import mkstemp
 
+rstfmt=False
+
 ctypes = {
 	'ACL':		"VCL_ACL",
 	'BACKEND':	"VCL_BACKEND",
@@ -85,9 +88,16 @@ def write_c_file_warning(fo):
 def write_rst_file_warning(fo):
 	write_file_warning(fo, "..", "..", "..")
 
+def write_rst_hdr(fo, s, below="-", above=None):
+	if above != None:
+		fo.write(above * len(s) + "\n")
+	fo.write(s + "\n")
+	if below != None:
+		fo.write(below * len(s) + "\n")
+
 #######################################################################
 
-def lwrap(s, width=72):
+def lwrap(s, width=64):
 	"""
 	Wrap a C-prototype like string into a number of lines.
 	"""
@@ -109,6 +119,11 @@ def lwrap(s, width=72):
 def quote(s):
 	return s.replace("\"", "\\\"")
 
+def indent(p,n):
+	n = len(p.expandtabs()) + n
+	p = "\t" * int(n / 8)
+	p += " " * int(n % 8)
+	return p
 
 #######################################################################
 
@@ -130,840 +145,691 @@ class FormatError(Exception):
 		self.details = details
 		Exception.__init__(self)
 
-
 #######################################################################
 
-class Token(object):
-	def __init__(self, ln, ch, tokstr):
-		self.ln = ln
-		self.ch = ch
-		self.str = tokstr
+def err(str, warn=True):
+	if opts.strict or not warn:
+		print("ERROR: " + str, file = sys.stderr)
+		exit(1)
+		raise FormatError(str, "")
+	else:
+		print("WARNING: " + str, file = sys.stderr)
 
-	def __repr__(self):
-		return "<@%d \"%s\">" % (self.ln, self.str)
+def fmt_cstruct(fo, mn, x):
+	a = "\ttd_" + mn + "_" + x
+	while len(a.expandtabs()) < 40:
+		a += "\t"
+	fo.write("%s*%s;\n" % (a, x))
 
 #######################################################################
 
-class Vmod(object):
-	def __init__(self, nam, dnam, sec):
-		if not is_c_name(nam):
-			raise ParseError("Module name '%s' is illegal" % nam)
-		self.nam = nam
-		self.dnam = dnam
-		self.sec = sec
-		self.event = None
-		self.funcs = list()
-		self.objs = list()
-		self.doc_str = []
-		self.doc_order = []
-
-	def set_event(self, nam):
-		if self.event is not None:
-			raise ParseError("Module %s already has $Event" %
-			    self.nam)
-		if not is_c_name(nam):
-			raise ParseError("$Event name '%s' is illegal" % nam)
-		self.event = nam
-
-	def add_func(self, fn):
-		self.funcs.append(fn)
-		self.doc_order.append(fn)
-
-	def add_obj(self, o):
-		self.objs.append(o)
-		self.doc_order.append(o)
-
-	def c_proto(self, fo):
-		for o in self.objs:
-			fo.write("/* Object %s */\n" % o.nam)
-			o.fixup(self.nam)
-			o.c_proto(fo)
-			fo.write("\n")
-		if len(self.funcs) > 0:
-			fo.write("/* Functions */\n")
-		for f in self.funcs:
-			for i in lwrap(f.c_proto()):
-				fo.write(i + "\n")
-		if self.event is not None:
-			fo.write("\n")
-			fo.write("#ifdef VCL_MET_MAX\n")
-			fo.write("vmod_event_f " + self.event + ";\n")
-			fo.write("#endif\n")
-
-	def c_typedefs_(self):
-		l = list()
-		for o in self.objs:
-			for t in o.c_typedefs(self.nam):
-				l.append(t)
-			l.append("")
-		if len(self.funcs) > 0:
-			l.append("/* Functions */")
-		for f in self.funcs:
-			l.append(f.c_typedef(self.nam))
-		l.append("")
-		return l
-
-	def c_typedefs(self, fo):
-		for i in self.c_typedefs_():
-			for j in lwrap(i):
-				fo.write(j + "\n")
-
-	def c_vmod(self, fo):
-		cs = self.c_struct()
-		fo.write(cs + ';\n')
-
-		vfn = 'Vmod_%s_Func' % self.nam
-
-		fo.write("/*lint -esym(754, %s::*) */\n" % vfn)
-		fo.write("\nstatic const struct %s Vmod_Func = " % vfn)
-		fo.write(self.c_initializer())
-		fo.write("\n")
-
-		fo.write("\nstatic const char Vmod_Proto[] =\n")
-		for t in self.c_typedefs_():
-			for i in lwrap(t, width=64):
-				fo.write('\t"' + i + '\\n"\n')
-		fo.write('\t"\\n"\n')
-		for i in (cs + ";").split("\n"):
-			fo.write('\n\t"' + i + '\\n"')
-		fo.write('\n\t"static struct ' + vfn + " " + vfn + ';";\n\n')
-
-		fo.write(self.c_strspec())
-
-		fo.write("\n")
+class ctype(object):
+	def __init__(self, vt, ct):
+		self.vt = vt
+		self.ct = ct
+		self.nm = None
+		self.defval = None
+		self.spec = None
+
+	def __str__(self):
+		s = "<" + self.vt
+		if self.nm != None:
+			s += " " + self.nm
+		if self.defval != None:
+			s += " VAL=" + self.defval
+		if self.spec != None:
+			s += " SPEC=" + str(self.spec)
+		return s + ">"
+
+	def vcl(self):
+		if self.vt == "STRING_LIST":
+			return "STRING"
+		if self.spec == None:
+			return self.vt
+		return self.vt + " {" + ",".join(self.spec) + "}"
+
+	def specstr(self, fo, p):
+		fo.write(p + '"' + self.vt)
+		fo.write('\\0"\n')
+		p = indent(p, 4)
+		if self.spec != None:
+			fo.write(p + '"\\1"\n')
+			p = indent(p, 4)
+			for i in self.spec:
+				fo.write(p + '"' + i + '\\0"\n')
+			p = indent(p, -4)
+			# This terminating \1 is necessary to ensure that
+			# a prototype always ends with three \0's
+			fo.write(p + '"\\1\\0"\n')
+		if self.nm != None:
+			fo.write(p + '"\\2" "' + self.nm + '\\0"\n')
+		if self.defval != None:
+			fo.write(p + '"\\3" "' + quote(self.defval) + '\\0"\n')
+
+def vtype(txt):
+	j = len(txt)
+	for i in (',', ' ', '\n', '\t'):
+		x = txt.find(i)
+		if x > 0:
+			j = min(j, x)
+	t = txt[:j]
+	r = txt[j:].lstrip()
+	if t not in ctypes:
+		err("Did not recognize type <%s>" % txt)
+	ct = ctype(t, ctypes[t])
+	if t != "ENUM":
+		return ct, r
+	assert r[0] == '{'
+	e = r[1:].split('}', maxsplit=1)
+	r = e[1].lstrip()
+	e = e[0].split(',')
+	ct.spec = []
+	for i in e:
+		ct.spec.append(i.strip())
+	return ct, r
+
+def arg(txt):
+	a,s = vtype(txt)
+	if len(s) == 0 or s[0] == ',':
+		return a,s
+
+	i = s.find('=')
+	j = s.find(',')
+	if j >= 0 and j < i:
+		i = -1
+	if i < 0:
+		i = s.find(',')
+		if i < 0:
+			i = len(s)
+		a.nm = s[:i]
+		s = s[i:]
+		return a, s
+
+	a.nm = s[:i]
+	s = s[i+1:]
+	if s[0] == '"' or s[0] == "'":
+		m = re.match("(['\"]).*?(\\1)", s)
+		if not m:
+			err("Unbalanced quote")
+		a.defval = s[:m.end()]
+		s = s[m.end():]
+	else:
+		i = s.find(',')
+		a.defval = s[:i]
+		s = s[i:]
 
-		nm = "Vmod_" + self.nam + "_Data"
-		fo.write("/*lint -esym(759, %s) */\n" % nm)
-		fo.write("const struct vmod_data " + nm + " = {\n")
-		fo.write("\t.vrt_major = VRT_MAJOR_VERSION,\n")
-		fo.write("\t.vrt_minor = VRT_MINOR_VERSION,\n")
-		fo.write("\t.name = \"%s\",\n" % self.nam)
-		fo.write("\t.func = &Vmod_Func,\n")
-		fo.write("\t.func_len = sizeof(Vmod_Func),\n")
-		fo.write("\t.proto = Vmod_Proto,\n")
-		fo.write("\t.spec = Vmod_Spec,\n")
-		fo.write("\t.abi = VMOD_ABI_Version,\n")
+	return a,s
 
-		# NB: Sort of hackish:
-		# Fill file_id with random stuff, so we can tell if
-		# VCC and VRT_Vmod_Init() dlopens the same file
-		#
-		fo.write("\t.file_id = \"")
-		for i in range(32):
-			fo.write("%c" % random.randint(0x40, 0x5a))
-		fo.write("\",\n")
-		fo.write("};\n")
+class prototype(object):
+	def __init__(self, st, retval=True, prefix=""):
+		l = st.line[1]
+		while True:
+			a1 = l.count("(")
+			a2 = l.count(")")
+			if a1 > 0 and a1 == a2:
+				break
+			n = st.doc.split("\n", maxsplit=1)
+			l += n[0]
+			st.doc = n[1]
 
-	def c_initializer(self):
-		s = '{\n'
-		for o in self.objs:
-			s += o.c_initializer()
+		if retval:
+			self.retval,s = vtype(l)
+		else:
+			self.retval = None
+			s = l
+		i = s.find("(")
+		assert i > 0
+		self.name = prefix + s[:i].strip()
+		s = s[i:].strip()
+		assert s[0] == "("
+		assert s[-1] == ")"
+		s = s[1:-1]
+		self.args = []
+		while len(s) > 0:
+			a,s = arg(s)
+			self.args.append(a)
+			if len(s) == 0:
+				break;
+			assert s[0] == ','
+			s = s[1:].lstrip()
+
+	def cname(self):
+		return self.name.replace(".", "_")
+
+	def vcl_proto(self, short):
+		s = ""
+		if self.retval != None:
+			s += self.retval.vcl() + " "
+		s += self.name + "("
+		l = []
+		for i in self.args:
+			t = i.vcl()
+			if not short:
+				if i.nm != None:
+					t += " " + i.nm
+				if i.defval != None:
+					t += "=" + i.defval
+			l.append(t)
+		s += ", ".join(l) + ")"
+		return s
 
-		s += "\n\t/* Functions */\n"
-		for f in self.funcs:
-			s += f.c_initializer()
+	def c_ret(self):
+		return self.retval.ct
+
+	def c_args(self):
+		if len(self.args) == 0:
+			return ""
+		l = [""]
+		for i in self.args:
+			l.append(i.ct)
+		return ", ".join(l)
+
+	def specstr(self, fo, p):
+		p = indent(p, 4)
+		if self.retval == None:
+			fo.write(p + '"VOID\\0"\n')
+		else:
+			self.retval.specstr(fo, p)
+		if self.args != None:
+			p = indent(p, 4)
+			for i in self.args:
+				i.specstr(fo, p)
+			p = indent(p, -4)
+		fo.write(p + '"\\0"\n')
 
-		s += "\n\t/* Init/Fini */\n"
-		if self.event is not None:
-			s += "\t" + self.event + ",\n"
-		s += "};"
+#######################################################################
 
-		return s
+class stanza(object):
+	def __init__(self, l0, doc, vcc):
+		self.line = l0
+		if len(doc) == 1:
+			self.doc = doc[0]
+		else:
+			self.doc = ""
+		self.vcc = vcc
+		self.rstlbl = None
+		self.methods = None
+		self.proto = None
+		self.parse()
+
+	def dump(self):
+		print(type(self), self.line)
+
+	def rstfile(self, fo, man):
+		if self.rstlbl != None:
+			fo.write(".. _" + self.rstlbl + ":\n\n")
+
+		self.rsthead(fo, man)
+		self.rstmid(fo, man)
+		self.rsttail(fo, man)
+
+	def rsthead(self, fo, man):
+		if self.proto == None:
+			return
+		if rstfmt:
+			s = self.proto.vcl_proto(short=False)
+			write_rst_hdr(fo, s, '-')
+		else:
+			write_rst_hdr(fo, self.proto.name, '-')
+			s = self.proto.vcl_proto(short=False)
+			fo.write("\n::\n\n\t%s\n" % s)
 
-	def c_struct(self):
-		s = 'struct Vmod_' + self.nam + '_Func {\n'
-		for o in self.objs:
-			s += o.c_struct(self.nam)
+	def rstmid(self, fo, man):
+		fo.write(self.doc + "\n")
 
-		s += "\n\t/* Functions */\n"
-		for f in self.funcs:
-			s += f.c_struct(self.nam)
+	def rsttail(self, fo, man):
+		return
 
-		s += "\n\t/* Init/Fini */\n"
-		if self.event is not None:
-			s += "\tvmod_event_f\t*_event;\n"
-		s += '}'
-		return s
+	def hfile(self, fo):
+		return
 
-	def c_strspec(self):
-		s = "/*lint -save -e786 -e840 */\n"
-		s += "static const char * const Vmod_Spec[] = {\n"
+	def cstruct(self, fo):
+		return
 
-		for o in self.objs:
-			s += o.c_strspec(self.nam) + ",\n\n"
+	def cstruct_init(self, fo):
+		return
 
-		if len(self.funcs) > 0:
-			s += "\t/* Functions */\n"
-		for f in self.funcs:
-			s += f.c_strspec(self.nam) + ',\n\n'
+	def specstr(self, fo):
+		return
 
-		if self.event is not None:
-			s += "\t/* Init/Fini */\n"
-			s += '\t"$EVENT\\0Vmod_' + self.nam + '_Func._event",\n'
+#######################################################################
 
-		s += "\t0\n"
-		s += "};\n"
-		s += "/*lint -restore */\n"
-		return s
+class s_module(stanza):
+	def parse(self):
+		a = self.line[1].split(maxsplit=2)
+		self.vcc.modname = a[0]
+		self.vcc.mansection = a[1]
+		self.vcc.moddesc = a[2]
+		self.rstlbl = "vmod_%s(%s)" % (
+		    self.vcc.modname,
+		    self.vcc.mansection
+		)
+		self.vcc.contents.append(self)
+
+	def rsthead(self, fo, man):
+
+		write_rst_hdr(fo, "vmod_" + self.vcc.modname, "=", "=")
+		fo.write("\n")
 
-	def doc(self, l):
-		self.doc_str.append(l)
+		write_rst_hdr(fo, self.vcc.moddesc, "-", "-")
 
-	def doc_dump(self, fo, suf):
-		fo.write(".. role:: ref(emphasis)\n\n")
-		i = "vmod_" + self.nam
-		fo.write(".. _" + i + "(" + self.sec + "):\n\n")
-		fo.write("=" * len(i) + "\n")
-		fo.write(i + "\n")
-		fo.write("=" * len(i) + "\n")
-		fo.write("\n")
-		i = self.dnam
-		fo.write("-" * len(i) + "\n")
-		fo.write(i + "\n")
-		fo.write("-" * len(i) + "\n")
 		fo.write("\n")
-		fo.write(":Manual section: %s\n" % self.sec)
+		fo.write(":Manual section: " + self.vcc.mansection + "\n")
+
 		fo.write("\n")
-		fo.write("SYNOPSIS\n")
-		fo.write("========\n")
+		write_rst_hdr(fo, "SYNOPSIS", "=")
 		fo.write("\n")
-		fo.write("import %s [from \"path\"] ;\n" % self.nam)
+		fo.write('import %s [from "path"] ;\n' % self.vcc.modname)
 		fo.write("\n")
-		for i in self.doc_str:
-			fo.write(i + "\n")
-		fo.write("CONTENTS\n")
-		fo.write("========\n")
+
+	def rsttail(self, fo, man):
+
+		write_rst_hdr(fo, "CONTENTS", "=")
 		fo.write("\n")
+
+		if man:
+			for i in self.vcc.contents[1:]:
+				if i.rstlbl == None:
+					continue
+				fo.write("* %s\n" %
+				    i.proto.vcl_proto(short=True))
+			fo.write("\n")
+			return
+
 		l = []
-		for i in self.funcs:
-			l.append(i.doc_idx(suf))
-		for i in self.objs:
-			l += i.doc_idx(suf)
+		for i in self.vcc.contents[1:]:
+			j = i.rstlbl
+			if j != None:
+				l.append([j.split("_", maxsplit=1)[1], j])
+			if i.methods == None:
+				continue
+			for x in i.methods:
+				j = x.rstlbl
+				l.append([j.split("_", maxsplit=1)[1], j])
+
 		l.sort()
 		for i in l:
-			fo.write("* " + i[1] + "\n")
+			fo.write("* :ref:`%s`\n" % i[1])
 		fo.write("\n")
-		for i in self.doc_order:
-			i.doc_dump(fo)
 
-#######################################################################
+class s_event(stanza):
+	def parse(self):
+		self.event_func = self.line[1]
+		self.vcc.contents.append(self)
 
-class Func(object):
-	def __init__(self, nam, retval, al):
-		#if not is_c_name(nam):
-		#	raise Exception("Func name '%s' is illegal" % nam)
-		if retval not in ctypes:
-			raise TypeError(
-			    "Return type '%s' not a valid type", retval)
-		self.nam = nam
-		self.cnam = nam.replace(".", "_")
-		self.al = al
-		self.retval = retval
-		self.pfx = None
-		self.doc_str = []
-
-	def __repr__(self):
-		return "<FUNC %s %s>" % (self.retval, self.nam)
-
-	def set_pfx(self, s):
-		self.pfx = s
-
-	def c_proto(self, fini=False):
-		s = ctypes[self.retval] + " vmod_" + self.cnam + "("
-		p = ""
-		if not fini:
-			s += "VRT_CTX"
-			p = ", "
-		if self.pfx is not None:
-			s += p + self.pfx
-			p = ", "
-		for a in self.al:
-			s += p + ctypes[a.typ]
-			p = ", "
-		s += ");"
-		return s
-
-	def c_typedef(self, modname, fini=False):
-		s = "typedef "
-		s += ctypes[self.retval]
-		s += " td_" + modname + "_" + self.cnam + "("
-		p = ""
-		if not fini:
-			s += "VRT_CTX"
-			p = ", "
-		if self.pfx is not None:
-			s += p + self.pfx
-			p = ", "
-		for a in self.al:
-			s += p + ctypes[a.typ]
-			p = ", "
-		s += ");"
-		return s
+	def rstfile(self, fo, man):
+		return
 
-	def c_struct(self, modname):
-		s = '\ttd_' + modname + "_" + self.cnam
-		if len(s.expandtabs()) >= 40:
-			s += "\n\t\t\t\t\t"
-		else:
-			while len(s.expandtabs()) < 40:
-				s += "\t"
-		s += "*" + self.cnam + ";\n"
-		return s
+	def hfile(self, fo):
+		fo.write("#ifdef VCL_MET_MAX\n")
+		fo.write("vmod_event_f %s;\n" % self.event_func)
+		fo.write("#endif\n")
 
-	def c_initializer(self):
-		return "\tvmod_" + self.cnam + ",\n"
-
-	def c_strspec(self, modnam, pfx="\t"):
-		s = pfx + '"' + modnam + "." + self.nam + '\\0"\n'
-		s += pfx + '"'
-		s += "Vmod_" + modnam + "_Func." + self.cnam + '\\0"\n'
-		s += pfx + '    "' + self.retval + '\\0"\n'
-		for a in self.al:
-			s += pfx + '\t"' + a.c_strspec() + '"\n'
-		s += pfx + '"\\0"'
-		return s
+	def cstruct(self, fo):
+		fo.write("\tvmod_event_f\t\t\t*_event;\n")
 
-	def doc(self, l):
-		self.doc_str.append(l)
+	def cstruct_init(self, fo):
+		fo.write("\t%s,\n" % self.event_func)
 
-	def doc_proto(self):
-		s = self.retval + " " + self.nam + "("
-		d = ""
-		for i in self.al:
-			s += d + i.typ
-			d = ", "
-		s += ")"
-		return s
+	def specstr(self, fo):
+		fo.write('\t"$EVENT\\0"\n\t    "Vmod_%s_Func._event",\n' %
+		    self.vcc.modname)
 
-	def doc_idx(self, suf):
-		if suf == "":
-			return (self.nam, ":ref:`func_" + self.nam + "`")
-		else:
-			return (self.nam, self.doc_proto())
+class s_function(stanza):
+	def parse(self):
+		self.proto = prototype(self)
+		self.rstlbl = "func_" + self.proto.name
+		self.vcc.contents.append(self)
 
-	def doc_dump(self, fo):
-		s = self.doc_proto()
-		fo.write(".. _func_" + self.nam + ":\n\n")
-		fo.write(s + "\n")
-		fo.write("-" * len(s) + "\n")
-		fo.write("\n")
-		fo.write("Prototype\n")
-		s = "\t" + self.retval + " " + self.nam + "("
-		d = ""
-		for i in self.al:
-			s += d + i.typ
-			if i.nam is not None:
-				s += " " + i.nam
-			d = ", "
-		fo.write(s + ")\n")
-		for i in self.doc_str:
+	def hfile(self, fo):
+		fn = "vmod_" + self.proto.name
+		s = "%s %s(VRT_CTX" % (self.proto.c_ret(), fn)
+		s += self.proto.c_args() + ");"
+		for i in lwrap(s):
 			fo.write(i + "\n")
 
-
-#######################################################################
-
-class Obj(object):
-	def __init__(self, nam):
-		self.nam = nam
-		self.init = None
-		self.fini = None
-		self.methods = list()
-		self.doc_str = []
-		self.st = None
-
-	def fixup(self, modnam):
-		assert self.nam is not None
-		self.st = "struct vmod_" + modnam + "_" + self.nam
-		self.init.set_pfx(self.st + " **, const char *")
-		self.fini.set_pfx(self.st + " **")
-		for m in self.methods:
-			m.set_pfx(self.st + " *")
-
-	def set_init(self, f):
-		self.init = f
-		self.fini = Func(f.nam, "VOID", [])
-		self.init.cnam += "__init"
-		self.fini.cnam += "__fini"
-
-	def add_method(self, m):
-		self.methods.append(m)
-
-	def c_typedefs(self, modnam):
-		l = list()
-		l.append("/* Object " + self.nam + " */")
-		l.append(self.st + ";")
-		l.append(self.init.c_typedef(modnam) + "")
-		l.append(self.fini.c_typedef(modnam, fini=True) + "")
-		for m in self.methods:
-			l.append(m.c_typedef(modnam) + "")
-		return l
-
-	def c_proto(self, fo):
-		fo.write(self.st + ";\n")
-		l = []
-		l += lwrap(self.init.c_proto())
-		l += lwrap(self.fini.c_proto(fini=True))
-		for m in self.methods:
-			l += lwrap(m.c_proto())
-		for i in l:
+	def cfile(self, fo):
+		fn = "td_" + self.vcc.modname + "_" + self.proto.name
+		s = "typedef %s %s(VRT_CTX" % (self.proto.c_ret(), fn)
+		s += self.proto.c_args() + ");"
+		for i in lwrap(s):
 			fo.write(i + "\n")
 
-	def c_struct(self, modnam):
-		s = "\t/* Object " + self.nam + " */\n"
-		s += self.init.c_struct(modnam)
-		s += self.fini.c_struct(modnam)
-		for m in self.methods:
-			s += m.c_struct(modnam)
-		return s
+	def cstruct(self, fo):
+		fmt_cstruct(fo, self.vcc.modname, self.proto.cname())
 
-	def c_initializer(self):
-		s = "\t/* Object " + self.nam + " */\n"
-		s += self.init.c_initializer()
-		s += self.fini.c_initializer()
-		for m in self.methods:
-			s += m.c_initializer()
-		return s
+	def cstruct_init(self, fo):
+		fo.write("\tvmod_" + self.proto.cname() + ",\n")
 
-	def c_strspec(self, modnam):
-		s = "\t/* Object " + self.nam + " */\n"
-		s += '\t"$OBJ\\0"\n'
-		s += self.init.c_strspec(modnam, pfx="\t\t") + '\n'
-		s += '\t\t"' + self.st + '\\0"\n'
-		s += self.fini.c_strspec(modnam, pfx="\t\t") + '\n'
-		for m in self.methods:
-			s += m.c_strspec(modnam, pfx="\t\t") + '\n'
-		s += '\t"\\0"'
-		return s
+	def specstr(self, fo):
+		fo.write('\t"$FUNC\\0"\n\t    "%s.%s\\0"\n' %
+		    (self.vcc.modname, self.proto.name))
+		fo.write('\t\t"Vmod_%s_Func.%s\\0"\n' %
+		    (self.vcc.modname, self.proto.cname()))
+		self.proto.specstr(fo, "\t\t")
 
-	def doc(self, l):
-		self.doc_str.append(l)
+		fo.write('\t"\\0",\n\n')
 
-	def doc_idx(self, suf):
-		l = []
-		if suf == "":
-			l.append((self.nam, ":ref:`obj_" + self.nam + "`"))
+class s_object(stanza):
+	def parse(self):
+		self.proto = prototype(self, retval=False)
+		self.rstlbl = "obj_" + self.proto.name
+		self.vcc.contents.append(self)
+		self.methods = []
+
+	def rsthead(self, fo, man):
+		if rstfmt:
+			s = self.proto.vcl_proto(short=False)
+			write_rst_hdr(fo, "new OBJ = " + s, '=')
 		else:
-			l.append((self.nam, "Object " + self.nam))
-		for i in self.methods:
-			l.append(i.doc_idx(suf))
-		return l
-
-	def doc_dump(self, fo):
-		fo.write(".. _obj_" + self.nam + ":\n\n")
-		s = "Object " + self.nam
-		fo.write(s + "\n")
-		fo.write("=" * len(s) + "\n")
-		fo.write("\n")
+			write_rst_hdr(fo, self.proto.name, '-')
+			s = "new OBJ = " + self.proto.vcl_proto(short=False)
+			fo.write("\n::\n\n\t%s\n" % s)
 
-		for i in self.doc_str:
-			fo.write(i + "\n")
+		fo.write(self.doc + "\n")
 
 		for i in self.methods:
-			i.doc_dump(fo)
+			i.rstfile(fo, man)
 
-#######################################################################
-
-class Arg(object):
-	def __init__(self, typ, nam=None, det=None):
-		self.nam = nam
-		self.typ = typ
-		self.det = det
-		self.val = None
+	def rstmid(self, fo, man):
+		return
 
-	def __repr__(self):
-		return "<ARG %s %s %s>" % (self.nam, self.typ, str(self.det))
+	def chfile(self, fo, h):
+		sn = "vmod_" + self.vcc.modname + "_" + self.proto.name
+		fo.write("struct %s;\n" % sn)
 
-	def c_strspec(self):
-		if self.det is None:
-			s = self.typ + "\\0"
+		if h:
+			def p(x):
+				return x + " vmod_"
 		else:
-			s = self.det
-		if self.nam is not None:
-			s += '"\n\t\t    "\\1' + self.nam + '\\0'
-		if self.val is not None:
-			# The space before the value is important to
-			# terminate the \2 escape sequence
-			s += '"\n\t\t\t"\\2 ' + quote(self.val) + "\\0"
-		return s
-
-#######################################################################
-#
-#
+			def p(x):
+				return "typedef " + x + \
+				    " td_%s_" % self.vcc.modname
+
+		s = p("VCL_VOID") + "%s__init(VRT_CTX, " % self.proto.name
+		s += "struct %s **, const char *" % sn
+		s += self.proto.c_args() + ");"
+		for i in lwrap(s):
+			fo.write(i + "\n")
 
-def parse_enum2(tl):
-	t = tl.get_token()
-	if t.str != "{":
-		raise ParseError("expected \"{\"")
-	s = "ENUM\\0"
-	t = None
-	while True:
-		if t is None:
-			t = tl.get_token()
-		if t.str == "}":
-			break
-		s += t.str + "\\0"
-		t = tl.get_token()
-		if t.str == ",":
-			t = None
-		elif t.str == "}":
-			break
-		else:
-			raise ParseError(
-			    "Expected \"}\" or \",\" not \"%s\"" % t.str)
-	s += "\\0"
-	return Arg("ENUM", det=s)
+		s = p("VCL_VOID")
+		s += "%s__fini(struct %s **);" % (self.proto.name, sn)
+		for i in lwrap(s):
+			fo.write(i + "\n")
 
+		for i in self.methods:
+			cn = i.proto.cname()
+			s = p(i.proto.c_ret())
+			s += "%s(VRT_CTX, struct %s *" % (cn, sn)
+			s += i.proto.c_args() + ");"
+			for i in lwrap(s):
+				fo.write(i + "\n")
+		fo.write("\n")
 
-def parse_arg(tl, al):
-	t = tl.get_token()
-	assert t is not None
+	def hfile(self, fo):
+		self.chfile(fo, True)
 
-	if t.str == ")":
-		return t
+	def cfile(self, fo):
+		self.chfile(fo, False)
 
-	if t.str == "ENUM":
-		al.append(parse_enum2(tl))
-	elif t.str in ctypes:
-		al.append(Arg(t.str))
-	else:
-		raise Exception("ARG? %s", t.str)
+	def cstruct(self, fo):
+		td = "td_" + self.vcc.modname + "_" + self.proto.name + "_"
+		fmt_cstruct(fo, self.vcc.modname, self.proto.name + "__init")
+		fmt_cstruct(fo, self.vcc.modname, self.proto.name + "__fini")
+		for i in self.methods:
+			i.cstruct(fo)
 
-	t = tl.get_token()
-	if t.str == "," or t.str == ")":
-		return t
+	def cstruct_init(self, fo):
+		p = "\tvmod_"
+		fo.write(p + self.proto.name + "__init,\n")
+		fo.write(p + self.proto.name + "__fini,\n")
+		for i in self.methods:
+			i.cstruct_init(fo)
+		fo.write("\n")
 
-	if not is_c_name(t.str):
-		raise ParseError(
-		    'Expected ")", "," or argument name, not "%s"' % t.str)
+	def specstr(self, fo):
 
-	al[-1].nam = t.str
-	t = tl.get_token()
+		fo.write('\t"$OBJ\\0"\n\t    "%s.%s\\0"\n' %
+		    (self.vcc.modname, self.proto.name))
 
-	if t.str == "," or t.str == ")":
-		return t
+		fo.write('\t\t"struct vmod_%s_%s\\0"\n' %
+		    (self.vcc.modname, self.proto.name))
+		fo.write("\n")
 
-	if t.str != "=":
-		raise ParseError(
-		    'Expected ")", "," or "=", not "%s"' % t.str)
+		fo.write('\t\t"Vmod_%s_Func.%s__init\\0"\n' %
+		    (self.vcc.modname, self.proto.name))
+		self.proto.specstr(fo, '\t\t')
+		fo.write('\t\t"\\0"\n\n')
 
-	t = tl.get_token()
-	al[-1].val = t.str
+		fo.write('\t\t"Vmod_%s_Func.%s__fini\\0"\n' %
+		    (self.vcc.modname, self.proto.name))
+		fo.write('\t\t    "VOID\\0"\n')
+		fo.write('\t\t    "\\0"\n')
+		fo.write('\t\t"\\0"\n\n')
 
-	t = tl.get_token()
-	return t
+		for i in self.methods:
+			i.specstr(fo)
 
-#######################################################################
-#
-#
+		fo.write('\t"\\0",\n\n')
 
-def parse_module(tl):
-	nm = tl.get_token().str
-	sec = tl.get_token().str
-	s = ""
-	while len(tl.tl) > 0:
-		s += " " + tl.get_token().str
-	dnm = s[1:]
-	return Vmod(nm, dnm, sec)
-
-
-def parse_func(tl, rt_type=None, pobj=None):
-	al = list()
-	if rt_type is None:
-		t = tl.get_token()
-		rt_type = t.str
-	if rt_type not in ctypes:
-		raise TypeError(
-		    "Return type '%s' not a valid type" % rt_type)
-
-	t = tl.get_token()
-	fname = t.str
-	if pobj is not None and fname[0] == "." and is_c_name(fname[1:]):
-		fname = pobj + fname
-	elif pobj is not None and fname[0] != ".":
-		raise ParseError("Method name '%s' must start with ." % fname)
-	elif pobj is not None and not is_c_name(fname[1:]):
-		raise ParseError("Method name '%s' is illegal" % fname[1:])
-	elif not is_c_name(fname):
-		raise ParseError("Function name '%s' is illegal" % fname)
-
-	t = tl.get_token()
-	if t.str != "(":
-		raise ParseError("Expected \"(\" got \"%s\"" % t.str)
-
-	while True:
-		t = parse_arg(tl, al)
-		if t.str == ")":
-			break
-		if t.str != ",":
-			raise ParseError("End Of Input looking for ')' or ','")
+	def dump(self):
+		super(s_object, self).dump()
+		for i in self.methods:
+			i.dump()
+
+class s_method(stanza):
+	def parse(self):
+		p = self.vcc.contents[-1]
+		assert type(p) == s_object
+		self.proto = prototype(self, prefix=p.proto.name)
+		self.rstlbl = "func_" + self.proto.name
+		p.methods.append(self)
+
+	def cstruct(self, fo):
+		fmt_cstruct(fo, self.vcc.modname, self.proto.cname())
+
+	def cstruct_init(self, fo):
+		fo.write('\t' + "vmod_" + self.proto.cname() + ",\n")
+
+	def specstr(self, fo):
+		fo.write('\t\t"%s.%s\\0"\n' %
+		    (self.vcc.modname, self.proto.name))
+		fo.write('\t\t    "Vmod_%s_Func.%s\\0"\n' %
+		    (self.vcc.modname, self.proto.cname()))
+		self.proto.specstr(fo, '\t\t    ')
+		fo.write('\t\t"\\0"\n\n')
 
-	f = Func(fname, rt_type, al)
+#######################################################################
 
-	return f
+class vcc(object):
+	def __init__(self, inputvcc, rstdir, outputprefix):
+		self.inputfile = inputvcc
+		self.rstdir = rstdir
+		self.pfx = outputprefix
+		self.contents = []
+		self.commit_files = []
+
+	def commit(self):
+		for i in self.commit_files:
+			os.rename(i + ".tmp", i)
+
+	def parse(self):
+		a = open(self.inputfile, "r").read()
+		a = a.split("\n$")
+		for i in range(len(a)):
+			b = a[i].split("\n", maxsplit=1)
+			if i == 0:
+				self.copyright = a[0]
+				continue
+			c = b[0].split(maxsplit=1)
+			if i == 1:
+				if c[0] != "Module":
+					err("$Module must be first stanze")
+			if c[0] == "Module":
+				s_module(c, b[1:], self)
+			elif c[0] == "Event":
+				s_event(c, b[1:], self)
+			elif c[0] == "Function":
+				s_function(c, b[1:], self)
+			elif c[0] == "Object":
+				s_object(c, b[1:], self)
+			elif c[0] == "Method":
+				s_method(c, b[1:], self)
+			else:
+				err("Unknown stanze $%s" % c[0])
+
+	def rst_copyright(self, fo):
+		write_rst_hdr(fo, "COPYRIGHT", "=")
+		fo.write("\n::\n\n")
+		a = self.copyright
+		a = a.replace("\n#", "\n ")
+		if a[:2] == "#\n":
+			a = a[2:]
+		if a[:3] == "#-\n":
+			a = a[3:]
+		fo.write(a + "\n")
+
+	def rstfile(self, man=False):
+		fn = self.rstdir + "/vmod_" + self.modname
+		if man:
+			fn += ".man"
+		fn += ".rst"
+		self.commit_files.append(fn)
+		fo = open(fn + ".tmp", "w")
+		write_rst_file_warning(fo)
+		fo.write(".. role:: ref(emphasis)\n\n")
 
-#######################################################################
-#
-#
+		for i in self.contents:
+			i.rstfile(fo, man)
 
-def parse_obj(tl):
-	f = parse_func(tl, "VOID")
-	o = Obj(f.nam)
-	o.set_init(f)
-	return o
+		self.rst_copyright(fo)
 
+		fo.close()
 
-#######################################################################
+	def hfile(self):
+		fn = self.rstdir + "/" + self.pfx + ".h"
+		self.commit_files.append(fn)
+		fo = open(fn + ".tmp", "w")
+		write_c_file_warning(fo)
+		fo.write("struct vmod_priv;\n")
+		fo.write("\n")
+		fo.write("extern const struct vmod_data Vmod_debug_Data;\n")
+		fo.write("\n")
 
-class FileSection(object):
-	"""
-	A section of the inputvcc, starting at a keyword.
-	"""
-	def __init__(self):
-		self.l = []
-		self.tl = []
+		for j in self.contents:
+			j.hfile(fo)
+		fo.close()
 
-	def add_line(self, ln, l):
-		self.l.append((ln, l))
+	def cstruct(self, fo, csn):
 
-	def get_token(self):
-		while True:
-			if len(self.tl) > 0:
-				# print("T\t", self.tl[0])
-				return self.tl.pop(0)
-			if len(self.l) == 0:
-				break
-			self.more_tokens()
-		return None
+		fo.write("\n%s {\n" % csn)
+		for j in self.contents:
+			j.cstruct(fo)
+		fo.write("};\n")
 
-	def more_tokens(self):
-		ln, l = self.l.pop(0)
-		if l == "":
-			return
-		l = re.sub("[ \t]*#.*$", "", l)
-		l = re.sub("[ \t]*\n", "", l)
-
-		if re.match("['\"]", l):
-			m = re.match("(['\"]).*?(\\1)", l)
-			if not m:
-				raise FormatError("Unbalanced quote",
-				    "Unbalanced quote on line %d" % ln)
-			self.tl.append(Token(ln, 0, l[:m.end()]))
-			self.l.insert(0, (ln, l[m.end():]))
-			return
+	def cstruct_init(self, fo, csn):
+		fo.write("\nstatic const %s Vmod_Func = {\n" % csn)
+		for j in self.contents:
+			j.cstruct_init(fo)
+		fo.write("};\n")
 
-		m = re.search("['\"]", l)
-		if m:
-			rest = l[m.start():]
-			self.l.insert(0, (ln, rest))
-			l = l[:m.start()]
+	def specstr(self, fo):
+		fo.write("\n/*lint -save -e786 -e840 */\n")
+		fo.write("static const char * const Vmod_Spec[] = {\n")
 
-		l = re.sub("([(){},=])", r' \1 ', l)
-		if l == "":
-			return
-		for j in l.split():
-			self.tl.append(Token(ln, 0, j))
+		for j in self.contents:
+			j.specstr(fo)
+		fo.write("\t0\n")
+		fo.write("};\n")
+		fo.write("/*lint -restore */\n")
+
+	def api(self, fo):
+		fo.write("\n/*lint -esym(759, Vmod_debug_Data) */\n")
+		fo.write("const struct vmod_data Vmod_%s_Data = {\n" %
+		    self.modname)
+		fo.write("\t.vrt_major =\tVRT_MAJOR_VERSION,\n")
+		fo.write("\t.vrt_minor =\tVRT_MINOR_VERSION,\n")
+		fo.write('\t.name =\t\t"%s",\n' % self.modname)
+		fo.write('\t.func =\t\t&Vmod_Func,\n')
+		fo.write('\t.func_len =\tsizeof(Vmod_Func),\n')
+		fo.write('\t.proto =\tVmod_Proto,\n')
+		fo.write('\t.spec =\t\tVmod_Spec,\n')
+		fo.write('\t.abi =\t\tVMOD_ABI_Version,\n')
+		# NB: Sort of hackish:
+		# Fill file_id with random stuff, so we can tell if
+		# VCC and VRT_Vmod_Init() dlopens the same file
+		#
+		fo.write("\t.file_id =\t\"")
+		for i in range(32):
+			fo.write("%c" % random.randint(0x40, 0x5a))
+		fo.write("\",\n")
+		fo.write("};\n")
 
-	def parse(self, vx):
-		t = self.get_token()
-		if t is None:
-			return
-		t0 = t.str
-		if t.str == "$Module":
-			o = parse_module(self)
-			vx.append(o)
-		elif t.str == "$Event":
-			x = self.get_token()
-			vx[0].set_event(x.str)
-			o = None
-		elif t.str == "$Function":
-			if len(vx) == 2:
-				vx.pop(-1)
-			o = parse_func(self)
-			vx[0].add_func(o)
-		elif t.str == "$Object":
-			if len(vx) == 2:
-				vx.pop(-1)
-			o = parse_obj(self)
-			vx[0].add_obj(o)
-			vx.append(o)
-		elif t.str == "$Method":
-			if len(vx) != 2:
-				raise FormatError("$Method outside $Object", "")
-			o = parse_func(self, pobj=vx[1].nam)
-			vx[1].add_method(o)
-		else:
-			if opts.strict:
-				raise FormatError("Unknown keyword: %s" %
-				    t.str, "")
-			else:
-				print("WARNING: Unknown keyword: %s:" %
-				    t.str, file=sys.stderr)
-				o = None
-				while len(self.tl) > 0:
-				    self.get_token()
-
-		assert len(self.tl) == 0
-		if o is None and len(self.l) > 0:
-			m = "%s description is not included in .rst" % t0
-			details = pformat(self.l)
-			if opts.strict:
-				raise FormatError(m, details)
-			else:
-				print("WARNING: %s:" % m, file=sys.stderr)
-				print(details, file=sys.stderr)
-		else:
-			for ln, i in self.l:
-				o.doc(i)
+	def cfile(self):
+		fn = self.rstdir + "/" + self.pfx + ".c"
+		self.commit_files.append(fn)
+		fo = open(fn + ".tmp", "w")
+		write_c_file_warning(fo)
 
-#######################################################################
+		fn2 = fn + ".tmp2"
 
-def polish(l):
-	"""
-	Polish the copyright message.
-	"""
-	if len(l[0]) == 0:
-		l.pop(0)
-		return True
-	c = l[0][0]
-	for i in l:
-		if len(i) == 0:
-			continue
-		if i[0] != c:
-			c = None
-			break
-	if c is not None:
-		for i in range(len(l)):
-			l[i] = l[i][1:]
-		return True
-	return False
+		for i in ["config", "vcl", "vrt", "vcc_if", "vmod_abi"]:
+			fo.write('#include "%s.h"\n' % i)
 
+		fo.write("\n")
 
-class SimpleTestCase(unittest.TestCase):
-	def test_included_vccs(self):
-		from tempfile import mktemp
-		from glob import glob
-		tmpfile = mktemp()
-		bdir = dirname(realpath(__file__))
-		for inputfile in glob(join(bdir, "../libvmod_*/vmod.vcc")):
-			runmain(inputfile, ".", tmpfile)
-			unlink(tmpfile + ".c")
-			unlink(tmpfile + ".h")
+		fx = open(fn2, "w")
 
+		for i in self.contents:
+			if type(i) == s_object:
+				i.cfile(fo)
+				i.cfile(fx)
 
-#######################################################################
-def runmain(inputvcc, rstdir, outputprefix):
-	# Read the file in
-	lines = []
-	with open(inputvcc, "r") as fp:
-		for i in fp:
-			lines.append(i.rstrip())
-	ln = 0
-
-	#######################################################################
-	# First collect the copyright:  All initial lines starting with '#'
-
-	copy_right = []
-	while len(lines[0]) > 0 and lines[0][0] == "#":
-		ln += 1
-		copy_right.append(lines.pop(0))
-
-	if len(copy_right) > 0:
-		if copy_right[0] == "#-":
-			copy_right = []
-		else:
-			while polish(copy_right):
-				continue
+		fx.write("/* Functions */\n")
+		for i in self.contents:
+			if type(i) == s_function:
+				i.cfile(fo)
+				i.cfile(fx)
 
-	if False:
-		for i in copy_right:
-			print("(C)\t", i)
-
-	#######################################################################
-	# Break into sections
-
-	sl = []
-	sc = FileSection()
-	sl.append(sc)
-	while len(lines) > 0:
-		ln += 1
-		l = lines.pop(0)
-		j = l.split()
-		if len(j) > 0 and re.match("^\$", j[0]):
-			sc = FileSection()
-			sl.append(sc)
-		sc.add_line(ln, l)
-
-	#######################################################################
-	# Parse each section
-
-	try:
-		vx = []
-		for i in sl:
-			i.parse(vx)
-			assert len(i.tl) == 0
-	except ParseError as e:
-		print("ERROR: Parse error reading \"%s\":" % inputvcc)
-		pprint(str(e))
-		exit(-1)
-	except FormatError as e:
-		print("ERROR: Format error reading \"%s\": %s" %
-		    (inputvcc, pformat(e.msg)))
-		print(e.details)
-		exit(-2)
+		csn = "Vmod_%s_Func" % self.modname
 
-	#######################################################################
-	# Parsing done, now process
-	#
-	fd_cfile, fn_cfile = mkstemp(dir='.')
-	cfile = fdopen(fd_cfile, "w")
+		self.cstruct(fo, "struct " + csn)
 
-	fd_headerfile, fn_headerfile = mkstemp(dir='.')
-	headerfile = fdopen(fd_headerfile, "w")
+		self.cstruct(fx, "struct " + csn)
 
-	write_c_file_warning(cfile)
-	write_c_file_warning(headerfile)
+		fo.write("\n/*lint -esym(754, Vmod_debug_Func::*) */\n")
+		self.cstruct_init(fo, "struct " + csn)
 
-	headerfile.write('struct vmod_priv;\n\n')
+		fx.close()
 
-	headerfile.write('extern const struct vmod_data Vmod_%s_Data;\n\n' % vx[0].nam)
+		fo.write("\nstatic const char Vmod_Proto[] =\n")
+		fi = open(fn2)
+		for i in fi:
+			fo.write('\t"%s\\n"\n' % i.rstrip())
+		fi.close()
+		fo.write('\t"static struct %s %s;";\n' % (csn, csn))
 
-	vx[0].c_proto(headerfile)
+		os.remove(fn2)
 
-	cfile.write('#include "config.h"\n')
-	cfile.write('#include "vcl.h"\n')
-	cfile.write('#include "vrt.h"\n')
-	cfile.write('#include "%s.h"\n' % outputprefix)
-	cfile.write('#include "vmod_abi.h"\n')
-	cfile.write('\n')
+		self.specstr(fo)
 
-	vx[0].c_typedefs(cfile)
-	vx[0].c_vmod(cfile)
+		self.api(fo)
 
-	cfile.close()
-	headerfile.close()
+		fo.close()
 
-	for suf in ("", ".man"):
-		fd, fn_fp = mkstemp(dir='.')
-		fp = fdopen(fd, "w")
-		write_rst_file_warning(fp)
+#######################################################################
 
-		vx[0].doc_dump(fp, suf)
+def runmain(inputvcc, rstdir, outputprefix):
 
-		if len(copy_right) > 0:
-			fp.write("\n")
-			fp.write("COPYRIGHT\n")
-			fp.write("=========\n")
-			fp.write("\n::\n\n")
-			for i in copy_right:
-				fp.write("  %s\n" % i)
-			fp.write("\n")
-		fp.close()
-		rename(fn_fp, join(rstdir, "vmod_%s%s.rst" % (vx[0].nam, suf)))
+	v = vcc(inputvcc, rstdir, outputprefix)
+	v.parse()
 
-	# Our makefile targets the .c file so make sure to put that in place last.
-	rename(fn_headerfile, outputprefix + ".h")
-	rename(fn_cfile, outputprefix + ".c")
+	v.rstfile(man=False)
+	v.rstfile(man=True)
+	v.hfile()
+	v.cfile()
 
+	v.commit()
 
 if __name__ == "__main__":
 	usagetext = "Usage: %prog [options] <vmod.vcc>"



More information about the varnish-commit mailing list