1218 lines
24 KiB
C
1218 lines
24 KiB
C
/*
|
|
* Copyright 1991 Silicon Graphics, Inc. All rights reserved.
|
|
*
|
|
* Recursive descent filter expression parser.
|
|
*/
|
|
#include <bstring.h>
|
|
#include <ctype.h>
|
|
#include "expr.h"
|
|
#include "heap.h"
|
|
#include "macros.h"
|
|
#include "protocol.h"
|
|
#include "scope.h"
|
|
#include "snooper.h"
|
|
#include "strings.h"
|
|
|
|
enum tokencode {
|
|
BAD = -1,
|
|
END,
|
|
ARG,
|
|
COMMA,
|
|
OR,
|
|
AND,
|
|
BITOR,
|
|
BITXOR,
|
|
BITAND,
|
|
EQ, NE,
|
|
LE, LT, GE, GT,
|
|
LSH, RSH,
|
|
PLUS, MINUS,
|
|
MUL, DIV, MOD,
|
|
NOT, BITNOT,
|
|
LB, RB, DOT,
|
|
NAME, NUMBER, STRING, QUOTEDSTRING,
|
|
LP, RP
|
|
};
|
|
|
|
static int maclevel; /* macro call nesting level */
|
|
static Snooper *snoop; /* invariant arg to recursive parser */
|
|
static ExprError exprerr; /* invariant error report arg/result */
|
|
|
|
typedef struct parsedata ParseData;
|
|
|
|
struct parsedata {
|
|
char *firstc; /* base of input buffer */
|
|
char *nextc; /* points to next input char */
|
|
char *token; /* last token recognized */
|
|
ExprSource *src; /* parser input information */
|
|
long numval; /* last number scanned */
|
|
Protocol *proto; /* protocol for path lookup */
|
|
Protocol *lastproto; /* last protocol in path lookup */
|
|
Scope *instruct; /* open structure scope or null */
|
|
ParseData *caller; /* macro calling context */
|
|
struct string *argv; /* macro call actual args */
|
|
};
|
|
|
|
static enum tokencode token(ParseData *);
|
|
static char *match(enum tokencode, ParseData *);
|
|
|
|
/*
|
|
* Note how input is zapped on first error, by setting pd->nextc to point
|
|
* at an empty string.
|
|
*/
|
|
static void
|
|
error(char *message, ParseData *pd)
|
|
{
|
|
if (exprerr.err_message)
|
|
return;
|
|
exprerr.err_message = message;
|
|
exprerr.err_token = pd->token;
|
|
if (pd->src)
|
|
exprerr.err_source = *pd->src;
|
|
pd->nextc = "";
|
|
}
|
|
|
|
/*
|
|
* Forward prototype declaration of expression string parser.
|
|
*/
|
|
static Expr *parse(struct string *, ExprSource *, Protocol *, struct string *,
|
|
ParseData *);
|
|
|
|
/*
|
|
* Public interface to the parser which takes a terminated string pointed
|
|
* at by an ExprSource descriptor.
|
|
*/
|
|
Expr *
|
|
ex_parse(ExprSource *src, Snooper *sn, Protocol *pr, ExprError *err)
|
|
{
|
|
struct string s;
|
|
Expr *ex;
|
|
|
|
snoop = sn;
|
|
bzero(&exprerr, sizeof exprerr);
|
|
s.s_ptr = src->src_buf;
|
|
s.s_len = strlen(src->src_buf);
|
|
ex = parse(&s, src, pr, 0, 0);
|
|
*err = exprerr;
|
|
return ex;
|
|
}
|
|
|
|
/*
|
|
* Forward prototype declarations for most of the grammar.
|
|
*/
|
|
static Expr *commaexpr(ParseData *);
|
|
static Expr *orexpr(ParseData *);
|
|
static Expr *andexpr(ParseData *);
|
|
static Expr *bitorexpr(ParseData *);
|
|
static Expr *bitxorexpr(ParseData *);
|
|
static Expr *bitandexpr(ParseData *);
|
|
static Expr *eqexpr(ParseData *);
|
|
static Expr *relexpr(ParseData *);
|
|
static Expr *shiftexpr(ParseData *);
|
|
static Expr *addexpr(ParseData *);
|
|
static Expr *mulexpr(ParseData *);
|
|
static Expr *unaryexpr(ParseData *);
|
|
static Expr *memberexpr(ParseData *);
|
|
static Expr *primaryexpr(ParseData *);
|
|
static void correct(Expr *, int, ParseData *);
|
|
|
|
/*
|
|
* Parse the expression in sp using src for diagnostics, pr as the raw
|
|
* protocol, argv for macro actuals, and caller for macro calls. If the
|
|
* user failed to supply an operator between two operands, try to add an
|
|
* appropriate operator.
|
|
*/
|
|
static Expr *
|
|
parse(struct string *sp, ExprSource *src, Protocol *pr, struct string *argv,
|
|
ParseData *caller)
|
|
{
|
|
ParseData pd;
|
|
char *end, save;
|
|
Expr *ex;
|
|
|
|
pd.firstc = pd.nextc = sp->s_ptr;
|
|
pd.src = holdExprSource(src);
|
|
pd.proto = pd.lastproto = pr;
|
|
pd.instruct = 0;
|
|
pd.caller = caller;
|
|
pd.argv = argv;
|
|
end = sp->s_ptr + sp->s_len;
|
|
save = *end;
|
|
*end = '\0';
|
|
|
|
for (;;) {
|
|
ex = commaexpr(&pd);
|
|
if (exprerr.err_message) {
|
|
ex_destroy(ex);
|
|
ex = 0;
|
|
break;
|
|
}
|
|
if (*pd.nextc == '\0')
|
|
break;
|
|
correct(ex, 0, &pd);
|
|
ex_destroy(ex);
|
|
}
|
|
dropExprSource(pd.src);
|
|
*end = save;
|
|
return ex;
|
|
}
|
|
|
|
static int
|
|
boolean(enum exop op)
|
|
{
|
|
switch (op) {
|
|
case EXOP_OR: /* logical, equality, relational, inverse */
|
|
case EXOP_AND:
|
|
case EXOP_EQ:
|
|
case EXOP_NE:
|
|
case EXOP_LT:
|
|
case EXOP_LE:
|
|
case EXOP_GT:
|
|
case EXOP_GE:
|
|
case EXOP_NOT:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
correct(Expr *ex, int pos, ParseData *pd)
|
|
{
|
|
char *from, *buf, *to;
|
|
int len, off, rem;
|
|
enum exop op;
|
|
static char *lastbuf;
|
|
|
|
/*
|
|
* Compute offset of missing operator in pd and allocate new space
|
|
* for the corrected version. Allow at most 4 characters (" == " or
|
|
* " && " depending on left context and white space).
|
|
*/
|
|
from = pd->firstc;
|
|
len = strlen(from);
|
|
off = pd->nextc - from;
|
|
rem = len - off;
|
|
len += 4;
|
|
buf = vnew(len + 1, char);
|
|
|
|
/*
|
|
* Check for a binary operator with a protocol path on the right.
|
|
* Non-boolean protocol expressions are corrected to become left
|
|
* operands of equality ops.
|
|
*/
|
|
op = ex->ex_op;
|
|
if (ex->ex_arity == EX_BINARY) {
|
|
switch (ex->ex_right->ex_op) {
|
|
case EXOP_PROTOCOL:
|
|
case EXOP_FIELD:
|
|
ex = ex->ex_right;
|
|
op = ex->ex_op;
|
|
}
|
|
}
|
|
while (op == EXOP_PROTOCOL) {
|
|
ex = ex->ex_member;
|
|
if (ex == 0) {
|
|
op = EXOP_EQ; /* boolean protocol path */
|
|
break;
|
|
}
|
|
op = ex->ex_op;
|
|
}
|
|
|
|
/*
|
|
* Now copy the old string to the new, inserting the correction at
|
|
* the offset where parsing stopped prematurely. Add white space
|
|
* only where it might be needed to terminate macro argument lists.
|
|
*/
|
|
to = buf;
|
|
bcopy(from, to, off);
|
|
from += off, to += off;
|
|
if (!isspace(from[-1]))
|
|
*to++ = ' ';
|
|
if (boolean(op))
|
|
bcopy("&&", to, 2);
|
|
else
|
|
bcopy("==", to, 2);
|
|
to += 2;
|
|
if (!isspace(*from))
|
|
*to++ = ' ';
|
|
bcopy(from, to, rem);
|
|
to += rem;
|
|
*to = '\0';
|
|
|
|
/*
|
|
* Delete any old correction and restore pd for another parse.
|
|
* We delete the old source late in order to avoid overlapping
|
|
* copies from freed store, above.
|
|
*/
|
|
if (pd->firstc == lastbuf)
|
|
delete(lastbuf);
|
|
pd->firstc = lastbuf = buf; /* XXX leaky */
|
|
pd->nextc = buf + pos;
|
|
if (pd->src)
|
|
pd->src->src_buf = buf;
|
|
pd->lastproto = pd->proto;
|
|
pd->instruct = 0;
|
|
}
|
|
|
|
static Expr *
|
|
binary(enum exop bop, Expr *lex, Expr *rex, char *token, ExprSource *src)
|
|
{
|
|
Expr *ex;
|
|
|
|
if (lex == 0 || rex == 0) {
|
|
ex_destroy(lex);
|
|
ex_destroy(rex);
|
|
return 0;
|
|
}
|
|
ex = expr(bop, EX_BINARY, token);
|
|
ex_holdsrc(ex, src);
|
|
ex->ex_left = lex;
|
|
ex->ex_right = rex;
|
|
if (ex_eval(ex, &exprerr) == ET_ERROR) {
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
unary(enum exop uop, Expr *kex, char *token, ExprSource *src)
|
|
{
|
|
Expr *ex;
|
|
|
|
if (kex == 0)
|
|
return 0;
|
|
ex = expr(uop, EX_UNARY, token);
|
|
ex_holdsrc(ex, src);
|
|
ex->ex_kid = kex;
|
|
if (ex_eval(ex, &exprerr) == ET_ERROR) {
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
nullary(enum exop op, char *token, ExprSource *src)
|
|
{
|
|
Expr *ex;
|
|
|
|
ex = expr(op, EX_NULLARY, token);
|
|
ex_holdsrc(ex, src);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
commaexpr(ParseData *pd)
|
|
{
|
|
Expr *ex;
|
|
|
|
ex = orexpr(pd);
|
|
while (ex && match(COMMA, pd)) {
|
|
ex_destroy(ex);
|
|
ex = orexpr(pd);
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
orexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = andexpr(pd);
|
|
while (ex && (t = match(OR, pd)) != 0)
|
|
ex = binary(EXOP_OR, ex, andexpr(pd), t, pd->src);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
andexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = bitorexpr(pd);
|
|
while (ex && (t = match(AND, pd)) != 0)
|
|
ex = binary(EXOP_AND, ex, bitorexpr(pd), t, pd->src);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
bitorexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = bitxorexpr(pd);
|
|
while (ex && (t = match(BITOR, pd)) != 0)
|
|
ex = binary(EXOP_BITOR, ex, bitxorexpr(pd), t, pd->src);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
bitxorexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = bitandexpr(pd);
|
|
while (ex && (t = match(BITXOR, pd)) != 0)
|
|
ex = binary(EXOP_BITXOR, ex, bitandexpr(pd), t, pd->src);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
bitandexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = eqexpr(pd);
|
|
while (ex && (t = match(BITAND, pd)) != 0)
|
|
ex = binary(EXOP_BITAND, ex, eqexpr(pd), t, pd->src);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
eqexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = relexpr(pd);
|
|
while (ex)
|
|
if (t = match(EQ, pd))
|
|
ex = binary(EXOP_EQ, ex, relexpr(pd), t, pd->src);
|
|
else if (t = match(NE, pd))
|
|
ex = binary(EXOP_NE, ex, relexpr(pd), t, pd->src);
|
|
else
|
|
break;
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
relexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = shiftexpr(pd);
|
|
while (ex)
|
|
if (t = match(LT, pd))
|
|
ex = binary(EXOP_LT, ex, shiftexpr(pd), t, pd->src);
|
|
else if (t = match(LE, pd))
|
|
ex = binary(EXOP_LE, ex, shiftexpr(pd), t, pd->src);
|
|
else if (t = match(GT, pd))
|
|
ex = binary(EXOP_GT, ex, shiftexpr(pd), t, pd->src);
|
|
else if (t = match(GE, pd))
|
|
ex = binary(EXOP_GE, ex, shiftexpr(pd), t, pd->src);
|
|
else
|
|
break;
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
shiftexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = addexpr(pd);
|
|
while (ex)
|
|
if (t = match(LSH, pd))
|
|
ex = binary(EXOP_LSH, ex, addexpr(pd), t, pd->src);
|
|
else if (t = match(RSH, pd))
|
|
ex = binary(EXOP_RSH, ex, addexpr(pd), t, pd->src);
|
|
else
|
|
break;
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
addexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = mulexpr(pd);
|
|
while (ex)
|
|
if (t = match(PLUS, pd))
|
|
ex = binary(EXOP_ADD, ex, mulexpr(pd), t, pd->src);
|
|
else if (t = match(MINUS, pd))
|
|
ex = binary(EXOP_SUB, ex, mulexpr(pd), t, pd->src);
|
|
else
|
|
break;
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
mulexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
ex = unaryexpr(pd);
|
|
while (ex)
|
|
if (t = match(MUL, pd))
|
|
ex = binary(EXOP_MUL, ex, unaryexpr(pd), t, pd->src);
|
|
else if (t = match(DIV, pd))
|
|
ex = binary(EXOP_DIV, ex, unaryexpr(pd), t, pd->src);
|
|
else if (t = match(MOD, pd))
|
|
ex = binary(EXOP_MOD, ex, unaryexpr(pd), t, pd->src);
|
|
else
|
|
break;
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
unaryexpr(ParseData *pd)
|
|
{
|
|
char *t;
|
|
Expr *ex;
|
|
|
|
if (t = match(NOT, pd))
|
|
ex = unary(EXOP_NOT, unaryexpr(pd), t, pd->src);
|
|
else if (t = match(BITNOT, pd))
|
|
ex = unary(EXOP_BITNOT, unaryexpr(pd), t, pd->src);
|
|
else if (t = match(MINUS, pd))
|
|
ex = unary(EXOP_NEG, unaryexpr(pd), t, pd->src);
|
|
else
|
|
ex = memberexpr(pd);
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
memberexpr(ParseData *pd)
|
|
{
|
|
Expr *ex, *rex, *lex;
|
|
char *t;
|
|
int pos;
|
|
Protocol *save;
|
|
|
|
ex = primaryexpr(pd);
|
|
while (ex) {
|
|
if (t = match(LB, pd)) {
|
|
/*
|
|
* Left side must be an array.
|
|
*/
|
|
if (!ex_typematch(ex, EXOP_ARRAY, &pd->instruct)) {
|
|
ex_error(ex, "not an array", &exprerr);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Correct missing operator errors in the bracketed
|
|
* index expression.
|
|
*/
|
|
pos = pd->nextc - pd->firstc;
|
|
while ((rex = commaexpr(pd)) != 0 && !match(RB, pd)) {
|
|
if (*pd->nextc == '\0') { /* [ */
|
|
error("missing ']'", pd);
|
|
ex_destroy(rex);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
correct(ex, pos, pd);
|
|
}
|
|
|
|
/*
|
|
* Right side must be an integer expression.
|
|
*/
|
|
if (!ex_typematch(rex, EXOP_NUMBER, &pd->instruct)) {
|
|
ex_error(rex, "illegal array index", &exprerr);
|
|
ex_destroy(rex);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
|
|
ex = binary(EXOP_ARRAY, ex, rex, t, pd->src);
|
|
} else if (t = match(DOT, pd)) {
|
|
/*
|
|
* Left side must be a struct or protocol path. Find
|
|
* the lowest struct or protocol node.
|
|
*/
|
|
lex = ex_typematch(ex, EXOP_STRUCT, &pd->instruct);
|
|
if (lex == 0) {
|
|
ex_error(ex, "illegal left side of '.'",
|
|
&exprerr);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the left side is a protocol path, the right side
|
|
* either replaces ex or connects to lex->ex_member.
|
|
*/
|
|
if (lex->ex_op == EXOP_PROTOCOL) {
|
|
save = pd->proto;
|
|
pd->proto = lex->ex_prsym->sym_proto;
|
|
rex = memberexpr(pd);
|
|
pd->proto = save;
|
|
if (rex == 0) {
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
switch (rex->ex_op) {
|
|
case EXOP_NUMBER: /* protocol constant */
|
|
case EXOP_ADDRESS:
|
|
case EXOP_STRING:
|
|
ex_destroy(ex);
|
|
ex = rex;
|
|
break;
|
|
default:
|
|
lex->ex_member = rex;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Parse the member expression on the right. Require
|
|
* that it be a field or a field aggregate.
|
|
*/
|
|
rex = memberexpr(pd);
|
|
if (rex == 0) {
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
switch (rex->ex_op) {
|
|
case EXOP_ARRAY:
|
|
case EXOP_STRUCT:
|
|
case EXOP_FIELD:
|
|
break;
|
|
default:
|
|
ex_error(rex, "illegal right side of '.'",
|
|
&exprerr);
|
|
ex_destroy(rex);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
ex = binary(EXOP_STRUCT, ex, rex, t, pd->src);
|
|
} else {
|
|
/*
|
|
* Mismatch: return to lower-precedence parsers.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
pd->instruct = 0;
|
|
return ex;
|
|
}
|
|
|
|
static Expr *pathexpr(char *, int, ParseData *);
|
|
static Expr *strexpr(char *, int, enum tokencode, ParseData *);
|
|
|
|
static Expr *
|
|
primaryexpr(ParseData *pd)
|
|
{
|
|
enum tokencode tc;
|
|
int len, pos;
|
|
Expr *ex;
|
|
|
|
tc = token(pd);
|
|
len = pd->nextc - pd->token;
|
|
switch (tc) {
|
|
case ARG:
|
|
if (maclevel == 0) {
|
|
error("formal argument outside macro", pd);
|
|
return 0;
|
|
}
|
|
ex = parse(&pd->argv[pd->numval-1], pd->caller->src,
|
|
pd->proto, pd->caller->argv, pd->caller->caller);
|
|
break;
|
|
case NAME:
|
|
ex = pathexpr(pd->token, len, pd);
|
|
break;
|
|
case NUMBER:
|
|
ex = nullary(EXOP_NUMBER, pd->token, pd->src);
|
|
ex->ex_val = pd->numval;
|
|
break;
|
|
case STRING:
|
|
ex = strexpr(pd->token, len, tc, pd);
|
|
break;
|
|
case QUOTEDSTRING:
|
|
ex = strexpr(pd->token + 1, len - 2, tc, pd);
|
|
break;
|
|
case LP:
|
|
pos = pd->nextc - pd->firstc;
|
|
while ((ex = commaexpr(pd)) != 0 && !match(RP, pd)) {
|
|
if (*pd->nextc == '\0') { /* ( */
|
|
error("missing ')'", pd);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
correct(ex, pos, pd);
|
|
}
|
|
break;
|
|
default:
|
|
if (tc == END)
|
|
error("incomplete expression", pd);
|
|
else if (tc != BAD)
|
|
error("missing operand", pd);
|
|
return 0;
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
/*
|
|
* Built-in functions and protocols.
|
|
*/
|
|
enum bintype { BIN_DEFINE, BIN_UNDEF, BIN_FETCH, BIN_STRING, BIN_SNOOP };
|
|
|
|
struct builtin {
|
|
char *name; /* builtin name */
|
|
int namlen; /* and name length */
|
|
enum bintype type; /* type code */
|
|
int nargs; /* number of arguments */
|
|
};
|
|
#define BIN(name, type, nargs) \
|
|
{ name, constrlen(name), type, nargs }
|
|
|
|
static struct builtin builtins[] = {
|
|
BIN("define", BIN_DEFINE, 2),
|
|
BIN("undef", BIN_UNDEF, 1),
|
|
BIN("fetch", BIN_FETCH, 2),
|
|
BIN("string", BIN_STRING, 2),
|
|
BIN("snoop", BIN_SNOOP, 0),
|
|
BIN("SNOOP", BIN_SNOOP, 0),
|
|
0,
|
|
};
|
|
|
|
static struct builtin *binfind(char *, int);
|
|
static Expr *binexpr(char *, struct builtin *, Protocol *, ParseData *);
|
|
static Expr *callexpr(char *, struct funcdef *, Protocol *, ParseData *);
|
|
static Expr *macexpr(char *, struct macrodef *, Protocol *, ParseData *);
|
|
|
|
static Expr *
|
|
pathexpr(char *path, int pathlen, ParseData *pd)
|
|
{
|
|
Protocol *pr;
|
|
Expr *ex, *rex;
|
|
char *name;
|
|
int namlen;
|
|
Symbol *sym;
|
|
|
|
pr = pd->proto;
|
|
nextcomponent:
|
|
name = path;
|
|
while (pathlen != 0 && *path != '.')
|
|
--pathlen, path++;
|
|
namlen = path - name;
|
|
|
|
/*
|
|
* If we're in a structure scope, look for the named struct member.
|
|
* Otherwise try pr's scope, then pd->lastproto's.
|
|
*/
|
|
if (pd->instruct) {
|
|
sym = sc_lookupsym(pd->instruct, name, namlen);
|
|
pd->instruct = 0;
|
|
if (sym == 0) {
|
|
error("unknown member", pd);
|
|
return 0;
|
|
}
|
|
} else {
|
|
sym = pr_lookupsym(pr, name, namlen);
|
|
if (sym == 0 && pr != pd->lastproto) {
|
|
pr = pd->lastproto;
|
|
sym = pr_lookupsym(pr, name, namlen);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we couldn't find name in a scope, check for a degenerate or
|
|
* reflexive path component like "ether" where pr is ðer_proto.
|
|
* If that fails, try a builtin. If name isn't known, make name
|
|
* and the rest of the path into a string expression.
|
|
*/
|
|
if (sym == 0) {
|
|
struct builtin *bin;
|
|
|
|
pr = pd->proto;
|
|
if (findprotobyname(name, namlen) == pr) {
|
|
if (pathlen != 0) {
|
|
--pathlen, path++;
|
|
goto nextcomponent;
|
|
}
|
|
ex = nullary(EXOP_NUMBER, "1", 0);
|
|
ex->ex_val = 1;
|
|
return ex;
|
|
}
|
|
bin = binfind(name, namlen);
|
|
if (bin == 0)
|
|
return strexpr(name, namlen + pathlen, STRING, pd);
|
|
ex = binexpr(name, bin, pr, pd);
|
|
if (ex == 0)
|
|
return 0;
|
|
} else {
|
|
/*
|
|
* Build an expression node for sym, collecting arguments
|
|
* from pd if necessary.
|
|
*/
|
|
switch (sym->sym_type) {
|
|
case SYM_ADDRESS:
|
|
ex = nullary(EXOP_ADDRESS, name, pd->src);
|
|
ex->ex_addr = sym->sym_addr;
|
|
break;
|
|
case SYM_NUMBER:
|
|
ex = nullary(EXOP_NUMBER, name, pd->src);
|
|
ex->ex_val = sym->sym_val;
|
|
break;
|
|
case SYM_PROTOCOL:
|
|
ex = nullary(EXOP_PROTOCOL, name, pd->src);
|
|
ex->ex_prsym = sym;
|
|
ex->ex_member = 0;
|
|
break;
|
|
case SYM_FIELD:
|
|
ex = nullary(EXOP_FIELD, name, pd->src);
|
|
ex->ex_field = sym->sym_field;
|
|
break;
|
|
case SYM_MACRO:
|
|
ex = macexpr(name, &sym->sym_def, pr, pd);
|
|
if (ex == 0)
|
|
return 0;
|
|
break;
|
|
case SYM_FUNCTION:
|
|
ex = callexpr(name, &sym->sym_func, pr, pd);
|
|
if (ex == 0)
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update pd->nextc if there are path components left, and update
|
|
* pd->lastproto if ex is a protocol node.
|
|
*/
|
|
if (pathlen != 0)
|
|
pd->nextc = path;
|
|
rex = ex;
|
|
while (ex->ex_op == EXOP_PROTOCOL) {
|
|
pr = ex->ex_prsym->sym_proto;
|
|
ex = ex->ex_member;
|
|
if (ex == 0)
|
|
break;
|
|
}
|
|
pd->lastproto = pr;
|
|
return rex;
|
|
}
|
|
|
|
static Expr *
|
|
strexpr(char *ptr, int len, enum tokencode tc, ParseData *pd)
|
|
{
|
|
char save;
|
|
Expr *ex;
|
|
|
|
save = ptr[len];
|
|
ptr[len] = '\0';
|
|
ex = pr_resolve(pd->lastproto, ptr, len, snoop);
|
|
if (ex == 0 && pd->lastproto != pd->proto)
|
|
ex = pr_resolve(pd->proto, ptr, len, snoop);
|
|
ptr[len] = save;
|
|
if (ex) {
|
|
ex_holdsrc(ex, pd->src);
|
|
} else if (tc == QUOTEDSTRING) {
|
|
ex = ex_string(ptr, len);
|
|
ex_holdsrc(ex, pd->src);
|
|
} else {
|
|
pd->token = ptr;
|
|
error("unresolved string", pd);
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static struct builtin *
|
|
binfind(char *name, int namlen)
|
|
{
|
|
struct builtin *bin;
|
|
|
|
for (bin = builtins; bin->name; bin++) {
|
|
if (bin->namlen == namlen && !bcmp(bin->name, name, namlen))
|
|
return bin;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int getargs(int, struct string *, ParseData *);
|
|
static int getshellstyleargs(int, struct string *, ParseData *);
|
|
static int getcppstyleargs(int, struct string *, ParseData *);
|
|
static int parseunsigned(struct string *, Protocol *, ParseData *, unsigned *);
|
|
|
|
static Expr *
|
|
binexpr(char *name, struct builtin *bin, Protocol *pr, ParseData *pd)
|
|
{
|
|
int argc;
|
|
struct string argv[MAXMACROARGS];
|
|
Expr *ex;
|
|
static Symbol snoopsym;
|
|
|
|
argc = getargs(bin->nargs, argv, pd);
|
|
if (argc < 0)
|
|
return 0;
|
|
switch (bin->type) {
|
|
case BIN_DEFINE:
|
|
pr_addmacro(pr, argv[0].s_ptr, argv[0].s_len,
|
|
argv[1].s_ptr, argv[1].s_len, pd->src);
|
|
ex = nullary(EXOP_NUMBER, name, pd->src);
|
|
ex->ex_val = 1;
|
|
break;
|
|
case BIN_UNDEF:
|
|
pr_deletesym(pr, argv[0].s_ptr, argv[0].s_len);
|
|
ex = nullary(EXOP_NUMBER, name, pd->src);
|
|
ex->ex_val = 1;
|
|
break;
|
|
case BIN_FETCH:
|
|
case BIN_STRING:
|
|
ex = nullary(EXOP_FETCH, name, pd->src);
|
|
if (!parseunsigned(&argv[0], pr, pd, &ex->ex_size)
|
|
|| !parseunsigned(&argv[1], pr, pd, &ex->ex_off)) {
|
|
ex_destroy(ex);
|
|
ex = 0;
|
|
} else if (bin->type == BIN_STRING) {
|
|
ex->ex_type = EXOP_STRING;
|
|
} else {
|
|
switch (ex->ex_size) {
|
|
case sizeof(char):
|
|
case sizeof(short):
|
|
case sizeof(long):
|
|
ex->ex_type = EXOP_NUMBER;
|
|
break;
|
|
default:
|
|
if (ex->ex_size <= sizeof ex->ex_addr)
|
|
ex->ex_type = EXOP_ADDRESS;
|
|
else
|
|
ex->ex_type = EXOP_STRING;
|
|
}
|
|
}
|
|
break;
|
|
case BIN_SNOOP:
|
|
ex = nullary(EXOP_PROTOCOL, name, pd->src);
|
|
if (snoopsym.sym_name == 0) {
|
|
snoopsym.sym_name = snoop_proto.pr_name;
|
|
snoopsym.sym_namlen = snoop_proto.pr_namlen;
|
|
snoopsym.sym_type = SYM_PROTOCOL;
|
|
snoopsym.sym_proto = &snoop_proto;
|
|
}
|
|
ex->ex_prsym = &snoopsym;
|
|
ex->ex_member = 0;
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
callexpr(char *name, struct funcdef *fd, Protocol *pr, ParseData *pd)
|
|
{
|
|
int argc, n;
|
|
struct string argv[MAXCALLARGS];
|
|
Expr *ex, *arg;
|
|
|
|
argc = getargs(fd->fd_nargs, argv, pd);
|
|
if (argc < 0)
|
|
return 0;
|
|
ex = nullary(EXOP_CALL, name, pd->src);
|
|
ex->ex_func = fd->fd_func;
|
|
for (n = 0; n < argc; n++) {
|
|
arg = parse(&argv[n], pd->src, pr, pd->argv, pd->caller);
|
|
if (arg == 0) {
|
|
while (--n >= 0)
|
|
ex_destroy(ex->ex_args[n]);
|
|
ex_destroy(ex);
|
|
return 0;
|
|
}
|
|
ex->ex_args[n] = arg;
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static Expr *
|
|
macexpr(char *name, struct macrodef *md, Protocol *pr, ParseData *pd)
|
|
{
|
|
int argc;
|
|
struct string argv[MAXMACROARGS];
|
|
Expr *ex;
|
|
|
|
if (maclevel >= 20) {
|
|
error("too much macro nesting", pd);
|
|
return 0;
|
|
}
|
|
argc = getargs(md->md_nargs, argv, pd);
|
|
if (argc < 0)
|
|
return 0;
|
|
maclevel++;
|
|
ex = parse(&md->md_string, md->md_src, pr, argv, pd);
|
|
--maclevel;
|
|
return ex;
|
|
}
|
|
|
|
static int
|
|
getargs(int nargs, struct string *argv, ParseData *pd)
|
|
{
|
|
int argc;
|
|
|
|
argc = 0;
|
|
if (nargs != 0) {
|
|
if (isspace(*pd->nextc))
|
|
argc = getshellstyleargs(nargs, argv, pd);
|
|
else if (match(LP, pd) && !match(RP, pd))
|
|
argc = getcppstyleargs(nargs, argv, pd);
|
|
if (argc < 0)
|
|
return argc;
|
|
}
|
|
if (nargs > 0 && argc != nargs) {
|
|
error("too few arguments", pd);
|
|
return -1;
|
|
}
|
|
return argc;
|
|
}
|
|
|
|
static int
|
|
getshellstyleargs(int nargs, struct string *argv, ParseData *pd)
|
|
{
|
|
int argc;
|
|
char *t, c;
|
|
struct string *ap;
|
|
|
|
t = pd->nextc;
|
|
for (argc = 0; argc < nargs; argc++) {
|
|
while (isspace(c = *t))
|
|
t++;
|
|
if (c == '\0')
|
|
break;
|
|
pd->token = t;
|
|
ap = &argv[argc];
|
|
ap->s_ptr = t;
|
|
while (!isspace(c = *t) && c != '\0')
|
|
t++;
|
|
ap->s_len = t - ap->s_ptr;
|
|
}
|
|
pd->nextc = t;
|
|
return argc;
|
|
}
|
|
|
|
static int
|
|
getcppstyleargs(int nargs, struct string *argv, ParseData *pd)
|
|
{
|
|
int argc;
|
|
enum tokencode tc;
|
|
|
|
argc = 0;
|
|
do {
|
|
char *arg;
|
|
int parens;
|
|
|
|
if (argc >= nargs) {
|
|
error("too many arguments", pd);
|
|
return -1;
|
|
}
|
|
arg = pd->nextc;
|
|
parens = 0;
|
|
while ((tc = token(pd)) != END) {
|
|
if (tc == BAD)
|
|
return -1;
|
|
if (tc == LP) {
|
|
parens++;
|
|
} else if (tc == COMMA) {
|
|
if (parens == 0)
|
|
break;
|
|
} else if (tc == RP) {
|
|
if (parens == 0)
|
|
break;
|
|
--parens;
|
|
}
|
|
}
|
|
argv[argc].s_ptr = arg;
|
|
argv[argc].s_len = pd->token - arg;
|
|
argc++;
|
|
} while (tc == COMMA);
|
|
if (tc != RP) {
|
|
error("missing ')'", pd);
|
|
return -1;
|
|
}
|
|
return argc;
|
|
}
|
|
|
|
static int
|
|
parseunsigned(struct string *sp, Protocol *pr, ParseData *pd, unsigned *ip)
|
|
{
|
|
Expr *ex;
|
|
int ok;
|
|
|
|
ex = parse(sp, pd->src, pr, pd->argv, pd->caller);
|
|
if (ex == 0)
|
|
return 0;
|
|
if (ex_eval(ex, &exprerr) == ET_SIMPLE && ex->ex_op == EXOP_NUMBER) {
|
|
*ip = ex->ex_val;
|
|
ok = 1;
|
|
} else {
|
|
ex_error(ex, "constant integer expression required", &exprerr);
|
|
ok = 0;
|
|
}
|
|
ex_destroy(ex);
|
|
return ok;
|
|
}
|
|
|
|
static enum tokencode
|
|
token(ParseData *pd)
|
|
{
|
|
char *t;
|
|
char c;
|
|
enum tokencode tc;
|
|
|
|
t = pd->nextc;
|
|
while (isspace(c = *t))
|
|
t++;
|
|
pd->token = t;
|
|
|
|
if (isalpha(c) || c == '_') {
|
|
int funny;
|
|
|
|
tc = NAME;
|
|
do {
|
|
c = *++t;
|
|
funny = (c == ':' || c == '-' || c == '@');
|
|
if (funny)
|
|
tc = STRING;
|
|
} while (isalnum(c) || c == '_' || c == '.' || funny);
|
|
pd->nextc = t;
|
|
if (tc == NAME) {
|
|
int len;
|
|
#define iskeyword(s) \
|
|
(len == constrlen(s) && !strncmp(pd->token, s, len))
|
|
|
|
len = t - pd->token;
|
|
if (iskeyword("and"))
|
|
return AND;
|
|
if (iskeyword("or"))
|
|
return OR;
|
|
if (iskeyword("not"))
|
|
return NOT;
|
|
#undef iskeyword
|
|
}
|
|
return tc;
|
|
}
|
|
|
|
if (isdigit(c) || c == ':') {
|
|
pd->numval = strtoul(t, &pd->nextc, 0);
|
|
t = pd->nextc;
|
|
c = *t;
|
|
if (isalpha(c) || c == '.' || c == ':' || c == '-') {
|
|
do {
|
|
c = *++t;
|
|
} while (isalnum(c) || c == '.' || c == ':' || c== '-');
|
|
pd->nextc = t;
|
|
return STRING;
|
|
}
|
|
return NUMBER;
|
|
}
|
|
|
|
if (c == '"' || c == '\'') {
|
|
char qc = c;
|
|
|
|
while ((c = *++t) != qc)
|
|
if (c == '\0') {
|
|
error("unterminated string", pd);
|
|
pd->nextc = t;
|
|
return BAD;
|
|
}
|
|
pd->nextc = t + 1;
|
|
return QUOTEDSTRING;
|
|
}
|
|
|
|
#define ifnext(c, y, n) (t[1] == (c) ? t++, (y) : (n))
|
|
switch (c) {
|
|
case '\0':
|
|
pd->nextc = t;
|
|
return END; /* NB: early return */
|
|
case '$':
|
|
t++;
|
|
pd->numval = strtol(t, &pd->nextc, 10);
|
|
if (pd->nextc == t
|
|
|| pd->numval <= 0 || MAXMACROARGS < pd->numval) {
|
|
error("illegal formal argument", pd);
|
|
return BAD;
|
|
}
|
|
return ARG; /* NB: early return */
|
|
case ',':
|
|
tc = COMMA;
|
|
break;
|
|
case '|':
|
|
tc = ifnext(c, OR, BITOR);
|
|
break;
|
|
case '&':
|
|
tc = ifnext(c, AND, BITAND);
|
|
break;
|
|
case '=':
|
|
tc = ifnext(c, EQ, EQ);
|
|
break;
|
|
case '!':
|
|
tc = ifnext('=', NE, NOT);
|
|
break;
|
|
case '<':
|
|
tc = ifnext(c, LSH, ifnext('=', LE, LT));
|
|
break;
|
|
case '>':
|
|
tc = ifnext(c, RSH, ifnext('=', GE, GT));
|
|
break;
|
|
case '^':
|
|
tc = BITXOR;
|
|
break;
|
|
case '+':
|
|
tc = PLUS;
|
|
break;
|
|
case '-':
|
|
tc = MINUS;
|
|
break;
|
|
case '*':
|
|
tc = MUL;
|
|
break;
|
|
case '/':
|
|
tc = DIV;
|
|
break;
|
|
case '%':
|
|
tc = MOD;
|
|
break;
|
|
case '~':
|
|
tc = BITNOT;
|
|
break;
|
|
case '[':
|
|
tc = LB;
|
|
break;
|
|
case ']':
|
|
tc = RB;
|
|
break;
|
|
case '.':
|
|
tc = DOT;
|
|
break;
|
|
case '(':
|
|
tc = LP;
|
|
break;
|
|
case ')':
|
|
tc = RP;
|
|
break;
|
|
default:
|
|
error("illegal character", pd);
|
|
tc = BAD;
|
|
}
|
|
#undef ifnext
|
|
|
|
pd->nextc = t + 1;
|
|
return tc;
|
|
}
|
|
|
|
/*
|
|
* Match tc against the next input token.
|
|
*/
|
|
static char *
|
|
match(enum tokencode tc, ParseData *pd)
|
|
{
|
|
enum tokencode ntc;
|
|
|
|
ntc = token(pd);
|
|
if (ntc == tc)
|
|
return pd->token;
|
|
if (ntc != BAD && ntc != END)
|
|
pd->nextc = pd->token;
|
|
return 0;
|
|
}
|