Add BOOL token_type, macros, and boolean operators

For clarity, it is valuable for the results of comparisons to show as "true"
  and "false" rather than "1" and "0". This change implements a BOOL type
  to facilitate that. Since we want logical operators to short-circuit, this
  change also obliges the introduction of macros, which are just like functions
  except their arguments are not evaluated before control is passed to the
  routine implementing the macro.
This commit is contained in:
Glen Whitney 2019-08-26 23:56:19 -07:00
parent 7e0ba7370d
commit 892fdcdb75
10 changed files with 871 additions and 688 deletions

View file

@ -425,35 +425,82 @@ size_t ptokatprec(char* dest, size_t size, size_t field_width, StringFormat sf,
default: /* infix argument */
assert(tok->u.funcall.argc > 1);
if (fp < atprec && cur < size) dest[cur++] = '(';
for (size_t ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ ai)
{
bool parenarg = false;
if (ai > 0) {
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
const char *use = tfunc[fid].display_symbol;
if (use == NULL) use = tfunc[fid].name;
strncpy(dest+cur, use, size-cur-1);
cur += strlen(use);
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
} else if (fp > PREFIX_NEG) {
char powbuf[4096];
size_t arglen = ptokatprec(powbuf, sizeof(powbuf), 0, sf, ff,
digits, ef, tok->u.funcall.argv, fp);
assert(arglen < sizeof(powbuf)-1);
if (powbuf[0] == '-') {
parenarg = true;
if (cur < size) dest[cur++] = '(';
/* Check for the special relation sequence notation */
bool specialrel = true;
if (fid != FUNC_AND || tok->u.funcall.argc < 2) specialrel = false;
else {
for (int ai = 0; ai < tok->u.funcall.argc; ++ai) {
if (tok->u.funcall.argv[ai].type != FUNCALL
|| tok->u.funcall.argv[ai].u.funcall.argc != 2
|| !IS_RELATION_FUNC(tok->u.funcall.argv[ai].u.funcall.fident))
{
specialrel = false;
break;
}
if (ai > 0
&& !tok_matches(tok->u.funcall.argv[ai].u.funcall.argv,
tok->u.funcall.argv[ai-1].u.funcall.argv+1))
{
specialrel = false;
break;
}
}
}
if (specialrel) {
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef, tok->u.funcall.argv + ai, fp);
if (parenarg && cur < size) dest[cur++] = ')';
digits, ef, tok->u.funcall.argv, INFIX_BOOL);
for (int ai = 1; ai < tok->u.funcall.argc && cur < size-1; ++ ai) {
dest[cur++] = ' ';
const char *use =
tfunc[tok->u.funcall.argv[ai].u.funcall.fident].name;
strncpy(dest+cur, use, size-cur-1);
cur += strlen(use);
if (cur < size) dest[cur++] = ' ';
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef,
tok->u.funcall.argv[ai].u.funcall.argv + 1,
INFIX_REL);
}
} else {
for (int ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ai)
{
bool parenarg = false;
if (ai > 0) {
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
const char *use = tfunc[fid].display_symbol;
if (use == NULL) use = tfunc[fid].name;
strncpy(dest+cur, use, size-cur-1);
cur += strlen(use);
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
} else if (fp > PREFIX_NEG) {
char powbuf[4096];
size_t arglen = ptokatprec(powbuf, sizeof(powbuf), 0, sf, ff,
digits, ef, tok->u.funcall.argv, fp);
assert(arglen < sizeof(powbuf)-1);
if (powbuf[0] == '-') {
parenarg = true;
if (cur < size) dest[cur++] = '(';
}
}
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef, tok->u.funcall.argv + ai, fp);
if (parenarg && cur < size) dest[cur++] = ')';
}
}
if (fp < atprec && cur < size) dest[cur++] = ')';
break;
}
break;
}
/* BOOL */ /*{{{*/
case BOOL:
{
const char *rep = "false";
if (tok->u.bl) rep = "true";
strncpy(dest+cur, rep, size-cur-1);
cur += strlen(rep);
break;
}
/* EEK */ /*{{{*/
case EEK:
{
@ -508,6 +555,18 @@ size_t printtok(char* dest, size_t size, size_t field_width,
}
/*}}}*/
static char dbgpbuf[4096];
/* dbgprint -- simple on the fly print for in the debugger */
const char *dbgprint(const Token* tok) {
size_t used = printtok(dbgpbuf, sizeof(dbgpbuf), 0, QUOTE_STRING, FLT_COMPACT,
0, VERBOSE_ERROR, tok);
if (used > sizeof(dbgpbuf) - 2) {
printf(_("Warning: buffer overflow in dbgprint"));
}
return dbgpbuf;
}
/* print -- print token sequence */ /*{{{*/
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
int digits, Token **n)

View file

@ -77,6 +77,7 @@ typedef enum {DIRECT_STRING, QUOTE_STRING} StringFormat;
typedef enum {TRUNCATED_ERROR, VERBOSE_ERROR, RESTORE_ERROR} ErrorFormat;
size_t printtok(char *dest, size_t size, size_t field_width, StringFormat sf,
FloatFormat ff, int digits, ErrorFormat ef, const Token *tok);
const char *dbgprint(const Token *tok); /* for use in debugging */
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
int digits, Token **n);
#ifdef __cplusplus

View file

