Commit 9299780d authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Rewrite of the "if (foo)" code in VCC to actually build expressions

the way we learned to do it back in the 1950'ies.

Until now, VCC has used sort of a massaged C-expression and with
the introduction of things like regexp matching and ACLs that turned
sort of ugly.

This code is classic recursive expression parsing with a couple of
twists:

1. We don't build a tree, because we are not going to optimize stuff,
   we leave that to the C compiler.

2. Instead of the tree we build a string with magic markers that
   let us get the indentation of the produced C code right anyway.

3. We do type-hinted parsing, in an attempt to produce intelligent
   error messages and sensible semantics.

This code passes all testcases with a single change:  We can now
compile "if (2 == 3)".  This is probably a good thing (?)

Beware of bugs...




git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@5130 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 4773d8d3
......@@ -95,7 +95,7 @@ varnish v1 -badvcl {
sub vcl_hash { if (req.hash != "foo") { } }
}
varnish v1 -badvcl {
varnish v1 -vcl {
backend b { .host = "127.0.0.1"; }
sub vcl_hash { if (2 == 3) { } }
}
......@@ -449,41 +449,19 @@ vcc_acl_emit(const struct vcc *tl, const char *acln, int anon)
}
void
vcc_Cond_Ip(struct vcc *tl, const char *a1)
vcc_Acl_Hack(struct vcc *tl, char *b)
{
unsigned tcond;
char acln[32];
unsigned tcond;
switch (tl->t->tok) {
/* XXX: T_NOMATCH */
case '~':
vcc_NextToken(tl);
ExpectErr(tl, ID);
vcc_AddRef(tl, tl->t, R_ACL);
Fb(tl, 1, "match_acl_named_%.*s(sp, %s)\n",
PF(tl->t), a1);
vcc_NextToken(tl);
break;
case T_EQ:
case T_NEQ:
VTAILQ_INIT(&tl->acl);
tcond = tl->t->tok;
vcc_NextToken(tl);
bprintf(acln, "%u", tl->cnt);
vcc_acl_entry(tl);
vcc_acl_emit(tl, acln, 1);
Fb(tl, 1, "%smatch_acl_anon_%s(sp, %s)\n",
(tcond == T_NEQ ? "!" : ""), acln, a1);
break;
default:
vsb_printf(tl->sb, "Invalid condition ");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, " on IP number variable\n");
vsb_printf(tl->sb, " only '==', '!=' and '~' are legal\n");
vcc_ErrWhere(tl, tl->t);
break;
}
VTAILQ_INIT(&tl->acl);
tcond = tl->t->tok;
vcc_NextToken(tl);
bprintf(acln, "%u", tl->cnt);
vcc_acl_entry(tl);
vcc_acl_emit(tl, acln, 1);
sprintf(b, "%smatch_acl_anon_%s(sp, \v1)",
(tcond == T_NEQ ? "!" : ""), acln);
}
void
......
......@@ -101,6 +101,7 @@ parse_error(struct vcc *tl)
/*--------------------------------------------------------------------*/
#if 1
static void
illegal_assignment(const struct vcc *tl, const char *type)
{
......@@ -110,12 +111,14 @@ illegal_assignment(const struct vcc *tl, const char *type)
vsb_printf(tl->sb,
" only '=' is legal for %s\n", type);
}
#endif
static void
parse_set(struct vcc *tl)
{
const struct var *vp;
struct token *at, *vt;
struct token *vt;
struct token *at;
vcc_NextToken(tl);
ExpectErr(tl, ID);
......@@ -124,6 +127,11 @@ parse_set(struct vcc *tl)
ERRCHK(tl);
assert(vp != NULL);
Fb(tl, 1, "%s", vp->lname);
#if 0
vcc_NextToken(tl);
SkipToken(tl, '=');
vcc_Expr(tl, vp->fmt);
#else
vcc_NextToken(tl);
switch (vp->fmt) {
case INT:
......@@ -212,6 +220,7 @@ parse_set(struct vcc *tl)
vcc_ErrWhere(tl, tl->t);
return;
}
#endif
}
/*--------------------------------------------------------------------*/
......
......@@ -174,7 +174,7 @@ struct method {
/* vcc_acl.c */
void vcc_Acl(struct vcc *tl);
void vcc_Cond_Ip(struct vcc *tl, const char *a1);
void vcc_Acl_Hack(struct vcc *tl, char *b);
/* vcc_action.c */
int vcc_ParseAction(struct vcc *tl);
......@@ -224,7 +224,7 @@ void vcc_RTimeVal(struct vcc *tl, double *);
void vcc_TimeVal(struct vcc *tl, double *);
unsigned vcc_UintVal(struct vcc *tl);
double vcc_DoubleVal(struct vcc *tl);
void vcc_Expr(struct vcc *tl, enum var_type fmt);
void vcc_Expr(struct vcc *tl, enum var_type typ);
/* vcc_dir_dns.c */
parsedirector_f vcc_ParseDnsDirector;
......
This diff is collapsed.
......@@ -44,7 +44,6 @@ SVNID("$Id$")
/*--------------------------------------------------------------------*/
static void vcc_Compound(struct vcc *tl);
static void vcc_Conditional(struct vcc *tl);
/*--------------------------------------------------------------------*/
......@@ -59,251 +58,6 @@ static void vcc_Conditional(struct vcc *tl);
tl->t->cnt = tl->cnt; \
} while (0)
/*--------------------------------------------------------------------*/
static void
vcc_inval_test(struct vcc *tl, const char *type, const char *valid)
{
vsb_printf(tl->sb, "Invalid test ");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, " on expression of type %s.\n", type);
vsb_printf(tl->sb, " only %s are legal\n", valid);
vcc_ErrWhere(tl, tl->t);
}
/*--------------------------------------------------------------------*/
static void
vcc_Cond_String(struct vcc *tl, const char *a1)
{
char *p;
switch (tl->t->tok) {
case '~':
case T_NOMATCH:
Fb(tl, 1, "%sVRT_re_match(",
tl->t->tok == '~' ? "" : "!");
vcc_NextToken(tl);
ExpectErr(tl, CSTR);
p = vcc_regexp(tl);
ERRCHK(tl);
vcc_NextToken(tl);
Fb(tl, 1, "%s, %s)\n", a1, p);
break;
case T_LEQ:
case T_GEQ:
case '>':
case '<':
vcc_inval_test(tl, "STRING", "'==', '!=', '~' and '!~'");
break;
case T_EQ:
case T_NEQ:
Fb(tl, 1, "%sVRT_strcmp(%s, ",
tl->t->tok == T_EQ ? "!" : "", a1);
vcc_NextToken(tl);
if (!vcc_StringVal(tl)) {
vcc_ExpectedStringval(tl);
break;
}
Fb(tl, 0, ")\n");
break;
default:
Fb(tl, 1, "%s != (void*)0\n", a1);
break;
}
}
static void
vcc_Cond_Bool(const struct vcc *tl, const char *a1)
{
Fb(tl, 1, "%s\n", a1);
}
static void
vcc_Cond_Backend(struct vcc *tl, const char *a1)
{
Fb(tl, 1, "%s\n", a1);
if (tl->t->tok == T_EQ || tl->t->tok == T_NEQ) {
Fb(tl, 1, " %.*s\n", PF(tl->t));
} else {
vcc_inval_test(tl, "BACKEND", "'==' and '!='");
return;
}
vcc_NextToken(tl);
vcc_ExpectCid(tl);
ERRCHK(tl);
vcc_AddRef(tl, tl->t, R_BACKEND);
Fb(tl, 1, "VGCDIR(_%.*s)\n", PF(tl->t));
vcc_NextToken(tl);
}
static void
vcc_Cond_Num(struct vcc *tl, enum var_type fmt, const char *fmtn,
const char *a1)
{
Fb(tl, 1, "%s ", a1);
switch (tl->t->tok) {
case T_EQ:
case T_NEQ:
case T_LEQ:
case T_GEQ:
case '>':
case '<':
Fb(tl, 0, "%.*s\n", PF(tl->t));
vcc_NextToken(tl);
vcc_Expr(tl, fmt);
break;
default:
vcc_inval_test(tl, fmtn,
"'==', '!=', '<', '>', '<=' and '>='");
break;
}
}
/*--------------------------------------------------------------------
* SYNTAX:
* Cond_3:
* Typed_Expr Relation Compat_Typed_Expr
* Typed_Expr:
* VarName
* FuncCall
* Relation:
* Subset('==', '!=', '<', '<=', '>', '>=', '~', '!~')
* Compat_Typed_Expr
* Typed_Expr
* Typed_Const
*
* Since we cannot tell if "10 s" is a TIME or DURATION type, or for that
* matter if "127.0.0.1" is a STRING or IP type, we demand that the expression
* before the relational operator provides us with a type which can be used to
* guide parsing of other expression.
*/
static void
vcc_Cond_3(struct vcc *tl)
{
const struct var *vp;
const struct symbol *sym;
const char *left;
sym = VCC_FindSymbol(tl, tl->t);
if (sym == NULL) {
vsb_printf(tl->sb,
"Syntax error in condition.\n"
"Expected '(', '!' or variable name.\n"
"Found ");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, "\n");
vcc_ErrWhere(tl, tl->t);
return;
}
vcc_AddUses(tl, tl->t, sym->r_methods, "Not available");
AN(sym->var);
vp = vcc_FindVar(tl, tl->t, 0, "cannot be read");
ERRCHK(tl);
assert(vp != NULL);
left = vp->rname;
vcc_NextToken(tl);
switch (vp->fmt) {
case BACKEND: L(tl, vcc_Cond_Backend(tl, left)); break;
case BOOL: L(tl, vcc_Cond_Bool(tl, left)); break;
case DURATION: L(tl, vcc_Cond_Num(tl, DURATION, "DURATION", left)); break;
case INT: L(tl, vcc_Cond_Num(tl, INT, "INT", left)); break;
case IP: L(tl, vcc_Cond_Ip(tl, left)); break;
case STRING: L(tl, vcc_Cond_String(tl, left)); break;
case TIME: L(tl, vcc_Cond_Num(tl, TIME, "TIME", left)); break;
default:
vsb_printf(tl->sb,
"Variable '%s'"
" has no conditions that can be checked\n",
vp->name);
vcc_ErrWhere(tl, tl->t);
return;
}
}
/*--------------------------------------------------------------------
* SYNTAX:
* Cond_2:
* '!'? '(' Conditional ')'
* '!'? Cond_3
*/
static void
vcc_Cond_2(struct vcc *tl)
{
C(tl, ",");
if (tl->t->tok == '!') {
Fb(tl, 1, "!");
vcc_NextToken(tl);
}
if (tl->t->tok == '(') {
vcc_Conditional(tl);
return;
}
if (tl->t->tok == ID) {
Fb(tl, 1, "(\n");
vcc_Cond_3(tl);
Fb(tl, 1, ")\n");
return;
}
vsb_printf(tl->sb,
"Syntax error in condition.\n"
"Expected '(', '!' or variable name.\n"
"Found ");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, "\n");
vcc_ErrWhere(tl, tl->t);
return;
}
/*--------------------------------------------------------------------
* SYNTAX:
* Cond_1:
* Cond_2 { '&&' Cond_2 }*
*/
static void
vcc_Cond_1(struct vcc *tl)
{
Fb(tl, 1, "(\n");
L(tl, vcc_Cond_2(tl));
while (tl->t->tok == T_CAND) {
vcc_NextToken(tl);
Fb(tl, 1, ") && (\n");
L(tl, vcc_Cond_2(tl));
ERRCHK(tl);
}
Fb(tl, 1, ")\n");
}
/*--------------------------------------------------------------------
* SYNTAX:
* Cond_0:
* Cond_1 { '||' Cond_1 }*
*/
static void
vcc_Cond_0(struct vcc *tl)
{
Fb(tl, 1, "(\n");
L(tl, vcc_Cond_1(tl));
while (tl->t->tok == T_COR) {
vcc_NextToken(tl);
Fb(tl, 1, ") || (\n");
L(tl, vcc_Cond_1(tl));
ERRCHK(tl);
}
Fb(tl, 1, ")\n");
}
/*--------------------------------------------------------------------
* SYNTAX:
* Conditional:
......@@ -316,7 +70,7 @@ vcc_Conditional(struct vcc *tl)
SkipToken(tl, '(');
Fb(tl, 1, "(\n");
L(tl, vcc_Cond_0(tl));
vcc_Expr(tl, BOOL);
ERRCHK(tl);
Fb(tl, 1, ")\n");
SkipToken(tl, ')');
......@@ -348,7 +102,7 @@ vcc_IfStmt(struct vcc *tl)
case T_ELSE:
vcc_NextToken(tl);
if (tl->t->tok != T_IF) {
Fb(tl, 1, "else \n");
Fb(tl, 1, "else\n");
L(tl, vcc_Compound(tl));
ERRCHK(tl);
return;
......
......@@ -105,6 +105,7 @@ vcc_Coord(const struct vcc *tl, struct vsb *vsb, const struct token *t)
vcc_icoord(vsb, t, NULL);
}
/* XXX: should take first+last token */
void
vcc_ErrWhere(struct vcc *tl, const struct token *t)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment