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:
parent
7e0ba7370d
commit
892fdcdb75
341
doc/teapot.lyx
341
doc/teapot.lyx
@ -647,7 +647,7 @@ Paper
|
||||
|
||||
\begin_layout Standard
|
||||
As the last step, save your work sheet to a file: F)ile, S)ave.
|
||||
The native file format is XDR, so choose that.
|
||||
The native file format is Teapot ASCII, so choose that.
|
||||
Up to now, your sheet does not have a name, so you will be prompted for
|
||||
one:
|
||||
\end_layout
|
||||
@ -4872,8 +4872,26 @@ Integer Integer values are exact, their range depends on the C type long
|
||||
An example is:
|
||||
\family typewriter
|
||||
42
|
||||
\family default
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
Boolean
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
true
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
or
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
false,
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
used for logical conditions.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
@ -4919,6 +4937,93 @@ Unlike other spreadsheets, the operators in teapot check the type of the
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
x
|
||||
\series medium
|
||||
\emph on
|
||||
|
||||
\begin_inset space ~
|
||||
\end_inset
|
||||
|
||||
|
||||
\family typewriter
|
||||
\series default
|
||||
\emph default
|
||||
and
|
||||
\family default
|
||||
\series medium
|
||||
\emph on
|
||||
|
||||
\begin_inset space ~
|
||||
\end_inset
|
||||
|
||||
|
||||
\series default
|
||||
\emph default
|
||||
y evaluates to the logical conjunction of boolean values
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
and
|
||||
\emph on
|
||||
y
|
||||
\emph default
|
||||
.
|
||||
Note that this evaluation short-circuits: if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
is false, then
|
||||
\emph on
|
||||
y
|
||||
\emph default
|
||||
is never evaluated, and so does not affect the value even if it is an error,
|
||||
for example.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
x
|
||||
\series medium
|
||||
\emph on
|
||||
|
||||
\begin_inset space ~
|
||||
\end_inset
|
||||
|
||||
|
||||
\family typewriter
|
||||
\series default
|
||||
\emph default
|
||||
or
|
||||
\family default
|
||||
\series medium
|
||||
\emph on
|
||||
|
||||
\begin_inset space ~
|
||||
\end_inset
|
||||
|
||||
|
||||
\series default
|
||||
\emph default
|
||||
y evaluates to the logical disjunction of boolean values
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
and
|
||||
\emph on
|
||||
y
|
||||
\emph default
|
||||
.
|
||||
As with
|
||||
\family typewriter
|
||||
and
|
||||
\family default
|
||||
, this evaluation short-circuits if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
is true.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series bold
|
||||
@ -4932,7 +5037,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to 1 if
|
||||
evaluates to Boolean true if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -4979,7 +5084,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to 1 if
|
||||
evaluates to true if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5004,7 +5109,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to 1 if
|
||||
evaluates to true if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5029,7 +5134,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to 1 if
|
||||
evaluates to true if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5055,7 +5160,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to 1 if
|
||||
evaluates to true if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5079,7 +5184,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to 1 if the floating point value
|
||||
evaluates to true if the floating point value
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5088,8 +5193,9 @@ x
|
||||
y
|
||||
\emph default
|
||||
.
|
||||
Almost equal means, the numbers are at most neighbours.
|
||||
It is an error to use this with any type but float.
|
||||
Almost equal means that the numbers are equal or neighbor each other in
|
||||
the floating point representation being used.
|
||||
It is an error to use this comparison with any type but float.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
@ -5116,6 +5222,26 @@ y
|
||||
.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
Note: a string of consecutive relational operators is interpreted as the
|
||||
conjunction of each consecutive (overlapping) pair.
|
||||
Thus
|
||||
\family typewriter
|
||||
2 <= y() <= 10
|
||||
\family default
|
||||
will evaluate to true precisely in rows 2 through 10, inclusive (it is
|
||||
shorthand for
|
||||
\family typewriter
|
||||
2 <= y() and y() <= 10
|
||||
\family default
|
||||
).
|
||||
Similarly
|
||||
\family typewriter
|
||||
@(cell1) == @(cell2) == @(cell3)
|
||||
\family default
|
||||
will return true exactly when all three cells have the same value.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series bold
|
||||
@ -5129,7 +5255,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to the sum if
|
||||
evaluates to the sum if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5148,16 +5274,6 @@ y
|
||||
\emph default
|
||||
are strings, the result is the concatenated string.
|
||||
If they are locations, this operates componentwise.
|
||||
There is no dedicated logical
|
||||
\noun on
|
||||
or
|
||||
\noun default
|
||||
operation, so use
|
||||
\family typewriter
|
||||
+
|
||||
\family default
|
||||
for that.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
@ -5173,7 +5289,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to the difference if
|
||||
evaluates to the difference if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5198,7 +5314,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to the product if
|
||||
evaluates to the product if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5207,15 +5323,6 @@ x
|
||||
y
|
||||
\emph default
|
||||
are numbers.
|
||||
There is no dedicated logical
|
||||
\noun on
|
||||
and
|
||||
\noun default
|
||||
operation, so use
|
||||
\family typewriter
|
||||
*
|
||||
\family default
|
||||
for that.
|
||||
You can multiply a location by an integer, or take the dot product of the
|
||||
coordinates of two locations (although the use case for that is unclear).
|
||||
\end_layout
|
||||
@ -5233,7 +5340,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to the quotient if
|
||||
evaluates to the quotient if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5258,7 +5365,7 @@ x
|
||||
y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to the remainder of the division if
|
||||
evaluates to the remainder of the division if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
@ -5273,24 +5380,6 @@ y
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series bold
|
||||
\emph on
|
||||
x^y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
to the power of
|
||||
\emph on
|
||||
y
|
||||
\emph default
|
||||
.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\family typewriter
|
||||
\series bold
|
||||
-
|
||||
@ -5310,13 +5399,38 @@ x
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
is a number or location.
|
||||
is a number or location, or the negation of
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
if
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
is Boolean.
|
||||
If
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
is empty, the result will be empty, too.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series bold
|
||||
\emph on
|
||||
x^y
|
||||
\series default
|
||||
\emph default
|
||||
evaluates to
|
||||
\emph on
|
||||
x
|
||||
\emph default
|
||||
to the power of
|
||||
\emph on
|
||||
y
|
||||
\emph default
|
||||
.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
@ -5517,8 +5631,8 @@ the arguments are interpreted in exactly the same way as for
|
||||
\series bold
|
||||
&
|
||||
\series default
|
||||
with no arguments or even parentheses evaluates to the location of the
|
||||
current cell.
|
||||
with no arguments (with or without parentheses) evaluates to the location
|
||||
of the current cell.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
@ -5603,14 +5717,21 @@ displaced (by).
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
Thus,
|
||||
Thus, both
|
||||
\family sans
|
||||
\series bold
|
||||
D
|
||||
\series default
|
||||
(-1)
|
||||
\family default
|
||||
returns the location of the cell immediately to the left of this one, and
|
||||
and
|
||||
\family sans
|
||||
\series bold
|
||||
D
|
||||
\series default
|
||||
(left)
|
||||
\family default
|
||||
return the location of the cell immediately to the left of this one, and
|
||||
\family sans
|
||||
\series bold
|
||||
D
|
||||
@ -5690,12 +5811,19 @@ R
|
||||
\series default
|
||||
(,,1)
|
||||
\family default
|
||||
returns the same cell as this one but on the following layer, as does
|
||||
returns the same cell as this one but on the following layer, as do
|
||||
\family sans
|
||||
\series bold
|
||||
R
|
||||
\series default
|
||||
(&(0,0,1)
|
||||
\family default
|
||||
) and
|
||||
\family sans
|
||||
\series bold
|
||||
R
|
||||
\series default
|
||||
(&(0,0,1)
|
||||
(below
|
||||
\family default
|
||||
), but
|
||||
\family sans
|
||||
@ -5703,7 +5831,8 @@ R
|
||||
R
|
||||
\family default
|
||||
\series default
|
||||
(TABLE,,1) returns the value of the cell just below the one labeled
|
||||
(TABLE,,1) returns the value of the cell immediately down from the one labeled
|
||||
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
@ -6553,6 +6682,18 @@ location
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series medium
|
||||
boolean
|
||||
\begin_inset space ~
|
||||
\end_inset
|
||||
|
||||
|
||||
\series default
|
||||
false represents the false Boolean value.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series medium
|
||||
float
|
||||
\begin_inset space ~
|
||||
@ -8003,6 +8144,18 @@ status open
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series medium
|
||||
boolean
|
||||
\begin_inset space ~
|
||||
\end_inset
|
||||
|
||||
|
||||
\series default
|
||||
true represents the true Boolean value.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
|
||||
\series medium
|
||||
float
|
||||
\begin_inset space ~
|
||||
@ -8456,7 +8609,11 @@ negterm
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
powterm::=
|
||||
powterm::= [
|
||||
\family typewriter
|
||||
-
|
||||
\family default
|
||||
]
|
||||
\emph on
|
||||
primary
|
||||
\emph default
|
||||
@ -8516,7 +8673,7 @@ mathterm
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
term::=
|
||||
relterm::=
|
||||
\emph on
|
||||
factor
|
||||
\emph default
|
||||
@ -8551,6 +8708,48 @@ factor
|
||||
}
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
conjterm::=
|
||||
\emph on
|
||||
relterm
|
||||
\family sans
|
||||
|
||||
\emph default
|
||||
{
|
||||
\family typewriter
|
||||
and
|
||||
\family sans
|
||||
|
||||
\family default
|
||||
\emph on
|
||||
relterm
|
||||
\family sans
|
||||
|
||||
\emph default
|
||||
}
|
||||
\end_layout
|
||||
|
||||
\begin_layout Description
|
||||
term::=
|
||||
\emph on
|
||||
conjterm
|
||||
\family sans
|
||||
|
||||
\emph default
|
||||
{
|
||||
\family typewriter
|
||||
or
|
||||
\family sans
|
||||
|
||||
\family default
|
||||
\emph on
|
||||
conjterm
|
||||
\family sans
|
||||
|
||||
\emph default
|
||||
}
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Implementation notes
|
||||
\end_layout
|
||||
@ -8634,6 +8833,20 @@ round
|
||||
to be used as keywords for the int() function.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Note also that some functions are actually implemented as
|
||||
\emph on
|
||||
macros
|
||||
\emph default
|
||||
, which are just like functions but receive their arguments unevaluated.
|
||||
The difference is meant to be transparent to usage of the function, so
|
||||
it is not mentioned elsewhere in this documentation.
|
||||
However, the macro facility is necessary when the function would need to
|
||||
control the evaluation of the arguments, as in the short-circuiting logical
|
||||
operations, or takes an expression as an argument, say to evaluate it on
|
||||
other cells.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Section
|
||||
Frequently Asked Questions
|
||||
\end_layout
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
/*}}}*/
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
/*}}}*/
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user