[master] 00346d0 Add VSL expression parser and executor

Martin Blix Grydeland martin at varnish-cache.org
Tue Oct 1 14:48:17 CEST 2013


commit 00346d0e7a8c2a20a55f092ddf07189445e2528e
Author: Martin Blix Grydeland <martin at varnish-software.com>
Date:   Tue Jul 30 15:29:23 2013 +0200

    Add VSL expression parser and executor
    
    This is based on the libvcl parser

diff --git a/bin/varnishtest/tests/l00000.vtc b/bin/varnishtest/tests/l00000.vtc
index 90d9a7a..adf7c8f 100644
--- a/bin/varnishtest/tests/l00000.vtc
+++ b/bin/varnishtest/tests/l00000.vtc
@@ -29,7 +29,7 @@ logexpect l1 -v v1 -g session {
 } -start
 
 # Check with a query (this selects only the backend request)
-logexpect l2 -v v1 -g vxid -q "bereq 1001" {
+logexpect l2 -v v1 -g vxid -q "Begin eq 'bereq 1001'" {
 	expect 0 1002	Begin
 	expect * =	End
 } -start
diff --git a/lib/libvarnishapi/Makefile.am b/lib/libvarnishapi/Makefile.am
index ac4fabc..375ab29 100644
--- a/lib/libvarnishapi/Makefile.am
+++ b/lib/libvarnishapi/Makefile.am
@@ -11,6 +11,8 @@ libvarnishapi_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0
 libvarnishapi_la_SOURCES = \
 	vsm_api.h \
 	vsl_api.h \
+	vxp.h \
+	vxp_tokens.h \
 	\
 	../libvarnish/vas.c \
 	../libvarnish/vav.c \
@@ -31,6 +33,10 @@ libvarnishapi_la_SOURCES = \
 	vsl_query.c \
 	vsl.c \
 	vsc.c \
+	vxp.c \
+	vxp_parse.c \
+	vxp_lexer.c \
+	vxp_fixed_token.c \
 	libvarnishapi.map
 
 libvarnishapi_la_CFLAGS = \
@@ -43,3 +49,31 @@ libvarnishapi_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libvarnishapi.map
 else
 libvarnishapi_la_LDFLAGS += -export-symbols-regex '^V'
 endif
+
+EXTRA_DIST = \
+	generate.py
+
+BUILT_SOURCES = \
+	vxp_fixed_token.c \
+	vxp_tokens.h
+
+CLEANFILES = \
+	$(builddir)/vxp_fixed_token.c \
+	$(builddir)/vxp_tokens.h
+
+vxp_fixed_token.c vxp_tokens.h: \
+	$(srcdir)/generate.py
+	@PYTHON@ $(srcdir)/generate.py $(srcdir) $(top_builddir)
+
+EXTRA_PROGRAMS = vxp_test
+
+vxp_test_LDADD = @PCRE_LIBS@ \
+	${RT_LIBS} ${LIBM} ${PTHREAD_LIBS}
+
+vxp_test_CFLAGS = \
+	-DVARNISH_STATE_DIR='"${VARNISH_STATE_DIR}"' \
+	-DVXP_DEBUG
+
+vxp_test_SOURCES = \
+	$(libvarnishapi_la_SOURCES) \
+	vxp_test.c
diff --git a/lib/libvarnishapi/generate.py b/lib/libvarnishapi/generate.py
new file mode 100755
index 0000000..1377f71
--- /dev/null
+++ b/lib/libvarnishapi/generate.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+#-
+# Copyright (c) 2006 Verdens Gang AS
+# Copyright (c) 2006-2013 Varnish Software AS
+# All rights reserved.
+#
+# Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+# Author: Martin Blix Grydeland <martin at varnish-software.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Generate various .c and .h files for the VSL query expression parser
+# and the interfaces for it.
+
+import sys
+import copy
+
+srcroot = "../.."
+buildroot = "../.."
+if len(sys.argv) == 3:
+        srcroot = sys.argv[1]
+        buildroot = sys.argv[2]
+
+#######################################################################
+# These are our tokens
+
+tokens = {
+        # Numerical comparisons
+        "T_EQ":         "==",
+        "T_NEQ":        "!=",
+        "T_LEQ":        "<=",
+        "T_GEQ":        ">=",
+
+        # String comparisons
+        "T_SEQ":        "eq",
+        "T_SNEQ":       "ne",
+
+        # Regular expression matching
+        "T_NOMATCH":    "!~",
+
+        # Boolean operators
+        "T_NOT":        "not",
+        "T_AND":        "and",
+        "T_OR":         "or",
+
+        # Miscellaneous
+        None:           "<>~![]{}()",
+
+        # These have handwritten recognizers
+        "VAL":          None,
+        "EOI":          None,
+}
+
+#######################################################################
+# Emit a function to recognize tokens in a string
+
+def emit_vxp_fixed_token(fo, tokens):
+        recog = list()
+        emit = dict()
+        for i in tokens:
+                j = tokens[i]
+                if (j != None):
+                        recog.append(j)
+                        emit[j] = i
+
+        recog.sort()
+        rrecog = copy.copy(recog)
+        rrecog.sort(key = lambda x: -len(x))
+
+        fo.write("""
+unsigned
+vxp_fixed_token(const char *p, const char **q)
+{
+
+\tswitch (p[0]) {
+""")
+        last_initial = None
+        for i in recog:
+                if (i[0] == last_initial):
+                        continue
+                last_initial = i[0]
+                fo.write("\tcase '%s':\n" % last_initial)
+                for j in rrecog:
+                        if (j[0] != last_initial):
+                                continue
+
+			fo.write("\t\tif (")
+			k = 1
+			l = len(j)
+			while (k < l):
+				fo.write("p[%d] == '%s'" % (k, j[k]))
+				fo.write(" &&\n\t\t    ")
+				k += 1
+			fo.write("(isword(p[%d]) ? !isword(p[%d]) : 1)) {\n" %
+				 (l - 1, l))
+			fo.write("\t\t\t*q = p + %d;\n" % l)
+			fo.write("\t\t\treturn (%s);\n" % emit[j])
+			fo.write("\t\t}\n");
+		fo.write("\t\treturn (0);\n")
+
+        fo.write("\tdefault:\n\t\treturn (0);\n\t}\n}\n")
+
+#######################################################################
+# Emit the vxp_tnames (token->string) conversion array
+
+def emit_vxp_tnames(fo, tokens):
+        fo.write("\nconst char * const vxp_tnames[256] = {\n")
+        l = list(tokens.keys())
+        l.sort()
+        for i in l:
+                j = tokens[i]
+                if j == None:
+                        j = i
+                if i[0] == "'":
+                        j = i
+                fo.write("\t[%s] = \"%s\",\n" % (i, j))
+        fo.write("};\n")
+
+#######################################################################
+
+def polish_tokens(tokens):
+        # Expand single char tokens
+        st = tokens[None]
+        del tokens[None]
+
+        for i in st:
+                tokens["'" + i + "'"] = i
+
+#######################################################################
+
+def file_header(fo):
+        fo.write("""/*
+ * NB:  This file is machine generated, DO NOT EDIT!
+ *
+ * Edit and run generate.py instead
+ */
+""")
+
+#######################################################################
+
+polish_tokens(tokens)
+
+fo = open(buildroot + "/lib/libvarnishapi/vxp_tokens.h", "w")
+
+file_header(fo)
+
+j = 128
+l = list(tokens.keys())
+l.sort()
+for i in l:
+        if i[0] == "'":
+                continue
+        fo.write("#define\t%s %d\n" % (i, j))
+        j += 1
+        assert j < 256
+
+fo.close()
+
+#######################################################################
+
+fo = open(buildroot + "/lib/libvarnishapi/vxp_fixed_token.c", "w")
+
+file_header(fo)
+fo.write("""
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "vxp.h"
+""")
+
+emit_vxp_fixed_token(fo, tokens)
+emit_vxp_tnames(fo, tokens)
+
+fo.close()
diff --git a/lib/libvarnishapi/vsl_query.c b/lib/libvarnishapi/vsl_query.c
index 436d7c9..09554cb 100644
--- a/lib/libvarnishapi/vsl_query.c
+++ b/lib/libvarnishapi/vsl_query.c
@@ -32,42 +32,121 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <string.h>
 
 #include "vas.h"
 #include "miniobj.h"
 #include "vre.h"
+#include "vsb.h"
 
 #include "vapi/vsl.h"
 #include "vsl_api.h"
+#include "vxp.h"
+
+#define NEEDLESS_RETURN(foo) return(foo)
 
 struct vslq_query {
 	unsigned		magic;
 #define VSLQ_QUERY_MAGIC	0x122322A5
 
-	vre_t			*regex;
+	struct vex		*vex;
 };
 
+static int
+vslq_test(const struct vex *vex, struct VSL_transaction * const ptrans[])
+{
+	struct VSL_transaction *t;
+	int i, reclen, vallen;
+	const char *recdata;
+
+	CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+	CHECK_OBJ_NOTNULL(vex->tag, VEX_TAG_MAGIC);
+	CHECK_OBJ_NOTNULL(vex->val, VEX_VAL_MAGIC);
+	AN(vex->val->val_string);
+
+	vallen = strlen(vex->val->val_string);
+	for (t = ptrans[0]; t != NULL; t = *++ptrans) {
+		AZ(VSL_ResetCursor(t->c));
+		while (1) {
+			i = VSL_Next(t->c);
+			if (i < 0)
+				return (i);
+			if (i == 0)
+				break;
+			assert(i == 1);
+			AN(t->c->rec.ptr);
+
+			if (vex->tag->tag != VSL_TAG(t->c->rec.ptr))
+				continue;
+
+			reclen = VSL_LEN(t->c->rec.ptr);
+			recdata = VSL_CDATA(t->c->rec.ptr);
+			if (reclen == vallen &&
+			    !strncmp(vex->val->val_string, recdata, reclen))
+				return (1);
+		}
+	}
+
+	return (0);
+}
+
+static int
+vslq_exec(const struct vex *vex, struct VSL_transaction * const ptrans[])
+{
+	int r;
+
+	CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+
+	switch (vex->tok) {
+	case T_OR:
+		AN(vex->a);
+		AN(vex->b);
+		r = vslq_exec(vex->a, ptrans);
+		if (r != 0)
+			return (r);
+		return (vslq_exec(vex->b, ptrans));
+	case T_AND:
+		AN(vex->a);
+		AN(vex->b);
+		r = vslq_exec(vex->a, ptrans);
+		if (r <= 0)
+			return (r);
+		return (vslq_exec(vex->b, ptrans));
+	case T_NOT:
+		AN(vex->a);
+		AZ(vex->b);
+		r = vslq_exec(vex->a, ptrans);
+		if (r < 0)
+			return (r);
+		return (!r);
+	default:
+		return (vslq_test(vex, ptrans));
+	}
+	NEEDLESS_RETURN(0);
+}
+
 struct vslq_query *
 vslq_newquery(struct VSL_data *vsl, enum VSL_grouping_e grouping,
     const char *querystring)
 {
-	struct vslq_query *query;
-	const char *error;
-	int pos;
-	vre_t *regex;
+	struct vsb *vsb;
+	struct vex *vex;
+	struct vslq_query *query = NULL;
 
 	(void)grouping;
 	AN(querystring);
-	regex = VRE_compile(querystring, 0, &error, &pos);
-	if (regex == NULL) {
-		vsl_diag(vsl, "failed to compile regex at pos %d: %s",
-		    pos, error);
-		return (NULL);
-	}
 
-	ALLOC_OBJ(query, VSLQ_QUERY_MAGIC);
-	if (query != NULL)
-		query->regex = regex;
+	vsb = VSB_new_auto();
+	AN(vsb);
+	vex = vex_New(querystring, vsb);
+	VSB_finish(vsb);
+	if (vex == NULL)
+		vsl_diag(vsl, "Query expression error:\n%s", VSB_data(vsb));
+	else {
+		ALLOC_OBJ(query, VSLQ_QUERY_MAGIC);
+		query->vex = vex;
+	}
+	VSB_delete(vsb);
 	return (query);
 }
 
@@ -81,45 +160,24 @@ vslq_deletequery(struct vslq_query **pquery)
 	*pquery = NULL;
 	CHECK_OBJ_NOTNULL(query, VSLQ_QUERY_MAGIC);
 
-	AN(query->regex);
-	VRE_free(&query->regex);
-	AZ(query->regex);
+	AN(query->vex);
+	vex_Free(&query->vex);
+	AZ(query->vex);
 
 	FREE_OBJ(query);
 }
 
 int
