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
325
doc/teapot.lyx
325
doc/teapot.lyx
@ -647,7 +647,7 @@ Paper
|
|||||||
|
|
||||||
\begin_layout Standard
|
\begin_layout Standard
|
||||||
As the last step, save your work sheet to a file: F)ile, S)ave.
|
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
|
Up to now, your sheet does not have a name, so you will be prompted for
|
||||||
one:
|
one:
|
||||||
\end_layout
|
\end_layout
|
||||||
@ -4872,8 +4872,26 @@ Integer Integer values are exact, their range depends on the C type long
|
|||||||
An example is:
|
An example is:
|
||||||
\family typewriter
|
\family typewriter
|
||||||
42
|
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
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
@ -4919,6 +4937,93 @@ Unlike other spreadsheets, the operators in teapot check the type of the
|
|||||||
|
|
||||||
\end_layout
|
\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
|
\begin_layout Description
|
||||||
|
|
||||||
\series bold
|
\series bold
|
||||||
@ -4932,7 +5037,7 @@ x
|
|||||||
y
|
y
|
||||||
\series default
|
\series default
|
||||||
\emph default
|
\emph default
|
||||||
evaluates to 1 if
|
evaluates to Boolean true if
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
@ -4979,7 +5084,7 @@ x
|
|||||||
y
|
y
|
||||||
\series default
|
\series default
|
||||||
\emph default
|
\emph default
|
||||||
evaluates to 1 if
|
evaluates to true if
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
@ -5004,7 +5109,7 @@ x
|
|||||||
y
|
y
|
||||||
\series default
|
\series default
|
||||||
\emph default
|
\emph default
|
||||||
evaluates to 1 if
|
evaluates to true if
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
@ -5029,7 +5134,7 @@ x
|
|||||||
y
|
y
|
||||||
\series default
|
\series default
|
||||||
\emph default
|
\emph default
|
||||||
evaluates to 1 if
|
evaluates to true if
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
@ -5055,7 +5160,7 @@ x
|
|||||||
y
|
y
|
||||||
\series default
|
\series default
|
||||||
\emph default
|
\emph default
|
||||||
evaluates to 1 if
|
evaluates to true if
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
@ -5079,7 +5184,7 @@ x
|
|||||||
y
|
y
|
||||||
\series default
|
\series default
|
||||||
\emph default
|
\emph default
|
||||||
evaluates to 1 if the floating point value
|
evaluates to true if the floating point value
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
@ -5088,8 +5193,9 @@ x
|
|||||||
y
|
y
|
||||||
\emph default
|
\emph default
|
||||||
.
|
.
|
||||||
Almost equal means, the numbers are at most neighbours.
|
Almost equal means that the numbers are equal or neighbor each other in
|
||||||
It is an error to use this with any type but float.
|
the floating point representation being used.
|
||||||
|
It is an error to use this comparison with any type but float.
|
||||||
\end_layout
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
@ -5116,6 +5222,26 @@ y
|
|||||||
.
|
.
|
||||||
\end_layout
|
\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
|
\begin_layout Description
|
||||||
|
|
||||||
\series bold
|
\series bold
|
||||||
@ -5148,16 +5274,6 @@ y
|
|||||||
\emph default
|
\emph default
|
||||||
are strings, the result is the concatenated string.
|
are strings, the result is the concatenated string.
|
||||||
If they are locations, this operates componentwise.
|
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
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
@ -5207,15 +5323,6 @@ x
|
|||||||
y
|
y
|
||||||
\emph default
|
\emph default
|
||||||
are numbers.
|
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
|
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).
|
coordinates of two locations (although the use case for that is unclear).
|
||||||
\end_layout
|
\end_layout
|
||||||
@ -5273,24 +5380,6 @@ y
|
|||||||
|
|
||||||
\begin_layout Description
|
\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
|
\family typewriter
|
||||||
\series bold
|
\series bold
|
||||||
-
|
-
|
||||||
@ -5310,13 +5399,38 @@ x
|
|||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\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
|
If
|
||||||
\emph on
|
\emph on
|
||||||
x
|
x
|
||||||
\emph default
|
\emph default
|
||||||
is empty, the result will be empty, too.
|
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
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
@ -5517,8 +5631,8 @@ the arguments are interpreted in exactly the same way as for
|
|||||||
\series bold
|
\series bold
|
||||||
&
|
&
|
||||||
\series default
|
\series default
|
||||||
with no arguments or even parentheses evaluates to the location of the
|
with no arguments (with or without parentheses) evaluates to the location
|
||||||
current cell.
|
of the current cell.
|
||||||
\end_layout
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
@ -5603,14 +5717,21 @@ displaced (by).
|
|||||||
\begin_inset Quotes erd
|
\begin_inset Quotes erd
|
||||||
\end_inset
|
\end_inset
|
||||||
|
|
||||||
Thus,
|
Thus, both
|
||||||
\family sans
|
\family sans
|
||||||
\series bold
|
\series bold
|
||||||
D
|
D
|
||||||
\series default
|
\series default
|
||||||
(-1)
|
(-1)
|
||||||
\family default
|
\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
|
\family sans
|
||||||
\series bold
|
\series bold
|
||||||
D
|
D
|
||||||
@ -5690,20 +5811,28 @@ R
|
|||||||
\series default
|
\series default
|
||||||
(,,1)
|
(,,1)
|
||||||
\family default
|
\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
|
\family sans
|
||||||
\series bold
|
\series bold
|
||||||
R
|
R
|
||||||
\series default
|
\series default
|
||||||
(&(0,0,1)
|
(&(0,0,1)
|
||||||
\family default
|
\family default
|
||||||
|
) and
|
||||||
|
\family sans
|
||||||
|
\series bold
|
||||||
|
R
|
||||||
|
\series default
|
||||||
|
(below
|
||||||
|
\family default
|
||||||
), but
|
), but
|
||||||
\family sans
|
\family sans
|
||||||
\series bold
|
\series bold
|
||||||
R
|
R
|
||||||
\family default
|
\family default
|
||||||
\series 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
|
\begin_inset Quotes eld
|
||||||
\end_inset
|
\end_inset
|
||||||
|
|
||||||
@ -6553,6 +6682,18 @@ location
|
|||||||
|
|
||||||
\begin_layout Description
|
\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
|
\series medium
|
||||||
float
|
float
|
||||||
\begin_inset space ~
|
\begin_inset space ~
|
||||||
@ -8003,6 +8144,18 @@ status open
|
|||||||
|
|
||||||
\begin_layout Description
|
\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
|
\series medium
|
||||||
float
|
float
|
||||||
\begin_inset space ~
|
\begin_inset space ~
|
||||||
@ -8456,7 +8609,11 @@ negterm
|
|||||||
\end_layout
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
powterm::=
|
powterm::= [
|
||||||
|
\family typewriter
|
||||||
|
-
|
||||||
|
\family default
|
||||||
|
]
|
||||||
\emph on
|
\emph on
|
||||||
primary
|
primary
|
||||||
\emph default
|
\emph default
|
||||||
@ -8516,7 +8673,7 @@ mathterm
|
|||||||
\end_layout
|
\end_layout
|
||||||
|
|
||||||
\begin_layout Description
|
\begin_layout Description
|
||||||
term::=
|
relterm::=
|
||||||
\emph on
|
\emph on
|
||||||
factor
|
factor
|
||||||
\emph default
|
\emph default
|
||||||
@ -8551,6 +8708,48 @@ factor
|
|||||||
}
|
}
|
||||||
\end_layout
|
\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
|
\begin_layout Subsection
|
||||||
Implementation notes
|
Implementation notes
|
||||||
\end_layout
|
\end_layout
|
||||||
@ -8634,6 +8833,20 @@ round
|
|||||||
to be used as keywords for the int() function.
|
to be used as keywords for the int() function.
|
||||||
\end_layout
|
\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
|
\begin_layout Section
|
||||||
Frequently Asked Questions
|
Frequently Asked Questions
|
||||||
\end_layout
|
\end_layout
|
||||||
|
@ -425,7 +425,44 @@ size_t ptokatprec(char* dest, size_t size, size_t field_width, StringFormat sf,
|
|||||||
default: /* infix argument */
|
default: /* infix argument */
|
||||||
assert(tok->u.funcall.argc > 1);
|
assert(tok->u.funcall.argc > 1);
|
||||||
if (fp < atprec && cur < size) dest[cur++] = '(';
|
if (fp < atprec && cur < size) dest[cur++] = '(';
|
||||||
for (size_t ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ ai)
|
/* 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, 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;
|
bool parenarg = false;
|
||||||
if (ai > 0) {
|
if (ai > 0) {
|
||||||
@ -449,11 +486,21 @@ size_t ptokatprec(char* dest, size_t size, size_t field_width, StringFormat sf,
|
|||||||
digits, ef, tok->u.funcall.argv + ai, fp);
|
digits, ef, tok->u.funcall.argv + ai, fp);
|
||||||
if (parenarg && cur < size) dest[cur++] = ')';
|
if (parenarg && cur < size) dest[cur++] = ')';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (fp < atprec && cur < size) dest[cur++] = ')';
|
if (fp < atprec && cur < size) dest[cur++] = ')';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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 */ /*{{{*/
|
/* EEK */ /*{{{*/
|
||||||
case 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 */ /*{{{*/
|
/* print -- print token sequence */ /*{{{*/
|
||||||
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
|
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
|
||||||
int digits, Token **n)
|
int digits, Token **n)
|
||||||
|
@ -77,6 +77,7 @@ typedef enum {DIRECT_STRING, QUOTE_STRING} StringFormat;
|
|||||||
typedef enum {TRUNCATED_ERROR, VERBOSE_ERROR, RESTORE_ERROR} ErrorFormat;
|
typedef enum {TRUNCATED_ERROR, VERBOSE_ERROR, RESTORE_ERROR} ErrorFormat;
|
||||||
size_t printtok(char *dest, size_t size, size_t field_width, StringFormat sf,
|
size_t printtok(char *dest, size_t size, size_t field_width, StringFormat sf,
|
||||||
FloatFormat ff, int digits, ErrorFormat ef, const Token *tok);
|
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,
|
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
|
||||||
int digits, Token **n);
|
int digits, Token **n);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -88,8 +88,18 @@ void tfree_protected(Token *n, const Token dontfree)
|
|||||||
case FUNCALL:
|
case FUNCALL:
|
||||||
if (difftype || n->u.funcall.argv != dontfree.u.funcall.argv)
|
if (difftype || n->u.funcall.argv != dontfree.u.funcall.argv)
|
||||||
{
|
{
|
||||||
for (int ai = 0; ai < n->u.funcall.argc; ++ai)
|
for (int ai = n->u.funcall.argc-1; ai >= 0; --ai)
|
||||||
tfree_protected(n->u.funcall.argv + ai, dontfree);
|
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) {
|
if (n->u.funcall.argv != NULLTOKEN) {
|
||||||
free(n->u.funcall.argv);
|
free(n->u.funcall.argv);
|
||||||
n->u.funcall.argv = NULLTOKEN;
|
n->u.funcall.argv = NULLTOKEN;
|
||||||
@ -587,36 +597,20 @@ Token tmul(Token l, Token r)
|
|||||||
/* tneg -- monadic - operator */ /*{{{*/
|
/* tneg -- monadic - operator */ /*{{{*/
|
||||||
Token tneg(Token x)
|
Token tneg(Token x)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
|
||||||
Token result;
|
Token result;
|
||||||
int len;
|
result.type = x.type;
|
||||||
/*}}}*/
|
|
||||||
|
|
||||||
if (x.type == EEK) return tcopy(x);
|
switch (x.type) {
|
||||||
if (x.type == EMPTY) return x;
|
case EEK: return tcopy(x);
|
||||||
if (x.type == INT)
|
case EMPTY: return x;
|
||||||
/* result is negated int argument */ /*{{{*/
|
case INT: result.u.integer = -x.u.integer; return result;
|
||||||
{
|
case FLOAT: result.u.flt = -x.u.flt; return result;
|
||||||
result.type = INT;
|
case LOCATION:
|
||||||
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)
|
for (size_t len = 0; len < 3; ++len)
|
||||||
result.u.location[len] = -x.u.location[len];
|
result.u.location[len] = -x.u.location[len];
|
||||||
return result;
|
return result;
|
||||||
|
case BOOL: result.u.bl = !x.u.bl; return result;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
result.type = EEK;
|
result.type = EEK;
|
||||||
const char* templ = "wrong type for - operator: %s";
|
const char* templ = "wrong type for - operator: %s";
|
||||||
@ -716,429 +710,190 @@ static Token relational_type_mismatch(Type l, Type r)
|
|||||||
/* tlt -- < operator */ /*{{{*/
|
/* tlt -- < operator */ /*{{{*/
|
||||||
Token tlt(Token l, Token r)
|
Token tlt(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
|
||||||
Token result;
|
Token result;
|
||||||
|
result.type = BOOL;
|
||||||
|
result.u.bl = false;
|
||||||
static char empty[] = "";
|
static char empty[] = "";
|
||||||
int len;
|
|
||||||
/*}}}*/
|
|
||||||
|
|
||||||
if (l.type==EEK) /* return left error argument */ /*{{{*/
|
if (l.type == EEK) return tcopy(l);
|
||||||
return tcopy(l);
|
if (r.type == EEK) return tcopy(r);
|
||||||
/*}}}*/
|
|
||||||
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 == EMPTY) /* try to assign 0 element of r.type */ /*{{{*/
|
||||||
{
|
{
|
||||||
l.type=r.type;
|
if (r.type == EMPTY) return result;
|
||||||
switch (r.type)
|
switch (r.type)
|
||||||
{
|
{
|
||||||
case INT: l.u.integer = 0; break;
|
case INT: l.u.integer = 0; break;
|
||||||
case FLOAT: l.u.flt = 0.0; break;
|
case FLOAT: l.u.flt = 0.0; break;
|
||||||
case STRING: l.u.string = empty; break;
|
case STRING: l.u.string = empty; break;
|
||||||
default: ;
|
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)
|
switch (l.type)
|
||||||
{
|
{
|
||||||
case INT: r.u.integer = 0; break;
|
case INT: r.u.integer = 0; break;
|
||||||
case FLOAT: r.u.flt = 0.0; break;
|
case FLOAT: r.u.flt = 0.0; break;
|
||||||
case STRING: r.u.string = empty; 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==INT && r.type==INT) /* return left int < right int */ /*{{{*/
|
if (l.type == INT) /* handle left operand integer */ /*{{{*/
|
||||||
{
|
{
|
||||||
result.type=INT;
|
if (r.type == INT) /* return left int < right 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.bl = l.u.integer < r.u.integer;
|
||||||
result.u.integer=(strcmp(l.u.string,r.u.string)<0);
|
return result;
|
||||||
}
|
} /*}}}*/
|
||||||
/*}}}*/
|
if (r.type == FLOAT) /* return left int < right float */ /*{{{*/
|
||||||
else if (l.type==FLOAT && r.type==FLOAT) /* return left float < right float */ /*{{{*/
|
|
||||||
{
|
{
|
||||||
result.type=INT;
|
result.u.bl = ((FltT)l.u.integer) < r.u.flt;
|
||||||
result.u.integer=l.u.flt<r.u.flt;
|
return result;
|
||||||
|
} /*}}}*/
|
||||||
|
return relational_type_mismatch(l.type, r.type);
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
else if (l.type==FLOAT && r.type==INT) /* return left float < right float */ /*{{{*/
|
if (l.type == STRING && r.type == STRING) /* return left string < right string */ /*{{{*/
|
||||||
{
|
{
|
||||||
result.type = INT;
|
result.u.bl = (strcmp(l.u.string,r.u.string)<0);
|
||||||
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 result;
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
if (l.type == FLOAT) /* handle left operand float */
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/*}}}*/
|
||||||
|
return relational_type_mismatch(l.type, r.type);
|
||||||
|
}
|
||||||
|
/*}}}*/
|
||||||
|
|
||||||
/* tle -- <= operator */ /*{{{*/
|
/* tle -- <= operator */ /*{{{*/
|
||||||
Token tle(Token l, Token r)
|
Token tle(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
return tor(tlt(l, r), teq(l, r));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
/* tge -- >= operator */ /*{{{*/
|
/* tge -- >= operator */ /*{{{*/
|
||||||
Token tge(Token l, Token r)
|
Token tge(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
return tor(tgt(l, r), teq(l, r));
|
||||||
Token result;
|
}
|
||||||
static char empty[]="";
|
|
||||||
int len;
|
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
if (l.type==EEK) /* return left error argument */ /*{{{*/
|
/* tgt -- < operator */ /*{{{*/
|
||||||
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;
|
|
||||||
}
|
|
||||||
/*}}}*/
|
|
||||||
/* tgt -- <= operator */ /*{{{*/
|
|
||||||
Token tgt(Token l, Token r)
|
Token tgt(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
return tlt(r, l);
|
||||||
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;
|
|
||||||
}
|
|
||||||
/*}}}*/
|
|
||||||
/* teq -- == operator */ /*{{{*/
|
/* teq -- == operator */ /*{{{*/
|
||||||
Token teq(Token l, Token r)
|
Token teq(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
|
||||||
Token result;
|
Token result;
|
||||||
|
result.type = BOOL;
|
||||||
|
result.u.bl = true;
|
||||||
static char empty[]="";
|
static char empty[]="";
|
||||||
int len;
|
|
||||||
/*}}}*/
|
|
||||||
|
|
||||||
if (l.type == EEK) return tcopy(l);
|
if (l.type == EEK) return tcopy(l);
|
||||||
else if (r.type==EEK) return tcopy(r);
|
if (r.type == EEK) return tcopy(r);
|
||||||
if (l.type == EMPTY)
|
if (l.type == EMPTY)
|
||||||
/* try to assign 0 element of r.type */ /*{{{*/
|
/* try to assign 0 element of r.type */ /*{{{*/
|
||||||
{
|
{
|
||||||
l.type=r.type;
|
if (r.type == EMPTY) return result;
|
||||||
switch (r.type)
|
switch (r.type)
|
||||||
{
|
{
|
||||||
case INT: l.u.integer = 0; break;
|
case INT: l.u.integer = 0; break;
|
||||||
case FLOAT: l.u.flt = 0.0; break;
|
case FLOAT: l.u.flt = 0.0; break;
|
||||||
case STRING: l.u.string = empty; break;
|
case STRING: l.u.string = empty; break;
|
||||||
default: ;
|
case BOOL: l.u.bl = false; break;
|
||||||
|
default: return relational_type_mismatch(l.type, r.type);
|
||||||
}
|
}
|
||||||
|
l.type = r.type;
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
if (r.type==EMPTY)
|
if (r.type==EMPTY)
|
||||||
/* try to assign 0 element of l.type */ /*{{{*/
|
/* try to assign 0 element of l.type */ /*{{{*/
|
||||||
{
|
{
|
||||||
r.type=l.type;
|
|
||||||
switch (l.type)
|
switch (l.type)
|
||||||
{
|
{
|
||||||
case INT: r.u.integer=0; break;
|
case INT: r.u.integer=0; break;
|
||||||
case FLOAT: r.u.flt=0.0; break;
|
case FLOAT: r.u.flt=0.0; break;
|
||||||
case STRING: r.u.string=empty; 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.bl = (l.u.flt == ((FltT)r.u.integer));
|
||||||
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 = 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;
|
|
||||||
}
|
|
||||||
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 result;
|
||||||
}
|
}
|
||||||
|
if (l.type == INT && r.type == FLOAT)
|
||||||
|
{
|
||||||
|
result.u.bl = (((FltT)l.u.integer) == r.u.flt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (l.type != r.type)
|
||||||
|
{
|
||||||
|
result.u.bl = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return relational_type_mismatch(l.type, r.type);
|
||||||
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
static bool nearly_equal(FltT a1, FltT a2)
|
static bool nearly_equal(FltT a1, FltT a2)
|
||||||
@ -1160,127 +915,64 @@ static bool nearly_equal(FltT a1, FltT a2)
|
|||||||
/* tabouteq -- ~= operator */ /*{{{*/
|
/* tabouteq -- ~= operator */ /*{{{*/
|
||||||
Token tabouteq(Token l, Token r)
|
Token tabouteq(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
|
||||||
Token result;
|
Token result;
|
||||||
/*}}}*/
|
result.type = BOOL;
|
||||||
|
|
||||||
if (l.type == EEK) return tcopy(l);
|
if (l.type == EEK) return tcopy(l);
|
||||||
if (r.type == EEK) return tcopy(r);
|
if (r.type == EEK) return tcopy(r);
|
||||||
if (l.type==EMPTY)
|
if (l.type == EMPTY && r.type == FLOAT)
|
||||||
/* try to assign 0 element of r.type */ /*{{{*/
|
|
||||||
{
|
{
|
||||||
l.type=r.type;
|
l.type = FLOAT;
|
||||||
switch (r.type)
|
l.u.flt = 0.0;
|
||||||
|
}
|
||||||
|
if (r.type == EMPTY && l.type == FLOAT)
|
||||||
{
|
{
|
||||||
case FLOAT: l.u.flt=0.0; break;
|
r.type = FLOAT;
|
||||||
default: ;
|
r.u.flt = 0.0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*}}}*/
|
|
||||||
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)
|
if (l.type == FLOAT && r.type == FLOAT)
|
||||||
{
|
{
|
||||||
result.type=INT;
|
result.u.bl = nearly_equal(l.u.flt, r.u.flt);
|
||||||
result.u.integer = (int)nearly_equal(l.u.flt, r.u.flt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.type=EEK;
|
|
||||||
result.u.err = strdup(_("Usage: ~= only compares float values"));
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
return duperror(&result, _("Usage: ~= only compares float values"));
|
||||||
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
/* tne -- != operator */ /*{{{*/
|
/* tne -- != operator */ /*{{{*/
|
||||||
Token tne(Token l, Token r)
|
Token tne(Token l, Token r)
|
||||||
{
|
{
|
||||||
/* variables */ /*{{{*/
|
return tneg(teq(l,r));
|
||||||
Token result;
|
}
|
||||||
static char empty[]="";
|
|
||||||
int len;
|
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
if (l.type==EEK) return tcopy(l);
|
/* tor -- logical or of two tokens */ /*{{{*/
|
||||||
if (r.type==EEK) return tcopy(r);
|
Token tor(Token l, Token r)
|
||||||
if (l.type==EMPTY)
|
|
||||||
/* try to assign 0 element of r.type */ /*{{{*/
|
|
||||||
{
|
{
|
||||||
l.type=r.type;
|
switch (l.type) {
|
||||||
switch (r.type)
|
case EEK: return tcopy(l);
|
||||||
{
|
case EMPTY : return tcopy(r);
|
||||||
case INT: l.u.integer=0; break;
|
case BOOL:
|
||||||
case FLOAT: l.u.flt=0.0; break;
|
if (r.type == EMPTY || l.u.bl) return l;
|
||||||
case STRING: l.u.string=empty; break;
|
return r;
|
||||||
default: ;
|
default: break;
|
||||||
}
|
}
|
||||||
|
return operand_type_error("or: ", l.type, r.type);
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
if (r.type==EMPTY)
|
|
||||||
/* try to assign 0 element of l.type */ /*{{{*/
|
/* tand -- logical and of two tokens */ /*{{{*/
|
||||||
|
Token tand(Token l, Token r)
|
||||||
{
|
{
|
||||||
r.type=l.type;
|
switch (l.type) {
|
||||||
switch (l.type)
|
case EEK: return tcopy(l);
|
||||||
{
|
case EMPTY : return l;
|
||||||
case INT: r.u.integer=0; break;
|
case BOOL:
|
||||||
case FLOAT: r.u.flt=0.0; break;
|
if (!l.u.bl) return l;
|
||||||
case STRING: r.u.string=empty; break;
|
return r;
|
||||||
default: ;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
return operand_type_error("and: ", l.type, r.type);
|
||||||
/*}}}*/
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
|
@ -25,5 +25,6 @@ Token tgt(Token l, Token r);
|
|||||||
Token teq(Token l, Token r);
|
Token teq(Token l, Token r);
|
||||||
Token tabouteq(Token l, Token r);
|
Token tabouteq(Token l, Token r);
|
||||||
Token tne(Token l, Token r);
|
Token tne(Token l, Token r);
|
||||||
|
Token tor(Token l, Token r);
|
||||||
|
Token tand(Token l, Token r);
|
||||||
#endif
|
#endif
|
||||||
|
@ -598,6 +598,45 @@ static Token error_func(FunctionIdentifier self, int argc, const Token argv[])
|
|||||||
return result;
|
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 */ /*{{{*/
|
/* string */ /*{{{*/
|
||||||
static Token string_func(FunctionIdentifier self, int argc, const Token argv[])
|
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]); }
|
if (start > 0) { result = tcopy(argv[start-1]); }
|
||||||
for (int i = start; i < argc; ++i) {
|
for (int i = start; i < argc; ++i) {
|
||||||
Token tmp = tbin(result, argv[i]);
|
Token tmp = tbin(result, argv[i]);
|
||||||
tfree(&result);
|
tfree_protected(&result, tmp);
|
||||||
result = tmp;
|
result = tmp;
|
||||||
if (result.type == EEK) return result;
|
if (result.type == EEK) return result;
|
||||||
}
|
}
|
||||||
@ -737,7 +776,7 @@ static void sum_updt(FunctionIdentifier id, Location *loc, Token *tok,
|
|||||||
{
|
{
|
||||||
assert(id == FUNC_SUM);
|
assert(id == FUNC_SUM);
|
||||||
Token tmp = tadd(*tok, *newtok);
|
Token tmp = tadd(*tok, *newtok);
|
||||||
tfree(tok);
|
tfree_protected(tok, tmp);
|
||||||
*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[])
|
static Token int_func(FunctionIdentifier self, int argc, const Token argv[])
|
||||||
{
|
{
|
||||||
assert(self == FUNC_INT);
|
assert(self == FUNC_INT);
|
||||||
/* variables */ /*{{{*/
|
const char *usage = _("Usage: int(string|bool|float[,rounding_function])");
|
||||||
Token result;
|
Token result;
|
||||||
/*}}}*/
|
|
||||||
|
|
||||||
if (argc == 1 && argv[0].type == FLOAT)
|
|
||||||
/* result is integer with cutoff fractional part */ /*{{{*/
|
|
||||||
{
|
|
||||||
result.type = INT;
|
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;
|
result.u.integer = (IntT)argv[0].u.flt;
|
||||||
return result;
|
return result;
|
||||||
}
|
} else { /* result is integer with given conversion */ /*{{{*/
|
||||||
/*}}}*/
|
|
||||||
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)
|
switch (argv[1].u.fident)
|
||||||
{
|
{
|
||||||
case FUNC_TRUNC: result.u.integer = (IntT)argv[0].u.flt; break;
|
case FUNC_TRUNC:
|
||||||
case FUNC_ROUND: result.u.integer = ROUNDFLTINT(argv[0].u.flt); break;
|
result.u.integer = (IntT)argv[0].u.flt; break;
|
||||||
case FUNC_FLOOR: result.u.integer = (IntT)FLOORFLT(argv[0].u.flt); break;
|
case FUNC_ROUND:
|
||||||
case FUNC_CEIL: result.u.integer = (IntT) CEILFLT(argv[0].u.flt); break;
|
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);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
/*}}}*/
|
case STRING:
|
||||||
if (argc == 1 && argv[0].type == STRING)
|
|
||||||
/* result is integer */ /*{{{*/
|
|
||||||
{
|
{
|
||||||
char *s;
|
char *s;
|
||||||
|
if (argc > 1) return duperror(&result, usage);
|
||||||
errno=0;
|
errno=0;
|
||||||
result.u.integer = STRTOINT(argv[0].u.string, &s, 10);
|
result.u.integer = STRTOINT(argv[0].u.string, &s, 10);
|
||||||
if (s==(char*)0 || *s) duperror(&result, _("int(string): invalid string"));
|
if (s==(char*)0 || *s)
|
||||||
else if (errno==ERANGE &&
|
return duperror(&result, _("int(string): invalid string"));
|
||||||
|
if (errno==ERANGE &&
|
||||||
(result.u.integer==LONG_MAX || result.u.integer==LONG_MIN))
|
(result.u.integer==LONG_MAX || result.u.integer==LONG_MIN))
|
||||||
duperror(&result, _("int(string): domain error"));
|
return duperror(&result, _("int(string): domain error"));
|
||||||
else result.type = INT;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
/*}}}*/
|
case BOOL:
|
||||||
return duperror(&result, _("Usage: int(string|float[,rounding_function])"));
|
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;
|
||||||
|
}
|
||||||
|
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)
|
for (size_t i = 2; i < argc; ++i)
|
||||||
{
|
{
|
||||||
tmp = tmul(result,argv[0]);
|
tmp = tmul(result,argv[0]);
|
||||||
tfree(&result);
|
tfree_protected(&result, tmp);
|
||||||
result = tmp;
|
result = tmp;
|
||||||
tmp = tadd(result,argv[i]);
|
tmp = tadd(result,argv[i]);
|
||||||
tfree(&result);
|
tfree_protected(&result, tmp);
|
||||||
result = tmp;
|
result = tmp;
|
||||||
if (result.type == EEK) return result;
|
if (result.type == EEK) return result;
|
||||||
}
|
}
|
||||||
@ -1386,6 +1427,12 @@ Tfunc tfunc[]=
|
|||||||
[FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 },
|
[FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 },
|
||||||
[FUNC_EVAL] = { "eval", eval_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 */
|
/* Type conversion functions */
|
||||||
[FUNC_FLOAT] = { "float", float_func, PREFIX_FUNC, FUNCT, 0 },
|
[FUNC_FLOAT] = { "float", float_func, PREFIX_FUNC, FUNCT, 0 },
|
||||||
[FUNC_INT] = { "int", int_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_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_R, FUNC_D, FUNC_CAP_X, FUNC_X_AMPERSAND, FUNC_NEGATE, FUNC_PLUS_SYMBOL,
|
||||||
FUNC_MINUS_SYMBOL, FUNC_ASTERISK, FUNC_SLASH,
|
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_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_PER_CENT, FUNC_CONCAT, FUNC_TAU, FUNC_SQRT, FUNC_FLOOR, FUNC_CEIL,
|
||||||
FUNC_TRUNC, FUNC_ROUND, FUNC_DECIMAL, FUNC_SCIENTIFIC, FUNC_COMPACT,
|
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_HEXACT, FUNC_RIGHT, FUNC_LEFT, FUNC_DOWN, FUNC_UP, FUNC_BELOW, FUNC_ABOVE,
|
||||||
|
FUNC_TRUE, FUNC_FALSE, FUNC_AND, FUNC_OR,
|
||||||
|
|
||||||
N_FUNCTION_IDS
|
N_FUNCTION_IDS
|
||||||
} FunctionIdentifier;
|
} 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 */
|
/* Forward declaration of Token, since this header is used in scanner.h */
|
||||||
typedef struct Token_struc Token;
|
typedef struct Token_struc Token;
|
||||||
|
|
||||||
typedef enum /* In increasing order of precedence */
|
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
|
PREFIX_NEG, INFIX_POW, PREFIX_FUNC
|
||||||
} FunctionPrecedence;
|
} FunctionPrecedence;
|
||||||
|
|
||||||
|
@ -32,9 +32,14 @@ extern char *strdup(const char* s);
|
|||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
/* prototypes */ /*{{{*/
|
/* 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
|
/* full_eval_funcall -- evaluate the args of a funcall token and then
|
||||||
call the function on them
|
call the function on them
|
||||||
*/
|
*/
|
||||||
@ -43,16 +48,23 @@ static Token full_eval_funcall(Token *t)
|
|||||||
assert(t->type == FUNCALL);
|
assert(t->type == FUNCALL);
|
||||||
if (t->u.funcall.argc < 1)
|
if (t->u.funcall.argc < 1)
|
||||||
return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0);
|
return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0);
|
||||||
Token *eval_argv = malloc(t->u.funcall.argc*sizeof(Token));
|
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) {
|
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) {
|
||||||
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
|
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv);
|
Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv);
|
||||||
|
if (tfunc[t->u.funcall.fident].eval_as == FUNCT) {
|
||||||
/* To allow a function to return one of its arguments, we need
|
/* To allow a function to return one of its arguments, we need
|
||||||
to be sure not to free that argument: */
|
to be sure not to free that argument: */
|
||||||
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
|
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
|
||||||
tfree_protected(&eval_argv[ai], result);
|
tfree_protected(&eval_argv[ai], result);
|
||||||
free(eval_argv);
|
free(eval_argv);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,8 +297,8 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
|
|||||||
if (meth == FULL)
|
if (meth == FULL)
|
||||||
{
|
{
|
||||||
Token result = tpow(l,r);
|
Token result = tpow(l,r);
|
||||||
tfree(&l);
|
tfree_protected(&l, result);
|
||||||
tfree(&r);
|
tfree_protected(&r, result);
|
||||||
if (result.type == EEK) return result;
|
if (result.type == EEK) return result;
|
||||||
l = result;
|
l = result;
|
||||||
} else {
|
} else {
|
||||||
@ -347,8 +359,8 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
|
|||||||
case MOD: result = tmod(l,r); break;
|
case MOD: result = tmod(l,r); break;
|
||||||
default: assert(0);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
tfree(&l);
|
tfree_protected(&l, result);
|
||||||
tfree(&r);
|
tfree_protected(&r, result);
|
||||||
if (result.type == EEK) return result;
|
if (result.type == EEK) return result;
|
||||||
l = result;
|
l = result;
|
||||||
} else {
|
} else {
|
||||||
@ -389,26 +401,24 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
|
|||||||
/* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/
|
/* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/
|
||||||
static Token factor(Token *n[], int *i, EvalMethod meth)
|
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;
|
FunctionIdentifier plusident = FUNC_PLUS_SYMBOL;
|
||||||
Token l;
|
|
||||||
Operator op = CP;
|
Operator op = CP;
|
||||||
bool first_funcall = true;
|
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;
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
|
||||||
|
|
||||||
while (op == PLUS || op == MINUS)
|
while (op == PLUS || op == MINUS)
|
||||||
{
|
{
|
||||||
Token r;
|
|
||||||
|
|
||||||
++(*i);
|
++(*i);
|
||||||
r = piterm(n, i, meth);
|
Token r = piterm(n, i, meth);
|
||||||
if (meth == FULL)
|
if (meth == FULL)
|
||||||
{
|
{
|
||||||
Token result = (op==PLUS ? tadd(l,r) : tsub(l,r));
|
Token result = (op==PLUS ? tadd(l,r) : tsub(l,r));
|
||||||
tfree(&l);
|
tfree_protected(&l, result);
|
||||||
tfree(&r);
|
tfree_protected(&r, result);
|
||||||
if (result.type == EEK) return result;
|
if (result.type == EEK) return result;
|
||||||
l = result;
|
l = result;
|
||||||
} else {
|
} else {
|
||||||
@ -433,8 +443,7 @@ static Token factor(Token *n[], int *i, EvalMethod meth)
|
|||||||
{
|
{
|
||||||
tfree(&r);
|
tfree(&r);
|
||||||
tfree(&l);
|
tfree(&l);
|
||||||
duperror(&l, _("Exceeded maximum sequence length of +"));
|
return duperror(&l, _("Exceeded maximum sequence length of +"));
|
||||||
return l;
|
|
||||||
}
|
}
|
||||||
l.u.funcall.argv[(l.u.funcall.argc)++] = r;
|
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 */ /*{{{*/
|
/* relterm -- parse and evaluate a relational term */ /*{{{*/
|
||||||
static Token term(Token *n[], int *i, EvalMethod meth)
|
static Token relterm(Token *n[], int *i, EvalMethod meth)
|
||||||
{
|
{
|
||||||
Token l = factor(n, i, meth);
|
Token l = factor(n, i, meth);
|
||||||
if (l.type == EEK) return l;
|
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);
|
++(*i);
|
||||||
r = factor(n, i, meth);
|
Token r = factor(n, i, LITERAL);
|
||||||
if (meth == FULL)
|
if (meth == FULL)
|
||||||
{
|
{
|
||||||
|
if (!firstcomp && result.type == BOOL && !result.u.bl) {
|
||||||
|
tfree(&r);
|
||||||
|
} else {
|
||||||
|
Token tmp = evaltoken(r, FULL);
|
||||||
|
tfree_protected(&r, tmp);
|
||||||
|
r = tmp;
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case LT: result=tlt(l,r); break;
|
case LT: tmp = tlt(l,r); break;
|
||||||
case LE: result=tle(l,r); break;
|
case LE: tmp = tle(l,r); break;
|
||||||
case GE: result=tge(l,r); break;
|
case GE: tmp = tge(l,r); break;
|
||||||
case GT: result=tgt(l,r); break;
|
case GT: tmp = tgt(l,r); break;
|
||||||
case ISEQUAL: result=teq(l,r); break;
|
case ISEQUAL: tmp = teq(l,r); break;
|
||||||
case ABOUTEQ: result=tabouteq(l,r); break;
|
case ABOUTEQ: tmp = tabouteq(l,r); break;
|
||||||
case NE: result=tne(l,r); break;
|
case NE: tmp = tne(l,r); break;
|
||||||
default: assert(0);
|
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);
|
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);
|
tfree(&r);
|
||||||
if (result.type == EEK) return result;
|
else {
|
||||||
l = result;
|
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;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (r.type == EEK)
|
if (r.type == EEK)
|
||||||
{
|
{
|
||||||
tfree(&l);
|
tfree(&l);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
if (first_funcall)
|
||||||
|
{
|
||||||
|
first_funcall = false;
|
||||||
Token tmp = l;
|
Token tmp = l;
|
||||||
l.type = FUNCALL;
|
l.type = FUNCALL;
|
||||||
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
|
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
|
||||||
l.u.funcall.argc = 2;
|
l.u.funcall.argc = 2;
|
||||||
l.u.funcall.argv = malloc(2*sizeof(Token));
|
l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
|
||||||
l.u.funcall.argv[0] = tmp;
|
l.u.funcall.argv[0] = tmp;
|
||||||
l.u.funcall.argv[1] = r;
|
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;
|
return l;
|
||||||
}
|
}
|
||||||
@ -518,8 +637,8 @@ Token eval(Token **n, EvalMethod meth)
|
|||||||
if (meth == FULL)
|
if (meth == FULL)
|
||||||
{
|
{
|
||||||
Token result = tconcat(l,r);
|
Token result = tconcat(l,r);
|
||||||
tfree(&l);
|
tfree_protected(&l, result);
|
||||||
tfree(&r);
|
tfree_protected(&r, result);
|
||||||
if (result.type == EEK) return result;
|
if (result.type == EEK) return result;
|
||||||
l = result;
|
l = result;
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,7 +28,8 @@ extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */
|
|||||||
const char *Type_Name[] =
|
const char *Type_Name[] =
|
||||||
{ [EMPTY] = "EMPTY", [STRING] = "STRING", [FLOAT] = "FLOAT", [INT] = "INT",
|
{ [EMPTY] = "EMPTY", [STRING] = "STRING", [FLOAT] = "FLOAT", [INT] = "INT",
|
||||||
[OPERATOR] = "OPERATOR", [LIDENT] = "LABEL", [FIDENT] = "FUNCTION",
|
[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[] =
|
const char *Op_Name[] =
|
||||||
@ -36,7 +37,7 @@ const char *Op_Name[] =
|
|||||||
[OP] = "(", [CP] = ")", [COMMA] = ",",
|
[OP] = "(", [CP] = ")", [COMMA] = ",",
|
||||||
[LT] = "<", [LE] = "<=", [GE] = ">=", [GT] = ">",
|
[LT] = "<", [LE] = "<=", [GE] = ">=", [GT] = ">",
|
||||||
[ISEQUAL] = "==", [ABOUTEQ] = "~=", [NE] = "!=",
|
[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 */
|
/* 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)
|
void cleartoken(Token* tok)
|
||||||
{
|
{
|
||||||
tok->type = EMPTY;
|
tok->type = EMPTY;
|
||||||
|
tok->u.flt = 0.0;
|
||||||
tok->u.location[0] = 0;
|
tok->u.location[0] = 0;
|
||||||
tok->u.location[1] = 0;
|
tok->u.location[1] = 0;
|
||||||
tok->u.location[2] = 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 */
|
/* duperror - Sets tok to an error and strdups the message into place */
|
||||||
Token duperror(Token* tok, const char* erro)
|
Token duperror(Token* tok, const char* erro)
|
||||||
{
|
{
|
||||||
@ -192,7 +222,17 @@ static Token *ident(const char **s)
|
|||||||
begin=*s; ++(*s);
|
begin=*s; ++(*s);
|
||||||
while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s);
|
while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s);
|
||||||
result = malloc(sizeof(Token));
|
result = malloc(sizeof(Token));
|
||||||
if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION)
|
if (*s-begin == 3 && strncmp(begin, "and", 3) == 0)
|
||||||
|
{
|
||||||
|
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->type = LIDENT;
|
||||||
result->u.lident=malloc((size_t)(*s-begin+1));
|
result->u.lident=malloc((size_t)(*s-begin+1));
|
||||||
@ -206,7 +246,7 @@ static Token *ident(const char **s)
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return (Token*)0;
|
return NULLTOKEN;
|
||||||
}
|
}
|
||||||
/*}}}*/
|
/*}}}*/
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ extern "C" {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
EMPTY
|
EMPTY
|
||||||
#ifndef __cplusplus
|
#ifndef __cplusplus
|
||||||
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK, FUNCALL
|
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK,
|
||||||
|
FUNCALL, BOOL
|
||||||
#endif
|
#endif
|
||||||
} Type;
|
} Type;
|
||||||
|
|
||||||
@ -25,13 +26,15 @@ extern const char *Type_Name[];
|
|||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
PLUS, MINUS, MUL, DIV, OP, CP, COMMA,
|
PLUS, MINUS, MUL, DIV, OP, CP, COMMA,
|
||||||
LT, /* MUST be the first relational operation for parsing to work */
|
|
||||||
LE, GE, GT, ISEQUAL, ABOUTEQ,
|
LT, /* Take care with the macros just below */
|
||||||
NE, /* MUST be the last relational operation for parsing to work */
|
LE, GE, GT, ISEQUAL, ABOUTEQ, NE,
|
||||||
POW, MOD
|
|
||||||
|
POW, MOD, LAND, LOR
|
||||||
} Operator;
|
} Operator;
|
||||||
|
|
||||||
#define MAX_OP_NAME_LENGTH 3
|
#define MAX_OP_NAME_LENGTH 3
|
||||||
|
#define IS_RELATION_OP(op) (((op)>= LT) && ((op)<=NE))
|
||||||
extern const char *Op_Name[];
|
extern const char *Op_Name[];
|
||||||
|
|
||||||
typedef int Location[3]; /* NOTE: Locations are passed by REFERENCE not value */
|
typedef int Location[3]; /* NOTE: Locations are passed by REFERENCE not value */
|
||||||
@ -159,6 +162,7 @@ typedef struct Token_struc
|
|||||||
Location location;
|
Location location;
|
||||||
FunctionCall funcall;
|
FunctionCall funcall;
|
||||||
char *err;
|
char *err;
|
||||||
|
bool bl;
|
||||||
} u;
|
} u;
|
||||||
} Token;
|
} Token;
|
||||||
|
|
||||||
@ -166,6 +170,7 @@ typedef struct Token_struc
|
|||||||
#define EMPTY_TVEC ((Token**)0)
|
#define EMPTY_TVEC ((Token**)0)
|
||||||
#define TOKISNUM(t) ((t).type == INT || (t).type == FLOAT || (t).type == EMPTY)
|
#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 duperror(Token* tok, const char* erro);
|
||||||
Token **scan(const char **s);
|
Token **scan(const char **s);
|
||||||
void cleartoken(Token* tok);
|
void cleartoken(Token* tok);
|
||||||
|
Loading…
Reference in New Issue
Block a user