Skip to content

Commit

Permalink
Fix #814: raise on div-0, add inf isinf nan isnan
Browse files Browse the repository at this point in the history
  • Loading branch information
nicowilliams committed Jun 18, 2015
1 parent bdc1feb commit b9c2a32
Show file tree
Hide file tree
Showing 8 changed files with 477 additions and 388 deletions.
45 changes: 41 additions & 4 deletions builtin.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ static jv f_multiply(jq_state *jq, jv input, jv a, jv b) {
static jv f_divide(jq_state *jq, jv input, jv a, jv b) {
jv_free(input);
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
if (jv_number_value(b) == 0.0)
return type_error2(a, b, "cannot be divided because the divisor is zero");
return jv_number(jv_number_value(a) / jv_number_value(b));
} else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) {
return jv_string_split(a, b);
Expand All @@ -227,12 +229,11 @@ static jv f_divide(jq_state *jq, jv input, jv a, jv b) {
static jv f_mod(jq_state *jq, jv input, jv a, jv b) {
jv_free(input);
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
if ((intmax_t)jv_number_value(b) == 0) {
return jv_invalid_with_msg(jv_string("Cannot mod by zero."));
}
if ((intmax_t)jv_number_value(b) == 0)
return type_error2(a, b, "cannot be divided (remainder) because the divisor is zero");
return jv_number((intmax_t)jv_number_value(a) % (intmax_t)jv_number_value(b));
} else {
return type_error2(a, b, "cannot be divided");
return type_error2(a, b, "cannot be divided (remainder)");
}
}

Expand Down Expand Up @@ -831,6 +832,38 @@ static jv f_type(jq_state *jq, jv input) {
return out;
}

static jv f_isinf(jq_state *jq, jv input) {
jv_kind k = jv_get_kind(input);
if (k != JV_KIND_NUMBER) {
jv_free(input);
return jv_false();
}
double n = jv_number_value(input);
jv_free(input);
return isinf(n) ? jv_true() : jv_false();
}

static jv f_isnan(jq_state *jq, jv input) {
jv_kind k = jv_get_kind(input);
if (k != JV_KIND_NUMBER) {
jv_free(input);
return jv_false();
}
double n = jv_number_value(input);
jv_free(input);
return isnan(n) ? jv_true() : jv_false();
}

static jv f_inf(jq_state *jq, jv input) {
jv_free(input);
return jv_number(INFINITY);
}

static jv f_nan(jq_state *jq, jv input) {
jv_free(input);
return jv_number(NAN);
}

static jv f_error(jq_state *jq, jv input, jv msg) {
jv_free(input);
return jv_invalid_with_msg(msg);
Expand Down Expand Up @@ -1183,6 +1216,10 @@ static const struct cfunction function_list[] = {
{(cfunction_ptr)f_contains, "contains", 2},
{(cfunction_ptr)f_length, "length", 1},
{(cfunction_ptr)f_type, "type", 1},
{(cfunction_ptr)f_isinf, "isinf", 1},
{(cfunction_ptr)f_isnan, "isnan", 1},
{(cfunction_ptr)f_inf, "inf", 1},
{(cfunction_ptr)f_nan, "nan", 1},
{(cfunction_ptr)f_sort, "sort", 1},
{(cfunction_ptr)f_sort_by_impl, "_sort_by_impl", 2},
{(cfunction_ptr)f_group_by_impl, "_group_by_impl", 2},
Expand Down
7 changes: 7 additions & 0 deletions compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define _GNU_SOURCE // for strdup
#endif
#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "compile.h"
Expand Down Expand Up @@ -161,6 +162,12 @@ int block_is_const(block b) {
return (block_is_single(b) && b.first->op == LOADK);
}

int block_is_const_inf(block b) {
return (block_is_single(b) && b.first->op == LOADK &&
jv_get_kind(b.first->imm.constant) == JV_KIND_NUMBER &&
isinf(jv_number_value(b.first->imm.constant)));
}

jv_kind block_const_kind(block b) {
assert(block_is_const(b));
return jv_get_kind(b.first->imm.constant);
Expand Down
1 change: 1 addition & 0 deletions compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ block gen_op_simple(opcode op);
block gen_const(jv constant);
block gen_const_global(jv constant, const char *name);
int block_is_const(block b);
int block_is_const_inf(block b);
jv_kind block_const_kind(block b);
jv block_const(block b);
block gen_op_target(opcode op, block target);
Expand Down
24 changes: 23 additions & 1 deletion docs/content/3.manual/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ sections:
that string that many times.
Dividing a string by another splits the first using the second
as separators.
as separators. Division by zero raises an error.
Multiplying two objects will merge them recursively: this works
like addition but if both objects contain a value for the
Expand All @@ -615,6 +615,9 @@ sections:
- program: '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}'
input: 'null'
output: ['{"k": {"a": 0, "b": 2, "c": 3}}']
- program: '.[] | (1 / .)?'
input: '[1,0,-1]'
output: ['1', '-1']


- title: "`length`"
Expand Down Expand Up @@ -1073,6 +1076,25 @@ sections:
input: '[0, false, [], {}, null, "hello"]'
output: ['["number", "boolean", "array", "object", "null", "string"]']

- title: "`inf`, `nan`, `isinf`, `isnan`"
body: |
Some arithmetic operations can yield infinities and "not a
number" (NaN) values. The `isinf` builtin returns `true` if
its input is infinite. The `isnan` builtin returns `true` if
its input is a NaN. The `inf` builtin returns a positive
infinite value. The `nan` builtin returns a NaN.
Note that division by zero raises an error.
examples:
- program: '.[] | (inf * .) < 0'
input: '[-1, 1]'
output: ['true', 'false']
- program: 'inf, nan | type'
input: 'null'
output: ['"number"']

- title: "`sort, sort_by(path_expression)`"
body: |
Expand Down
Loading

0 comments on commit b9c2a32

Please sign in to comment.