Change parse-time representation of float literals (which include oversize

integers) to be strings instead of 'double'.  We convert from string form
to internal representation only after type resolution has determined the
correct type for the constant.  This eliminates loss-of-precision worries
and gets rid of the change in behavior seen at 17 digits with the
previous kluge.
This commit is contained in:
Tom Lane 2000-02-21 18:47:12 +00:00
parent ee97d103cc
commit 393f313227
12 changed files with 186 additions and 186 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.107 2000/02/20 21:32:05 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.108 2000/02/21 18:47:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1501,14 +1501,12 @@ _copyValue(Value *from)
newnode->type = from->type;
switch (from->type)
{
case T_String:
newnode->val.str = pstrdup(from->val.str);
break;
case T_Integer:
newnode->val.ival = from->val.ival;
break;
case T_Float:
newnode->val.dval = from->val.dval;
case T_String:
newnode->val.str = pstrdup(from->val.str);
break;
default:
break;
@ -1722,8 +1720,8 @@ copyObject(void *from)
* VALUE NODES
*/
case T_Integer:
case T_String:
case T_Float:
case T_String:
retval = _copyValue(from);
break;
case T_List:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.62 2000/02/20 21:32:05 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.63 2000/02/21 18:47:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -737,12 +737,11 @@ _equalValue(Value *a, Value *b)
switch (a->type)
{
case T_String:
return strcmp(a->val.str, b->val.str);
case T_Integer:
return a->val.ival == b->val.ival;
case T_Float:
return a->val.dval == b->val.dval;
case T_String:
return strcmp(a->val.str, b->val.str) == 0;
default:
break;
}
@ -870,8 +869,8 @@ equal(void *a, void *b)
retval = _equalEState(a, b);
break;
case T_Integer:
case T_String:
case T_Float:
case T_String:
retval = _equalValue(a, b);
break;
case T_List:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.37 2000/02/20 21:32:05 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.38 2000/02/21 18:47:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1130,7 +1130,8 @@ _freeValue(Value *node)
{
switch (node->type)
{
case T_String:
case T_Float:
case T_String:
pfree(node->val.str);
break;
default:
@ -1345,8 +1346,8 @@ freeObject(void *node)
* VALUE NODES
*/
case T_Integer:
case T_String:
case T_Float:
case T_String:
_freeValue(node);
break;
case T_List:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.29 2000/02/06 03:27:32 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.30 2000/02/21 18:47:00 tgl Exp $
*
* NOTES
* XXX a few of the following functions are duplicated to handle
@ -73,19 +73,23 @@ makeInteger(long i)
/*
* makeFloat
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeFloat(double d)
makeFloat(char *numericStr)
{
Value *v = makeNode(Value);
v->type = T_Float;
v->val.dval = d;
v->val.str = numericStr;
return v;
}
/*
* makeString
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeString(char *str)

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.109 2000/02/20 21:32:05 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.110 2000/02/21 18:47:00 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -1265,16 +1265,19 @@ _outValue(StringInfo str, Value *value)
{
switch (value->type)
{
case T_String:
appendStringInfo(str, " \"");
_outToken(str, value->val.str);
appendStringInfo(str, "\" ");
break;
case T_Integer:
appendStringInfo(str, " %ld ", value->val.ival);
break;
case T_Float:
appendStringInfo(str, " %.17g ", value->val.dval);
/* We assume the value is a valid numeric literal
* and so does not need quoting.
*/
appendStringInfo(str, " %s ", value->val.str);
break;
case T_String:
appendStringInfo(str, " \"");
_outToken(str, value->val.str);
appendStringInfo(str, "\" ");
break;
default:
elog(NOTICE, "_outValue: don't know how to print type %d ",

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.20 2000/01/26 05:56:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.21 2000/02/21 18:47:00 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -18,6 +18,7 @@
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <errno.h>
#include "postgres.h"
@ -193,30 +194,32 @@ static NodeTag
nodeTokenType(char *token, int length)
{
NodeTag retval;
char *numptr;
int numlen;
char *endptr;
/*
* Check if the token is a number (decimal or integer, positive or
* negative)
* Check if the token is a number
*/
if (isdigit(*token) ||
(length >= 2 && *token == '-' && isdigit(token[1])))
numptr = token;
numlen = length;
if (*numptr == '+' || *numptr == '-')
numptr++, numlen--;
if ((numlen > 0 && isdigit(*numptr)) ||
(numlen > 1 && *numptr == '.' && isdigit(numptr[1])))
{
/*
* skip the optional '-' (i.e. negative number)
* Yes. Figure out whether it is integral or float;
* this requires both a syntax check and a range check.
* strtol() can do both for us.
* We know the token will end at a character that strtol will
* stop at, so we do not need to modify the string.
*/
if (*token == '-')
token++, length--;
/*
* See if there is a decimal point
*/
while (length > 0 && *token != '.')
token++, length--;
/*
* if there isn't, token's an int, otherwise it's a float.
*/
retval = (*token != '.') ? T_Integer : T_Float;
errno = 0;
(void) strtol(token, &endptr, 10);
if (endptr != token+length || errno == ERANGE)
return T_Float;
return T_Integer;
}
/*
* these three cases do not need length checks, since lsptok()
@ -317,17 +320,23 @@ nodeRead(bool read_car_only)
make_dotted_pair_cell = true;
}
break;
case T_Float:
/* we know that the token terminates on a char atof will stop at */
this_value = (Node *) makeFloat(atof(token));
case T_Integer:
/* we know that the token terminates on a char atol will stop at */
this_value = (Node *) makeInteger(atol(token));
make_dotted_pair_cell = true;
break;
case T_Integer:
/* we know that the token terminates on a char atoi will stop at */
this_value = (Node *) makeInteger(atoi(token));
make_dotted_pair_cell = true;
case T_Float:
{
char *fval = (char *) palloc(tok_len + 1);
memcpy(fval, token, tok_len);
fval[tok_len] = '\0';
this_value = (Node *) makeFloat(fval);
make_dotted_pair_cell = true;
}
break;
case T_String:
/* need to remove leading and trailing quotes, and backslashes */
this_value = (Node *) makeString(debackslash(token+1, tok_len-2));
make_dotted_pair_cell = true;
break;

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.147 2000/02/20 02:14:58 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.148 2000/02/21 18:47:02 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -78,6 +78,7 @@ static Node *makeRowExpr(char *opr, List *largs, List *rargs);
static void mapTargetColumns(List *source, List *target);
static void param_type_init(Oid *typev, int nargs);
static Node *doNegate(Node *n);
static void doNegateFloat(Value *v);
/* old versions of flex define this as a macro */
#if defined(yywrap)
@ -88,7 +89,6 @@ static Node *doNegate(Node *n);
%union
{
double dval;
int ival;
char chr;
char *str;
@ -352,9 +352,8 @@ static Node *doNegate(Node *n);
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
/* Special keywords, not in the query language - see the "lex" file */
%token <str> IDENT, SCONST, Op
%token <str> IDENT, FCONST, SCONST, Op
%token <ival> ICONST, PARAM
%token <dval> FCONST
/* these are not real. they are here so that they get generated as #define's*/
%token OP
@ -1567,7 +1566,7 @@ FloatOnly: FCONST
| '-' FCONST
{
$$ = makeFloat($2);
$$->val.dval = - $$->val.dval;
doNegateFloat($$);
}
;
@ -1722,16 +1721,11 @@ TriggerFuncArgs: TriggerFuncArg
TriggerFuncArg: ICONST
{
char *s = (char *) palloc (256);
char *s = (char *) palloc(64);
sprintf (s, "%d", $1);
$$ = s;
}
| FCONST
{
char *s = (char *) palloc (256);
sprintf (s, "%g", $1);
$$ = s;
}
| FCONST { $$ = $1; }
| Sconst { $$ = $1; }
| IDENT { $$ = $1; }
;
@ -5183,7 +5177,7 @@ AexprConst: Iconst
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Float;
n->val.val.dval = $1;
n->val.val.str = $1;
$$ = (Node *)n;
}
| Sconst
@ -5621,7 +5615,8 @@ Oid param_type(int t)
* a few cycles throughout the parse and rewrite stages if we collapse
* the minus into the constant sooner rather than later...
*/
static Node *doNegate(Node *n)
static Node *
doNegate(Node *n)
{
if (IsA(n, A_Const))
{
@ -5634,10 +5629,30 @@ static Node *doNegate(Node *n)
}
if (con->val.type == T_Float)
{
con->val.val.dval = -con->val.val.dval;
doNegateFloat(&con->val);
return n;
}
}
return makeA_Expr(OP, "-", NULL, n);
}
static void
doNegateFloat(Value *v)
{
char *oldval = v->val.str;
Assert(IsA(v, Float));
if (*oldval == '+')
oldval++;
if (*oldval == '-')
v->val.str = oldval; /* just strip the '-' */
else
{
char *newval = (char *) palloc(strlen(oldval) + 2);
*newval = '-';
strcpy(newval+1, oldval);
v->val.str = newval;
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.69 2000/02/20 21:32:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.70 2000/02/21 18:47:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -726,23 +726,19 @@ parser_typecast_constant(Value *expr, TypeName *typename)
switch (nodeTag(expr))
{
case T_String:
const_string = DatumGetPointer(expr->val.str);
break;
case T_Integer:
string_palloced = true;
const_string = int4out(expr->val.ival);
break;
case T_Float:
string_palloced = true;
const_string = float8out(&expr->val.dval);
case T_String:
const_string = expr->val.str;
break;
case T_Null:
isNull = true;
break;
default:
elog(ERROR,
"Cannot cast this expression to type '%s'",
elog(ERROR, "Cannot cast this expression to type '%s'",
typename->name);
}

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.64 2000/02/19 04:17:25 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.65 2000/02/21 18:47:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -324,7 +324,7 @@ other .
}
{param} {
yylval.ival = atoi((char*)&yytext[1]);
yylval.ival = atol((char*)&yytext[1]);
return PARAM;
}
@ -332,46 +332,21 @@ other .
char* endptr;
errno = 0;
yylval.ival = strtol((char *)yytext,&endptr,10);
yylval.ival = strtol((char *)yytext, &endptr, 10);
if (*endptr != '\0' || errno == ERANGE)
{
errno = 0;
#if 0
yylval.dval = strtod(((char *)yytext),&endptr);
if (*endptr != '\0' || errno == ERANGE)
elog(ERROR,"Bad integer input '%s'",yytext);
CheckFloat8Val(yylval.dval);
elog(NOTICE,"Integer input '%s' is out of range; promoted to float", yytext);
return FCONST;
#endif
/* integer too large, treat it as a float */
yylval.str = pstrdup((char*)yytext);
return SCONST;
return FCONST;
}
return ICONST;
}
{decimal} {
char* endptr;
if (strlen((char *)yytext) <= 17)
{
errno = 0;
yylval.dval = strtod((char *)yytext,&endptr);
if (*endptr != '\0' || errno == ERANGE)
elog(ERROR,"Bad float input '%s'",yytext);
CheckFloat8Val(yylval.dval);
return FCONST;
}
yylval.str = pstrdup((char*)yytext);
return SCONST;
return FCONST;
}
{real} {
char* endptr;
errno = 0;
yylval.dval = strtod((char *)yytext,&endptr);
if (*endptr != '\0' || errno == ERANGE)
elog(ERROR,"Bad float input '%s'",yytext);
CheckFloat8Val(yylval.dval);
yylval.str = pstrdup((char*)yytext);
return FCONST;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.73 2000/02/17 05:00:38 inoue Exp $
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.74 2000/02/21 18:47:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -453,6 +453,7 @@ BufferAlloc(Relation reln,
*/
Assert(buf->refcount == 0);
buf->refcount = 1;
Assert(PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] == 0);
PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 1;
if (buf->flags & BM_DIRTY)
@ -542,6 +543,7 @@ BufferAlloc(Relation reln,
inProgress = FALSE;
buf->flags &= ~BM_IO_IN_PROGRESS;
TerminateBufferIO(buf);
Assert(PrivateRefCount[BufferDescriptorGetBuffer(buf)-1] == 1);
PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
buf->refcount--;
buf = (BufferDesc *) NULL;
@ -568,6 +570,7 @@ BufferAlloc(Relation reln,
{
TerminateBufferIO(buf);
/* give up the buffer since we don't need it any more */
Assert(PrivateRefCount[BufferDescriptorGetBuffer(buf)-1] == 1);
PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
Assert(buf->refcount > 0);
buf->refcount--;
@ -1469,8 +1472,16 @@ ReleaseRelationBuffers(Relation rel)
if (!(buf->flags & BM_FREE))
{
/* Assert checks that buffer will actually get freed! */
Assert(PrivateRefCount[i - 1] == 1 &&
buf->refcount == 1);
Assert(buf->refcount == 1);
if (PrivateRefCount[i - 1] <= 0)
{
fprintf(stderr, "Nonpositive PrivateRefCount on buffer for %s\n",
RelationGetRelationName(rel));
fflush(stderr);
* ((char *) 0) = 0;
abort();
}
Assert(PrivateRefCount[i - 1] == 1);
/* ReleaseBuffer expects we do not hold the lock at entry */
SpinRelease(BufMgrLock);
holding = false;

View File

@ -3,22 +3,23 @@
* is for IP V4 CIDR notation, but prepared for V6: just
* add the necessary bits where the comments indicate.
*
* $Id: network.c,v 1.16 1999/09/23 17:42:23 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/network.c,v 1.17 2000/02/21 18:47:07 tgl Exp $
*
* Jon Postel RIP 16 Oct 1998
*/
#include "postgres.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "postgres.h"
#include "utils/builtins.h"
static int v4bitncmp(unsigned int a1, unsigned int a2, int bits);
static int v4bitncmp(unsigned long a1, unsigned long a2, int bits);
/*
* Access macros. Add IPV6 support.
@ -39,6 +40,7 @@ static int v4bitncmp(unsigned int a1, unsigned int a2, int bits);
#define ip_v4addr(inetptr) \
(((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr)
/* Common input routine */
static inet *
network_in(char *src, int type)
@ -127,7 +129,8 @@ cidr_out(inet *src)
}
/*
* Boolean tests for magnitude. Add V4/V6 testing!
* Boolean tests for ordering operators --- must agree with sorting
* operator network_cmp().
*/
bool
@ -135,19 +138,7 @@ network_lt(inet *a1, inet *a2)
{
if (!PointerIsValid(a1) || !PointerIsValid(a2))
return FALSE;
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
{
int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
return ((order < 0) || ((order == 0) && (ip_bits(a1) < ip_bits(a2))));
}
else
{
/* Go for an IPV6 address here, before faulting out: */
elog(ERROR, "cannot compare address families %d and %d",
ip_family(a1), ip_family(a2));
return FALSE;
}
return (bool) (network_cmp(a1, a2) < 0);
}
bool
@ -155,7 +146,7 @@ network_le(inet *a1, inet *a2)
{
if (!PointerIsValid(a1) || !PointerIsValid(a2))
return FALSE;
return (network_lt(a1, a2) || network_eq(a1, a2));
return (bool) (network_cmp(a1, a2) <= 0);
}
bool
@ -163,18 +154,7 @@ network_eq(inet *a1, inet *a2)
{
if (!PointerIsValid(a1) || !PointerIsValid(a2))
return FALSE;
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
{
return ((ip_bits(a1) == ip_bits(a2))
&& (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
}
else
{
/* Go for an IPV6 address here, before faulting out: */
elog(ERROR, "cannot compare address families %d and %d",
ip_family(a1), ip_family(a2));
return FALSE;
}
return (bool) (network_cmp(a1, a2) == 0);
}
bool
@ -182,7 +162,7 @@ network_ge(inet *a1, inet *a2)
{
if (!PointerIsValid(a1) || !PointerIsValid(a2))
return FALSE;
return (network_gt(a1, a2) || network_eq(a1, a2));
return (bool) (network_cmp(a1, a2) >= 0);
}
bool
@ -190,19 +170,7 @@ network_gt(inet *a1, inet *a2)
{
if (!PointerIsValid(a1) || !PointerIsValid(a2))
return FALSE;
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
{
int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
return ((order > 0) || ((order == 0) && (ip_bits(a1) > ip_bits(a2))));
}
else
{
/* Go for an IPV6 address here, before faulting out: */
elog(ERROR, "cannot compare address families %d and %d",
ip_family(a1), ip_family(a2));
return FALSE;
}
return (bool) (network_cmp(a1, a2) > 0);
}
bool
@ -210,7 +178,34 @@ network_ne(inet *a1, inet *a2)
{
if (!PointerIsValid(a1) || !PointerIsValid(a2))
return FALSE;
return (!network_eq(a1, a2));
return (bool) (network_cmp(a1, a2) != 0);
}
/*
* Comparison function for sorting. Add V4/V6 testing!
*/
int4
network_cmp(inet *a1, inet *a2)
{
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
{
int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2),
(ip_bits(a1) < ip_bits(a2)) ?
ip_bits(a1) : ip_bits(a2));
if (order)
return order;
/* They agree in the first N bits, so shorter one comes first */
return (int) ip_bits(a1) - (int) ip_bits(a2);
}
else
{
/* Go for an IPV6 address here, before faulting out: */
elog(ERROR, "cannot compare address families %d and %d",
ip_family(a1), ip_family(a2));
return 0;
}
}
bool
@ -293,28 +288,6 @@ network_supeq(inet *a1, inet *a2)
}
}
/*
* Comparison function for sorting. Add V4/V6 testing!
*/
int4
network_cmp(inet *a1, inet *a2)
{
if (ntohl(ip_v4addr(a1)) < ntohl(ip_v4addr(a2)))
return (-1);
if (ntohl(ip_v4addr(a1)) > ntohl(ip_v4addr(a2)))
return (1);
if (ip_bits(a1) < ip_bits(a2))
return (-1);
if (ip_bits(a1) > ip_bits(a2))
return (1);
return 0;
}
text *
network_host(inet *ip)
{
@ -476,7 +449,7 @@ network_netmask(inet *ip)
*/
static int
v4bitncmp(unsigned int a1, unsigned int a2, int bits)
v4bitncmp(unsigned long a1, unsigned long a2, int bits)
{
unsigned long mask = 0;
int i;
@ -485,9 +458,11 @@ v4bitncmp(unsigned int a1, unsigned int a2, int bits)
mask = (mask >> 1) | 0x80000000;
a1 = ntohl(a1);
a2 = ntohl(a2);
if ((a1 & mask) < (a2 & mask))
a1 &= mask;
a2 &= mask;
if (a1 < a2)
return (-1);
else if ((a1 & mask) > (a2 & mask))
else if (a1 > a2)
return (1);
return (0);
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_list.h,v 1.15 2000/02/06 03:27:35 tgl Exp $
* $Id: pg_list.h,v 1.16 2000/02/21 18:47:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,21 @@
/*----------------------
* Value node
*
* The same Value struct is used for three node types: T_Integer,
* T_Float, and T_String. Integral values are actually represented
* by a machine integer, but both floats and strings are represented
* as strings. Using T_Float as the node type simply indicates that
* the contents of the string look like a valid numeric literal.
*
* (Before Postgres 7.0, we used a double to represent T_Float,
* but that creates loss-of-precision problems when the value is
* ultimately destined to be converted to NUMERIC. Since Value nodes
* are only used in the parsing process, not for runtime data, it's
* better to use the more general representation.)
*
* Note that an integer-looking string will get lexed as T_Float if
* the value is too large to fit in a 'long'.
*----------------------
*/
typedef struct Value
@ -30,14 +45,13 @@ typedef struct Value
NodeTag type; /* tag appropriately (eg. T_String) */
union ValUnion
{
long ival; /* machine integer */
char *str; /* string */
long ival;
double dval;
} val;
} Value;
#define intVal(v) (((Value *)(v))->val.ival)
#define floatVal(v) (((Value *)(v))->val.dval)
#define floatVal(v) atof(((Value *)(v))->val.str)
#define strVal(v) (((Value *)(v))->val.str)
@ -89,9 +103,9 @@ extern List *lconsi(int datum, List *list);
extern bool member(void *datum, List *list);
extern bool intMember(int datum, List *list);
extern Value *makeInteger(long i);
extern Value *makeFloat(double d);
extern Value *makeFloat(char *numericStr);
extern Value *makeString(char *str);
extern List *makeList(void *elem,...);
extern List *makeList(void *elem, ...);
extern List *lappend(List *list, void *datum);
extern List *lappendi(List *list, int datum);
extern List *lremove(void *elem, List *list);