-vslq_runquery(const struct vslq_query *query, struct VSL_transaction * const ptrans[])
+vslq_runquery(const struct vslq_query *query,
+    struct VSL_transaction * const ptrans[])
 {
 	struct VSL_transaction *t;
-	struct VSL_cursor *c;
-	int i, len;
-	const char *data;
+	int r;
 
 	CHECK_OBJ_NOTNULL(query, VSLQ_QUERY_MAGIC);
-	AN(query->regex);
 
-	t = ptrans[0];
-	while (t) {
-		c = t->c;
-		while (1) {
-			i = VSL_Next(c);
-			if (i == 0)
-				break;
-			assert(i == 1);
-			AN(c->rec.ptr);
-			len = VSL_LEN(c->rec.ptr);
-			data = VSL_CDATA(c->rec.ptr);
-			i = VRE_exec(query->regex, data, len, 0, 0, NULL, 0,
-			    NULL);
-			if (i != VRE_ERROR_NOMATCH) {
-				AZ(VSL_ResetCursor(c));
-				return (1);
-			}
-		}
-		AZ(VSL_ResetCursor(c));
-		t = *++ptrans;
-	}
-
-	return (0);
+	r = vslq_exec(query->vex, ptrans);
+	for (t = ptrans[0]; t != NULL; t = *++ptrans)
+		AZ(VSL_ResetCursor(t->c));
+	return (r);
 }
