mirror of
https://github.com/stedolan/jq.git
synced 2024-05-11 05:55:39 +00:00
Fix #814: raise on div-0, add inf isinf nan isnan
This commit is contained in:
45
builtin.c
45
builtin.c
@@ -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);
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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},
|
||||
|
@@ -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"
|
||||
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
@@ -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`"
|
||||
@@ -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: |
|
||||
|
||||
|
4
parser.h
4
parser.h
@@ -40,7 +40,7 @@
|
||||
extern int yydebug;
|
||||
#endif
|
||||
/* "%code requires" blocks. */
|
||||
#line 10 "parser.y" /* yacc.c:1909 */
|
||||
#line 11 "parser.y" /* yacc.c:1909 */
|
||||
|
||||
#include "locfile.h"
|
||||
struct lexer_param;
|
||||
@@ -156,7 +156,7 @@ struct lexer_param;
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 30 "parser.y" /* yacc.c:1909 */
|
||||
#line 31 "parser.y" /* yacc.c:1909 */
|
||||
|
||||
jv literal;
|
||||
block blk;
|
||||
|
7
parser.y
7
parser.y
@@ -1,7 +1,8 @@
|
||||
%{
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "compile.h"
|
||||
#include "jv_alloc.h"
|
||||
#define YYMALLOC jv_mem_alloc
|
||||
@@ -423,10 +424,14 @@ Exp "*=" Exp {
|
||||
|
||||
Exp '/' Exp {
|
||||
$$ = gen_binop($1, $3, '/');
|
||||
if (block_is_const_inf($$))
|
||||
FAIL(@$, "Division by zero?");
|
||||
} |
|
||||
|
||||
Exp '%' Exp {
|
||||
$$ = gen_binop($1, $3, '%');
|
||||
if (block_is_const_inf($$))
|
||||
FAIL(@$, "Remainder by zero?");
|
||||
} |
|
||||
|
||||
Exp "/=" Exp {
|
||||
|
@@ -1236,3 +1236,15 @@ try join(",") catch .
|
||||
null
|
||||
{"if":0,"and":1,"or":2,"then":3,"else":4,"elif":5,"end":6,"as":7,"def":8,"reduce":9,"foreach":10,"try":11,"catch":12,"label":13,"import":14,"module":15}
|
||||
|
||||
try (1/.) catch .
|
||||
0
|
||||
"number (1) and number (0) cannot be divided because the divisor is zero"
|
||||
|
||||
try (1%.) catch .
|
||||
0
|
||||
"number (1) and number (0) cannot be divided (remainder) because the divisor is zero"
|
||||
|
||||
%%FAIL
|
||||
1/0
|
||||
jq: error: Division by zero? at <top-level>, line 1:
|
||||
|
||||
|
Reference in New Issue
Block a user