static void vcc_Eval_Regsub(struct vcc *tl, struct expr **e, const struct symbol *sym) { struct expr *e2; int all = sym->eval_priv == NULL ? 0 : 1; char *p; char buf[128]; vcc_delete_expr(*e); SkipToken(tl, ID); SkipToken(tl, '('); vcc_expr0(tl, &e2, STRING); if (e2->fmt != STRING) vcc_expr_tostring(&e2, STRING); SkipToken(tl, ','); ExpectErr(tl, CSTR); p = vcc_regexp(tl); vcc_NextToken(tl); bprintf(buf, "VRT_regsub(sp, %d,\n\v1,\n%s\n", all, p); *e = vcc_expr_edit(STRING, buf, e2, *e); SkipToken(tl, ','); vcc_expr0(tl, &e2, STRING); if (e2->fmt != STRING) vcc_expr_tostring(&e2, STRING); *e = vcc_expr_edit(STRING, "\v1, \v2)", *e, e2); SkipToken(tl, ')'); }
static void vcc_expr0(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e2; struct token *tk; *e = NULL; vcc_expr_cand(tl, e, fmt); ERRCHK(tl); if ((*e)->fmt != BOOL || tl->t->tok != T_COR) return; *e = vcc_expr_edit(BOOL, "(\v+\n\v1", *e, NULL); while (tl->t->tok == T_COR) { vcc_NextToken(tl); tk = tl->t; vcc_expr_cand(tl, &e2, fmt); ERRCHK(tl); if (e2->fmt != BOOL) { VSB_printf(tl->sb, "'||' must be followed by BOOL, found "); VSB_printf(tl->sb, "%s.\n", vcc_Type(e2->fmt)); vcc_ErrWhere2(tl, tk, tl->t); return; } *e = vcc_expr_edit(BOOL, "\v1\v-\n||\v+\n\v2", *e, e2); } *e = vcc_expr_edit(BOOL, "\v1\v-\n)", *e, NULL); }
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); ERRCHK(tl); if (fmt == STRING || fmt == STRING_LIST) vcc_expr_tostring(&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) { if (e->fmt == STRING_LIST) { e = vcc_expr_edit(STRING_LIST, "\v+\n\v1,\nvrt_magic_string_end\v-", e, NULL); } vcc_expr_fmt(tl->fb, tl->indent, e); VSB_putc(tl->fb, '\n'); } else { if (t1 != tl->t) vcc_ErrWhere2(tl, t1, tl->t); } vcc_delete_expr(e); }
static void vcc_expr_tostring(struct expr **e, enum var_type fmt) { const char *p; CHECK_OBJ_NOTNULL(*e, EXPR_MAGIC); AN(fmt == STRING || fmt == STRING_LIST); p = NULL; switch((*e)->fmt) { case BACKEND: p = "VRT_backend_string(req, \v1)"; break; case BOOL: p = "VRT_bool_string(req, \v1)"; break; case DURATION: p = "VRT_double_string(req, \v1)"; break; /* XXX: should DURATION insist on "s" suffix ? */ case INT: p = "VRT_int_string(req, \v1)"; break; case IP: p = "VRT_IP_string(req, \v1)"; break; case BYTES: p = "VRT_double_string(req, \v1)"; break; /* XXX */ case REAL: p = "VRT_double_string(req, \v1)"; break; case TIME: p = "VRT_time_string(req, \v1)"; break; default: break; } if (p != NULL) { *e = vcc_expr_edit(fmt, p, *e, NULL); (*e)->constant = 0; } }
static void vcc_expr_string_add(struct vcc *tl, struct expr **e) { struct expr *e2; enum var_type f2; f2 = (*e)->fmt; while (tl->t->tok == '+') { vcc_NextToken(tl); vcc_expr_mul(tl, &e2, STRING); ERRCHK(tl); if (e2->fmt != STRING && e2->fmt != STRING_LIST) { vcc_expr_tostring(tl, &e2, f2); ERRCHK(tl); } ERRCHK(tl); assert(e2->fmt == STRING || e2->fmt == STRING_LIST); if (vcc_isconst(*e) && vcc_isconst(e2)) { assert((*e)->fmt == STRING); assert(e2->fmt == STRING); *e = vcc_expr_edit(STRING, "\v1\n\v2", *e, e2); (*e)->constant = EXPR_CONST; } else if (((*e)->constant & EXPR_STR_CONST) && vcc_isconst(e2)) { assert((*e)->fmt == STRING_LIST); assert(e2->fmt == STRING); *e = vcc_expr_edit(STRING_LIST, "\v1\n\v2", *e, e2); (*e)->constant = EXPR_VAR | EXPR_STR_CONST; } else if (e2->fmt == STRING && vcc_isconst(e2)) { *e = vcc_expr_edit(STRING_LIST, "\v1,\n\v2", *e, e2); (*e)->constant = EXPR_VAR | EXPR_STR_CONST; } else { *e = vcc_expr_edit(STRING_LIST, "\v1,\n\v2", *e, e2); (*e)->constant = EXPR_VAR; } } }
static void vcc_expr_strfold(struct vcc *tl, struct expr **e, enum var_type fmt) { vcc_expr_add(tl, e, fmt); ERRCHK(tl); if (fmt != STRING_LIST && (*e)->fmt == STRING_LIST) *e = vcc_expr_edit(STRING, "\v+VRT_CollectString(ctx,\n\v1,\nvrt_magic_string_end)\v-", *e, NULL); if (fmt == STRING_LIST && (*e)->fmt == STRING) (*e)->fmt = STRING_LIST; }
static void vcc_expr_mul(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e2; enum var_type f2, f3; struct token *tk; *e = NULL; vcc_expr4(tl, e, fmt); ERRCHK(tl); f3 = f2 = (*e)->fmt; switch(f2) { case INT: f2 = INT; break; case DURATION: f2 = REAL; break; case BYTES: f2 = REAL; break; default: if (tl->t->tok != '*' && tl->t->tok != '/') return; VSB_printf(tl->sb, "Operator %.*s not possible on type %s.\n", PF(tl->t), vcc_Type(f2)); vcc_ErrWhere(tl, tl->t); return; } while (tl->t->tok == '*' || tl->t->tok == '/') { tk = tl->t; vcc_NextToken(tl); vcc_expr4(tl, &e2, f2); ERRCHK(tl); assert(e2->fmt == f2); if (tk->tok == '*') *e = vcc_expr_edit(f3, "(\v1*\v2)", *e, e2); else *e = vcc_expr_edit(f3, "(\v1/\v2)", *e, e2); } }
static void vcc_expr_tostring(struct vcc *tl, struct expr **e, enum var_type fmt) { const char *p; uint8_t constant = EXPR_VAR; CHECK_OBJ_NOTNULL(*e, EXPR_MAGIC); AN(fmt == STRING || fmt == STRING_LIST); p = NULL; switch((*e)->fmt) { case BACKEND: p = "VRT_BACKEND_string(\v1)"; break; case BOOL: p = "VRT_BOOL_string(\v1)"; break; case DURATION: p = "VRT_REAL_string(ctx, \v1)"; break; /* XXX: should DURATION insist on "s" suffix ? */ case INT: if (vcc_isconst(*e)) { p = "\"\v1\""; constant = EXPR_CONST; } else { p = "VRT_INT_string(ctx, \v1)"; } break; case IP: p = "VRT_IP_string(ctx, \v1)"; break; case BYTES: p = "VRT_REAL_string(ctx, \v1)"; break; /* XXX */ case REAL: p = "VRT_REAL_string(ctx, \v1)"; break; case TIME: p = "VRT_TIME_string(ctx, \v1)"; break; case HEADER: p = "VRT_GetHdr(ctx, \v1)"; break; case ENUM: case STRING: case STRING_LIST: break; case BLOB: VSB_printf(tl->sb, "Wrong use of BLOB value.\n" "BLOBs can only be used as arguments to VMOD" " functions.\n"); vcc_ErrWhere2(tl, (*e)->t1, tl->t); return; default: INCOMPL(); break; } if (p != NULL) { *e = vcc_expr_edit(fmt, p, *e, NULL); (*e)->constant = constant; } }
static void vcc_expr_not(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e2; struct token *tk; *e = NULL; if (fmt != BOOL || tl->t->tok != '!') { vcc_expr_cmp(tl, e, fmt); return; } vcc_NextToken(tl); tk = tl->t; vcc_expr_cmp(tl, &e2, fmt); ERRCHK(tl); if (e2->fmt == BOOL) { *e = vcc_expr_edit(BOOL, "!(\v1)", e2, NULL); return; } VSB_printf(tl->sb, "'!' must be followed by BOOL, found "); VSB_printf(tl->sb, "%s.\n", vcc_Type(e2->fmt)); vcc_ErrWhere2(tl, tk, tl->t); }
static void vcc_expr_cmp(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e2; const struct cmps *cp; char buf[256]; char *re; const char *not; struct token *tk; *e = NULL; 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); ERRCHK(tl); if (e2->fmt != (*e)->fmt) { /* XXX */ VSB_printf(tl->sb, "Comparison of different types: "); VSB_printf(tl->sb, "%s ", vcc_Type((*e)->fmt)); vcc_ErrToken(tl, tk); VSB_printf(tl->sb, " %s\n", vcc_Type(e2->fmt)); 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); ExpectErr(tl, CSTR); re = vcc_regexp(tl); ERRCHK(tl); vcc_NextToken(tl); bprintf(buf, "%sVRT_re_match(\v1, %s)", not, re); *e = vcc_expr_edit(BOOL, buf, *e, NULL); return; } if ((*e)->fmt == IP && (tl->t->tok == '~' || tl->t->tok == T_NOMATCH)) { not = tl->t->tok == '~' ? "" : "!"; vcc_NextToken(tl); ExpectErr(tl, ID); vcc_AddRef(tl, tl->t, SYM_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, SYM_BACKEND); bprintf(buf, "(\v1 %.*s VGCDIR(_%.*s))", PF(tk), PF(tl->t)); vcc_NextToken(tl); *e = vcc_expr_edit(BOOL, buf, *e, NULL); return; } switch (tl->t->tok) { case T_EQ: case T_NEQ: case '<': case T_LEQ: case '>': case T_GEQ: case '~': case T_NOMATCH: VSB_printf(tl->sb, "Operator %.*s not possible on %s\n", PF(tl->t), vcc_Type((*e)->fmt)); vcc_ErrWhere(tl, tl->t); return; default: break; } if (fmt == BOOL && (*e)->fmt == STRING) { *e = vcc_expr_edit(BOOL, "(\v1 != 0)", *e, NULL); return; } }
static void vcc_expr_add(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e2; enum var_type f2; struct token *tk; *e = NULL; vcc_expr_mul(tl, e, fmt); ERRCHK(tl); f2 = (*e)->fmt; if ((f2 == STRING_LIST || f2 == STRING) && tl->t->tok == '+') { while (tl->t->tok == '+') { vcc_NextToken(tl); vcc_expr_mul(tl, &e2, STRING); ERRCHK(tl); if (e2->fmt != STRING && e2->fmt != STRING_LIST) vcc_expr_tostring(&e2, f2); ERRCHK(tl); assert(e2->fmt == STRING || e2->fmt == STRING_LIST); if ((*e)->constant && e2->constant) { assert((*e)->fmt == STRING); assert(e2->fmt == STRING); *e = vcc_expr_edit(STRING, "\v1\n\v2", *e, e2); } else { *e = vcc_expr_edit(STRING_LIST, "\v1,\n\v2", *e, e2); } } } if (fmt != STRING_LIST && (*e)->fmt == STRING_LIST) *e = vcc_expr_edit(STRING, "\v+VRT_WrkString(sp,\n\v1,\nvrt_magic_string_end)", *e, NULL); if (fmt == STRING_LIST && (*e)->fmt == STRING) (*e)->fmt = STRING_LIST; switch(f2) { case INT: break; case TIME: break; case DURATION: break; case BYTES: break; default: if (tl->t->tok != '+' && tl->t->tok != '-') return; VSB_printf(tl->sb, "Operator %.*s not possible on type %s.\n", PF(tl->t), vcc_Type(f2)); vcc_ErrWhere(tl, tl->t); return; } while (tl->t->tok == '+' || tl->t->tok == '-') { if (f2 == TIME) f2 = DURATION; tk = tl->t; vcc_NextToken(tl); vcc_expr_mul(tl, &e2, f2); ERRCHK(tl); if (tk->tok == '-' && (*e)->fmt == TIME && e2->fmt == TIME) { /* OK */ } else if (tk->tok == '-' && (*e)->fmt == BYTES && e2->fmt == BYTES) { /* OK */ } else if (e2->fmt != f2) { VSB_printf(tl->sb, "%s %.*s %s not possible.\n", vcc_Type((*e)->fmt), PF(tk), vcc_Type(e2->fmt)); vcc_ErrWhere2(tl, tk, tl->t); return; } if (tk->tok == '+') *e = vcc_expr_edit(f2, "(\v1+\v2)", *e, e2); else if (f2 == TIME && e2->fmt == TIME) *e = vcc_expr_edit(DURATION, "(\v1-\v2)", *e, e2); else *e = vcc_expr_edit(f2, "(\v1-\v2)", *e, e2); } }
static void vcc_expr4(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e1, *e2; const struct symbol *sym; double d; *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; } switch(tl->t->tok) { case ID: /* * XXX: what if var and func/proc had same name ? * XXX: look for SYM_VAR first for consistency ? */ sym = VCC_FindSymbol(tl, tl->t, SYM_NONE); if (sym == NULL || sym->eval == NULL) { VSB_printf(tl->sb, "Symbol not found: "); vcc_ErrToken(tl, tl->t); VSB_printf(tl->sb, " (expected type %s):\n", vcc_Type(fmt)); vcc_ErrWhere(tl, tl->t); return; } AN(sym); switch(sym->kind) { case SYM_VAR: case SYM_FUNC: case SYM_BACKEND: AN(sym->eval); AZ(*e); sym->eval(tl, e, sym); return; default: break; } VSB_printf(tl->sb, "Symbol type (%s) can not be used in expression.\n", VCC_SymKind(tl, sym)); vcc_ErrWhere(tl, tl->t); return; case CSTR: assert(fmt != VOID); e1 = vcc_new_expr(); EncToken(e1->vsb, tl->t); e1->fmt = STRING; e1->t1 = tl->t; e1->constant = 1; vcc_NextToken(tl); AZ(VSB_finish(e1->vsb)); *e = e1; break; case CNUM: /* * XXX: %g may not have enough decimals by default * XXX: but %a is ugly, isn't it ? */ assert(fmt != VOID); if (fmt == DURATION) { vcc_RTimeVal(tl, &d); ERRCHK(tl); e1 = vcc_mk_expr(DURATION, "%g", d); } else if (fmt == BYTES) { vcc_ByteVal(tl, &d); ERRCHK(tl); e1 = vcc_mk_expr(BYTES, "%.1f", d); ERRCHK(tl); } else if (fmt == REAL) { e1 = vcc_mk_expr(REAL, "%g", vcc_DoubleVal(tl)); ERRCHK(tl); } else { e1 = vcc_mk_expr(INT, "%.*s", PF(tl->t)); vcc_NextToken(tl); } e1->constant = 1; *e = e1; break; default: VSB_printf(tl->sb, "Unknown token "); vcc_ErrToken(tl, tl->t); VSB_printf(tl->sb, " when looking for %s\n\n", vcc_Type(fmt)); vcc_ErrWhere(tl, tl->t); break; } }
void vcc_Eval_Func(struct vcc *tl, struct expr **e, const struct symbol *sym) { const char *p, *r; struct expr *e1, *e2; enum var_type fmt; char buf[32]; assert(sym->kind == SYM_FUNC || sym->kind == SYM_PROC); AN(sym->cfunc); AN(sym->args); SkipToken(tl, ID); SkipToken(tl, '('); p = sym->args; e2 = vcc_mk_expr(vcc_arg_type(&p), "%s(sp\v+", sym->cfunc); while (*p != '\0') { e1 = NULL; fmt = vcc_arg_type(&p); if (fmt == VOID && !strcmp(p, "PRIV_VCL")) { r = strchr(sym->name, '.'); AN(r); e1 = vcc_mk_expr(VOID, "&vmod_priv_%.*s", r - sym->name, sym->name); p += strlen(p) + 1; } else if (fmt == VOID && !strcmp(p, "PRIV_CALL")) { bprintf(buf, "vmod_priv_%u", tl->nvmodpriv++); Fh(tl, 0, "struct vmod_priv %s;\n", buf); e1 = vcc_mk_expr(VOID, "&%s", buf); p += strlen(p) + 1; } else if (fmt == ENUM) { ExpectErr(tl, ID); ERRCHK(tl); r = p; do { if (vcc_IdIs(tl->t, p)) break; p += strlen(p) + 1; } while (*p != '\0'); if (*p == '\0') { VSB_printf(tl->sb, "Wrong enum value."); VSB_printf(tl->sb, " Expected one of:\n"); do { VSB_printf(tl->sb, "\t%s\n", r); r += strlen(r) + 1; } while (*r != '\0'); vcc_ErrWhere(tl, tl->t); return; } e1 = vcc_mk_expr(VOID, "\"%.*s\"", PF(tl->t)); while (*p != '\0') p += strlen(p) + 1; p++; SkipToken(tl, ID); if (*p != '\0') SkipToken(tl, ','); } else if (fmt == HEADER) { const struct var *v; sym = VCC_FindSymbol(tl, tl->t, SYM_NONE); ERRCHK(tl); SkipToken(tl, ID); if (sym == NULL) { VSB_printf(tl->sb, "Symbol not found.\n"); vcc_ErrWhere(tl, tl->t); return; } vcc_AddUses(tl, tl->t, sym->r_methods, "Not available"); if (sym->kind != SYM_VAR) { VSB_printf(tl->sb, "Wrong kind of symbol.\n"); vcc_ErrWhere(tl, tl->t); return; } AN(sym->var); v = sym->var; if (v->http == NULL) { VSB_printf(tl->sb, "Variable not an HTTP header.\n"); vcc_ErrWhere(tl, tl->t); return; } e1 = vcc_mk_expr(VOID, "%s, \"%s\"", v->http, v->hdr); if (*p != '\0') SkipToken(tl, ','); } else { vcc_expr0(tl, &e1, fmt); ERRCHK(tl); if (e1->fmt != fmt) { VSB_printf(tl->sb, "Wrong argument type."); VSB_printf(tl->sb, " Expected %s.", vcc_Type(fmt)); VSB_printf(tl->sb, " Got %s.\n", vcc_Type(e1->fmt)); vcc_ErrWhere2(tl, e1->t1, tl->t); return; } assert(e1->fmt == fmt); if (e1->fmt == STRING_LIST) { e1 = vcc_expr_edit(STRING_LIST, "\v+\n\v1,\nvrt_magic_string_end\v-", e1, NULL); } if (*p != '\0') SkipToken(tl, ','); } e2 = vcc_expr_edit(e2->fmt, "\v1,\n\v2", e2, e1); } SkipToken(tl, ')'); e2 = vcc_expr_edit(e2->fmt, "\v1\n)\v-", e2, NULL); *e = e2; }
static void vcc_expr_add(struct vcc *tl, struct expr **e, enum var_type fmt) { struct expr *e2; enum var_type f2; struct token *tk; *e = NULL; vcc_expr_mul(tl, e, fmt); ERRCHK(tl); f2 = (*e)->fmt; /* Unless we specifically ask for a HEADER, fold them to string here */ if (fmt != HEADER && f2 == HEADER) { vcc_expr_tostring(tl, e, STRING); ERRCHK(tl); f2 = (*e)->fmt; assert(f2 == STRING); } if (tl->t->tok != '+' && tl->t->tok != '-') return; switch(f2) { case STRING: case STRING_LIST: vcc_expr_string_add(tl, e); return; case INT: break; case TIME: break; case DURATION: break; case BYTES: break; default: VSB_printf(tl->sb, "Operator %.*s not possible on type %s.\n", PF(tl->t), vcc_Type(f2)); vcc_ErrWhere(tl, tl->t); return; } while (tl->t->tok == '+' || tl->t->tok == '-') { if (f2 == TIME) f2 = DURATION; tk = tl->t; vcc_NextToken(tl); vcc_expr_mul(tl, &e2, f2); ERRCHK(tl); if (tk->tok == '-' && (*e)->fmt == TIME && e2->fmt == TIME) { /* OK */ } else if ((*e)->fmt == TIME && e2->fmt == DURATION) { f2 = TIME; /* OK */ } else if (tk->tok == '-' && (*e)->fmt == BYTES && e2->fmt == BYTES) { /* OK */ } else if (e2->fmt != f2) { VSB_printf(tl->sb, "%s %.*s %s not possible.\n", vcc_Type((*e)->fmt), PF(tk), vcc_Type(e2->fmt)); vcc_ErrWhere2(tl, tk, tl->t); return; } if (tk->tok == '+') *e = vcc_expr_edit(f2, "(\v1+\v2)", *e, e2); else if (f2 == TIME && e2->fmt == TIME) *e = vcc_expr_edit(DURATION, "(\v1-\v2)", *e, e2); else *e = vcc_expr_edit(f2, "(\v1-\v2)", *e, e2); } }
static void vcc_func(struct vcc *tl, struct expr **e, const char *cfunc, const char *extra, const char *name, const char *args) { const char *p, *r; struct expr *e1, *e2; struct inifin *ifp; enum var_type fmt; char buf[32]; AN(cfunc); AN(args); AN(name); SkipToken(tl, '('); p = args; if (extra == NULL) extra = ""; e1 = vcc_mk_expr(vcc_arg_type(&p), "%s(ctx%s\v+", cfunc, extra); while (*p != '\0') { e2 = NULL; fmt = vcc_arg_type(&p); if (fmt == VOID && !strcmp(p, "PRIV_VCL")) { r = strchr(name, '.'); AN(r); e2 = vcc_mk_expr(VOID, "&vmod_priv_%.*s", (int) (r - name), name); p += strlen(p) + 1; } else if (fmt == VOID && !strcmp(p, "PRIV_CALL")) { bprintf(buf, "vmod_priv_%u", tl->unique++); ifp = New_IniFin(tl); Fh(tl, 0, "static struct vmod_priv %s;\n", buf); VSB_printf(ifp->fin, "\tvmod_priv_fini(&%s);", buf); e2 = vcc_mk_expr(VOID, "&%s", buf); p += strlen(p) + 1; } else if (fmt == ENUM) { ExpectErr(tl, ID); ERRCHK(tl); r = p; do { if (vcc_IdIs(tl->t, p)) break; p += strlen(p) + 1; } while (*p != '\0'); if (*p == '\0') { VSB_printf(tl->sb, "Wrong enum value."); VSB_printf(tl->sb, " Expected one of:\n"); do { VSB_printf(tl->sb, "\t%s\n", r); r += strlen(r) + 1; } while (*r != '\0'); vcc_ErrWhere(tl, tl->t); return; } e2 = vcc_mk_expr(VOID, "\"%.*s\"", PF(tl->t)); while (*p != '\0') p += strlen(p) + 1; p++; SkipToken(tl, ID); if (*p != '\0') /*lint !e448 */ SkipToken(tl, ','); } else { vcc_expr0(tl, &e2, fmt); ERRCHK(tl); if (e2->fmt != fmt) { VSB_printf(tl->sb, "Wrong argument type."); VSB_printf(tl->sb, " Expected %s.", vcc_Type(fmt)); VSB_printf(tl->sb, " Got %s.\n", vcc_Type(e2->fmt)); vcc_ErrWhere2(tl, e2->t1, tl->t); return; } assert(e2->fmt == fmt); if (e2->fmt == STRING_LIST) { e2 = vcc_expr_edit(STRING_LIST, "\v+\n\v1,\nvrt_magic_string_end\v-", e2, NULL); } if (*p != '\0') SkipToken(tl, ','); } e1 = vcc_expr_edit(e1->fmt, "\v1,\n\v2", e1, e2); } SkipToken(tl, ')'); e1 = vcc_expr_edit(e1->fmt, "\v1\n)\v-", e1, NULL); *e = e1; }