@ -88,8 +88,18 @@ void tfree_protected(Token *n, const Token dontfree)
case FUNCALL:
if (difftype || n->u.funcall.argv != dontfree.u.funcall.argv)
{
for (int ai = 0; ai < n->u.funcall.argc; ++ai)
tfree_protected(n->u.funcall.argv + ai, dontfree);
for (int ai = n->u.funcall.argc-1; ai >= 0; --ai)
if (n->u.funcall.fident = FUNC_AND && ai > 0
&& n->u.funcall.argv[ai].type == FUNCALL
&& n->u.funcall.argv[ai-1].type == FUNCALL
&& n->u.funcall.argv[ai].u.funcall.argc == 2
&& n->u.funcall.argv[ai-1].u.funcall.argc == 2
&& IS_RELATION_FUNC(n->u.funcall.argv[ai].u.funcall.fident)
&& IS_RELATION_FUNC(n->u.funcall.argv[ai-1].u.funcall.fident))
{
tfree_protected(n->u.funcall.argv + ai,
n->u.funcall.argv[ai-1].u.funcall.argv[1]);
} else tfree_protected(n->u.funcall.argv + ai, dontfree);
if (n->u.funcall.argv != NULLTOKEN) {
free(n->u.funcall.argv);
n->u.funcall.argv = NULLTOKEN;
@ -587,36 +597,20 @@ Token tmul(Token l, Token r)
/* tneg -- monadic - operator */ /*{{{*/
Token tneg(Token x)
{
/* variables */ /*{{{*/
Token result;
int len;
/*}}}*/
result.type = x.type;
if (x.type == EEK) return tcopy(x);
if (x.type == EMPTY) return x;
if (x.type == INT)
/* result is negated int argument */ /*{{{*/
{
result.type = INT;
result.u.integer = -x.u.integer;
return result;
}
/*}}}*/
if (x.type == FLOAT)
/* result is negated float argument */ /*{{{*/
{
result.type = FLOAT;
result.u.flt = -x.u.flt;
return result;
}
/*}}}*/
if (x.type == LOCATION)
/* result is component-wise negation of location */ /*{{{*/
{
result.type = LOCATION;
for (size_t len = 0; len < 3; ++len)
result.u.location[len] = -x.u.location[len];
return result;
switch (x.type) {
case EEK: return tcopy(x);
case EMPTY: return x;
case INT: result.u.integer = -x.u.integer; return result;
case FLOAT: result.u.flt = -x.u.flt; return result;
case LOCATION:
for (size_t len = 0; len < 3; ++len)
result.u.location[len] = -x.u.location[len];
return result;
case BOOL: result.u.bl = !x.u.bl; return result;
default: break;
}
result.type = EEK;
const char* templ = "wrong type for - operator: %s";
@ -716,428 +710,189 @@ static Token relational_type_mismatch(Type l, Type r)
/* tlt -- < operator */ /*{{{*/
Token tlt(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
static char empty[]="";
int len;
/*}}}*/
result.type = BOOL;
result.u.bl = false;
static char empty[] = "";
if (l.type==EEK) /* return left error argument */ /*{{{*/
return tcopy(l);
/*}}}*/
if (r.type==EEK) /* return right error argument */ /*{{{*/
return tcopy(r);
/*}}}*/
if (l.type==EMPTY) /* try to assign 0 element of r.type */ /*{{{*/
if (l.type == EEK) return tcopy(l);
if (r.type == EEK) return tcopy(r);
if (l.type == EMPTY) /* try to assign 0 element of r.type */ /*{{{*/
{
l.type=r.type;
if (r.type == EMPTY) return result;
switch (r.type)
{
case INT: l.u.integer=0; break;
case FLOAT: l.u.flt=0.0; break;
case STRING: l.u.string=empty; break;
default: ;
case INT: l.u.integer = 0; break;
case FLOAT: l.u.flt = 0.0; break;
case STRING: l.u.string = empty; break;
case BOOL: l.u.bl = false; break;
default: return relational_type_mismatch(l.type, r.type);
}
l.type = r.type;
}
/*}}}*/
if (r.type==EMPTY) /* try to assign 0 element of l.type */ /*{{{*/
if (r.type == EMPTY) /* try to assign 0 element of l.type */ /*{{{*/
{
r.type=l.type;
switch (l.type)
{
case INT: r.u.integer=0; break;
case FLOAT: r.u.flt=0.0; break;
case STRING: r.u.string=empty; break;
default: ;
case INT: r.u.integer = 0; break;
case FLOAT: r.u.flt = 0.0; break;
case STRING: r.u.string = empty; break;
case BOOL: r.u.bl = false; break;
default: return relational_type_mismatch(l.type, r.type);;
}
r.type = l.type;
}
/*}}}*/
if (l.type==INT && r.type==INT) /* return left int < right int */ /*{{{*/
if (l.type == INT) /* handle left operand integer */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.integer<r.u.integer;
if (r.type == INT) /* return left int < right int */ /*{{{*/
{
result.u.bl = l.u.integer < r.u.integer;
return result;
} /*}}}*/
if (r.type == FLOAT) /* return left int < right float */ /*{{{*/
{
result.u.bl = ((FltT)l.u.integer) < r.u.flt;
return result;
} /*}}}*/
return relational_type_mismatch(l.type, r.type);
}
/*}}}*/
else if (l.type==STRING && r.type==STRING) /* return left string < right string */ /*{{{*/
if (l.type == STRING && r.type == STRING) /* return left string < right string */ /*{{{*/
{
result.type=INT;
result.u.integer=(strcmp(l.u.string,r.u.string)<0);
result.u.bl = (strcmp(l.u.string,r.u.string)<0);
return result;
}
/*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) /* return left float < right float */ /*{{{*/
if (l.type == FLOAT) /* handle left operand float */
{
result.type=INT;
result.u.integer=l.u.flt<r.u.flt;
if (r.type == FLOAT) /* return left float < right float */ /*{{{*/
{
result.u.bl = l.u.flt < r.u.flt;
return result;
}
/*}}}*/
if (r.type == INT) /* return left float < right float */ /*{{{*/
{
result.u.bl = l.u.flt < ((FltT)r.u.integer);
return result;
}
/*}}}*/
return relational_type_mismatch(l.type, r.type);
}
if (l.type == LOCATION && r.type == LOCATION)
/* result is true if l is strictly in rectangle from origin to r */ /*{{{*/
{
Dimensions dim = X;
while (dim < HYPER)
{
if (l.u.location[dim] > r.u.location[dim]) break;
else if (l.u.location[dim] < r.u.location[dim]) result.u.bl = true;
++dim;
}
if (dim < HYPER) result.u.bl = false;
return result;
}
/*}}}*/
else if (l.type==FLOAT && r.type==INT) /* return left float < right float */ /*{{{*/
{
result.type = INT;
result.u.integer = l.u.flt < ((FltT)r.u.integer);
}
/*}}}*/
else if (l.type==INT && r.type==FLOAT) /* return left int < right float */ /*{{{*/
{
result.type = INT;
result.u.integer = ((FltT)l.u.integer) < r.u.flt;
}
/*}}}*/
else if (l.type == LOCATION && r.type == LOCATION)
/* result is true if l is strictly in rectangle from origin to r
In addition the value tells how many coordinates were strictly less */ /*{{{*/
{
result.type = INT;
result.u.integer = 0;
for (len = 0; len < 3; ++len)
if (l.u.location[len] > r.u.location[len]) break;
else if (l.u.location[len] < r.u.location[len]) ++result.u.integer;
if (len < 3) result.u.integer = 0;
}
/*}}}*/
else return relational_type_mismatch(l.type, r.type);
return result;
return relational_type_mismatch(l.type, r.type);
}
/*}}}*/
/* tle -- <= operator */ /*{{{*/
Token tle(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
static char empty[]="";
int len;
/*}}}*/
if (l.type==EEK) /* return left error argument */ /*{{{*/
return tcopy(l);
/*}}}*/
if (r.type==EEK) /* return right error argument */ /*{{{*/
return tcopy(r);
/*}}}*/
if (l.type==EMPTY) /* try to assign 0 element of r.type */ /*{{{*/
{
l.type=r.type;
switch (r.type)
{
case INT: l.u.integer=0; break;
case FLOAT: l.u.flt=0.0; break;
case STRING: l.u.string=empty; break;
default: ;
}
}
/*}}}*/
if (r.type==EMPTY) /* try to assign 0 element of l.type */ /*{{{*/
{
r.type=l.type;
switch (l.type)
{
case INT: r.u.integer=0; break;
case FLOAT: r.u.flt=0.0; break;
case STRING: r.u.string=empty; break;
default: ;
}
}
/*}}}*/
if (l.type==INT && r.type==INT) /* result is left int <= right int */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.integer<=r.u.integer;
}
/*}}}*/
else if (l.type==STRING && r.type==STRING) /* result is left string <= right string */ /*{{{*/
{
result.type=INT;
result.u.integer=(strcmp(l.u.string,r.u.string)<=0);
}
/*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) /* result is left float <= right float */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.flt<=r.u.flt;
}
/*}}}*/
else if (l.type==FLOAT && r.type==INT) /* result is left float <= (FltT)right int */ /*{{{*/
{
result.type = INT;
result.u.integer = l.u.flt <= ((FltT)r.u.integer);
}
/*}}}*/
else if (l.type==INT && r.type==FLOAT) /* result is (FltT)left int <= right float */ /*{{{*/
{
result.type = INT;
result.u.integer = ((FltT)l.u.integer) <= r.u.flt;
}
/*}}}*/
else if (l.type==EMPTY && r.type==EMPTY) /* result is 1 */ /*{{{*/
{
result.type = INT;
result.u.integer = 1;
}
/*}}}*/
else if (l.type == LOCATION && r.type == LOCATION)
/* result is true if l is in rectangle from origin to r */ /*{{{*/
{
result.type = INT;
result.u.integer = 1;
for (len = 0; len < 3; ++len)
if (l.u.location[len] > r.u.location[len]) break;
if (len < 3) result.u.integer = 0;
}
/*}}}*/
else return relational_type_mismatch(l.type, r.type);
return result;
return tor(tlt(l, r), teq(l, r));
}
/*}}}*/
/* tge -- >= operator */ /*{{{*/
Token tge(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
static char empty[]="";
int len;
/*}}}*/
if (l.type==EEK) /* return left error argument */ /*{{{*/
return tcopy(l);
/*}}}*/
if (r.type==EEK) /* return right error argument */ /*{{{*/
return tcopy(r);
/*}}}*/
if (l.type==EMPTY) /* try to assign 0 element of r.type */ /*{{{*/
{
l.type=r.type;
switch (r.type)
{
case INT: l.u.integer=0; break;
case FLOAT: l.u.flt=0.0; break;
case STRING: l.u.string=empty; break;
default: ;
}
}
/*}}}*/
if (r.type==EMPTY) /* try to assign 0 element of l.type */ /*{{{*/
{
r.type=l.type;
switch (l.type)
{
case INT: r.u.integer=0; break;
case FLOAT: r.u.flt=0.0; break;
case STRING: r.u.string=empty; break;
default: ;
}
}
/*}}}*/
if (l.type==INT && r.type==INT) /* result is left int >= right int */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.integer>=r.u.integer;
}
/*}}}*/
else if (l.type==STRING && r.type==STRING) /* return left string >= right string */ /*{{{*/
{
result.type=INT;
result.u.integer=(strcmp(l.u.string,r.u.string)>=0);
}
/*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) /* return left float >= right float */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.flt>=r.u.flt;
}
/*}}}*/
else if (l.type==FLOAT && r.type==INT) /* return left float >= (FltT) right int */ /*{{{*/
{
result.type = INT;
result.u.integer = l.u.flt >= ((FltT)r.u.integer);
}
/*}}}*/
else if (l.type==INT && r.type==FLOAT) /* return (FltT) left int >= right float */ /*{{{*/
{
result.type = INT;
result.u.integer = ((FltT)l.u.integer) >= r.u.flt;
}
/*}}}*/
else if (l.type == LOCATION && r.type == LOCATION)
/* result is true if r is in rectangle from origin to l */ /*{{{*/
{
result.type = INT;
result.u.integer = 1;
for (len = 0; len < 3; ++len)
if (r.u.location[len] > l.u.location[len]) break;
if (len < 3) result.u.integer = 0;
}
/*}}}*/
else return relational_type_mismatch(l.type, r.type);
return result;
return tor(tgt(l, r), teq(l, r));
}
/*}}}*/
/* tgt -- <= operator */ /*{{{*/
/* tgt -- < operator */ /*{{{*/
Token tgt(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
static char empty[]="";
int len;
/*}}}*/
if (l.type==EEK) /* return left error argument */ /*{{{*/
return tcopy(l);
/*}}}*/
if (r.type==EEK) /* return right error argument */ /*{{{*/
return tcopy(r);
/*}}}*/
if (l.type==EMPTY) /* try to assign 0 element of r.type */ /*{{{*/
{
l.type=r.type;
switch (r.type)
{
case INT: l.u.integer=0; break;
case FLOAT: l.u.flt=0.0; break;
case STRING: l.u.string=empty; break;
default: ;
}
}
/*}}}*/
if (r.type==EMPTY) /* try to assign 0 element of l.type */ /*{{{*/
{
r.type=l.type;
switch (l.type)
{
case INT: r.u.integer=0; break;
case FLOAT: r.u.flt=0.0; break;
case STRING: r.u.string=empty; break;
default: ;
}
}
/*}}}*/
if (l.type==INT && r.type==INT) /* result is left int > right int */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.integer>r.u.integer;
}
/*}}}*/
else if (l.type==STRING && r.type==STRING) /* result is left string > right string */ /*{{{*/
{
result.type=INT;
result.u.integer=(strcmp(l.u.string,r.u.string)>0);
}
/*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) /* result is left float > right float */ /*{{{*/
{
result.type=INT;
result.u.integer=l.u.flt>r.u.flt;
}
/*}}}*/
else if (l.type==FLOAT && r.type==INT) /* result is left float > (FltT) right int */ /*{{{*/
{
result.type = INT;
result.u.integer = l.u.flt > ((FltT)r.u.integer);
}
/*}}}*/
else if (l.type==INT && r.type==FLOAT) /* result is left int > right float */ /*{{{*/
{
result.type = INT;
result.u.integer = ((FltT)l.u.integer) > r.u.flt;
}
/*}}}*/
else if (l.type == LOCATION && r.type == LOCATION)
/* result is true if r is strictly in rectangle from origin to l
In addition the value tells how many coordinates were strictly greater */ /*{{{*/
{
result.type = INT;
result.u.integer = 0;
for (len = 0; len < 3; ++len)
if (r.u.location[len] > l.u.location[len]) break;
else if (r.u.location[len] < l.u.location[len]) ++result.u.integer;
if (len < 3) result.u.integer = 0;
}
else return relational_type_mismatch(l.type, r.type);
return result;
return tlt(r, l);
}
/*}}}*/
/* teq -- == operator */ /*{{{*/
Token teq(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
result.type = BOOL;
result.u.bl = true;
static char empty[]="";
int len;
/*}}}*/
if (l.type==EEK) return tcopy(l);
else if (r.type==EEK) return tcopy(r);
if (l.type==EMPTY)
if (l.type == EEK) return tcopy(l);
if (r.type == EEK) return tcopy(r);
if (l.type == EMPTY)
/* try to assign 0 element of r.type */ /*{{{*/
{
l.type=r.type;
if (r.type == EMPTY) return result;
switch (r.type)
{
case INT: l.u.integer=0; break;
case FLOAT: l.u.flt=0.0; break;
case STRING: l.u.string=empty; break;
default: ;
case INT: l.u.integer = 0; break;
case FLOAT: l.u.flt = 0.0; break;
case STRING: l.u.string = empty; break;
case BOOL: l.u.bl = false; break;
default: return relational_type_mismatch(l.type, r.type);
}
l.type = r.type;
}
/*}}}*/
if (r.type==EMPTY)
/* try to assign 0 element of l.type */ /*{{{*/
{
r.type=l.type;
switch (l.type)
{
case INT: r.u.integer=0; break;
case FLOAT: r.u.flt=0.0; break;
case STRING: r.u.string=empty; break;
default: ;
case BOOL: r.u.bl = false; break;
default: return relational_type_mismatch(l.type, r.type);
}
r.type=l.type;
}
/*}}}*/
if (l.type==FLOAT && r.type==INT)
if (l.type == FLOAT && r.type == INT)
{
result.type = INT;
result.u.integer = (l.u.flt == ((FltT)r.u.integer));
result.u.bl = (l.u.flt == ((FltT)r.u.integer));
return result;
}
else if (l.type==INT && r.type==FLOAT)
if (l.type == INT && r.type == FLOAT)
{
result.type = INT;
result.u.integer = (((FltT)l.u.integer) == r.u.flt);
}
else if (l.type != r.type)
{
result.type = INT;
result.u.integer = 0;
result.u.bl = (((FltT)l.u.integer) == r.u.flt);
return result;
}
else if (l.type==INT)
if (l.type != r.type)
{
result.type=INT;
result.u.integer=l.u.integer==r.u.integer;
result.u.bl = false;
return result;
}
else if (l.type==STRING)
{
result.type=INT;
result.u.integer=(strcmp(l.u.string,r.u.string)==0);
switch (l.type) {
case INT:
result.u.bl = l.u.integer == r.u.integer; return result;
case STRING:
result.u.bl = (strcmp(l.u.string,r.u.string)==0); return result;
case FLOAT:
result.u.bl = l.u.flt == r.u.flt; return result;
case BOOL:
result.u.bl = l.u.bl == r.u.bl; return result;
case LOCATION: {
/* result is true if locations are component-wise equal */
Dimensions dim = X;
while (dim < HYPER && l.u.location[dim] == r.u.location[dim]) ++dim;
if (dim < HYPER) result.u.bl = false;
return result;
}
default: break;
}
else if (l.type==FLOAT)
{
result.type=INT;
result.u.integer=l.u.flt==r.u.flt;
}
else if (l.type==EMPTY)
{
result.type=INT;
result.u.integer=1;
}
else if (l.type == LOCATION)
/* result is true if locations are component-wise equal */ /*{{{*/
{
result.type = INT;
result.u.integer = 1;
for (len = 0; len < 3 && l.u.location[len] == r.u.location[len]; ++len);
if (len < 3) result.u.integer = 0;
}
/*}}}*/
else return relational_type_mismatch(l.type, r.type);
return result;
return relational_type_mismatch(l.type, r.type);
}
/*}}}*/
@ -1160,127 +915,64 @@ static bool nearly_equal(FltT a1, FltT a2)
/* tabouteq -- ~= operator */ /*{{{*/
Token tabouteq(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
result.type = BOOL;
if (l.type==EEK) return tcopy(l);
if (r.type==EEK) return tcopy(r);
if (l.type==EMPTY)
/* try to assign 0 element of r.type */ /*{{{*/
if (l.type == EEK) return tcopy(l);
if (r.type == EEK) return tcopy(r);
if (l.type == EMPTY && r.type == FLOAT)
{
l.type=r.type;
switch (r.type)
{
case FLOAT: l.u.flt=0.0; break;
default: ;
}
l.type = FLOAT;
l.u.flt = 0.0;
}
/*}}}*/
if (r.type==EMPTY)
/* try to assign 0 element of l.type */ /*{{{*/
if (r.type == EMPTY && l.type == FLOAT)
{
r.type=l.type;
switch (l.type)
{
case FLOAT: r.u.flt=0.0; break;
default: ;
}
r.type = FLOAT;
r.u.flt = 0.0;
}
/*}}}*/
if (l.type==FLOAT && r.type==FLOAT)
if (l.type == FLOAT && r.type == FLOAT)
{
result.type=INT;
result.u.integer = (int)nearly_equal(l.u.flt, r.u.flt);
result.u.bl = nearly_equal(l.u.flt, r.u.flt);
return result;
}
else
{
result.type=EEK;
result.u.err = strdup(_("Usage: ~= only compares float values"));
}
return result;
return duperror(&result, _("Usage: ~= only compares float values"));
}
/*}}}*/
/* tne -- != operator */ /*{{{*/
Token tne(Token l, Token r)
{
/* variables */ /*{{{*/
Token result;
static char empty[]="";
int len;
/*}}}*/
if (l.type==EEK) return tcopy(l);
if (r.type==EEK) return tcopy(r);
if (l.type==EMPTY)
/* try to assign 0 element of r.type */ /*{{{*/
{
l.type=r.type;
switch (r.type)
{
case INT: l.u.integer=0; break;
case FLOAT: l.u.flt=0.0; break;
case STRING: l.u.string=empty; break;
default: ;
}
return tneg(teq(l,r));
}
/*}}}*/
/* tor -- logical or of two tokens */ /*{{{*/
Token tor(Token l, Token r)
{
switch (l.type) {
case EEK: return tcopy(l);
case EMPTY : return tcopy(r);
case BOOL:
if (r.type == EMPTY || l.u.bl) return l;
return r;
default: break;
}
/*}}}*/
if (r.type==EMPTY)
/* try to assign 0 element of l.type */ /*{{{*/
{
r.type=l.type;
switch (l.type)
{
case INT: r.u.integer=0; break;
case FLOAT: r.u.flt=0.0; break;
case STRING: r.u.string=empty; break;
default: ;
}
return operand_type_error("or: ", l.type, r.type);
}
/*}}}*/
/* tand -- logical and of two tokens */ /*{{{*/
Token tand(Token l, Token r)
{
switch (l.type) {
case EEK: return tcopy(l);
case EMPTY : return l;
case BOOL:
if (!l.u.bl) return l;
return r;
default: break;
}
/*}}}*/
if (l.type==FLOAT && r.type==INT)
{
result.type = INT;
result.u.integer = l.u.flt != ((FltT)r.u.integer);
}
else if (l.type==INT && r.type==FLOAT)
{
result.type = INT;
result.u.integer = ((FltT)l.u.integer)!=r.u.flt;
}
else if (l.type != r.type)
{
result.type = INT;
result.u.integer = 1;
}
else if (l.type==INT)
{
result.type=INT;
result.u.integer=l.u.integer!=r.u.integer;
}
else if (l.type==STRING)
{
result.type=INT;
result.u.integer=(strcmp(l.u.string,r.u.string)!=0);
}
else if (l.type==FLOAT)
{
result.type=INT;
result.u.integer=l.u.flt!=r.u.flt;
}
else if (l.type==EMPTY) /* both empty, so equal, i.e. not inequal */
{
result.type=INT;
result.u.integer=0;
}
else if (l.type == LOCATION) {
result.type = INT;
result.u.integer = 0;
for (len = 0; len < 3 && l.u.location[len] == r.u.location[len]; ++len);
if (len < 3) result.u.integer = 1;
}
else return relational_type_mismatch(l.type, r.type);
return result;
return operand_type_error("and: ", l.type, r.type);
}
/*}}}*/

View file

@ -25,5 +25,6 @@ Token tgt(Token l, Token r);
Token teq(Token l, Token r);
Token tabouteq(Token l, Token r);
Token tne(Token l, Token r);
Token tor(Token l, Token r);
Token tand(Token l, Token r);
#endif

View file

@ -598,6 +598,45 @@ static Token error_func(FunctionIdentifier self, int argc, const Token argv[])
return result;
}
/*}}}*/
/* blcnst_func -- common implementation of true/false */ /*{{{*/
static Token blcnst_func(FunctionIdentifier self, int argc, const Token argv[])
{
assert(self == FUNC_FALSE || self == FUNC_TRUE);
if (argc >= 0) {
Token err;
const char *templ = _("'%s' must be used as a bare word");
err.type = EEK;
err.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 1);
sprintf(err.u.err, templ, tfunc[self].name);
return err;
}
Token res;
res.type = BOOL;
res.u.bl = (self == FUNC_TRUE);
return res;
}
/*}}}*/
/* blop_macro -- common handler for short-circuiting boolean ops */ /*{{{*/
static Token blop_macro(FunctionIdentifier self, int argc, const Token argv[])
{
assert(self == FUNC_AND || self == FUNC_OR);
Token result;
result.type = BOOL;
result.u.bl = (self == FUNC_AND);
for (int i = 0; i < argc; ++i) {
Token argres = evaltoken(argv[i], FULL);
Token tmp = (self == FUNC_AND) ? tand(result, argres) : tor(result, argres);
tfree_protected(&argres, tmp);
tfree_protected(&result, tmp);
result = tmp;
if (result.type == EEK) return result;
}
return result;
}
/*}}}*/
/* string */ /*{{{*/
static Token string_func(FunctionIdentifier self, int argc, const Token argv[])
{
@ -658,7 +697,7 @@ static Token accum_func(FunctionIdentifier self, int argc, const Token argv[])
if (start > 0) { result = tcopy(argv[start-1]); }
for (int i = start; i < argc; ++i) {
Token tmp = tbin(result, argv[i]);
tfree(&result);
tfree_protected(&result, tmp);
result = tmp;
if (result.type == EEK) return result;
}
@ -737,7 +776,7 @@ static void sum_updt(FunctionIdentifier id, Location *loc, Token *tok,
{
assert(id == FUNC_SUM);
Token tmp = tadd(*tok, *newtok);
tfree(tok);
tfree_protected(tok, tmp);
*tok = tmp;
}
@ -877,49 +916,51 @@ static Token binop_func(FunctionIdentifier self, int argc, const Token argv[])
static Token int_func(FunctionIdentifier self, int argc, const Token argv[])
{
assert(self == FUNC_INT);
/* variables */ /*{{{*/
const char *usage = _("Usage: int(string|bool|float[,rounding_function])");
Token result;
/*}}}*/
if (argc == 1 && argv[0].type == FLOAT)
/* result is integer with cutoff fractional part */ /*{{{*/
{
result.type = INT;
result.u.integer = (IntT)argv[0].u.flt;
return result;
}
/*}}}*/
if (argc == 2 && argv[0].type==FLOAT && argv[1].type == FIDENT)
/* result is integer with given conversion */ /*{{{*/
{
result.type = INT;
switch (argv[1].u.fident)
result.type = INT;
if (argc < 1 || argc > 2) return duperror(&result, usage);
switch (argv[0].type) {
case FLOAT:
if (argc == 1) {
result.u.integer = (IntT)argv[0].u.flt;
return result;
} else { /* result is integer with given conversion */ /*{{{*/
switch (argv[1].u.fident)
{
case FUNC_TRUNC:
result.u.integer = (IntT)argv[0].u.flt; break;
case FUNC_ROUND:
result.u.integer = ROUNDFLTINT(argv[0].u.flt); break;
case FUNC_FLOOR:
result.u.integer = (IntT)FLOORFLT(argv[0].u.flt); break;
case FUNC_CEIL:
result.u.integer = (IntT) CEILFLT(argv[0].u.flt); break;
default: assert(0);
}
return result;
}
case STRING:
{
case FUNC_TRUNC: result.u.integer = (IntT)argv[0].u.flt; break;
case FUNC_ROUND: result.u.integer = ROUNDFLTINT(argv[0].u.flt); break;
case FUNC_FLOOR: result.u.integer = (IntT)FLOORFLT(argv[0].u.flt); break;
case FUNC_CEIL: result.u.integer = (IntT) CEILFLT(argv[0].u.flt); break;
default: assert(0);
char *s;
if (argc > 1) return duperror(&result, usage);
errno=0;
result.u.integer = STRTOINT(argv[0].u.string, &s, 10);
if (s==(char*)0 || *s)
return duperror(&result, _("int(string): invalid string"));
if (errno==ERANGE &&
(result.u.integer==LONG_MAX || result.u.integer==LONG_MIN))
return duperror(&result, _("int(string): domain error"));
return result;
}
return result;
case BOOL:
if (argc > 1) return duperror(&result, usage);
if (argv[0].u.bl) result.u.integer = 1;
else result.u.integer = 0;
return result;
default: break;
}
/*}}}*/
if (argc == 1 && argv[0].type == STRING)
/* result is integer */ /*{{{*/
{
char *s;
errno=0;
result.u.integer = STRTOINT(argv[0].u.string, &s, 10);
if (s==(char*)0 || *s) duperror(&result, _("int(string): invalid string"));
else if (errno==ERANGE &&
(result.u.integer==LONG_MAX || result.u.integer==LONG_MIN))
duperror(&result, _("int(string): domain error"));
else result.type = INT;
return result;
}
/*}}}*/
return duperror(&result, _("Usage: int(string|float[,rounding_function])"));
return duperror(&result, usage);
}
/*}}}*/
@ -1161,10 +1202,10 @@ static Token poly_func(FunctionIdentifier self, int argc, const Token argv[])
for (size_t i = 2; i < argc; ++i)
{
tmp = tmul(result,argv[0]);
tfree(&result);
tfree_protected(&result, tmp);
result = tmp;
tmp = tadd(result,argv[i]);
tfree(&result);
tfree_protected(&result, tmp);
result = tmp;
if (result.type == EEK) return result;
}
@ -1386,6 +1427,12 @@ Tfunc tfunc[]=
[FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 },
[FUNC_EVAL] = { "eval", eval_func, PREFIX_FUNC, FUNCT, 0 },
/* Boolean functions */
[FUNC_AND] = { "and", blop_macro, INFIX_BOOL, MACRO, 0 },
[FUNC_FALSE] = { "false", blcnst_func, PREFIX_FUNC, FUNCT, 0 },
[FUNC_OR] = { "or", blop_macro, INFIX_BOOL, MACRO, 0 },
[FUNC_TRUE] = { "true", blcnst_func, PREFIX_FUNC, FUNCT, 0 },
/* Type conversion functions */
[FUNC_FLOAT] = { "float", float_func, PREFIX_FUNC, FUNCT, 0 },
[FUNC_INT] = { "int", int_func, PREFIX_FUNC, FUNCT, 0 },

View file

@ -22,21 +22,27 @@ typedef enum
FUNC_RND, FUNC_SUBSTR, FUNC_STRPTIME, FUNC_TIME, FUNC_BITAND, FUNC_BITOR,
FUNC_R, FUNC_D, FUNC_CAP_X, FUNC_X_AMPERSAND, FUNC_NEGATE, FUNC_PLUS_SYMBOL,
FUNC_MINUS_SYMBOL, FUNC_ASTERISK, FUNC_SLASH,
/* Note use of thes in the macro below */
FUNC_LESS_EQUAL, FUNC_GREATER_EQUAL, FUNC_LESS_THAN, FUNC_GREATER_THAN,
FUNC_EQUAL_EQUAL, FUNC_TILDE_EQUAL, FUNC_BANG_EQUAL, FUNC_CARET,
FUNC_EQUAL_EQUAL, FUNC_TILDE_EQUAL, FUNC_BANG_EQUAL,
FUNC_CARET,
FUNC_PER_CENT, FUNC_CONCAT, FUNC_TAU, FUNC_SQRT, FUNC_FLOOR, FUNC_CEIL,
FUNC_TRUNC, FUNC_ROUND, FUNC_DECIMAL, FUNC_SCIENTIFIC, FUNC_COMPACT,
FUNC_HEXACT, FUNC_RIGHT, FUNC_LEFT, FUNC_DOWN, FUNC_UP, FUNC_BELOW, FUNC_ABOVE,
FUNC_TRUE, FUNC_FALSE, FUNC_AND, FUNC_OR,
N_FUNCTION_IDS
} FunctionIdentifier;
#define IS_RELATION_FUNC(f) (((f) >= FUNC_LESS_EQUAL) && ((f) <= FUNC_BANG_EQUAL))
/* Forward declaration of Token, since this header is used in scanner.h */
typedef struct Token_struc Token;
typedef enum /* In increasing order of precedence */
{
NO_PRECEDENCE, INFIX_CONC, INFIX_REL, INFIX_PLUS, INFIX_MUL,
NO_PRECEDENCE, INFIX_CONC, INFIX_BOOL, INFIX_REL, INFIX_PLUS, INFIX_MUL,
PREFIX_NEG, INFIX_POW, PREFIX_FUNC
} FunctionPrecedence;

View file

@ -32,9 +32,14 @@ extern char *strdup(const char* s);
/*}}}*/
/* prototypes */ /*{{{*/
static Token term(Token *n[], int *i, EvalMethod meth);
static Token boolterm(Operator bop, Token *n[], int *i, EvalMethod meth);
/*}}}*/
static Token term(Token *n[], int* i, EvalMethod meth)
{
return boolterm(LOR, n, i, meth);
}
/* full_eval_funcall -- evaluate the args of a funcall token and then
call the function on them
*/
@ -43,16 +48,23 @@ static Token full_eval_funcall(Token *t)
assert(t->type == FUNCALL);
if (t->u.funcall.argc < 1)
return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0);
Token *eval_argv = malloc(t->u.funcall.argc*sizeof(Token));
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) {
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
Token *eval_argv;
if (tfunc[t->u.funcall.fident].eval_as == MACRO)
eval_argv = t->u.funcall.argv;
else {
eval_argv = malloc(t->u.funcall.argc*sizeof(Token));
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) {
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
}
}
Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv);
/* To allow a function to return one of its arguments, we need
to be sure not to free that argument: */
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
tfree_protected(&eval_argv[ai], result);
free(eval_argv);
if (tfunc[t->u.funcall.fident].eval_as == FUNCT) {
/* To allow a function to return one of its arguments, we need
to be sure not to free that argument: */
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
tfree_protected(&eval_argv[ai], result);
free(eval_argv);
}
return result;
}
@ -285,8 +297,8 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
if (meth == FULL)
{
Token result = tpow(l,r);
tfree(&l);
tfree(&r);
tfree_protected(&l, result);
tfree_protected(&r, result);
if (result.type == EEK) return result;
l = result;
} else {
@ -347,8 +359,8 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
case MOD: result = tmod(l,r); break;
default: assert(0);
}
tfree(&l);
tfree(&r);
tfree_protected(&l, result);
tfree_protected(&r, result);
if (result.type == EEK) return result;
l = result;
} else {
@ -389,26 +401,24 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
/* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/
static Token factor(Token *n[], int *i, EvalMethod meth)
{
Token l = piterm(n, i, meth);
if (l.type == EEK) return l;
FunctionIdentifier plusident = FUNC_PLUS_SYMBOL;
Token l;
Operator op = CP;
bool first_funcall = true;
l = piterm(n, i, meth);
if (l.type == EEK) return l;
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
while (op == PLUS || op == MINUS)
{
Token r;
++(*i);
r = piterm(n, i, meth);
Token r = piterm(n, i, meth);
if (meth == FULL)
{
Token result = (op==PLUS ? tadd(l,r) : tsub(l,r));
tfree(&l);
tfree(&r);
tfree_protected(&l, result);
tfree_protected(&r, result);
if (result.type == EEK) return result;
l = result;
} else {
@ -433,8 +443,7 @@ static Token factor(Token *n[], int *i, EvalMethod meth)
{
tfree(&r);
tfree(&l);
duperror(&l, _("Exceeded maximum sequence length of +"));
return l;
return duperror(&l, _("Exceeded maximum sequence length of +"));
}
l.u.funcall.argv[(l.u.funcall.argc)++] = r;
}
@ -446,54 +455,164 @@ static Token factor(Token *n[], int *i, EvalMethod meth)
}
/*}}}*/
/* term -- parse and evaluate a relational term */ /*{{{*/
static Token term(Token *n[], int *i, EvalMethod meth)
/* relterm -- parse and evaluate a relational term */ /*{{{*/
static Token relterm(Token *n[], int *i, EvalMethod meth)
{
Token l = factor(n, i, meth);
if (l.type == EEK) return l;
/* a < b < c used to mean (a < b) < c, but that does not make sense really
because there is not an ordering on bools (if we had a separate bool
type). So restrict to a single binary relation; one can still use parens
to get the old, odd behavior */
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR
&& n[*i]->u.op >= LT && n[*i]->u.op <= NE)
{
Operator op = n[*i]->u.op;
Token result, r;
bool firstcomp = true;
Token result = l;
Operator op = CP;
/* a < b < c now means a < b and b < c, so we have to save both the running
result and the last term
*/
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
while (IS_RELATION_OP(op))
{
++(*i);
r = factor(n, i, meth);
Token r = factor(n, i, LITERAL);
if (meth == FULL)
{
switch (op)
{
case LT: result=tlt(l,r); break;
case LE: result=tle(l,r); break;
case GE: result=tge(l,r); break;
case GT: result=tgt(l,r); break;
case ISEQUAL: result=teq(l,r); break;
case ABOUTEQ: result=tabouteq(l,r); break;
case NE: result=tne(l,r); break;
default: assert(0);
if (!firstcomp && result.type == BOOL && !result.u.bl) {
tfree(&r);
} else {
Token tmp = evaltoken(r, FULL);
tfree_protected(&r, tmp);
r = tmp;
switch (op)
{
case LT: tmp = tlt(l,r); break;
case LE: tmp = tle(l,r); break;
case GE: tmp = tge(l,r); break;
case GT: tmp = tgt(l,r); break;
case ISEQUAL: tmp = teq(l,r); break;
case ABOUTEQ: tmp = tabouteq(l,r); break;
case NE: tmp = tne(l,r); break;
default: assert(0);
}
tfree_protected(&l, result);
l = r;
if (firstcomp) {
result = tmp;
} else {
Token newres = tand(result, tmp);
tfree_protected(&result, newres);
tfree_protected(&tmp, newres);
result = newres;
}
if (result.type == EEK) {
tfree_protected(&l, result);
return result;
}
}
} else { /* meth = LITERAL */
if (r.type == EEK)
{
tfree(&l);
return r;
}
Token newcomp;
newcomp.type = FUNCALL;
newcomp.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
newcomp.u.funcall.argc = 2;
newcomp.u.funcall.argv = malloc(2*sizeof(Token));
newcomp.u.funcall.argv[0] = l;
newcomp.u.funcall.argv[1] = r;
l = r;
if (firstcomp) {
result = newcomp;
firstcomp = false;
} else if (result.u.fident != FUNC_AND) {
Token holdres = result;
result.u.funcall.fident = FUNC_AND;
result.u.funcall.argc = 2;
result.u.funcall.argv = malloc(MAXARGC*sizeof(Token));
result.u.funcall.argv[0] = holdres;
result.u.funcall.argv[1] = newcomp;
} else if (result.u.funcall.argc >= MAXARGC) {
tfree(&result);
return
duperror(&result,
_("Exeeded maximum sequence length of comparisons"));
} else {
result.u.funcall.argv[(result.u.funcall.argc)++] = newcomp;
}
}
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i] -> u.op;
else op = CP;
} /* while next op is a comparison */
return result;
}
/*}}}*/
/* boolterm -- parse and evaluate a boolean term */ /*{{{*/
static Token boolterm(Operator bop, Token *n[], int *i, EvalMethod meth)
{
assert (bop == LAND || bop == LOR);
Token l;
Operator op = CP;
bool first_funcall = true;
l = (bop == LAND) ? relterm(n, i, meth) : boolterm(LAND, n, i, meth);
if (l.type == EEK) return l;
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
while (op == bop)
{
++(*i);
/* Need to evaluate right operand literally for the sake of short
circuiting
*/
Token r =
(bop == LAND) ? relterm(n, i, LITERAL) : boolterm(LAND, n, i, LITERAL);
if (meth == FULL) {
if (l.type == BOOL
&& ((bop == LAND && !l.u.bl) || (bop == LOR && l.u.bl)))
tfree(&r);
else {
Token result = evaltoken(r, FULL);
tfree_protected(&r, result);
Token tmp = (bop == LAND) ? tand(l, result) : tor(r, result);
tfree_protected(&result, tmp);
tfree_protected(&l, tmp);
if (tmp.type == EEK) return tmp;
l = tmp;
}
tfree(&l);
tfree(&r);
if (result.type == EEK) return result;
l = result;
} else {
if (r.type == EEK)
{
tfree(&l);
return r;
}
Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
l.u.funcall.argc = 2;
l.u.funcall.argv = malloc(2*sizeof(Token));
l.u.funcall.argv[0] = tmp;
l.u.funcall.argv[1] = r;
if (first_funcall)
{
first_funcall = false;
Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
l.u.funcall.argc = 2;
l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
l.u.funcall.argv[0] = tmp;
l.u.funcall.argv[1] = r;
} else {
if (l.u.funcall.argc >= MAXARGC)
{
tfree(&r);
tfree(&l);
const char* templ = _("Exceeded max sequence length of %s");
l.type = EEK;
l.u.err = malloc(strlen(templ) + MAX_OP_NAME_LENGTH + 1);
sprintf(l.u.err, templ, Op_Name[op]);
return l;
}
l.u.funcall.argv[(l.u.funcall.argc)++] = r;
}
}
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
else op = CP;
}
return l;
}
@ -518,8 +637,8 @@ Token eval(Token **n, EvalMethod meth)
if (meth == FULL)
{
Token result = tconcat(l,r);
tfree(&l);
tfree(&r);
tfree_protected(&l, result);
tfree_protected(&r, result);
if (result.type == EEK) return result;
l = result;
} else {

View file

@ -28,7 +28,8 @@ extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */
const char *Type_Name[] =
{ [EMPTY] = "EMPTY", [STRING] = "STRING", [FLOAT] = "FLOAT", [INT] = "INT",
[OPERATOR] = "OPERATOR", [LIDENT] = "LABEL", [FIDENT] = "FUNCTION",
[LOCATION] = "LOCATION", [FUNCALL] = "FUNCTION-CALL", [EEK] = "ERROR"
[LOCATION] = "LOCATION", [FUNCALL] = "FUNCTION-CALL", [EEK] = "ERROR",
[BOOL] = "BOOL"
};
const char *Op_Name[] =
@ -36,7 +37,7 @@ const char *Op_Name[] =
[OP] = "(", [CP] = ")", [COMMA] = ",",
[LT] = "<", [LE] = "<=", [GE] = ">=", [GT] = ">",
[ISEQUAL] = "==", [ABOUTEQ] = "~=", [NE] = "!=",
[POW] = "^", [MOD] = "%"
[POW] = "^", [MOD] = "%", [LAND] = "and", [LOR] = "or"
};
/* loc_in_box -- returns true if test is in the box determined by b and c */
@ -55,11 +56,40 @@ bool loc_in_box(const Location test,
void cleartoken(Token* tok)
{
tok->type = EMPTY;
tok->u.flt = 0.0;
tok->u.location[0] = 0;
tok->u.location[1] = 0;
tok->u.location[2] = 0;
}
/* tok_matches - return true if l and r appear to be the same token */ /*{{{*/
bool tok_matches(const Token* l, const Token *r)
{
if (l->type != r->type) return false;
switch (l->type) {
case EMPTY: return true;
case STRING: return l->u.string == r->u.string;
case FLOAT: return l->u.flt == r->u.flt;
case INT: return l->u.integer == r->u.integer;
case OPERATOR: return l->u.op == r->u.op;
case LIDENT: return l->u.lident == r->u.lident;
case FIDENT: return l->u.fident == r->u.fident;
case LOCATION:
return l->u.location[X] == r->u.location[X]
&& l->u.location[Y] == r->u.location[Y]
&& l->u.location[Z] == r->u.location[Z];
case EEK: return l->u.err == r->u.err;
case FUNCALL:
return l->u.funcall.fident == r->u.funcall.fident
&& l->u.funcall.argc == r->u.funcall.argc
&& l->u.funcall.argv == r->u.funcall.argv;
case BOOL:
return l->u.bl == r->u.bl;
}
assert(0);
return false;
}
/* duperror - Sets tok to an error and strdups the message into place */
Token duperror(Token* tok, const char* erro)
{
@ -191,22 +221,32 @@ static Token *ident(const char **s)
begin=*s; ++(*s);
while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s);
result=malloc(sizeof(Token));
if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION)
result = malloc(sizeof(Token));
if (*s-begin == 3 && strncmp(begin, "and", 3) == 0)
{
result->type=LIDENT;
result->type = OPERATOR;
result->u.op = LAND;
}
else if (*s-begin == 2 && strncmp(begin, "or", 2) == 0)
{
result->type = OPERATOR;
result->u.op = LOR;
}
else if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION)
{
result->type = LIDENT;
result->u.lident=malloc((size_t)(*s-begin+1));
(void)strncpy(result->u.lident,begin,(size_t)(*s-begin));
result->u.lident[*s-begin]='\0';
}
else
{
result->type=FIDENT;
result->u.fident=fident;
result->type = FIDENT;
result->u.fident = fident;
}
return result;
}
return (Token*)0;
return NULLTOKEN;
}
/*}}}*/

