diff options
author | Jo-Philipp Wich <jo@mein.io> | 2020-09-07 15:50:52 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2020-09-07 16:30:34 +0200 |
commit | a35469d34be384d3a5ae7b852acb6d980f6b702e (patch) | |
tree | bf94fae7176da654944e765ed5ae1ecc1b2a0fee | |
parent | dcf0d1285d12684a5907fecb8aadca7bc2e5f44b (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.h | 1 | ||||
-rw-r--r-- | eval.c | 4 | ||||
-rw-r--r-- | lexer.c | 15 |
3 files changed, 17 insertions, 3 deletions
@@ -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 { @@ -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: @@ -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; } } |