summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dimension/common.prologue1
-rw-r--r--dimension/common.rules14
-rw-r--r--dimension/parse.c69
-rw-r--r--tests/dimension/Makefile.am2
4 files changed, 68 insertions, 18 deletions
diff --git a/dimension/common.prologue b/dimension/common.prologue
index 8bbeaea..d79d194 100644
--- a/dimension/common.prologue
+++ b/dimension/common.prologue
@@ -22,6 +22,7 @@
#include "parse.h"
#include "tokenize.h"
#include "utility.h"
+#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/dimension/common.rules b/dimension/common.rules
index f4ce31f..b4cf631 100644
--- a/dimension/common.rules
+++ b/dimension/common.rules
@@ -726,16 +726,14 @@ INT: FLOAT {
;
FLOAT_LITERAL: "integer" {
- $$ = dmnsn_new_astnode(DMNSN_AST_INTEGER, @$);
- $$.ptr = dmnsn_malloc(sizeof(long));
- *(long *)$$.ptr = strtol($1, NULL, 0);
- dmnsn_free($1);
+ dmnsn_astnode string = dmnsn_new_astnode(DMNSN_AST_STRING, @$);
+ string.ptr = $1;
+ $$ = dmnsn_new_astnode1(DMNSN_AST_VAL, @$, string);
}
| "float" {
- $$ = dmnsn_new_astnode(DMNSN_AST_FLOAT, @$);
- $$.ptr = dmnsn_malloc(sizeof(double));
- *(double *)$$.ptr = strtod($1, NULL);
- dmnsn_free($1);
+ dmnsn_astnode string = dmnsn_new_astnode(DMNSN_AST_STRING, @$);
+ string.ptr = $1;
+ $$ = dmnsn_new_astnode1(DMNSN_AST_VAL, @$, string);
}
;
diff --git a/dimension/parse.c b/dimension/parse.c
index 5e6b6b1..f75c466 100644
--- a/dimension/parse.c
+++ b/dimension/parse.c
@@ -19,8 +19,11 @@
#include "parse.h"
#include "utility.h"
+#include <errno.h>
#include <fenv.h>
+#include <limits.h>
#include <math.h>
+#include <stdint.h>
#include <stdlib.h>
/*
@@ -698,7 +701,11 @@ dmnsn_eval_unary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
break;
case DMNSN_AST_NEGATE:
- dmnsn_make_ast_integer(&ret, -n);
+ if ((n >= 0 && LONG_MIN + n <= 0) || (n < 0 && LONG_MAX + n >= 0)) {
+ dmnsn_make_ast_integer(&ret, -n);
+ } else {
+ dmnsn_make_ast_float(&ret, -(double)n);
+ }
break;
case DMNSN_AST_NOT:
@@ -706,7 +713,11 @@ dmnsn_eval_unary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
break;
case DMNSN_AST_ABS:
- dmnsn_make_ast_integer(&ret, labs(n));
+ if (n >= 0 || LONG_MAX + n >= 0) {
+ dmnsn_make_ast_integer(&ret, labs(n));
+ } else {
+ dmnsn_make_ast_float(&ret, fabs(n));
+ }
break;
case DMNSN_AST_ACOS:
dmnsn_make_ast_float(&ret, acos(n));
@@ -904,14 +915,21 @@ dmnsn_eval_unary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
break;
case DMNSN_AST_VAL:
{
+ errno = 0;
char *endptr;
long l = strtol(rhs.ptr, &endptr, 0);
- if (*endptr != '\0' || endptr == rhs.ptr) {
+ if (*endptr != '\0' || endptr == rhs.ptr
+ || (l != 0 && errno == ERANGE))
+ {
+ errno = 0;
double d = strtod(rhs.ptr, &endptr);
if (*endptr != '\0' || endptr == rhs.ptr) {
dmnsn_diagnostic(astnode.location, "invalid numeric string '%s'",
(const char *)rhs.ptr);
ret.type = DMNSN_AST_NONE;
+ } else if (d != 0 && errno == ERANGE) {
+ dmnsn_diagnostic(astnode.location, "float value overflowed");
+ ret.type = DMNSN_AST_NONE;
} else {
dmnsn_make_ast_float(&ret, d);
}
@@ -1313,16 +1331,40 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
switch (astnode.type) {
case DMNSN_AST_ADD:
- dmnsn_make_ast_integer(&ret, l + r);
+ if ((r < 0 && LONG_MIN - r <= l) || (r >= 0 && LONG_MAX - r >= l)) {
+ dmnsn_make_ast_integer(&ret, l + r);
+ } else {
+ dmnsn_make_ast_float(&ret, (double)l + r);
+ }
break;
case DMNSN_AST_SUB:
- dmnsn_make_ast_integer(&ret, l - r);
+ if ((r >= 0 && LONG_MIN + r <= l) || (r < 0 && LONG_MAX + r >= l)) {
+ dmnsn_make_ast_integer(&ret, l - r);
+ } else {
+ dmnsn_make_ast_float(&ret, (double)l - r);
+ }
break;
case DMNSN_AST_MUL:
- dmnsn_make_ast_integer(&ret, l*r);
- break;
+ {
+#if (ULONG_MAX > 4294967295)
+ __int128_t prod = l;
+#else
+ int64_t prod = l;
+#endif
+ prod *= r;
+ if (prod >= LONG_MIN && prod <= LONG_MAX) {
+ dmnsn_make_ast_integer(&ret, prod);
+ } else {
+ dmnsn_make_ast_float(&ret, prod);
+ }
+ break;
+ }
case DMNSN_AST_DIV:
- if (r == 0) {
+ if (r == -1
+ && ((l >= 0 && LONG_MIN + l <= 0) || (l < 0 && LONG_MAX + l >= 0)))
+ {
+ dmnsn_make_ast_float(&ret, -(double)l);
+ } else if (r == 0) {
dmnsn_diagnostic(astnode.location, "WARNING: division by zero");
dmnsn_make_ast_float(&ret, (double)l/0.0);
} else if (l%r == 0) {
@@ -1361,7 +1403,11 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
dmnsn_make_ast_float(&ret, atan2(l, r));
break;
case DMNSN_AST_INT_DIV:
- if (r == 0) {
+ if (r == -1
+ && ((l >= 0 && LONG_MIN + l <= 0) || (l < 0 && LONG_MAX + l >= 0)))
+ {
+ dmnsn_make_ast_float(&ret, -(double)l);
+ } else if (r == 0) {
dmnsn_diagnostic(astnode.location, "WARNING: division by zero");
dmnsn_make_ast_float(&ret, (double)l/0.0);
} else {
@@ -1375,7 +1421,10 @@ dmnsn_eval_binary(dmnsn_astnode astnode, dmnsn_symbol_table *symtable)
dmnsn_make_ast_integer(&ret, l < r ? l : r);
break;
case DMNSN_AST_MOD:
- if (r == 0) {
+ if (r == -1) {
+ /* LONG_MIN % -1 is potentially an overflow */
+ dmnsn_make_ast_integer(&ret, 0);
+ } else if (r == 0) {
dmnsn_diagnostic(astnode.location, "WARNING: division by zero");
dmnsn_make_ast_float(&ret, fmod(l, 0.0));
} else {
diff --git a/tests/dimension/Makefile.am b/tests/dimension/Makefile.am
index 61535a0..93c0a4b 100644
--- a/tests/dimension/Makefile.am
+++ b/tests/dimension/Makefile.am
@@ -28,6 +28,7 @@ TESTS = punctuation.sh \
transformations.sh \
invalid-macro.sh \
tbuffer-overlap.sh \
+ integer-overflow.sh \
csg.sh \
demo.sh
TESTS_ENVIRONMENT = top_builddir=$(top_builddir)
@@ -46,6 +47,7 @@ EXTRA_DIST = $(TESTS) \
transformations.pov \
invalid-macro.pov \
tbuffer-overlap.pov \
+ integer-overflow.pov \
csg.pov \
demo.pov