summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-09-07 15:50:52 +0200
committerJo-Philipp Wich <jo@mein.io>2020-09-07 16:30:34 +0200
commita35469d34be384d3a5ae7b852acb6d980f6b702e (patch)
treebf94fae7176da654944e765ed5ae1ecc1b2a0fee
parentdcf0d1285d12684a5907fecb8aadca7bc2e5f44b (diff)
ast, eval, lexer: keep track of overflows when parsing numbers
This allows number literals that exceed the range INT64_MIN..INT64_MAX to be truncated to the respective min and max values in a defined manner. It also makes it possible to have the expression `{{ -9223372036854775808 }}` actually result in `-9223372036854775808`. Since negation and number declaration are separate operations, the value would be first truncated to `9223372036854775807` and then negated, making it impossible to write a literal INT64_MIN value without tracking the overflow. Also fix the number parsing logic to not trucate intergers to 32bit. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--ast.h1
-rw-r--r--eval.c4
-rw-r--r--lexer.c15
3 files changed, 17 insertions, 3 deletions
diff --git a/ast.h b/ast.h
index b3dfa3b..b45ee41 100644
--- a/ast.h
+++ b/ast.h
@@ -54,6 +54,7 @@ struct ut_op {
uint16_t type;
uint16_t is_first:1;
uint16_t is_op:1;
+ uint16_t is_overflow:1;
uint32_t off;
struct json_object *val;
union {
diff --git a/eval.c b/eval.c
index ef80886..7e45f52 100644
--- a/eval.c
+++ b/eval.c
@@ -995,6 +995,7 @@ static struct json_object *
ut_execute_unary_plus_minus(struct ut_state *state, uint32_t off)
{
struct ut_op *op = ut_get_op(state, off);
+ struct ut_op *op1 = ut_get_child(state, off, 0);
struct json_object *val = ut_execute_op(state, op ? op->tree.operand[0] : 0);
enum json_type t;
int64_t n;
@@ -1006,6 +1007,9 @@ ut_execute_unary_plus_minus(struct ut_state *state, uint32_t off)
switch (t) {
case json_type_int:
+ if (op1->is_overflow)
+ return json_object_new_int64(((n >= 0) == (op->type == T_SUB)) ? INT64_MIN : INT64_MAX);
+
return json_object_new_int64((op->type == T_SUB) ? -n : n);
default:
diff --git a/lexer.c b/lexer.c
index dbac029..0220421 100644
--- a/lexer.c
+++ b/lexer.c
@@ -22,6 +22,7 @@
#include <ctype.h>
#include <regex.h>
#include <math.h>
+#include <errno.h>
#include "ast.h"
#include "lexer.h"
@@ -618,9 +619,9 @@ parse_label(const char *buf, struct ut_op *op, struct ut_state *s)
static int
parse_number(const char *buf, struct ut_op *op, struct ut_state *s)
{
+ long long int n;
double d;
char *e;
- int n;
if (!strncmp(buf, "Infinity", 8)) {
op->type = T_DOUBLE;
@@ -635,7 +636,7 @@ parse_number(const char *buf, struct ut_op *op, struct ut_state *s)
return 3;
}
- n = strtol(buf, &e, 0);
+ n = strtoll(buf, &e, 0);
if (e > buf) {
if (*e == '.') {
@@ -649,8 +650,10 @@ parse_number(const char *buf, struct ut_op *op, struct ut_state *s)
}
}
+
op->type = T_NUMBER;
op->val = json_object_new_int64(n);
+ op->is_overflow = (errno == ERANGE);
return (e - buf);
}
@@ -711,6 +714,7 @@ ut_get_token(struct ut_state *s, const char *input, int *mlen)
{
struct ut_op op = { 0 };
const char *o, *p;
+ uint32_t rv;
for (o = p = input; *p; p++) {
if (s->blocktype == UT_BLOCK_NONE) {
@@ -805,7 +809,12 @@ ut_get_token(struct ut_state *s, const char *input, int *mlen)
if (op.type == T_LSTM || op.type == T_RSTM || op.type == 0)
return 0;
- return ut_new_op(s, op.type, op.val, UINT32_MAX);
+ rv = ut_new_op(s, op.type, op.val, UINT32_MAX);
+
+ if (rv)
+ s->pool[rv - 1].is_overflow = op.is_overflow;
+
+ return rv;
}
}