diff --git a/lib/libvarnishapi/vxp.c b/lib/libvarnishapi/vxp.c
new file mode 100644
index 0000000..fffa550
--- /dev/null
+++ b/lib/libvarnishapi/vxp.c
@@ -0,0 +1,232 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "vsb.h"
+#include "vas.h"
+#include "miniobj.h"
+
+#include "vxp.h"
+
+static void
+vxp_ErrToken(const struct vxp *vxp, const struct token *t)
+{
+
+	if (t->tok == EOI)
+		VSB_printf(vxp->sb, "end of input");
+	else
+		VSB_printf(vxp->sb, "'%.*s'", PF(t));
+}
+
+static void
+vxp_Pos(struct vxp *vxp, struct vsb *vsb, const struct token *t, int tokoff)
+{
+	unsigned pos;
+
+	AN(vxp);
+	AN(vsb);
+	AN(t);
+	assert(t->b >= vxp->b);
+	pos = (unsigned)(t->b - vxp->b);
+	if (tokoff > 0)
+		pos += tokoff;
+	VSB_printf(vsb, "(Pos %u)", pos + 1);
+}
+
+static void
+vxp_quote(struct vxp *vxp, const char *b, const char *e, int tokoff)
+{
+	const char *p;
+	char c;
+
+	assert(b <= e);
+	assert(b >= vxp->b);
+	assert(e <= vxp->e);
+	for (p = vxp->b; p < vxp->e; p++) {
+		if (isspace(*p))
+			VSB_bcat(vxp->sb, " ", 1);
+		else
+			VSB_bcat(vxp->sb, p, 1);
+	}
+	VSB_putc(vxp->sb, '\n');
+	for (p = vxp->b; p < vxp->e; p++) {
+		if (p >= b && p < e) {
+			if (p - b == tokoff)
+				c = '^';
+			else
+				c = '#';
+		} else
+			c = '-';
+		VSB_putc(vxp->sb, c);
+	}
+	VSB_putc(vxp->sb, '\n');
+}
+
+void
+vxp_ErrWhere(struct vxp *vxp, const struct token *t, int tokoff)
+{
+
+	AN(vxp);
+	AN(t);
+	vxp_Pos(vxp, vxp->sb, t, tokoff);
+	VSB_putc(vxp->sb, '\n');
+	vxp_quote(vxp, t->b, t->e, tokoff);
+	VSB_putc(vxp->sb, '\n');
+	vxp->err = 1;
+}
+
+void
+vxp_NextToken(struct vxp *vxp)
+{
+
+	AN(vxp->t);
+	vxp->t = VTAILQ_NEXT(vxp->t, list);
+	if (vxp->t == NULL) {
+		VSB_printf(vxp->sb,
+		    "Ran out of input, something is missing or"
+		    " maybe unbalanced parenthesis\n");
+		vxp->err = 1;
+	}
+}
+
+void
+vxp__Expect(struct vxp *vxp, unsigned tok)
+{
+
+	if (vxp->t->tok == tok)
+		return;
+	VSB_printf(vxp->sb, "Expected %s got ", vxp_tnames[tok]);
+	vxp_ErrToken(vxp, vxp->t);
+	VSB_putc(vxp->sb, ' ');
+	vxp_ErrWhere(vxp, vxp->t, -1);
+}
+
+static void
+vxp_DoFree(struct vxp *vxp, void *p)
+{
+	struct membit *mb;
+
+	mb = calloc(sizeof *mb, 1);
+	AN(mb);
+	mb->ptr = p;
+	VTAILQ_INSERT_TAIL(&vxp->membits, mb, list);
+}
+
+void *
+vxp_Alloc(struct vxp *vxp, unsigned len)
+{
+	void *p;
+
+	p = calloc(len, 1);
+	AN(p);
+	vxp_DoFree(vxp, p);
+	return (p);
+}
+
+static struct vxp *
+vxp_New(struct vsb *sb)
+{
+	struct vxp *vxp;
+
+	AN(sb);
+
+	ALLOC_OBJ(vxp, VXP_MAGIC);
+	AN(vxp);
+	VTAILQ_INIT(&vxp->membits);
+	VTAILQ_INIT(&vxp->tokens);
+	vxp->sb = sb;
+
+	return (vxp);
+}
+
+static void
+vxp_Delete(struct vxp **pvxp)
+{
+	struct vxp *vxp;
+	struct membit *mb;
+
+	AN(pvxp);
+	vxp = *pvxp;
+	*pvxp = NULL;
+	CHECK_OBJ_NOTNULL(vxp, VXP_MAGIC);
+
+	while (!VTAILQ_EMPTY(&vxp->membits)) {
+		mb = VTAILQ_FIRST(&vxp->membits);
+		VTAILQ_REMOVE(&vxp->membits, mb, list);
+		free(mb->ptr);
+		free(mb);
+	}
+
+	FREE_OBJ(vxp);
+}
+
+struct vex *
+vex_New(const char *query, struct vsb *sb)
+{
+	struct vxp *vxp;
+	struct vex *vex;
+
+	AN(query);
+	AN(sb);
+	vxp = vxp_New(sb);
+	vxp->b = query;
+	vxp->e = query + strlen(query);
+
+	vxp_Lexer(vxp);
+
+#ifdef VXP_DEBUG
+	vxp_PrintTokens(vxp);
+#endif
+
+	if (vxp->err) {
+		vxp_Delete(&vxp);
+		AZ(vxp);
+		return (NULL);
+	}
+
+	vex = vxp_Parse(vxp);
+
+#ifdef VXP_DEBUG
+	if (vex != NULL)
+		vex_PrintTree(vex);
+#endif
+
+	vxp_Delete(&vxp);
+	AZ(vxp);
+
+	return (vex);
+}
diff --git a/lib/libvarnishapi/vxp.h b/lib/libvarnishapi/vxp.h
new file mode 100644
index 0000000..9243e0b
--- /dev/null
+++ b/lib/libvarnishapi/vxp.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+
+#include "vqueue.h"
+#include "vre.h"
+
+#include "vxp_tokens.h"
+
+#define isword(c) \
+	(isalpha(c) || isdigit(c) || (c) == '_' || (c) == '-' || (c) == '.')
+
+#define PF(t)	(int)((t)->e - (t)->b), (t)->b
+
+/* From vex_fixed_token.c */
+unsigned vxp_fixed_token(const char *p, const char **q);
+extern const char * const vxp_tnames[256];
+
+struct membit {
+	VTAILQ_ENTRY(membit)	list;
+	void			*ptr;
+};
+
+struct token {
+	unsigned		tok;
+	const char		*b;
+	const char		*e;
+	VTAILQ_ENTRY(token)	list;
+	unsigned		cnt;
+	char			*dec;
+};
+
+struct vxp {
+	unsigned		magic;
+#define VXP_MAGIC		0x59C7F6AC
+
+	const char		*src;
+	const char		*b;
+	const char		*e;
+
+	VTAILQ_HEAD(, token)	tokens;
+	VTAILQ_HEAD(, membit)	membits;
+	struct token		*t;
+
+	struct vsb		*sb;
+	int			err;
+};
+
+struct vex;
+
+struct vex_tag {
+	unsigned		magic;
+#define VEX_TAG_MAGIC		0x1AD3D78D
+	int			tag;
+	int			field;
+	int			level_min;
+	int			level_max;
+};
+
+enum vex_val_e {
+	VEX__UNSET,
+	VEX_INT,
+	VEX_FLOAT,
+	VEX_STRING,
+	VEX_REGEX,
+};
+
+struct vex_val {
+	unsigned		magic;
+#define VEX_VAL_MAGIC		0x3F109965
+	enum vex_val_e		type;
+	long long		val_int;
+	double			val_float;
+	char			*val_string;
+	vre_t			*val_regex;
+};
+
+struct vex {
+	unsigned		magic;
+#define VEX_MAGIC		0xC7DB792D
+	unsigned		tok;
+	struct vex		*a, *b;
+	struct vex_tag		*tag;
+	struct vex_val		*val;
+};
+
+/* VXP internals */
+
+#define ERRCHK(tl)	do { if ((tl)->err) return; } while (0)
+#define Expect(a, b)	vxp__Expect(a, b)
+#define ExpectErr(a, b)	\
+    do { vxp__Expect(a, b); ERRCHK(a); } while (0)
+#define SkipToken(a, b) \
+    do { vxp__Expect(a, b); ERRCHK(a); vxp_NextToken(a); } while (0)
+
+void vxp__Expect(struct vxp *vxp, unsigned tok);
+void vxp_ErrWhere(struct vxp *vxp, const struct token *t, int tokoff);
+void vxp_NextToken(struct vxp *vxp);
+void * vxp_Alloc(struct vxp *vxp, unsigned len);
+void vxp_Lexer(struct vxp *vxp);
+struct vex * vxp_Parse(struct vxp *vxp);
+
+/* API internal interface */
+
+struct vex * vex_New(const char *query, struct vsb *sb);
+void vex_Free(struct vex **pvex);
+
+/* Debug routines */
+#ifdef VXP_DEBUG
+void vxp_PrintTokens(const struct vxp *vxp);
+void vex_PrintTree(const struct vex *vex);
+#endif /* VXP_DEBUG */
diff --git a/lib/libvarnishapi/vxp_lexer.c b/lib/libvarnishapi/vxp_lexer.c
new file mode 100644
index 0000000..076edaa
--- /dev/null
+++ b/lib/libvarnishapi/vxp_lexer.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "vsb.h"
+#include "vas.h"
+
+#include "vxp.h"
+
+static void
+vxp_add_token(struct vxp *vxp, unsigned tok, const char *b, const char *e)
+{
+	struct token *t;
+
+	t = vxp_Alloc(vxp, sizeof *t);
+	AN(t);
+	t->tok = tok;
+	t->b = b;
+	t->e = e;
+	if (vxp->t != NULL)
+		VTAILQ_INSERT_AFTER(&vxp->tokens, vxp->t, t, list);
+	else
+		VTAILQ_INSERT_TAIL(&vxp->tokens, t, list);
+	vxp->t = t;
+}
+
+static int
+vxp_decstr(struct vxp *vxp, int quoted)
+{
+	const char *b, *e, *p;
+	char *s;
+	unsigned l;
+	int esc = 0;
+
+	assert(vxp->t->tok == VAL);
+
+	b = vxp->t->b;
+	e = vxp->t->e;
+	if (quoted) {
+		assert(e - b >= 2);
+		b++;
+		e--;
+	}
+	l = e - b;
+	s = vxp->t->dec = vxp_Alloc(vxp, l + 1);
+	AN(vxp->t->dec);
+	for (p = b; p < e; p++) {
+		if (!esc && *p == '\\') {
+			esc = 1;
+			continue;
+		}
+		esc = 0;
+		*s++ = *p;
+	}
+	*s = '\0';
+	if (esc || p != e) {
+		VSB_printf(vxp->sb, "Syntax error ");
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * Build a token list
+ */
+
+void
+vxp_Lexer(struct vxp *vxp)
+{
+	const char *p, *q;
+	unsigned u;
+	char quote;
+
+	for (p = vxp->b; p < vxp->e; ) {
+
+		/* Skip any whitespace */
+		if (isspace(*p)) {
+			p++;
+			continue;
+		}
+
+		/* Match for the fixed tokens */
+		u = vxp_fixed_token(p, &q);
+		if (u != 0) {
+			AN(q);
+			vxp_add_token(vxp, u, p, q);
+			p = q;
+			continue;
+		}
+
+		/* Match quoted strings */
+		if (*p == '"' || *p == '\'') {
+			quote = *p;
+			for (q = p + 1; q < vxp->e; q++) {
+				if (q[-1] == '\\')
+					continue;
+				if (*q == quote) {
+					q++;
+					quote = '\0';
+					break;
+				}
+			}
+			vxp_add_token(vxp, VAL, p, q);
+			if (quote != '\0') {
+				VSB_printf(vxp->sb, "Unterminated string ");
+				vxp_ErrWhere(vxp, vxp->t, q - p - 1);
+				return;
+			}
+			if (vxp_decstr(vxp, 1))
+				return;
+			p = q;
+			continue;
+		}
+
+		/* Match bareword */
+		if (isword(*p)) {
+			for (q = p; q < vxp->e; q++)
+				if (!isword(*q))
+					break;
+			vxp_add_token(vxp, VAL, p, q);
+			if (vxp_decstr(vxp, 0))
+				return;
+			p = q;
+			continue;
+		}
+
+		/* Error */
+		vxp_add_token(vxp, EOI, p, p + 1);
+		VSB_printf(vxp->sb, "Syntax error ");
+		vxp_ErrWhere(vxp, vxp->t, q - p);
+		return;
+	}
+
+	/* Finished */
+	vxp_add_token(vxp, EOI, vxp->e, vxp->e);
+}
+
+#ifdef VXP_DEBUG
+void
+vxp_PrintTokens(const struct vxp *vxp)
+{
+	struct token *t;
+
+	fprintf(stderr, "Token list:\n");
+	fprintf(stderr, "  %-5s %-20s %s\n", "TOK", "SUBSTR", "DECODED");
+	VTAILQ_FOREACH(t, &vxp->tokens, list) {
+		fprintf(stderr, "  ");
+		fprintf(stderr, "%-5s", vxp_tnames[t->tok]);
+		fprintf(stderr, " %-20.*s", (unsigned)(t->e - t->b), t->b);
+		if (t->dec)
+			fprintf(stderr, " '%s'", t->dec);
+		fprintf(stderr, "\n");
+	}
+	fprintf(stderr, "\n");
+}
+#endif /* VXP_DEBUG */
diff --git a/lib/libvarnishapi/vxp_parse.c b/lib/libvarnishapi/vxp_parse.c
new file mode 100644
index 0000000..390be8d
--- /dev/null
+++ b/lib/libvarnishapi/vxp_parse.c
@@ -0,0 +1,477 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "vas.h"
+#include "vsb.h"
+#include "miniobj.h"
+#include "vapi/vsl.h"
+
+#include "vxp.h"
+
+static void vxp_expr_or(struct vxp *vxp, struct vex **pvex);
+
+static void
+vxp_expr_tag(struct vxp *vxp, struct vex_tag **ptag)
+{
+
+	/* XXX: Tag wildcards */
+	AN(ptag);
+	AZ(*ptag);
+	if (vxp->t->tok != VAL) {
+		VSB_printf(vxp->sb, "Expected VSL tag got '%.*s' ", PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	}
+	ALLOC_OBJ(*ptag, VEX_TAG_MAGIC);
+	AN(*ptag);
+	(*ptag)->tag = VSL_Name2Tag(vxp->t->dec, -1);
+	if ((*ptag)->tag == -1) {
+		VSB_printf(vxp->sb, "Could not match '%.*s' to any tag ",
+		    PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	} else if ((*ptag)->tag == -2) {
+		VSB_printf(vxp->sb, "'%.*s' matches multiple tags ",
+		    PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	}
+	vxp_NextToken(vxp);
+
+	/* XXX: Tag limiting operators ([], {}) */
+}
+
+static void
+vxp_expr_num(struct vxp *vxp, struct vex_val **pval)
+{
+	char *endptr;
+
+	AN(pval);
+	AZ(*pval);
+	if (vxp->t->tok != VAL) {
+		VSB_printf(vxp->sb, "Expected number got '%.*s' ", PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	}
+	AN(vxp->t->dec);
+	ALLOC_OBJ(*pval, VEX_VAL_MAGIC);
+	AN(*pval);
+	if (strchr(vxp->t->dec, '.')) {
+		(*pval)->type = VEX_FLOAT;
+		(*pval)->val_float = strtod(vxp->t->dec, &endptr);
+		while (isspace(*endptr))
+			endptr++;
+		if (*endptr != '\0') {
+			VSB_printf(vxp->sb, "Floating point parse error ");
+			vxp_ErrWhere(vxp, vxp->t, -1);
+			return;
+		}
+	} else {
+		(*pval)->type = VEX_INT;
+		(*pval)->val_int = strtoll(vxp->t->dec, &endptr, 0);
+		while (isspace(*endptr))
+			endptr++;
+		if (*endptr != '\0') {
+			VSB_printf(vxp->sb, "Integer parse error ");
+			vxp_ErrWhere(vxp, vxp->t, -1);
+			return;
+		}
+	}
+	vxp_NextToken(vxp);
+}
+
+static void
+vxp_expr_str(struct vxp *vxp, struct vex_val **pval)
+{
+
+	AN(pval);
+	AZ(*pval);
+	if (vxp->t->tok != VAL) {
+		VSB_printf(vxp->sb, "Expected string got '%.*s' ", PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	}
+	AN(vxp->t->dec);
+	ALLOC_OBJ(*pval, VEX_VAL_MAGIC);
+	AN(*pval);
+	(*pval)->type = VEX_STRING;
+	(*pval)->val_string = strdup(vxp->t->dec);
+	AN((*pval)->val_string);
+	vxp_NextToken(vxp);
+}
+
+static void
+vxp_expr_regex(struct vxp *vxp, struct vex_val **pval)
+{
+	const char *errptr;
+	int erroff;
+
+	/* XXX: Caseless option */
+
+	AN(pval);
+	AZ(*pval);
+	if (vxp->t->tok != VAL) {
+		VSB_printf(vxp->sb, "Expected regular expression got '%.*s' ",
+		    PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	}
+	AN(vxp->t->dec);
+	ALLOC_OBJ(*pval, VEX_VAL_MAGIC);
+	AN(*pval);
+	(*pval)->type = VEX_REGEX;
+	(*pval)->val_string = strdup(vxp->t->dec);
+	(*pval)->val_regex = VRE_compile(vxp->t->dec, 0, &errptr, &erroff);
+	if ((*pval)->val_regex == NULL) {
+		AN(errptr);
+		VSB_printf(vxp->sb, "Regular expression error: %s ", errptr);
+		vxp_ErrWhere(vxp, vxp->t, erroff);
+		return;
+	}
+	vxp_NextToken(vxp);
+}
+
+/*
+ * SYNTAX:
+ *   expr_cmp:
+ *     tag
+ *     tag <operator> num|str|regex
+ */
+
+static void
+vxp_expr_cmp(struct vxp *vxp, struct vex **pvex)
+{
+
+	AN(pvex);
+	AZ(*pvex);
+	ALLOC_OBJ(*pvex, VEX_MAGIC);
+	AN(*pvex);
+	vxp_expr_tag(vxp, &(*pvex)->tag);
+	ERRCHK(vxp);
+
+	/* Test operator */
+	switch (vxp->t->tok) {
+
+	/* Single tag expressions don't take any more tokens */
+	case EOI:
+	case T_AND:
+	case T_OR:
+	case ')':
+		return;
+
+	/* Valid operators */
+	case T_EQ:		/* == */
+	case T_NEQ:		/* != */
+	case T_SEQ:		/* eq */
+	case T_SNEQ:		/* ne */
+	case '~':		/* ~ */
+	case T_NOMATCH:		/* !~ */
+		(*pvex)->tok = vxp->t->tok;
+		break;
+
+	/* Error */
+	default:
+		VSB_printf(vxp->sb, "Expected operator got '%.*s' ",
+		    PF(vxp->t));
+		vxp_ErrWhere(vxp, vxp->t, -1);
+		return;
+	}
+	vxp_NextToken(vxp);
+	ERRCHK(vxp);
+
+	/* Value */
+	switch((*pvex)->tok) {
+	case '\0':
+		WRONG("Missing token");
+	case T_EQ:		/* == */
+	case T_GEQ:		/* >= */
+	case T_LEQ:		/* <= */
+	case T_NEQ:		/* != */
+		vxp_expr_num(vxp, &(*pvex)->val);
+		break;
+	case T_SEQ:		/* eq */
+	case T_SNEQ:		/* ne */
+		vxp_expr_str(vxp, &(*pvex)->val);
+		break;
+	case '~':		/* ~ */
+	case T_NOMATCH:		/* !~ */
+		vxp_expr_regex(vxp, &(*pvex)->val);
+		break;
+	default:
+		INCOMPL();
+	}
+}
+
+/*
+ * SYNTAX:
+ *   expr_group:
+ *     '(' expr_or ')'
+ *     expr_not
+ */
+
+static void
+vxp_expr_group(struct vxp *vxp, struct vex **pvex)
+{
+
+	AN(pvex);
+	AZ(*pvex);
+
+	if (vxp->t->tok == '(') {
+		SkipToken(vxp, '(');
+		vxp_expr_or(vxp, pvex);
+		ERRCHK(vxp);
+		SkipToken(vxp, ')');
+		return;
+	}
+
+	vxp_expr_cmp(vxp, pvex);
+}
+
+/*
+ * SYNTAX:
+ *   expr_not:
+ *     '!' expr_group
+ *     expr_group
+ */
+
+static void
+vxp_expr_not(struct vxp *vxp, struct vex **pvex)
+{
+
+	AN(pvex);
+	AZ(*pvex);
+
+	if (vxp->t->tok == '!') {
+		ALLOC_OBJ(*pvex, VEX_MAGIC);
+		AN(*pvex);
+		(*pvex)->tok = vxp->t->tok;
+		vxp_NextToken(vxp);
+		vxp_expr_group(vxp, &(*pvex)->a);
+		return;
+	}
+
+	vxp_expr_group(vxp, pvex);
+	return;
+}
+
+/*
+ * SYNTAX:
+ *   expr_and:
+ *     expr_not { 'and' expr_not }*
+ */
+
+static void
+vxp_expr_and(struct vxp *vxp, struct vex **pvex)
+{
+	struct vex *a;
+
+	AN(pvex);
+	AZ(*pvex);
+	vxp_expr_not(vxp, pvex);
+	ERRCHK(vxp);
+	while (vxp->t->tok == T_AND) {
+		a = *pvex;
+		ALLOC_OBJ(*pvex, VEX_MAGIC);
+		AN(*pvex);
+		(*pvex)->tok = vxp->t->tok;
+		(*pvex)->a = a;
+		vxp_NextToken(vxp);
+		ERRCHK(vxp);
+		vxp_expr_not(vxp, &(*pvex)->b);
+		ERRCHK(vxp);
+	}
+}
+
+/*
+ * SYNTAX:
+ *   expr_or:
+ *     expr_and { 'or' expr_and }*
+ */
+
+static void
+vxp_expr_or(struct vxp *vxp, struct vex **pvex)
+{
+	struct vex *a;
+
+	AN(pvex);
+	AZ(*pvex);
+	vxp_expr_and(vxp, pvex);
+	ERRCHK(vxp);
+	while (vxp->t->tok == T_OR) {
+		a = *pvex;
+		ALLOC_OBJ(*pvex, VEX_MAGIC);
+		AN(*pvex);
+		(*pvex)->tok = vxp->t->tok;
+		(*pvex)->a = a;
+		vxp_NextToken(vxp);
+		ERRCHK(vxp);
+		vxp_expr_and(vxp, &(*pvex)->b);
+		ERRCHK(vxp);
+	}
+}
+
+/*
+ * SYNTAX:
+ *   expr:
+ *     expr_or EOI
+ */
+
+static void
+vxp_expr(struct vxp *vxp, struct vex **pvex)
+{
+	vxp_expr_or(vxp, pvex);
+	ERRCHK(vxp);
+	ExpectErr(vxp, EOI);
+}
+
+/*
+ * Build a struct vex tree from the token list in vxp
+ */
+
+struct vex *
+vxp_Parse(struct vxp *vxp)
+{
+	struct vex *vex = NULL;
+
+	vxp->t = VTAILQ_FIRST(&vxp->tokens);
+	if (vxp->t == NULL)
+		return (NULL);
+
+	vxp_expr(vxp, &vex);
+
+	if (vxp->err) {
+		if (vex)
+			vex_Free(&vex);
+		AZ(vex);
+		return (NULL);
+	}
+
+	return (vex);
+}
+
+/*
+ * Free a struct vex tree
+ */
+
+void
+vex_Free(struct vex **pvex)
+{
+
+	if ((*pvex)->tag != NULL)
+		FREE_OBJ((*pvex)->tag);
+	if ((*pvex)->val != NULL) {
+		if ((*pvex)->val->val_string)
+			free((*pvex)->val->val_string);
+		if ((*pvex)->val->val_regex)
+			VRE_free(&(*pvex)->val->val_regex);
+		FREE_OBJ((*pvex)->val);
+	}
+	if ((*pvex)->a != NULL) {
+		vex_Free(&(*pvex)->a);
+		AZ((*pvex)->a);
+	}
+	if ((*pvex)->b != NULL) {
+		vex_Free(&(*pvex)->b);
+		AZ((*pvex)->b);
+	}
+	FREE_OBJ(*pvex);
+	*pvex = NULL;
+}
+
+#ifdef VXP_DEBUG
+
+static void
+vex_print_val(const struct vex_val *val)
+{
+
+	CHECK_OBJ_NOTNULL(val, VEX_VAL_MAGIC);
+	switch (val->type) {
+	case VEX_INT:
+		fprintf(stderr, "INT=%jd", (intmax_t)val->val_int);
+		break;
+	case VEX_FLOAT:
+		fprintf(stderr, "FLOAT=%f", val->val_float);
+		break;
+	case VEX_STRING:
+		AN(val->val_string);
+		fprintf(stderr, "STRING='%s'", val->val_string);
+		break;
+	case VEX_REGEX:
+		AN(val->val_string);
+		AN(val->val_regex);
+		fprintf(stderr, "REGEX='%s'", val->val_string);
+		break;
+	default:
+		WRONG("value type");
+		break;
+	}
+}
+
+static void
+vex_print(const struct vex *vex, int indent)
+{
+	CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+
+	fprintf(stderr, "%*s%s", indent, "", vxp_tnames[vex->tok]);
+	if (vex->tag != NULL) {
+		CHECK_OBJ_NOTNULL(vex->tag, VEX_TAG_MAGIC);
+		fprintf(stderr, " tag=%s", VSL_tags[vex->tag->tag]);
+	}
+	if (vex->val != NULL) {
+		fprintf(stderr, " ");
+		vex_print_val(vex->val);
+	}
+	fprintf(stderr, "\n");
+	if (vex->a != NULL)
+		vex_print(vex->a, indent + 2);
+	if (vex->b != NULL)
+		vex_print(vex->b, indent + 2);
+}
+
+void
+vex_PrintTree(const struct vex *vex)
+{
+
+	CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+	fprintf(stderr, "VEX tree:\n");
+	vex_print(vex, 2);
+}
+
+#endif /* VXP_DEBUG */
diff --git a/lib/libvarnishapi/vxp_test.c b/lib/libvarnishapi/vxp_test.c
new file mode 100644
index 0000000..19be714
--- /dev/null
+++ b/lib/libvarnishapi/vxp_test.c
@@ -0,0 +1,41 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "vxp.h"
+#include "vas.h"
+#include "vsb.h"
+
+int
+main(int argc, char **argv)
+{
+	int i;
+	unsigned l;
+	char *s;
+	struct vsb *vsb;
+	struct vex *vex;
+
+	l = 0;
+	for (i = 1; i < argc; i++)
+		l += strlen(argv[i]) + 1;
+	s = calloc(l + 1, sizeof (char));
+	for (i = 1; i < argc; strcat(s, " "), i++)
+		strcat(s, argv[i]);
+
+	vsb = VSB_new_auto();
+	AN(vsb);
+	vex = vex_New(s, vsb);
+
+	if (vex == NULL) {
+		VSB_finish(vsb);
+		fprintf(stderr, "Error:\n%s", VSB_data(vsb));
+		exit(1);
+	}
+	VSB_delete(vsb);
+
+	vex_Free(&vex);
+	AZ(vex);
+
+	return (0);
+}



More information about the varnish-commit mailing list