cxcomp.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 2002-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* C expression library parser
*
* recursive descent parser snarfed from libast strexpr()
* which was snarfed from ksh83
*
* Glenn Fowler
* AT&T Research
*/
#include "cxlib.h"
/*
* clear the current input line
*/
static void
{
if (cx->include)
}
/*
* return next input character and advance
*/
static int
{
int c;
return 0;
for (;;)
{
{
{
return '\n';
}
{
{
}
else
}
if ((cx->include->base = sfgetr(cx->include->sp, '\n', 0)) && !(cx->include->newline = 0) || (cx->include->base = sfgetr(cx->include->sp, '\n', -1)) && (cx->include->newline = 2))
{
error_info.line++;
sfprintf(sfstderr, "+%d+ %-.*s%s", error_info.line, cx->include->last - cx->include->next, cx->include->base, cx->include->newline ? "\n" : "");
}
{
return 0;
}
return 0;
}
for (;;)
{
/*
* NOTE: sgi.mips3 gets memory fault here if
* cx->include->last is on the boundary of an
* unreadable page -- we caught one of
* these on the 3b2 in 85
*/
#ifdef __sgi
#else
#endif
return c;
break;
return c;
break;
}
}
}
#if 0
static int
{
int c;
char buf[2];
buf[0] = c;
buf[1] = 0;
error(-2, "cxcomp next include=%p eof=%d '%s'", cx->include, cx->include->eof, c ? fmtesc(buf) : "\\0");
return c;
}
#define next(p) _trace_next(p)
#endif
/*
* push back last input character
*/
static void
{
{
else
}
}
/*
* peek next input character
*/
static int
{
int c;
if (c)
return c;
}
/*
* return current input line context
*/
char*
{
char* s;
char* t;
else
{
for (s = t - 30; s > cx->include->base && (cx->ctype[*(unsigned char*)s] & (CX_CTYPE_ALPHA|CX_CTYPE_DIGIT)); s--);
}
s = "out of space";
return s;
}
/*
* return operator name for code
*/
char*
cxcodename(int code)
{
register char* s;
char* b;
int n;
int i;
b = s = fmtbuf(n = 16);
else
{
switch (name[i])
{
case 'C':
s = strcopy(s, "CALL");
break;
case 'D':
s = strcopy(s, "DEL");
break;
case 'G':
s = strcopy(s, "GET");
break;
case 'J':
s = strcopy(s, "JMP");
break;
case 'L':
s = strcopy(s, "LOG");
break;
case 'R':
s = strcopy(s, "RET");
break;
case 'S':
break;
case 'e':
s = strcopy(s, "END");
break;
case 'n':
s = strcopy(s, "NUM");
break;
case 'p':
s = strcopy(s, "POP");
break;
case 's':
s = strcopy(s, "STR");
break;
case 't':
s = strcopy(s, "TST");
break;
case '0':
s = strcopy(s, "NOP");
break;
case '~':
if (code == CX_NOMATCH)
*s++ = '!';
else
{
*s++ = '=';
}
/*FALLTHROUGH*/
default:
*s++ = name[i];
*s++ = name[i];
*s++ = '=';
break;
}
*s = 0;
}
return b;
}
/*
* trace instruction at pc to the standard error
*/
void
cxcodetrace(Cx_t* cx, const char* fun, Cxinstruction_t* pc, unsigned int offset, Cxoperand_t* bp, Cxoperand_t* sp)
{
char* o;
char val[64];
char a[64];
char b[64];
if ((*o == 'G' || *o == 'S') && *(o + 1) == 'E')
else if (*o == 'C')
{
}
else
val[0] = 0;
if (bp)
{
else
a[0] = 0;
else
b[0] = 0;
}
else
{
a[0] = 0;
b[0] = 0;
}
}
/*
* return operator name
*/
char*
{
char* b;
char* o;
int n;
o = cxcodename(code);
return o;
b = fmtbuf(n = 32);
else
return b;
}
/*
* push file or string on the include stack
*/
void*
{
char* path;
int prompt;
int retain;
if (sp)
{
retain = 1;
}
else
{
retain = 0;
{
if (sp)
return 0;
}
}
{
path = 0;
if (!sp)
{
retain = 1;
}
}
else
{
if (sp)
{
return 0;
}
{
return 0;
}
prompt = 0;
}
{
if (!retain)
return 0;
}
error_info.line = 0;
return ip;
}
/*
* pop the top file off the include stack until and including matching cxpush() pop
* pop==0 pops everything
* 1 returned if there are more items on the stack after the pop
* 0 returned if the stack is empty after the pop
* -1 returned if the stack is empty before the pop
*/
int
{
{
return -1;
}
do
{
return cx->include ? 1 : 0;
}
/*
* add (*donef)(cx,data,disc) to the list to be called at cxfree(cx,expr)
* donef==0 pops the list
*/
int
{
if (donef)
{
{
return -1;
}
}
else
{
}
return 0;
}
/*
* parse and return identifier with first char c if c!=0
* results placed in operand r
* r->refs == 1 if identifier has :: library binding
*/
static int
{
r->refs = 0;
if (!c)
if (!cx->referencef)
{
return -1;
}
if (c == '.')
else
{
for (;;)
{
do
{
if (c != ':' || r->refs)
break;
{
break;
}
r->refs = 1;
}
}
return 0;
}
/*
* parse and return variable with first char c if c!=0
*/
static Cxvariable_t*
{
Cxoperand_t r;
Cxoperand_t a;
Cxoperand_t b;
return 0;
b.type = m;
if (a.refs)
{
return 0;
}
else
{
a.refs = 0;
if (type && (*type = (Cxtype_t*)dtmatch(cx->types, a.value.string.data)) || (*cx->referencef)(cx, NiL, &r, &b, &a, NiL, cx->disc))
return 0;
}
}
/*
* eval time cast
*/
static int
cast(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
{
if ((*r->type->internalf)(cx, r->type, NiL, &r->type->format, r, r->value.string.data, r->value.string.size, cx->em, cx->disc) < 0)
{
(*cx->disc->errorf)(cx, cx->disc, 2, "cannot cast from %s to %s", r->type->name, r->type->fundamental->name);
return -1;
}
return 0;
}
/*
* eval time edit
*/
static int
edit(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
{
}
/*
* output cast instruction
*/
static int
{
c.type = t;
c.pp = 0;
return 0;
}
/*
* encode and output instruction
*/
static Cxtype_t*
code(Cx_t* cx, Cxcompile_t* cc, Cxexpr_t* expr, int op, int pp, Cxtype_t* type1, Cxtype_t* type2, void* pointer, Cxnumber_t number, Cxvariable_t* ref)
{
Cxoperand_t r;
Cxrecode_f f;
Cxunsigned_t m;
char* s;
static Cxformat_t format;
if (!pointer)
else
x.callout = 0;
{
if (((f = cxrecode(cx, op, type1, type2, cx->disc)) || (f = cxrecode(cx, op, cx->state->type_void, cx->state->type_void, cx->disc))) && (*f)(cx, expr, &x, NiL, NiL, NiL, cx->disc))
return 0;
{
x.pp--;
}
}
else
{
if ((i1->op == CX_GET || i1->op == CX_CAST && (i1 - 1)->op == CX_GET) && (f = cxrecode(cx, op, type1, type2, cx->disc)))
{
return 0;
}
}
if (x.callout)
goto done;
goto done;
{
goto done;
}
{
if (v1 && (c.callout = cxcallout(cx, CX_CAST, type1, type2, cx->disc)) && (x.callout = cxcallout(cx, op, type2, type2, cx->disc)))
{
c.pp = 0;
goto done;
}
{
if (v1 && v1->format.map && cxisstring(type2) && !cxstr2num(cx, &v1->format, i2->data.string.data, i2->data.string.size, &m))
{
goto done;
}
if (v2 && v2->format.map && cxisstring(type1) && !cxstr2num(cx, &v2->format, i1->data.string.data, i1->data.string.size, &m))
{
goto done;
}
if (cxisstring(type2))
{
{
goto done;
}
{
if ((*type1->internalf)(cx, type1, NiL, &format, &r, i2->data.string.data, i2->data.string.size, cc->vm, cx->disc) < 0)
return 0;
goto done;
}
}
{
if (i1->op == CX_STR && i1->data.string.size == 1 && (x.callout = cxcallout(cx, op, type2, type2, cx->disc)))
{
goto done;
}
}
}
if (type1->fundamental == type2->fundamental && (x.callout = cxcallout(cx, op, type1->fundamental, type1->fundamental, cx->disc)))
goto done;
if (v1 && cxisstring(type1) && !v2 && cxisnumber(type2) && (x.callout = cxcallout(cx, op, type1->fundamental, type1->fundamental, cx->disc)) && !cxnum2str(cx, &v1->format, i2->data.number, &s))
{
{
return 0;
}
goto done;
}
if (v2 && cxisstring(type2) && !v1 && cxisnumber(type1) && (x.callout = cxcallout(cx, op, type2->fundamental, type2->fundamental, cx->disc)) && !cxnum2str(cx, &v2->format, i1->data.number, &s))
{
{
return 0;
}
goto done;
}
if (cxisstring(type2) && type1->internalf && (x.callout = cxcallout(cx, op, type1->fundamental, type1->fundamental, cx->disc)))
{
if (cxisnumber(type1) && v1 && v1->format.map && !cxstr2num(cx, &v1->format, i2->data.string.data, i2->data.string.size, &m))
{
goto done;
}
if (!v2)
{
if ((*type1->internalf)(cx, type1, NiL, &format, &r, i2->data.string.data, i2->data.string.size, cc->vm, cx->disc) < 0)
return 0;
goto done;
}
}
if (cxisstring(type1) && type2->internalf && (x.callout = cxcallout(cx, op, type2->fundamental, type2, cx->disc)))
{
if (cxisnumber(type2) && v2 && v2->format.map && !cxstr2num(cx, &v2->format, i1->data.string.data, i1->data.string.size, &m))
{
goto done;
}
if (!v1)
{
if ((*type2->internalf)(cx, type2, NiL, &format, &r, i1->data.string.data, i1->data.string.size, cc->vm, cx->disc) < 0)
return 0;
goto done;
}
}
}
goto done;
(*cx->disc->errorf)(cx, cx->disc, 2, "%s %s not supported [%d:%d] %p", cxcontext(cx), cxopname(op, type1, type2), cxrepresentation(type1), cxrepresentation(type2), cxcallout(cx, op, cx->state->type_string, cx->state->type_string, cx->disc));
return 0;
done:
{
return 0;
}
if (op == CX_GET && (v1 = (Cxvariable_t*)pointer) && cxisstring(v1->type) && v1->format.map && v1->format.map->part && v1->format.map->part->edit)
}
/*
* return the next expected argument type for v
* return:
* >0 ok
* =0 no more arguments
* <0 error
*/
static int
{
char* s;
char* e;
char* u;
Cxtype_t* t;
int c;
int o;
int r;
char buf[256];
s = *sp;
e = *ep;
o = *op;
t = 0;
r = 0;
for (;;)
{
if ((c = *s++) == 0)
{
s--;
break;
}
else if (c == '[')
o++;
else if (c == ']')
o--;
else if (c == '.')
{
if (!e)
{
e = s;
o = 0;
while (--e > (char*)v->prototype)
{
if ((c = *e) == '[' || !o--)
break;
else if (c == ']')
o++;
}
o = 1;
}
s = e;
if (*s == '.')
{
r = 1;
break;
}
}
else if (c == '*')
{
r = 1;
break;
}
else if (c == '&' && (cx->ctype[*(unsigned char*)s] & CX_CTYPE_ALPHA) || (cx->ctype[c] & CX_CTYPE_ALPHA))
{
if (c != '&')
s--;
*u++ = *s;
*u = 0;
{
if (c == '&')
r = 1;
}
else
{
r = -1;
}
break;
}
}
*sp = s;
*ep = e;
*op = o;
*tp = t;
return r;
}
/*
* parse the next (sub)expression
*/
static Cxtype_t*
{
register int c;
register int p;
char* s;
char* e;
Cxtype_t* g;
Cxtype_t* m;
Cxtype_t* t;
Cxtype_t* u;
Cxvariable_t* f;
Cxvariable_t* h;
Cxvariable_t* v;
int i;
int k;
int o;
int x;
int q;
int r;
long z;
double n;
Cxoperand_t w;
g = 0;
h = 0;
m = 0;
x = 0;
v = 0;
{
{
i = 0;
{
o |= CX_X2;
i++;
}
if (p == '~')
{
if (c == '=')
{
o = CX_MATCH;
i++;
}
else if (c == '!')
{
o = CX_NOMATCH;
i++;
}
else
}
else if (p == '=' && !(o & CX_ASSIGN))
{
o |= CX_ASSIGN;
o &= ~CX_UNARY;
i++;
}
else
{
if (!v)
{
if (x)
{
goto bad;
}
goto bad;
h = v;
m = 0;
goto undefined;
}
goto bad;
goto bad;
goto bad;
goto bad;
if (x)
{
goto bad;
goto bad;
}
v = 0;
x = 1;
continue;
}
if (!x)
o |= CX_UNARY;
{
while (i--)
goto done;
}
{
if (!v)
{
goto bad;
}
if (o != CX_SET)
{
goto undefined;
o &= ~CX_ASSIGN;
goto bad;
}
}
else if (o == CX_REF)
{
goto bad;
m = 0;
goto bad;
v = 0;
x = 1;
continue;
}
else
{
if (x == 2)
{
goto bad;
}
else if (v)
{
goto undefined;
goto bad;
v = 0;
}
}
if (!p)
{
if ((o & CX_UNARY) && c == '/')
goto quote;
{
if (o & CX_UNARY)
else
}
goto bad;
}
if (o & CX_UNARY)
{
if (x)
goto operator_expected;
}
else
{
if (!x)
goto operand_expected;
}
if (precedence >= p && o != CX_SET)
{
while (i--)
goto done;
}
z = 0;
{
if (cc->type != cx->state->type_number && !code(cx, cc, expr, CX_LOG, 0, cc->type, cx->state->type_void, NiL, 0, NiL))
goto bad;
{
if (!code(cx, cc, expr, (o == CX_ANDAND) ? CX_SC0 : CX_SC1, 0, cx->state->type_number, cx->state->type_void, NiL, 0, NiL))
goto bad;
}
}
goto bad;
if (cx->table->logical[o] && cc->type != cx->state->type_number && !code(cx, cc, expr, CX_LOG, 0, cc->type, cx->state->type_void, NiL, 0, NiL))
goto bad;
if (o != CX_SET && ((o & CX_UNARY) ? !code(cx, cc, expr, o, 0, cc->type, cx->state->type_void, NiL, 0, NiL) : !code(cx, cc, expr, o, -1, t, cc->type, NiL, 0, h)))
goto bad;
h = 0;
if (v)
{
{
{
(*cx->disc->errorf)(cx, cx->disc, 2, "%s cannot assign %s to %s", cxcontext(cx), cc->type->name, v->type->name);
goto bad;
}
}
goto bad;
v = 0;
}
if (z)
((Cxinstruction_t*)(sfstrbase(cc->xp) + z))->data.number = (sfstrtell(cc->xp) - z) / sizeof(Cxinstruction_t);
x = 1;
}
{
if (x)
goto operator_expected;
i = 0;
{
switch (c)
{
case '#':
i = 4;
break;
case '.':
i = 1;
break;
case 'e':
case 'E':
if (i < 3)
{
i = 3;
else
}
break;
}
}
if (!(i &= 1))
if (i || *e)
n = strtod(s, &e);
if (*e)
{
goto bad;
}
goto bad;
x = 1;
}
{
if (x)
goto operator_expected;
{
if (t)
{
x = 2;
continue;
}
goto bad;
}
h = v;
m = 0;
if (v->function)
{
i = 0;
if (c == '(')
p = q = ')';
else
{
p = '\n';
q = ';';
if (c && c != p && c != q)
}
while (c == ' ' || c == '\t' || c == '\r')
if (v->type != cx->state->type_void && !code(cx, cc, expr, CX_NUM, 1, cx->state->type_number, cx->state->type_void, NiL, 0, NiL))
goto bad;
o = 0;
s = (char*)v->prototype;
e = 0;
goto bad;
if (c != p && c != q)
{
cc->collecting++;
for (;;)
{
{
cc->collecting--;
goto bad;
}
{
i++;
{
continue;
if (r < 0)
{
cc->collecting--;
goto bad;
}
{
if (r < 0)
(*cx->disc->errorf)(cx, cx->disc, 2, "%s too many arguments for %s(%s)", cxcontext(cx), v->name, v->prototype);
else
(*cx->disc->errorf)(cx, cx->disc, 2, "%s argument type mismatch for %s(%s)", cxcontext(cx), v->name, v->prototype);
}
cc->collecting--;
goto bad;
}
{
cc->collecting--;
goto bad;
}
}
{
cc->collecting--;
goto bad;
}
if (c == p || c == q)
break;
if (c != ',')
}
cc->collecting--;
if (c != p && c != q)
{
(*cx->disc->errorf)(cx, cx->disc, 2, "%s missing %s in formal argument list", cxcontext(cx), p == '\n' ? "statement terminator" : ")");
goto bad;
}
}
if (r > 0 && !o)
{
(*cx->disc->errorf)(cx, cx->disc, 2, "%s not enough arguments for %s(%s)", cxcontext(cx), v->name, v->prototype);
goto bad;
}
if (c == '\n')
goto bad;
if (g)
{
goto bad;
g = 0;
}
v = 0;
}
else if (c == '(')
{
goto bad;
}
else
x = 1;
}
else
switch (c)
{
case 0:
goto done;
case '.':
x = 1;
if (!x || !v ||
{
goto bad;
}
goto bad;
x = 0;
v = 0;
continue;
case ',':
if (cc->collecting)
{
goto done;
}
goto bad;
goto again;
case ';':
if (cc->collecting)
x = 1;
else
precedence = 0;
goto done;
case '#':
goto done;
case '\n':
if (cc->collecting)
{
goto done;
}
else if (precedence <= cx->table->precedence[CX_CALL] || precedence > cx->table->precedence[CX_PAREN] && x)
goto done;
continue;
case ' ':
case '\t':
case '\r':
continue;
case '(':
if (x)
goto operator_expected;
{
if (!identifier(cx, cc, c, &w) && (c = next(cx)) == ')' && ((c = next(cx)) == '(' || (cx->ctype[c] & CX_CTYPE_ALPHA)))
{
{
goto bad;
}
if (c != '(')
goto alpha;
}
else
}
else
x = 1;
o = cc->collecting;
cc->collecting = 0;
h = f;
cc->collecting = o;
goto bad;
for (;;)
{
{
case ' ':
case '\n':
case '\r':
case '\t':
continue;
case ')':
if (k)
goto keep;
do
{
{
goto done;
goto keep;
}
} while (isspace(c));
if (c == '|' || c == '?')
do
{
goto keep;
} while (isspace(*s++));
break;
default:
goto bad;
}
break;
}
if (g)
{
g = 0;
}
break;
case ')':
if (!precedence)
{
goto bad;
}
goto done;
case '?':
if (!x)
goto operand_expected;
goto done;
{
goto bad;
{
goto bad;
}
}
goto bad;
goto bad;
goto bad;
break;
case ':':
goto done;
case '"':
case '\'':
case '/':
if (x)
goto operator_expected;
{
if (!p)
{
goto bad;
}
}
{
goto bad;
}
goto bad;
x = 1;
break;
default:
goto bad;
}
}
if (!x)
{
if (ref)
*ref = h;
return t;
}
keep:
c = 0;
done:
goto operand_expected;
if (x == 2)
{
goto bad;
}
else if (v)
{
goto undefined;
goto bad;
goto bad;
}
if (ref)
*ref = h;
goto bad;
if (cc->collecting)
goto done;
goto bad;
bad:
if (precedence)
else
return 0;
}
/*
* allocate an expression node
*/
static Cxexpr_t*
{
{
return 0;
}
return expr;
}
/*
* compile the next complete expression
*/
static Cxexpr_t*
{
size_t n;
return 0;
return 0;
return 0;
#if 0
/*
* this is a failed attempt at a logical cast for naked variable tests
*/
{
if (pc->op == CX_GET && !code(cx, cc, expr, CX_LOG, 0, pc->type, cx->state->type_void, NiL, 0, NiL))
return 0;
}
#endif
return 0;
{
return 0;
}
{
{
return 0;
}
}
return expr;
}
/*
* compose dynamic and interpreted queries
*/
static Cxexpr_t*
{
int* x;
char** v;
char* f;
char* r;
char* s;
int c;
int m;
int n;
int p;
int q;
unsigned long o;
fp = 0;
rp = 0;
p = 0;
q = 0;
r = 0;
for (;;)
{
{
case '"':
case '\'':
if (c == q)
q = 0;
else if (!q)
q = c;
else
continue;
case '\\':
break;
continue;
case '{':
if (q)
goto syntax;
else
{
return 0;
{
return 0;
}
if (c != 0 && c != '|' && c != '?' && c != ':' && c != ';' && c != ',' && c != '}' && c != '>')
{
return 0;
}
{
return 0;
}
}
continue;
case '(':
if (q)
goto syntax;
else
{
return 0;
return fp;
}
continue;
case ')':
if (q)
else
goto syntax;
continue;
case ':':
{
continue;
}
/*FALLTHROUGH*/
case ',':
if (c == ',')
/*FALLTHROUGH*/
case 0:
case ' ':
case '\n':
case '\r':
case '\t':
case '>':
case '|':
case '?':
case ';':
case '}':
if (q)
{
if (c)
else
{
return 0;
}
}
else
{
{
if (r)
{
if (!*s)
goto syntax;
{
return 0;
}
}
else
{
}
}
if (c == '>')
{
if (r)
goto syntax;
r = "a";
else
{
r = "w";
}
}
else if (!isspace(c))
{
{
}
if (!fp)
{
if (!n)
goto syntax;
return 0;
s = (char*)(v + m + 1);
while (m-- > 0)
*v++ = s + *x++;
*v = 0;
{
return 0;
}
return 0;
}
else if (n)
goto syntax;
if (r)
{
{
return 0;
}
r = 0;
}
if (!rp)
if (c == 0)
break;
{
if (c == ';' || c == '}')
{
break;
}
}
else if (prec == '}')
{
if (c == '}')
{
break;
}
}
if (c == ':')
{
break;
}
else if (c == ';' || c == ',')
{
return 0;
}
else if (c == '?')
{
return 0;
{
return 0;
}
}
return 0;
p = 0;
}
}
continue;
default:
continue;
}
break;
}
return rp;
{
if (c)
else
}
return 0;
}
/*
* propagate expression defaults
*/
static void
{
do
{
}
/*
* cxlist helper
*/
static void
{
register Cxinstruction_t* pc;
char** v;
for (;;)
{
{
}
{
{
else
pc++;
}
}
else
{
}
{
}
break;
}
}
/*
* list query expression
*/
int
{
}
/*
* compile the next expression/query
*/
{
int c;
error(-1, "cxcomp push include=%p file=%s eof=%d", cx->include, cx->include ? cx->include->file : 0, cx->eof);
return 0;
expr = 0;
if (!(cc = vmnewof(cx->vm, 0, Cxcompile_t, 1, 0)) || !(cc->tp = sfstropen()) || !(cc->xp = sfstropen()))
goto nospace;
cc->reclaim = !!(cx->deletef = cxcallout(cx, CX_DEL, cx->state->type_void, cx->state->type_void, cx->disc));
{
goto done;
}
goto nospace;
{
goto done;
}
else if (c == '{' || c == '(')
{
goto done;
}
goto done;
goto done;
done:
if (cc)
{
}
error(-1, "cxcomp done include=%p file=%s eof=%d", cx->include, cx->include ? cx->include->file : 0, cx->eof);
return expr;
}
/*
* free a cxcomp() expr
*/
int
{
return -1;
return 0;
}
{
return -1;
return -1;
return -1;
}