View file

@ -15,7 +15,8 @@ extern "C" {
typedef enum {
EMPTY
#ifndef __cplusplus
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK, FUNCALL
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK,
FUNCALL, BOOL
#endif
} Type;
@ -25,13 +26,15 @@ extern const char *Type_Name[];
typedef enum
{
PLUS, MINUS, MUL, DIV, OP, CP, COMMA,
LT, /* MUST be the first relational operation for parsing to work */
LE, GE, GT, ISEQUAL, ABOUTEQ,
NE, /* MUST be the last relational operation for parsing to work */
POW, MOD
LT, /* Take care with the macros just below */
LE, GE, GT, ISEQUAL, ABOUTEQ, NE,
POW, MOD, LAND, LOR
} Operator;
#define MAX_OP_NAME_LENGTH 3
#define IS_RELATION_OP(op) (((op)>= LT) && ((op)<=NE))
extern const char *Op_Name[];
typedef int Location[3]; /* NOTE: Locations are passed by REFERENCE not value */
@ -159,6 +162,7 @@ typedef struct Token_struc
Location location;
FunctionCall funcall;
char *err;
bool bl;
} u;
} Token;
@ -166,6 +170,7 @@ typedef struct Token_struc
#define EMPTY_TVEC ((Token**)0)
#define TOKISNUM(t) ((t).type == INT || (t).type == FLOAT || (t).type == EMPTY)
bool tok_matches(const Token *l, const Token *r);
Token duperror(Token* tok, const char* erro);
Token **scan(const char **s);
void cleartoken(Token* tok);