2019-07-22 20:32:33 +00:00
|
|
|
/* #includes */ /*{{{C}}}*//*{{{*/
|
|
|
|
#ifndef NO_POSIX_SOURCE
|
|
|
|
#undef _POSIX_SOURCE
|
|
|
|
#define _POSIX_SOURCE 1
|
|
|
|
#undef _POSIX_C_SOURCE
|
|
|
|
#define _POSIX_C_SOURCE 2
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DMALLOC
|
|
|
|
#include "dmalloc.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <float.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2019-07-30 19:07:54 +00:00
|
|
|
extern char *strdup(const char* s);
|
2019-07-22 20:32:33 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "default.h"
|
|
|
|
#include "eval.h"
|
|
|
|
#include "func.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "scanner.h"
|
|
|
|
#include "sheet.h"
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
/* tcopy -- return copy of token */ /*{{{*/
|
|
|
|
Token tcopy(Token n)
|
|
|
|
{
|
|
|
|
/* result */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
/*}}}*/
|
|
|
|
|
2019-08-04 14:39:20 +00:00
|
|
|
result = n;
|
|
|
|
switch (n.type) {
|
|
|
|
case STRING: result.u.string = strdup(n.u.string); break;
|
|
|
|
case LIDENT: result.u.lident = strdup(n.u.lident); break;
|
|
|
|
case FUNCALL:
|
|
|
|
if (n.u.funcall.argc > 0)
|
|
|
|
{
|
|
|
|
result.u.funcall.argv = malloc(n.u.funcall.argc*sizeof(Token));
|
|
|
|
for (size_t ai = 0; ai < n.u.funcall.argc; ++ai)
|
|
|
|
result.u.funcall.argv[ai] = tcopy(n.u.funcall.argv[ai]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EEK: result.u.err = strdup(n.u.err); break;
|
|
|
|
default: break;
|
|
|
|
}
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
|
2019-07-22 20:32:33 +00:00
|
|
|
/* tfree -- free dynamic data of token */ /*{{{*/
|
|
|
|
void tfree(Token *n)
|
|
|
|
{
|
2019-07-30 19:07:54 +00:00
|
|
|
Token fake;
|
|
|
|
fake.type = INT;
|
|
|
|
tfree_protected(n, fake);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
/* tfree_protected -- free dynamic data of token but not if same as protected */ /*{{{*/
|
|
|
|
void tfree_protected(Token *n, const Token dontfree)
|
|
|
|
{
|
2019-08-04 14:39:20 +00:00
|
|
|
bool difftype = (dontfree.type != n->type);
|
|
|
|
switch (n->type) {
|
|
|
|
case STRING:
|
|
|
|
if (difftype || n->u.string != dontfree.u.string)
|
|
|
|
{
|
|
|
|
free(n->u.string);
|
|
|
|
n->u.string=(char*)0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LIDENT:
|
|
|
|
if (difftype || n->u.lident != dontfree.u.lident)
|
|
|
|
{
|
|
|
|
free(n->u.lident);
|
|
|
|
n->u.lident=(char*)0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
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);
|
|
|
|
if (n->u.funcall.argv != NULLTOKEN) {
|
|
|
|
free(n->u.funcall.argv);
|
|
|
|
n->u.funcall.argv = NULLTOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EEK:
|
|
|
|
if (difftype || n->u.err != dontfree.u.err)
|
|
|
|
{
|
|
|
|
free(n->u.err);
|
|
|
|
n->u.err=(char*)0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2019-07-22 20:32:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
|
Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
short-cutting in getvalue() when the contents of a cell were empty,
preventing the update of the internal cache of the value of the cell.
However, tracking this down (and getting the associated memory management
correct) necessitated implementing a debugging mode in which I could
dump the internal states of cells and print various other stuff to standard
output. It also involved understanding the meaning of various pointers in
the code, in the process of which I renamed some commonly used macros,
particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
also replaced several very repeatedly used patterns of checking the validity
of locations and pointers with macros, now defined in sheet.h.
Therefore, unfortunately the (relatively small in the end) bugfix for this
major issue is entangled with numerous textual changes to the code made
in tracking it down.
Fixes #18.
Closes #19.
2019-07-24 17:47:39 +00:00
|
|
|
/* tvecfreetoks -- free the tokens in vector of pointer to tokens */ /*{{{*/
|
|
|
|
void tvecfreetoks(Token **tvec)
|
2019-07-22 20:32:33 +00:00
|
|
|
{
|
Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
short-cutting in getvalue() when the contents of a cell were empty,
preventing the update of the internal cache of the value of the cell.
However, tracking this down (and getting the associated memory management
correct) necessitated implementing a debugging mode in which I could
dump the internal states of cells and print various other stuff to standard
output. It also involved understanding the meaning of various pointers in
the code, in the process of which I renamed some commonly used macros,
particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
also replaced several very repeatedly used patterns of checking the validity
of locations and pointers with macros, now defined in sheet.h.
Therefore, unfortunately the (relatively small in the end) bugfix for this
major issue is entangled with numerous textual changes to the code made
in tracking it down.
Fixes #18.
Closes #19.
2019-07-24 17:47:39 +00:00
|
|
|
if (tvec!=EMPTY_TVEC)
|
|
|
|
while (*tvec != NULLTOKEN)
|
2019-07-22 20:32:33 +00:00
|
|
|
{
|
Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
short-cutting in getvalue() when the contents of a cell were empty,
preventing the update of the internal cache of the value of the cell.
However, tracking this down (and getting the associated memory management
correct) necessitated implementing a debugging mode in which I could
dump the internal states of cells and print various other stuff to standard
output. It also involved understanding the meaning of various pointers in
the code, in the process of which I renamed some commonly used macros,
particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
also replaced several very repeatedly used patterns of checking the validity
of locations and pointers with macros, now defined in sheet.h.
Therefore, unfortunately the (relatively small in the end) bugfix for this
major issue is entangled with numerous textual changes to the code made
in tracking it down.
Fixes #18.
Closes #19.
2019-07-24 17:47:39 +00:00
|
|
|
tfree(*tvec);
|
|
|
|
free(*tvec);
|
|
|
|
*(tvec++) = NULLTOKEN;
|
2019-07-22 20:32:33 +00:00
|
|
|
}
|
Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
short-cutting in getvalue() when the contents of a cell were empty,
preventing the update of the internal cache of the value of the cell.
However, tracking this down (and getting the associated memory management
correct) necessitated implementing a debugging mode in which I could
dump the internal states of cells and print various other stuff to standard
output. It also involved understanding the meaning of various pointers in
the code, in the process of which I renamed some commonly used macros,
particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
also replaced several very repeatedly used patterns of checking the validity
of locations and pointers with macros, now defined in sheet.h.
Therefore, unfortunately the (relatively small in the end) bugfix for this
major issue is entangled with numerous textual changes to the code made
in tracking it down.
Fixes #18.
Closes #19.
2019-07-24 17:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*}}}*/
|
|
|
|
/* tvecfree -- free a vector of pointer to tokens entirely */ /*{{{*/
|
|
|
|
void tvecfree(Token **tvec)
|
|
|
|
{
|
|
|
|
tvecfreetoks(tvec);
|
|
|
|
free(tvec);
|
2019-07-22 20:32:33 +00:00
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-27 04:14:26 +00:00
|
|
|
|
Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
short-cutting in getvalue() when the contents of a cell were empty,
preventing the update of the internal cache of the value of the cell.
However, tracking this down (and getting the associated memory management
correct) necessitated implementing a debugging mode in which I could
dump the internal states of cells and print various other stuff to standard
output. It also involved understanding the meaning of various pointers in
the code, in the process of which I renamed some commonly used macros,
particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
also replaced several very repeatedly used patterns of checking the validity
of locations and pointers with macros, now defined in sheet.h.
Therefore, unfortunately the (relatively small in the end) bugfix for this
major issue is entangled with numerous textual changes to the code made
in tracking it down.
Fixes #18.
Closes #19.
2019-07-24 17:47:39 +00:00
|
|
|
/* tveclen -- return length of a vector of pointer to tokens */ /*{{{*/
|
|
|
|
size_t tveclen(Token **tvec)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
if (tvec == EMPTY_TVEC) return 0;
|
2019-07-27 04:14:26 +00:00
|
|
|
for (len = 0; *tvec != NULLTOKEN; ++len,++tvec);
|
Prevent phantom values when clocking, resetting, and clocking again
In the end it turned out that the cause of the phantom values was
short-cutting in getvalue() when the contents of a cell were empty,
preventing the update of the internal cache of the value of the cell.
However, tracking this down (and getting the associated memory management
correct) necessitated implementing a debugging mode in which I could
dump the internal states of cells and print various other stuff to standard
output. It also involved understanding the meaning of various pointers in
the code, in the process of which I renamed some commonly used macros,
particularly the former SHEET(s,x,y,z) which was not returning a Sheet at
all but rather a pointer to a Cell. So this macro is now called CELL_AT. I
also replaced several very repeatedly used patterns of checking the validity
of locations and pointers with macros, now defined in sheet.h.
Therefore, unfortunately the (relatively small in the end) bugfix for this
major issue is entangled with numerous textual changes to the code made
in tracking it down.
Fixes #18.
Closes #19.
2019-07-24 17:47:39 +00:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-27 04:14:26 +00:00
|
|
|
|
2019-07-22 20:32:33 +00:00
|
|
|
/* tadd -- + operator */ /*{{{*/
|
|
|
|
Token tadd(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
const char *msg;
|
2019-07-25 13:05:24 +00:00
|
|
|
char *buf;
|
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK)
|
|
|
|
/* return left error */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (r.type==EEK)
|
|
|
|
/* return right error */ /*{{{*/
|
|
|
|
return tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==INT)
|
|
|
|
/* result is int sum of two ints */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.integer+r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==STRING && r.type==STRING)
|
|
|
|
/* result is concatenated strings */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=STRING;
|
|
|
|
result.u.string=malloc(strlen(l.u.string)+strlen(r.u.string)+1);
|
|
|
|
(void)strcpy(result.u.string,l.u.string);
|
|
|
|
(void)strcat(result.u.string,r.u.string);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && (r.type==INT || r.type==STRING || r.type==FLOAT || r.type==EMPTY))
|
|
|
|
/* return right argument */ /*{{{*/
|
|
|
|
return tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if ((l.type==INT || l.type==STRING || l.type==FLOAT) && r.type==EMPTY)
|
|
|
|
/* return left argument */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT)
|
|
|
|
/* result is float sum of int and float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=((double)l.u.integer)+r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==INT)
|
|
|
|
/* result is float sum of float and int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt+((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==FLOAT)
|
|
|
|
/* result is float sum of float and float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt+r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (l.type == LOCATION && r.type == LOCATION)
|
|
|
|
/* result is component-wise sum of locations */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = l.u.location[len] + r.u.location[len];
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-22 20:32:33 +00:00
|
|
|
else if (l.type==EMPTY && r.type==EMPTY)
|
|
|
|
/* result is emty */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EMPTY;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else
|
|
|
|
/* result is type error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
2019-07-25 13:05:24 +00:00
|
|
|
len = strlen(_("wrong types for + operator"));
|
2019-07-30 19:07:54 +00:00
|
|
|
buf = malloc(len + 5 + 2*MAX_TYPE_NAME_LENGTH + 1);
|
2019-07-25 13:05:24 +00:00
|
|
|
strcpy(buf, _("wrong types for + operator"));
|
|
|
|
snprintf(buf + len, 128, ": %s + %s", Type_Name[l.type], Type_Name[r.type]);
|
|
|
|
result.u.err = buf;
|
2019-07-22 20:32:33 +00:00
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
|
|
|
|
/* result is error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=malloc(strlen(msg)+4);
|
|
|
|
(void)strcpy(result.u.err,"+: ");
|
|
|
|
(void)strcat(result.u.err,msg);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tsub -- binary - operator */ /*{{{*/
|
|
|
|
Token tsub(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
const char *msg;
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK)
|
|
|
|
/* return left error */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (r.type==EEK)
|
|
|
|
/* return right error */ /*{{{*/
|
|
|
|
return tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==INT)
|
|
|
|
/* result is int difference between left int and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.integer-r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==FLOAT)
|
|
|
|
/* result is float difference between left float and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt-r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY)
|
|
|
|
/* return negated right argument */ /*{{{*/
|
|
|
|
return tneg(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if ((l.type==INT || l.type==FLOAT) && r.type==EMPTY)
|
|
|
|
/* return left argument */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT)
|
|
|
|
/* result is float difference of left integer and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=((double)l.u.integer)-r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==INT)
|
|
|
|
/* result is float difference between left float and right integer */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt-((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (l.type == LOCATION && r.type == LOCATION)
|
|
|
|
/* result is component-wise difference of locations */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = l.u.location[len] - r.u.location[len];
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-22 20:32:33 +00:00
|
|
|
else
|
|
|
|
/* result is difference type error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("wrong types for - operator"))+1),_("wrong types for - operator"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
|
|
|
|
/* result is error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=malloc(strlen(msg)+4);
|
|
|
|
(void)strcpy(result.u.err,"-: ");
|
|
|
|
(void)strcat(result.u.err,msg);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tdiv -- / operator */ /*{{{*/
|
|
|
|
Token tdiv(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
const char *msg;
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK)
|
|
|
|
/* return left error */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (r.type==EEK)
|
|
|
|
/* return right error */ /*{{{*/
|
|
|
|
return tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if ((r.type==INT && r.u.integer==0) || (r.type==FLOAT && r.u.flt==0.0) || (r.type==EMPTY))
|
|
|
|
/* result is division by 0 error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("division by 0"))+1),_("division by 0"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==INT)
|
|
|
|
/* result is quotient of left int and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.integer/r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==FLOAT)
|
|
|
|
/* result is quotient of left float and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt/r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && r.type==INT)
|
|
|
|
/* result is 0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && r.type==FLOAT)
|
|
|
|
/* result is 0.0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=0.0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT)
|
|
|
|
/* result is float quotient of left int and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=((double)l.u.integer)/r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==INT)
|
|
|
|
/* result is float quotient of left float and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt/((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (l.type == LOCATION && r.type == INT)
|
|
|
|
/* result is divide each component of location by right */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = l.u.location[len] / r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-22 20:32:33 +00:00
|
|
|
else
|
|
|
|
/* result is quotient type error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("wrong types for / operator"))+1),_("wrong types for / operator"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
|
|
|
|
/* result is error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=malloc(strlen(msg)+4);
|
|
|
|
(void)strcpy(result.u.err,"/: ");
|
|
|
|
(void)strcat(result.u.err,msg);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tmod -- % operator */ /*{{{*/
|
|
|
|
Token tmod(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
const char *msg;
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK) /* return left error */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (r.type==EEK) /* return right error */ /*{{{*/
|
|
|
|
return tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if ((r.type==INT && r.u.integer==0) || (r.type==FLOAT && r.u.flt==0.0) || (r.type==EMPTY)) /* result is modulo 0 error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("modulo 0"))+1),_("modulo 0"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==INT) /* result is remainder of left int and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.integer%r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==FLOAT) /* result is remainder of left float and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=fmod(l.u.flt,r.u.flt);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && r.type==INT) /* result is 0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && r.type==FLOAT) /* result is 0.0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=0.0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT) /* result is float remainder of left int and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=fmod((double)l.u.integer,r.u.flt);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==INT) /* result is float remainder of left float and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=fmod(l.u.flt,(double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (l.type == LOCATION && r.type == LOCATION)
|
|
|
|
/* result is component-wise mod of locations */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = l.u.location[len] % r.u.location[len];
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type == LOCATION && r.type == INT)
|
|
|
|
/* result is mod each component of location by right */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = l.u.location[len] % r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-22 20:32:33 +00:00
|
|
|
else /* result is remainder type error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("wrong types for % operator"))+1),_("wrong types for % operator"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) /* result is error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=malloc(strlen(msg)+4);
|
|
|
|
(void)strcpy(result.u.err,"%: ");
|
|
|
|
(void)strcat(result.u.err,msg);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tmul -- * operator */ /*{{{*/
|
|
|
|
Token tmul(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
const char *msg;
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK)
|
|
|
|
/* return left error */ /*{{{*/
|
|
|
|
result=tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (r.type==EEK)
|
|
|
|
/* return right error */ /*{{{*/
|
|
|
|
result=tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==INT)
|
|
|
|
/* result is int product of left int and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result=l;
|
|
|
|
result.u.integer=l.u.integer*r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==FLOAT)
|
|
|
|
/* result is float product of left float and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt*r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if ((l.type==EMPTY && r.type==INT) || (l.type==INT && r.type==EMPTY))
|
|
|
|
/* result is 0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if ((l.type==EMPTY && r.type==FLOAT) || (l.type==FLOAT && r.type==EMPTY))
|
|
|
|
/* result is 0.0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=0.0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT)
|
|
|
|
/* result is float product of left int and right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=((double)l.u.integer)*r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==FLOAT && r.type==INT)
|
|
|
|
/* result is float product of left float and right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=l.u.flt*((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && r.type==EMPTY)
|
|
|
|
/* result is empty */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EMPTY;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (l.type == LOCATION && r.type == INT)
|
|
|
|
/* result is each component of location times right */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = l.u.location[len] * r.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type == LOCATION && r.type == LOCATION)
|
|
|
|
/* result is dot product of locations */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = INT;
|
|
|
|
result.u.integer = 0;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.integer += l.u.location[len] * r.u.location[len];
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-22 20:32:33 +00:00
|
|
|
else
|
|
|
|
/* result is product type error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("wrong types for * operator"))+1),_("wrong types for * operator"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
|
|
|
|
/* result is error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=malloc(strlen(msg)+4);
|
|
|
|
(void)strcpy(result.u.err,"*: ");
|
|
|
|
(void)strcat(result.u.err,msg);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tneg -- monadic - operator */ /*{{{*/
|
|
|
|
Token tneg(Token x)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (x.type==EEK)
|
|
|
|
/* return error */ /*{{{*/
|
|
|
|
return tcopy(x);
|
|
|
|
/*}}}*/
|
|
|
|
else if (x.type==INT)
|
|
|
|
/* result is negated int argument */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=-x.u.integer;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (x.type==FLOAT)
|
|
|
|
/* result is negated float argument */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=FLOAT;
|
|
|
|
result.u.flt=-x.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (x.type==EMPTY)
|
|
|
|
/* result is argument itself */ /*{{{*/
|
|
|
|
{
|
|
|
|
result=tcopy(x);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (x.type == LOCATION)
|
|
|
|
/* result is component-wise negation of location */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type = LOCATION;
|
|
|
|
for (len = 0; len < 3; ++len)
|
|
|
|
result.u.location[len] = -x.u.location[len];
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-22 20:32:33 +00:00
|
|
|
else
|
|
|
|
/* result is negation error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("wrong type for - operator"))+1),_("wrong type for - operator"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tpow -- ^ operator */ /*{{{*/
|
|
|
|
Token tpow(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
const char *msg;
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK) /* return left error */ /*{{{*/
|
|
|
|
return tcopy(l);
|
|
|
|
/*}}}*/
|
|
|
|
else if (r.type==EEK) /* return right error */ /*{{{*/
|
|
|
|
return tcopy(r);
|
|
|
|
/*}}}*/
|
|
|
|
else if ((l.type==INT || l.type==FLOAT || l.type==EMPTY) && (r.type==INT || r.type==FLOAT || l.type==EMPTY)) /* do the real work */ /*{{{*/
|
|
|
|
{
|
|
|
|
if ((l.type==INT || l.type==EMPTY) && ((r.type==INT && r.u.integer>=0) || r.type==EMPTY))
|
|
|
|
/* int^int, return int or error if 0^0 */ /*{{{*/
|
|
|
|
{
|
|
|
|
long x,y;
|
|
|
|
|
|
|
|
if (l.type==EMPTY) x=0;
|
|
|
|
else x=l.u.integer;
|
|
|
|
if (r.type==EMPTY) y=0;
|
|
|
|
else y=r.u.integer;
|
|
|
|
if (x==0 && y==0)
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("0^0 is not defined"))+1),_("0^0 is not defined"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
long i;
|
|
|
|
|
|
|
|
result.type=INT;
|
|
|
|
if (x==0) result.u.integer=0;
|
|
|
|
else if (y==0) result.u.integer=1;
|
|
|
|
else for (result.u.integer=x,i=1; i<y; ++i) result.u.integer*=x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else
|
|
|
|
/* float^float */ /*{{{*/
|
|
|
|
{
|
|
|
|
double x=0.0,y=0.0;
|
|
|
|
|
|
|
|
switch (l.type)
|
|
|
|
{
|
|
|
|
case INT: x=(double)l.u.integer; break;
|
|
|
|
case FLOAT: x=l.u.flt; break;
|
|
|
|
case EMPTY: x=0.0; break;
|
|
|
|
default: assert(0);
|
|
|
|
}
|
|
|
|
switch (r.type)
|
|
|
|
{
|
|
|
|
case INT: y=(double)r.u.integer; break;
|
|
|
|
case FLOAT: y=r.u.flt; break;
|
|
|
|
case EMPTY: y=0.0; break;
|
|
|
|
default: assert(0);
|
|
|
|
}
|
|
|
|
result.type=FLOAT;
|
|
|
|
errno=0; /* there is no portable EOK :( */
|
|
|
|
result.u.flt=pow(x,y);
|
|
|
|
switch (errno)
|
|
|
|
{
|
|
|
|
case 0: result.type=FLOAT; break;
|
|
|
|
case ERANGE:
|
|
|
|
case EDOM: result.type=EEK; result.u.err=strcpy(malloc(strlen(_("^ caused a domain error"))+1),_("^ caused a domain error")); break;
|
|
|
|
default: assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else /* result is type error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=strcpy(malloc(strlen(_("wrong types for ^ operator"))+1),_("wrong types for ^ operator"));
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) /* result is error */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
|
|
|
result.u.err=malloc(strlen(msg)+4);
|
|
|
|
(void)strcpy(result.u.err,"^: ");
|
|
|
|
(void)strcat(result.u.err,msg);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tfuncall -- function operator */ /*{{{*/
|
|
|
|
Token tfuncall(Token *ident, int argc, Token argv[])
|
|
|
|
{
|
|
|
|
return tfunc[ident->u.fident].func(argc, argv);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
|
|
|
|
static Token relational_type_mismatch(Type l, Type r)
|
|
|
|
{
|
|
|
|
Token mismatch;
|
|
|
|
mismatch.type = EEK;
|
|
|
|
char *templ = _("Type mismatch: cannot compare %s and %s");
|
|
|
|
mismatch.u.err = malloc(strlen(templ) + 2*MAX_TYPE_NAME_LENGTH + 1);
|
|
|
|
sprintf(mismatch.u.err, templ, Type_Name[l], Type_Name[r]);
|
|
|
|
return mismatch;
|
|
|
|
}
|
|
|
|
|
2019-07-22 20:32:33 +00:00
|
|
|
/* tlt -- < operator */ /*{{{*/
|
|
|
|
Token tlt(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
static char empty[]="";
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
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) /* return 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 < right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.flt<((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT) /* return left int < right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=((double)l.u.integer)<r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
else return relational_type_mismatch(l.type, r.type);
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tle -- <= operator */ /*{{{*/
|
|
|
|
Token tle(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
static char empty[]="";
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
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 <= (double)right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.flt<=((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT) /* result is (double)left int <= right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=((double)l.u.integer)<=r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==EMPTY && r.type==EMPTY) /* result is 1 */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=1;
|
|
|
|
}
|
2019-07-25 13:05:24 +00:00
|
|
|
/*}}}*/
|
|
|
|
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;
|
|
|
|
}
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
else return relational_type_mismatch(l.type, r.type);
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tge -- >= operator */ /*{{{*/
|
|
|
|
Token tge(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
static char empty[]="";
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
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 >= (double) right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.flt>=((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT) /* return (double) left int >= right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=((double)l.u.integer)>=r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
else return relational_type_mismatch(l.type, r.type);
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tgt -- <= operator */ /*{{{*/
|
|
|
|
Token tgt(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
static char empty[]="";
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
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 > (double) right int */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.flt>((double)r.u.integer);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
else if (l.type==INT && r.type==FLOAT) /* result is left int > right float */ /*{{{*/
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=((double)l.u.integer)>r.u.flt;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-25 13:05:24 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-07-30 19:07:54 +00:00
|
|
|
else return relational_type_mismatch(l.type, r.type);
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* teq -- == operator */ /*{{{*/
|
|
|
|
Token teq(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
static char empty[]="";
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK) return tcopy(l);
|
|
|
|
else 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: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
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==FLOAT && r.type==INT)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.flt==((double)r.u.integer);
|
|
|
|
}
|
|
|
|
else if (l.type==INT && r.type==FLOAT)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=((double)l.u.integer)==r.u.flt;
|
|
|
|
}
|
|
|
|
else if (l.type!=r.type)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=0;
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=1;
|
|
|
|
}
|
2019-07-25 13:05:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
else return relational_type_mismatch(l.type, r.type);
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
2019-07-30 19:07:54 +00:00
|
|
|
|
2019-07-22 20:32:33 +00:00
|
|
|
/* tabouteq -- ~= operator */ /*{{{*/
|
|
|
|
Token tabouteq(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK) return tcopy(l);
|
|
|
|
else 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 FLOAT: l.u.flt=0.0; break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (r.type==EMPTY)
|
|
|
|
/* try to assign 0 element of l.type */ /*{{{*/
|
|
|
|
{
|
|
|
|
r.type=l.type;
|
|
|
|
switch (l.type)
|
|
|
|
{
|
|
|
|
case FLOAT: r.u.flt=0.0; break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
if (l.type==FLOAT && r.type==FLOAT)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=(fabs(l.u.flt-r.u.flt)<=DBL_EPSILON);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.type=EEK;
|
2019-07-30 19:07:54 +00:00
|
|
|
result.u.err = strdup(_("Usage: ~= only compares float values"));
|
2019-07-22 20:32:33 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
/* tne -- != operator */ /*{{{*/
|
|
|
|
Token tne(Token l, Token r)
|
|
|
|
{
|
|
|
|
/* variables */ /*{{{*/
|
|
|
|
Token result;
|
|
|
|
static char empty[]="";
|
2019-07-25 13:05:24 +00:00
|
|
|
int len;
|
2019-07-22 20:32:33 +00:00
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
if (l.type==EEK) return tcopy(l);
|
|
|
|
else 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: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
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==FLOAT && r.type==INT)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=l.u.flt!=((double)r.u.integer);
|
|
|
|
}
|
|
|
|
else if (l.type==INT && r.type==FLOAT)
|
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=((double)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;
|
|
|
|
}
|
2019-07-25 13:05:24 +00:00
|
|
|
else if (l.type==EMPTY) /* both empty, so equal, i.e. not inequal */
|
2019-07-22 20:32:33 +00:00
|
|
|
{
|
|
|
|
result.type=INT;
|
|
|
|
result.u.integer=0;
|
|
|
|
}
|
2019-07-25 13:05:24 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-07-30 19:07:54 +00:00
|
|
|
else return relational_type_mismatch(l.type, r.type);
|
2019-07-22 20:32:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
|
|
|
|
|