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 { ...@@ -95,7 +95,7 @@ varnish v1 -badvcl {
sub vcl_hash { if (req.hash != "foo") { } } sub vcl_hash { if (req.hash != "foo") { } }
} }
varnish v1 -badvcl { varnish v1 -vcl {
backend b { .host = "127.0.0.1"; } backend b { .host = "127.0.0.1"; }
sub vcl_hash { if (2 == 3) { } } sub vcl_hash { if (2 == 3) { } }
} }
...@@ -449,23 +449,10 @@ vcc_acl_emit(const struct vcc *tl, const char *acln, int anon) ...@@ -449,23 +449,10 @@ vcc_acl_emit(const struct vcc *tl, const char *acln, int anon)
} }
void void
vcc_Cond_Ip(struct vcc *tl, const char *a1) vcc_Acl_Hack(struct vcc *tl, char *b)
{ {
unsigned tcond;
char acln[32]; 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); VTAILQ_INIT(&tl->acl);
tcond = tl->t->tok; tcond = tl->t->tok;
...@@ -473,17 +460,8 @@ vcc_Cond_Ip(struct vcc *tl, const char *a1) ...@@ -473,17 +460,8 @@ vcc_Cond_Ip(struct vcc *tl, const char *a1)
bprintf(acln, "%u", tl->cnt); bprintf(acln, "%u", tl->cnt);
vcc_acl_entry(tl); vcc_acl_entry(tl);
vcc_acl_emit(tl, acln, 1); vcc_acl_emit(tl, acln, 1);
Fb(tl, 1, "%smatch_acl_anon_%s(sp, %s)\n", sprintf(b, "%smatch_acl_anon_%s(sp, \v1)",
(tcond == T_NEQ ? "!" : ""), acln, a1); (tcond == T_NEQ ? "!" : ""), acln);
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;
}
} }
void void
......
...@@ -101,6 +101,7 @@ parse_error(struct vcc *tl) ...@@ -101,6 +101,7 @@ parse_error(struct vcc *tl)
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
#if 1
static void static void
illegal_assignment(const struct vcc *tl, const char *type) illegal_assignment(const struct vcc *tl, const char *type)
{ {
...@@ -110,12 +111,14 @@ 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, vsb_printf(tl->sb,
" only '=' is legal for %s\n", type); " only '=' is legal for %s\n", type);
} }
#endif
static void static void
parse_set(struct vcc *tl) parse_set(struct vcc *tl)
{ {
const struct var *vp; const struct var *vp;
struct token *at, *vt; struct token *vt;
struct token *at;
vcc_NextToken(tl); vcc_NextToken(tl);
ExpectErr(tl, ID); ExpectErr(tl, ID);
...@@ -124,6 +127,11 @@ parse_set(struct vcc *tl) ...@@ -124,6 +127,11 @@ parse_set(struct vcc *tl)
ERRCHK(tl); ERRCHK(tl);
assert(vp != NULL); assert(vp != NULL);
Fb(tl, 1, "%s", vp->lname); Fb(tl, 1, "%s", vp->lname);
#if 0
vcc_NextToken(tl);
SkipToken(tl, '=');
vcc_Expr(tl, vp->fmt);
#else
vcc_NextToken(tl); vcc_NextToken(tl);
switch (vp->fmt) { switch (vp->fmt) {
case INT: case INT:
...@@ -212,6 +220,7 @@ parse_set(struct vcc *tl) ...@@ -212,6 +220,7 @@ parse_set(struct vcc *tl)
vcc_ErrWhere(tl, tl->t); vcc_ErrWhere(tl, tl->t);
return; return;
} }
#endif
} }
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
......
...@@ -174,7 +174,7 @@ struct method { ...@@ -174,7 +174,7 @@ struct method {
/* vcc_acl.c */ /* vcc_acl.c */
void vcc_Acl(struct vcc *tl); 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 */ /* vcc_action.c */
int vcc_ParseAction(struct vcc *tl); int vcc_ParseAction(struct vcc *tl);
...@@ -224,7 +224,7 @@ void vcc_RTimeVal(struct vcc *tl, double *); ...@@ -224,7 +224,7 @@ void vcc_RTimeVal(struct vcc *tl, double *);
void vcc_TimeVal(struct vcc *tl, double *); void vcc_TimeVal(struct vcc *tl, double *);
unsigned vcc_UintVal(struct vcc *tl); unsigned vcc_UintVal(struct vcc *tl);
double vcc_DoubleVal(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 */ /* vcc_dir_dns.c */
parsedirector_f vcc_ParseDnsDirector; parsedirector_f vcc_ParseDnsDirector;
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * 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 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
*
* XXX: add VRT_count()'s
*/ */
#include "config.h" #include "config.h"
...@@ -43,20 +45,17 @@ SVNID("$Id$") ...@@ -43,20 +45,17 @@ SVNID("$Id$")
#include "vcc_compile.h" #include "vcc_compile.h"
#include "libvarnish.h" #include "libvarnish.h"
/*--------------------------------------------------------------------*/ static const char *
vcc_Type(enum var_type fmt)
#define L(tl, foo) do { \ {
tl->indent += INDENT; \ switch(fmt) {
foo; \ #define VCC_TYPE(a) case a: return(#a);
tl->indent -= INDENT; \ #include "vcc_types.h"
} while (0) #undef VCC_TYPE
default:
#if 0 return("Unknown Type");
#define C(tl, sep) do { \ }
Fb(tl, 1, "VRT_count(sp, %u)%s\n", ++tl->cnt, sep); \ }
tl->t->cnt = tl->cnt; \
} while (0)
#endif
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
* Recognize and convert units of time, return seconds. * Recognize and convert units of time, return seconds.
...@@ -190,158 +189,507 @@ vcc_TimeVal(struct vcc *tl, double *d) ...@@ -190,158 +189,507 @@ vcc_TimeVal(struct vcc *tl, double *d)
*d = v * sc; *d = v * sc;
} }
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------
*
*/
struct expr {
unsigned magic;
#define EXPR_MAGIC 0x38c794ab
enum var_type fmt;
struct vsb *vsb;
/* XXX: first and last token */
};
static void vcc_expr0(struct vcc *tl, struct expr **e, enum var_type fmt);
static struct expr *
vcc_new_expr(void)
{
struct expr *e;
/* XXX: use TlAlloc() ? */
ALLOC_OBJ(e, EXPR_MAGIC);
AN(e);
e->vsb = vsb_newauto();
e->fmt = VOID;
return (e);
}
static void
vcc_delete_expr(struct expr *e)
{
if (e == NULL)
return;
CHECK_OBJ_NOTNULL(e, EXPR_MAGIC);
vsb_delete(e->vsb);
FREE_OBJ(e);
}
/*--------------------------------------------------------------------
* We want to get the indentation right in the emitted C code so we have
* to represent it symbolically until we are ready to render.
*
* Many of the operations have very schematic output syntaxes, so we
* use the same facility to simplify the text-processing of emitting
* a given operation on two subexpressions.
*
* We use '\v' as the magic escape character.
* \v1 insert subexpression 1
* \v2 insert subexpression 2
* \v+ increase indentation
* \v- increase indentation
* anything else is literal
*
* When editing, we check if any of the subexpressions contain a newline
* and issue it as an indented block of so.
*
* XXX: check line lengths in edit, should pass indent in for this
*/
static struct expr *
vcc_expr_edit(enum var_type fmt, const char *p, struct expr *e1, struct expr *e2)
{
struct expr *e;
char *q;
q = strchr(vsb_data(e1->vsb), '\n');
if (q == NULL && e2 != NULL)
q = strchr(vsb_data(e2->vsb), '\n');
e = vcc_new_expr();
while (*p != '\0') {
if (*p != '\v') {
vsb_putc(e->vsb, *p);
p++;
continue;
}
p++;
switch(*p) {
case '+': vsb_cat(e->vsb, "\v+"); break;
case '-': vsb_cat(e->vsb, "\v-"); break;
case '1':
case '2':
if (q != NULL)
vsb_cat(e->vsb, "\v+\n");
if (*p == '1')
vsb_cat(e->vsb, vsb_data(e1->vsb));
else
vsb_cat(e->vsb, vsb_data(e2->vsb));
if (q != NULL)
vsb_cat(e->vsb, "\v-\n");
break;
default:
assert(__LINE__ == 0);
}
p++;
}
vsb_finish(e->vsb);
AZ(vsb_overflowed(e->vsb));
vcc_delete_expr(e1);
vcc_delete_expr(e2);
e->fmt = fmt;
return (e);
}
/*--------------------------------------------------------------------
* Expand finished expression into C-source code
*/
static void static void
vcc_Expr2(struct vcc *tl, enum var_type *fmt) vcc_expr_fmt(struct vsb *d, int ind, const struct expr *e1)
{ {
char *p;
int i;
for (i = 0; i < ind; i++)
vsb_cat(d, " ");
for (p = vsb_data(e1->vsb); *p != '\0'; p++) {
if (*p == '\n') {
vsb_putc(d, '\n');
if (p[1] != '\0') {
for (i = 0; i < ind; i++)
vsb_cat(d, " ");
}
continue;
}
if (*p != '\v') {
vsb_putc(d, *p);
continue;
}
p++;
switch(*p) {
case '+': ind += 2; break;
case '-': ind -= 2; break;
default:
assert(__LINE__ == 0);
}
}
}
/*--------------------------------------------------------------------
* SYNTAX:
* Expr4:
* '(' Expr0 ')'
* CNUM
* CSTR
*/
static void
vcc_expr4(struct vcc *tl, struct expr **e, enum var_type fmt)
{
struct expr *e1, *e2;
const struct symbol *sym; const struct symbol *sym;
const struct var *vp; const struct var *vp;
double d; double d;
int frac;
*fmt = VOID; *e = NULL;
if (tl->t->tok == '(') {
SkipToken(tl, '(');
vcc_expr0(tl, &e2, fmt);
ERRCHK(tl);
SkipToken(tl, ')');
*e = vcc_expr_edit(e2->fmt, "(\v1)", e2, NULL);
return;
}
e1 = vcc_new_expr();
switch(tl->t->tok) { switch(tl->t->tok) {
case ID: case ID:
sym = VCC_FindSymbol(tl, tl->t); sym = VCC_FindSymbol(tl, tl->t);
if (sym == NULL) { if (sym == NULL) {
vsb_printf(tl->sb, vsb_printf(tl->sb, "Symbol not found: ");
"Unknown symbol in numeric expression:\n");
vcc_ErrToken(tl, tl->t); vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, "\n");
vcc_ErrWhere(tl, tl->t); vcc_ErrWhere(tl, tl->t);
return; return;
} }
AN(sym);
vcc_AddUses(tl, tl->t, sym->r_methods, "Not available"); vcc_AddUses(tl, tl->t, sym->r_methods, "Not available");
AN(sym->var); AN(sym->var);
vp = vcc_FindVar(tl, tl->t, 0, "cannot be read"); vp = vcc_FindVar(tl, tl->t, 0, "cannot be read");
ERRCHK(tl); ERRCHK(tl);
assert(vp != NULL); assert(vp != NULL);
Fb(tl, 1, "%s\n", vp->rname); vsb_printf(e1->vsb, "%s", vp->rname);
*fmt = sym->fmt; e1->fmt = vp->fmt;
vcc_NextToken(tl); vcc_NextToken(tl);
return; break;
case CSTR:
assert(fmt != VOID);
EncToken(e1->vsb, tl->t);
e1->fmt = STRING;
vcc_NextToken(tl);
break;
case CNUM: case CNUM:
vcc_NumVal(tl, &d, &frac); assert(fmt != VOID);
ERRCHK(tl); if (fmt == DURATION) {
if (tl->t->tok == ID) { vcc_RTimeVal(tl, &d);
d *= vcc_TimeUnit(tl);
ERRCHK(tl); ERRCHK(tl);
*fmt = DURATION; vsb_printf(e1->vsb, "%g", d);
} else if (!frac) { e1->fmt = DURATION;
*fmt = INT;
} else { } else {
WRONG("numeric constant botch"); vsb_printf(e1->vsb, "%.*s", PF(tl->t));
vcc_NextToken(tl);
e1->fmt = INT;
} }
Fb(tl, 1, "%g\n", d); break;
default:
e1->fmt = fmt;
vsb_printf(e1->vsb, "<E4 %.*s %u>", PF(tl->t), tl->t->tok);
vcc_NextToken(tl);
break;
}
vsb_finish(e1->vsb);
AZ(vsb_overflowed(e1->vsb));
*e = e1;
}
/*--------------------------------------------------------------------
* SYNTAX:
* Expr3:
* Expr4 { {'*'|'/'} Expr4 } *
*/
static void
vcc_expr_mul(struct vcc *tl, struct expr **e, enum var_type fmt)
{
struct expr *e2;
enum var_type f2, f3;
*e = NULL;
vcc_expr4(tl, e, fmt);
ERRCHK(tl);
f3 = f2 = (*e)->fmt;
switch(f2) {
case INT: f2 = INT; break;
case DURATION: f2 = INT; break; /* XXX: should be Double */
default:
return; return;
}
while (tl->t->tok == '+' || tl->t->tok == '-') {
vcc_NextToken(tl);
vcc_expr4(tl, &e2, f2);
ERRCHK(tl);
if (tl->t->tok == '+')
*e = vcc_expr_edit(f3, "(\v1+\v2)", *e, e2);
else
*e = vcc_expr_edit(f3, "(\v1-\v2)", *e, e2);
}
}
/*--------------------------------------------------------------------
* SYNTAX:
* ExprAdd:
* ExprMul { {'+'|'-'} ExprMul } *
*/
static void
vcc_expr_add(struct vcc *tl, struct expr **e, enum var_type fmt)
{
struct expr *e2;
enum var_type f2;
*e = NULL;
vcc_expr_mul(tl, e, fmt);
ERRCHK(tl);
f2 = (*e)->fmt;
switch(f2) {
case INT: break;
case TIME: f2 = DURATION; break;
case DURATION: f2 = DURATION; break;
default: default:
vsb_printf(tl->sb,
"Unknown token in numeric expression:\n");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, "\n");
vcc_ErrWhere(tl, tl->t);
return; return;
} }
while (tl->t->tok == '+' || tl->t->tok == '-') {
vcc_NextToken(tl);
vcc_expr_mul(tl, &e2, f2);
ERRCHK(tl);
if (tl->t->tok == '+')
*e = vcc_expr_edit(f2, "(\v1+\v2)", *e, e2);
else
*e = vcc_expr_edit(f2, "(\v1-\v2)", *e, e2);
}
} }
/*--------------------------------------------------------------------
* SYNTAX:
* ExprCmp:
* ExprAdd
* ExprAdd Relation ExprAdd
* ExprAdd(STRING) '~' CString
* ExprAdd(STRING) '!~' CString
* ExprAdd(IP) '~' IP
* ExprAdd(IP) '!~' IP
*/
static const struct cmps {
enum var_type fmt;
unsigned token;
const char *emit;
} vcc_cmps[] = {
{INT, T_EQ, "(\v1 == \v2)" },
{INT, T_NEQ, "(\v1 != \v2)" },
{INT, T_LEQ, "(\v1 <= \v2)" },
{INT, T_GEQ, "(\v1 >= \v2)" },
{INT, '<', "(\v1 < \v2)" },
{INT, '>', "(\v1 > \v2)" },
{DURATION, T_EQ, "(\v1 == \v2)" },
{DURATION, T_NEQ, "(\v1 != \v2)" },
{DURATION, T_LEQ, "(\v1 <= \v2)" },
{DURATION, T_GEQ, "(\v1 >= \v2)" },
{DURATION, '<', "(\v1 < \v2)" },
{DURATION, '>', "(\v1 > \v2)" },
{STRING, T_EQ, "!VRT_strcmp(\v1, \v2)" },
{STRING, T_NEQ, "VRT_strcmp(\v1, \v2)" },
{VOID, 0, NULL}
};
static void static void
vcc_Expr1(struct vcc *tl, enum var_type fmt) vcc_expr_cmp(struct vcc *tl, struct expr **e, enum var_type fmt)
{ {
enum var_type lfmt, rfmt, afmt; struct expr *e2;
struct token *top; const struct cmps *cp;
struct token *tfirst; char buf[256];
char *re;
const char *not;
struct token *tk;
*e = NULL;
tfirst = tl->t; if (fmt == BOOL && tl->t->tok == '!') {
Fb(tl, 1, "(\n"); vcc_NextToken(tl);
L(tl, vcc_Expr2(tl, &lfmt)); vcc_expr_add(tl, &e2, fmt);
ERRCHK(tl); ERRCHK(tl);
afmt = lfmt; *e = vcc_expr_edit(BOOL, "!(\v1)", e2, NULL);
while (1) { return;
top = tl->t; }
if (tl->t->tok == '+') {
vcc_expr_add(tl, e, fmt);
ERRCHK(tl);
if ((*e)->fmt == BOOL)
return;
tk = tl->t;
for (cp = vcc_cmps; cp->fmt != VOID; cp++)
if ((*e)->fmt == cp->fmt && tl->t->tok == cp->token)
break;
if (cp->fmt != VOID) {
vcc_NextToken(tl);
vcc_expr_add(tl, &e2, (*e)->fmt);
if (e2->fmt != (*e)->fmt) { /* XXX */
vsb_printf(tl->sb, "Comparison of different types\n");
vsb_printf(tl->sb, "Left side has type %s\n",
vcc_Type((*e)->fmt));
vsb_printf(tl->sb, "Right side has type %s\n",
vcc_Type(e2->fmt));
vcc_ErrToken(tl, tk);
vcc_ErrWhere(tl, tk);
return;
}
*e = vcc_expr_edit(BOOL, cp->emit, *e, e2);
return;
}
if ((*e)->fmt == STRING &&
(tl->t->tok == '~' || tl->t->tok == T_NOMATCH)) {
not = tl->t->tok == '~' ? "" : "!";
vcc_NextToken(tl); vcc_NextToken(tl);
Fb(tl, 1, " +\n"); ExpectErr(tl, CSTR);
L(tl, vcc_Expr2(tl, &rfmt)); re = vcc_regexp(tl);
ERRCHK(tl); ERRCHK(tl);
if (lfmt == INT && rfmt == INT) vcc_NextToken(tl);
afmt = INT; bprintf(buf, "%sVRT_re_match(\v1, %s)", not, re);
else if (lfmt == DURATION && rfmt == DURATION) *e = vcc_expr_edit(BOOL, buf, *e, NULL);
afmt = DURATION;
else if (lfmt == TIME && rfmt == DURATION)
afmt = TIME;
else if (lfmt == DURATION && rfmt == TIME)
afmt = TIME;
else {
vsb_printf(tl->sb,
/* XXX print actual types */
"Incompatible types in addition.\n"
"Legal combinations:\n"
"\tINT+INT,\n"
"\tDURATION+DURATION,\n"
"\tDURATION+TIME,\n"
"\tTIME+DURATION\n");
vcc_ErrWhere(tl, top);
return; return;
} }
} else if (tl->t->tok == '-') { if ((*e)->fmt == IP &&
(tl->t->tok == '~' || tl->t->tok == T_NOMATCH)) {
not = tl->t->tok == '~' ? "" : "!";
vcc_NextToken(tl); vcc_NextToken(tl);
Fb(tl, 1, " -\n"); ExpectErr(tl, ID);
L(tl, vcc_Expr2(tl, &rfmt)); vcc_AddRef(tl, tl->t, R_ACL);
if (lfmt == INT && rfmt == INT) bprintf(buf, "%smatch_acl_named_%.*s(sp, \v1)", not, PF(tl->t));
afmt = INT; vcc_NextToken(tl);
else if (lfmt == DURATION && rfmt == DURATION) *e = vcc_expr_edit(BOOL, buf, *e, NULL);
afmt = DURATION; return;
else if (lfmt == TIME && rfmt == DURATION) }
afmt = TIME; if ((*e)->fmt == IP && (tl->t->tok == T_EQ || tl->t->tok == T_NEQ)) {
else if (lfmt == TIME && rfmt == TIME) vcc_Acl_Hack(tl, buf);
afmt = DURATION; *e = vcc_expr_edit(BOOL, buf, *e, NULL);
else { return;
vsb_printf(tl->sb, }
/* XXX print actual types */ if ((*e)->fmt == BACKEND &&
"Incompatible types in subtraction.\n" (tl->t->tok == T_EQ || tl->t->tok == T_NEQ)) {
"Legal combinations:\n" vcc_NextToken(tl);
"\tINT-INT,\n" ExpectErr(tl, ID);
"\tDURATION-DURATION,\n" vcc_AddRef(tl, tl->t, R_BACKEND);
"\tTIME-DURATION,\n" bprintf(buf, "(\v1 %.*s VGCDIR(_%.*s))", PF(tk), PF(tl->t));
"\tTIME-TIME,\n"); vcc_NextToken(tl);
vcc_ErrWhere(tl, top); *e = vcc_expr_edit(BOOL, buf, *e, NULL);
return; return;
} }
} else if (fmt == BOOL) {
switch((*e)->fmt) {
case STRING:
*e = vcc_expr_edit(BOOL, "(\v1 != 0)", *e, NULL);
return;
default:
break; break;
lfmt = afmt; }
} }
Fb(tl, 1, ")\n"); if (fmt == VOID || fmt != (*e)->fmt) {
if (fmt != afmt) { vsb_printf(tl->sb, "WANT: %s has %s next %.*s (%s)\n",
vsb_printf(tl->sb, vcc_Type(fmt), vcc_Type((*e)->fmt),
/* XXX print actual types */ PF(tl->t), vsb_data((*e)->vsb));
"Add/Subtract results in wrong type.\n" tl->err = 1;
"\nExpression starting at:\n" ); }
vcc_ErrWhere(tl, tfirst); }
vsb_printf(tl->sb, "\nending before:\n\n");
vcc_ErrWhere(tl, tl->t); /*--------------------------------------------------------------------
* SYNTAX:
* ExprCand:
* ExprAdd { '&&' ExprAdd } *
*/
static void
vcc_expr_cand(struct vcc *tl, struct expr **e, enum var_type fmt)
{
struct expr *e2;
*e = NULL;
vcc_expr_cmp(tl, e, fmt);
ERRCHK(tl);
if ((*e)->fmt != BOOL)
return; return;
while (tl->t->tok == T_CAND) {
vcc_NextToken(tl);
vcc_expr_cmp(tl, &e2, fmt);
ERRCHK(tl);
*e = vcc_expr_edit(BOOL, "(\v1&&\v2)", *e, e2);
} }
} }
void /*--------------------------------------------------------------------
vcc_Expr(struct vcc *tl, enum var_type fmt) * SYNTAX:
* Expr0:
* ExprCand { '||' ExprCand } *
*/
static void
vcc_expr0(struct vcc *tl, struct expr **e, enum var_type fmt)
{ {
struct expr *e2;
switch (fmt) { *e = NULL;
case DURATION: vcc_expr_cand(tl, e, fmt);
case INT:
case TIME:
/* These types support addition and subtraction */
Fb(tl, 1, "(\n");
L(tl, vcc_Expr1(tl, fmt));
ERRCHK(tl); ERRCHK(tl);
Fb(tl, 1, ")\n"); if ((*e)->fmt != BOOL)
return; return;
default: while (tl->t->tok == T_COR) {
WRONG("type not implemented yet"); vcc_NextToken(tl);
vcc_expr_cand(tl, &e2, fmt);
ERRCHK(tl);
*e = vcc_expr_edit(BOOL, "(\v1||\v2)", *e, e2);
} }
} }
/*--------------------------------------------------------------------
* This function parses and emits the C-code to evaluate an expression
*
* We know up front what kind of type we want the expression to be,
* and this function is the backstop if that doesn't succeed.
*/
void
vcc_Expr(struct vcc *tl, enum var_type fmt)
{
struct expr *e;
struct token *t1;
assert(fmt != VOID);
t1 = tl->t;
vcc_expr0(tl, &e, fmt);
if (!tl->err && fmt != e->fmt) {
vsb_printf(tl->sb, "Expression has type %s, expected %s\n",
vcc_Type(e->fmt), vcc_Type(fmt));
tl->err = 1;
}
if (!tl->err) {
vcc_expr_fmt(tl->fb, tl->indent, e);
vsb_putc(tl->fb, '\n');
} else {
vsb_printf(tl->sb, "Expression starts here:\n");
vcc_ErrWhere(tl, t1);
if (t1 != tl->t) {
vsb_printf(tl->sb, "Expression ends here:\n");
vcc_ErrWhere(tl, tl->t);
}
}
vcc_delete_expr(e);
}
...@@ -44,7 +44,6 @@ SVNID("$Id$") ...@@ -44,7 +44,6 @@ SVNID("$Id$")
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
static void vcc_Compound(struct vcc *tl); 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); ...@@ -59,251 +58,6 @@ static void vcc_Conditional(struct vcc *tl);
tl->t->cnt = tl->cnt; \ tl->t->cnt = tl->cnt; \
} while (0) } 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: * SYNTAX:
* Conditional: * Conditional:
...@@ -316,7 +70,7 @@ vcc_Conditional(struct vcc *tl) ...@@ -316,7 +70,7 @@ vcc_Conditional(struct vcc *tl)
SkipToken(tl, '('); SkipToken(tl, '(');
Fb(tl, 1, "(\n"); Fb(tl, 1, "(\n");
L(tl, vcc_Cond_0(tl)); vcc_Expr(tl, BOOL);
ERRCHK(tl); ERRCHK(tl);
Fb(tl, 1, ")\n"); Fb(tl, 1, ")\n");
SkipToken(tl, ')'); SkipToken(tl, ')');
...@@ -348,7 +102,7 @@ vcc_IfStmt(struct vcc *tl) ...@@ -348,7 +102,7 @@ vcc_IfStmt(struct vcc *tl)
case T_ELSE: case T_ELSE:
vcc_NextToken(tl); vcc_NextToken(tl);
if (tl->t->tok != T_IF) { if (tl->t->tok != T_IF) {
Fb(tl, 1, "else \n"); Fb(tl, 1, "else\n");
L(tl, vcc_Compound(tl)); L(tl, vcc_Compound(tl));
ERRCHK(tl); ERRCHK(tl);
return; return;
......
...@@ -105,6 +105,7 @@ vcc_Coord(const struct vcc *tl, struct vsb *vsb, const struct token *t) ...@@ -105,6 +105,7 @@ vcc_Coord(const struct vcc *tl, struct vsb *vsb, const struct token *t)
vcc_icoord(vsb, t, NULL); vcc_icoord(vsb, t, NULL);
} }
/* XXX: should take first+last token */
void void
vcc_ErrWhere(struct vcc *tl, const struct token *t) 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