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,23 +449,10 @@ 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];
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:
unsigned tcond;
VTAILQ_INIT(&tl->acl);
tcond = tl->t->tok;
......@@ -473,17 +460,8 @@ vcc_Cond_Ip(struct vcc *tl, const char *a1)
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;
}
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;
......
......@@ -25,6 +25,8 @@
* 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.
*
* XXX: add VRT_count()'s
*/
#include "config.h"
......@@ -43,20 +45,17 @@ SVNID("$Id$")
#include "vcc_compile.h"
#include "libvarnish.h"
/*--------------------------------------------------------------------*/
#define L(tl, foo) do { \
tl->indent += INDENT; \
foo; \
tl->indent -= INDENT; \
} while (0)
#if 0
#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
static const char *
vcc_Type(enum var_type fmt)
{
switch(fmt) {
#define VCC_TYPE(a) case a: return(#a);
#include "vcc_types.h"
#undef VCC_TYPE
default:
return("Unknown Type");
}
}
/*--------------------------------------------------------------------
* Recognize and convert units of time, return seconds.
......@@ -190,158 +189,507 @@ vcc_TimeVal(struct vcc *tl, double *d)
*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
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 var *vp;
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) {
case ID:
sym = VCC_FindSymbol(tl, tl->t);
if (sym == NULL) {
vsb_printf(tl->sb,
"Unknown symbol in numeric expression:\n");
vsb_printf(tl->sb, "Symbol not found: ");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, "\n");
vcc_ErrWhere(tl, tl->t);
return;
}
AN(sym);
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);
Fb(tl, 1, "%s\n", vp->rname);
*fmt = sym->fmt;
vsb_printf(e1->vsb, "%s", vp->rname);
e1->fmt = vp->fmt;
vcc_NextToken(tl);
return;
break;
case CSTR:
assert(fmt != VOID);
EncToken(e1->vsb, tl->t);
e1->fmt = STRING;
vcc_NextToken(tl);
break;
case CNUM:
vcc_NumVal(tl, &d, &frac);
ERRCHK(tl);
if (tl->t->tok == ID) {
d *= vcc_TimeUnit(tl);
assert(fmt != VOID);
if (fmt == DURATION) {
vcc_RTimeVal(tl, &d);
ERRCHK(tl);
*fmt = DURATION;
} else if (!frac) {
*fmt = INT;
vsb_printf(e1->vsb, "%g", d);
e1->fmt = DURATION;
} 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;
}
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:
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;
}
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
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 token *top;
struct token *tfirst;
struct expr *e2;
const struct cmps *cp;
char buf[256];
char *re;
const char *not;
struct token *tk;
*e = NULL;
tfirst = tl->t;
Fb(tl, 1, "(\n");
L(tl, vcc_Expr2(tl, &lfmt));
if (fmt == BOOL && tl->t->tok == '!') {
vcc_NextToken(tl);
vcc_expr_add(tl, &e2, fmt);
ERRCHK(tl);
afmt = lfmt;
while (1) {
top = tl->t;
if (tl->t->tok == '+') {
*e = vcc_expr_edit(BOOL, "!(\v1)", e2, NULL);
return;
}
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);
Fb(tl, 1, " +\n");
L(tl, vcc_Expr2(tl, &rfmt));
ExpectErr(tl, CSTR);
re = vcc_regexp(tl);
ERRCHK(tl);
if (lfmt == INT && rfmt == INT)
afmt = INT;
else if (lfmt == DURATION && rfmt == DURATION)
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);
vcc_NextToken(tl);
bprintf(buf, "%sVRT_re_match(\v1, %s)", not, re);
*e = vcc_expr_edit(BOOL, buf, *e, NULL);
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);
Fb(tl, 1, " -\n");
L(tl, vcc_Expr2(tl, &rfmt));
if (lfmt == INT && rfmt == INT)
afmt = INT;
else if (lfmt == DURATION && rfmt == DURATION)
afmt = DURATION;
else if (lfmt == TIME && rfmt == DURATION)
afmt = TIME;
else if (lfmt == TIME && rfmt == TIME)
afmt = DURATION;
else {
vsb_printf(tl->sb,
/* XXX print actual types */
"Incompatible types in subtraction.\n"
"Legal combinations:\n"
"\tINT-INT,\n"
"\tDURATION-DURATION,\n"
"\tTIME-DURATION,\n"
"\tTIME-TIME,\n");
vcc_ErrWhere(tl, top);
ExpectErr(tl, ID);
vcc_AddRef(tl, tl->t, R_ACL);
bprintf(buf, "%smatch_acl_named_%.*s(sp, \v1)", not, PF(tl->t));
vcc_NextToken(tl);
*e = vcc_expr_edit(BOOL, buf, *e, NULL);
return;
}
if ((*e)->fmt == IP && (tl->t->tok == T_EQ || tl->t->tok == T_NEQ)) {
vcc_Acl_Hack(tl, buf);
*e = vcc_expr_edit(BOOL, buf, *e, NULL);
return;
}
if ((*e)->fmt == BACKEND &&
(tl->t->tok == T_EQ || tl->t->tok == T_NEQ)) {
vcc_NextToken(tl);
ExpectErr(tl, ID);
vcc_AddRef(tl, tl->t, R_BACKEND);
bprintf(buf, "(\v1 %.*s VGCDIR(_%.*s))", PF(tk), PF(tl->t));
vcc_NextToken(tl);
*e = vcc_expr_edit(BOOL, buf, *e, NULL);
return;
}
} else
if (fmt == BOOL) {
switch((*e)->fmt) {
case STRING:
*e = vcc_expr_edit(BOOL, "(\v1 != 0)", *e, NULL);
return;
default:
break;
lfmt = afmt;
}
Fb(tl, 1, ")\n");
if (fmt != afmt) {
vsb_printf(tl->sb,
/* XXX print actual types */
"Add/Subtract results in wrong type.\n"
"\nExpression starting at:\n" );
vcc_ErrWhere(tl, tfirst);
vsb_printf(tl->sb, "\nending before:\n\n");
vcc_ErrWhere(tl, tl->t);
}
}
if (fmt == VOID || fmt != (*e)->fmt) {
vsb_printf(tl->sb, "WANT: %s has %s next %.*s (%s)\n",
vcc_Type(fmt), vcc_Type((*e)->fmt),
PF(tl->t), vsb_data((*e)->vsb));
tl->err = 1;
}
}
/*--------------------------------------------------------------------
* 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;
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) {
case DURATION:
case INT:
case TIME:
/* These types support addition and subtraction */
Fb(tl, 1, "(\n");
L(tl, vcc_Expr1(tl, fmt));
*e = NULL;
vcc_expr_cand(tl, e, fmt);
ERRCHK(tl);
Fb(tl, 1, ")\n");
if ((*e)->fmt != BOOL)
return;
default:
WRONG("type not implemented yet");
while (tl->t->tok == T_COR) {
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$")
/*--------------------------------------------------------------------*/
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