Constant fold all kinds of constants
This patch exports all the binary operator builtins functions from
builtin.c and uses them for constant folding in the parser, allowing
constant folding to work will all kinds and combinations of constants.
Now string*number, $ARGS+$ARGS, string/string, etc will also be
constant folded and the implementation of constant folded operators and
runtime operators will be the same.
And thanks to the new ERRORK bytecode operation, errors are constant
folded too! (e.g. 1 / 0 [] * {} etc)
This commit is contained in:
1 parent
5fc5453409
commit
3b6e7ddd72
5 files changed
+768
-821
No files matched your search
+28
-35
@@ -45,6 +45,15 @@ void *alloca (size_t);
|
|||||||
#include "jv_alloc.h"
|
#include "jv_alloc.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define BINOP(name) \
|
||||||
|
static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \
|
||||||
|
jv_free(input); \
|
||||||
|
return binop_ ## name(a, b); \
|
||||||
|
}
|
||||||
|
BINOPS
|
||||||
|
#undef BINOP
|
||||||
|
|
||||||
|
|
||||||
static jv type_error(jv bad, const char* msg) {
|
static jv type_error(jv bad, const char* msg) {
|
||||||
char errbuf[15];
|
char errbuf[15];
|
||||||
jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s",
|
jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s",
|
||||||
@@ -79,8 +88,7 @@ static inline jv ret_error2(jv bad1, jv bad2, jv msg) {
|
|||||||
return jv_invalid_with_msg(msg);
|
return jv_invalid_with_msg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_plus(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_plus(jv a, jv b) {
|
||||||
jv_free(input);
|
|
||||||
if (jv_get_kind(a) == JV_KIND_NULL) {
|
if (jv_get_kind(a) == JV_KIND_NULL) {
|
||||||
jv_free(a);
|
jv_free(a);
|
||||||
return b;
|
return b;
|
||||||
@@ -312,8 +320,7 @@ static jv f_rtrimstr(jq_state *jq, jv input, jv right) {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_minus(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_minus(jv a, jv b) {
|
||||||
jv_free(input);
|
|
||||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||||
jv r = jv_number(jv_number_value(a) - jv_number_value(b));
|
jv r = jv_number(jv_number_value(a) - jv_number_value(b));
|
||||||
jv_free(a);
|
jv_free(a);
|
||||||
@@ -341,10 +348,9 @@ static jv f_minus(jq_state *jq, jv input, jv a, jv b) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_multiply(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_multiply(jv a, jv b) {
|
||||||
jv_kind ak = jv_get_kind(a);
|
jv_kind ak = jv_get_kind(a);
|
||||||
jv_kind bk = jv_get_kind(b);
|
jv_kind bk = jv_get_kind(b);
|
||||||
jv_free(input);
|
|
||||||
if (ak == JV_KIND_NUMBER && bk == JV_KIND_NUMBER) {
|
if (ak == JV_KIND_NUMBER && bk == JV_KIND_NUMBER) {
|
||||||
jv r = jv_number(jv_number_value(a) * jv_number_value(b));
|
jv r = jv_number(jv_number_value(a) * jv_number_value(b));
|
||||||
jv_free(a);
|
jv_free(a);
|
||||||
@@ -380,8 +386,7 @@ 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 binop_divide(jv a, jv b) {
|
||||||
jv_free(input);
|
|
||||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||||
if (jv_number_value(b) == 0.0)
|
if (jv_number_value(b) == 0.0)
|
||||||
return type_error2(a, b, "cannot be divided because the divisor is zero");
|
return type_error2(a, b, "cannot be divided because the divisor is zero");
|
||||||
@@ -397,8 +402,7 @@ static jv f_divide(jq_state *jq, jv input, jv a, jv b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define dtoi(n) ((n) < INTMAX_MIN ? INTMAX_MIN : -(n) < INTMAX_MIN ? INTMAX_MAX : (intmax_t)(n))
|
#define dtoi(n) ((n) < INTMAX_MIN ? INTMAX_MIN : -(n) < INTMAX_MIN ? INTMAX_MAX : (intmax_t)(n))
|
||||||
static jv f_mod(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_mod(jv a, jv b) {
|
||||||
jv_free(input);
|
|
||||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||||
double na = jv_number_value(a);
|
double na = jv_number_value(a);
|
||||||
double nb = jv_number_value(b);
|
double nb = jv_number_value(b);
|
||||||
@@ -421,13 +425,11 @@ static jv f_mod(jq_state *jq, jv input, jv a, jv b) {
|
|||||||
}
|
}
|
||||||
#undef dtoi
|
#undef dtoi
|
||||||
|
|
||||||
static jv f_equal(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_equal(jv a, jv b) {
|
||||||
jv_free(input);
|
|
||||||
return jv_bool(jv_equal(a, b));
|
return jv_bool(jv_equal(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_notequal(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_notequal(jv a, jv b) {
|
||||||
jv_free(input);
|
|
||||||
return jv_bool(!jv_equal(a, b));
|
return jv_bool(!jv_equal(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,8 +440,7 @@ enum cmp_op {
|
|||||||
CMP_OP_GREATEREQ
|
CMP_OP_GREATEREQ
|
||||||
};
|
};
|
||||||
|
|
||||||
static jv order_cmp(jv input, jv a, jv b, enum cmp_op op) {
|
static jv order_cmp(jv a, jv b, enum cmp_op op) {
|
||||||
jv_free(input);
|
|
||||||
int r = jv_cmp(a, b);
|
int r = jv_cmp(a, b);
|
||||||
return jv_bool((op == CMP_OP_LESS && r < 0) ||
|
return jv_bool((op == CMP_OP_LESS && r < 0) ||
|
||||||
(op == CMP_OP_LESSEQ && r <= 0) ||
|
(op == CMP_OP_LESSEQ && r <= 0) ||
|
||||||
@@ -447,20 +448,20 @@ static jv order_cmp(jv input, jv a, jv b, enum cmp_op op) {
|
|||||||
(op == CMP_OP_GREATER && r > 0));
|
(op == CMP_OP_GREATER && r > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_less(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_less(jv a, jv b) {
|
||||||
return order_cmp(input, a, b, CMP_OP_LESS);
|
return order_cmp(a, b, CMP_OP_LESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_greater(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_greater(jv a, jv b) {
|
||||||
return order_cmp(input, a, b, CMP_OP_GREATER);
|
return order_cmp(a, b, CMP_OP_GREATER);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_lesseq(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_lesseq(jv a, jv b) {
|
||||||
return order_cmp(input, a, b, CMP_OP_LESSEQ);
|
return order_cmp(a, b, CMP_OP_LESSEQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_greatereq(jq_state *jq, jv input, jv a, jv b) {
|
jv binop_greatereq(jv a, jv b) {
|
||||||
return order_cmp(input, a, b, CMP_OP_GREATEREQ);
|
return order_cmp(a, b, CMP_OP_GREATEREQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv f_contains(jq_state *jq, jv a, jv b) {
|
static jv f_contains(jq_state *jq, jv a, jv b) {
|
||||||
@@ -1735,12 +1736,10 @@ static const struct cfunction function_list[] = {
|
|||||||
#ifdef HAVE_LGAMMA_R
|
#ifdef HAVE_LGAMMA_R
|
||||||
{f_lgamma_r,"lgamma_r", 1},
|
{f_lgamma_r,"lgamma_r", 1},
|
||||||
#endif
|
#endif
|
||||||
{f_plus, "_plus", 3},
|
|
||||||
{f_negate, "_negate", 1},
|
{f_negate, "_negate", 1},
|
||||||
{f_minus, "_minus", 3},
|
#define BINOP(name) {f_ ## name, "_" #name, 3},
|
||||||
{f_multiply, "_multiply", 3},
|
BINOPS
|
||||||
{f_divide, "_divide", 3},
|
#undef BINOP
|
||||||
{f_mod, "_mod", 3},
|
|
||||||
{f_dump, "tojson", 1},
|
{f_dump, "tojson", 1},
|
||||||
{f_json_parse, "fromjson", 1},
|
{f_json_parse, "fromjson", 1},
|
||||||
{f_tonumber, "tonumber", 1},
|
{f_tonumber, "tonumber", 1},
|
||||||
@@ -1759,12 +1758,6 @@ static const struct cfunction function_list[] = {
|
|||||||
{f_getpath, "getpath", 2},
|
{f_getpath, "getpath", 2},
|
||||||
{f_delpaths, "delpaths", 2},
|
{f_delpaths, "delpaths", 2},
|
||||||
{f_has, "has", 2},
|
{f_has, "has", 2},
|
||||||
{f_equal, "_equal", 3},
|
|
||||||
{f_notequal, "_notequal", 3},
|
|
||||||
{f_less, "_less", 3},
|
|
||||||
{f_greater, "_greater", 3},
|
|
||||||
{f_lesseq, "_lesseq", 3},
|
|
||||||
{f_greatereq, "_greatereq", 3},
|
|
||||||
{f_contains, "contains", 2},
|
{f_contains, "contains", 2},
|
||||||
{f_length, "length", 1},
|
{f_length, "length", 1},
|
||||||
{f_utf8bytelength, "utf8bytelength", 1},
|
{f_utf8bytelength, "utf8bytelength", 1},
|
||||||
|
|||||||
@@ -7,4 +7,22 @@
|
|||||||
|
|
||||||
int builtins_bind(jq_state *, block*);
|
int builtins_bind(jq_state *, block*);
|
||||||
|
|
||||||
|
#define BINOPS \
|
||||||
|
BINOP(plus) \
|
||||||
|
BINOP(minus) \
|
||||||
|
BINOP(multiply) \
|
||||||
|
BINOP(divide) \
|
||||||
|
BINOP(mod) \
|
||||||
|
BINOP(equal) \
|
||||||
|
BINOP(notequal) \
|
||||||
|
BINOP(less) \
|
||||||
|
BINOP(lesseq) \
|
||||||
|
BINOP(greater) \
|
||||||
|
BINOP(greatereq) \
|
||||||
|
|
||||||
|
|
||||||
|
#define BINOP(name) jv binop_ ## name(jv, jv);
|
||||||
|
BINOPS
|
||||||
|
#undef BINOP
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Generated
+702
-734
File diff suppressed because it is too large.
Load diff
Generated
+2
-2
@@ -45,7 +45,7 @@
|
|||||||
extern int yydebug;
|
extern int yydebug;
|
||||||
#endif
|
#endif
|
||||||
/* "%code requires" blocks. */
|
/* "%code requires" blocks. */
|
||||||
#line 11 "src/parser.y"
|
#line 12 "src/parser.y"
|
||||||
|
|
||||||
#include "locfile.h"
|
#include "locfile.h"
|
||||||
struct lexer_param;
|
struct lexer_param;
|
||||||
@@ -178,7 +178,7 @@ struct lexer_param;
|
|||||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||||
union YYSTYPE
|
union YYSTYPE
|
||||||
{
|
{
|
||||||
#line 31 "src/parser.y"
|
#line 32 "src/parser.y"
|
||||||
|
|
||||||
jv literal;
|
jv literal;
|
||||||
block blk;
|
block blk;
|
||||||
|
|||||||
+18
-50
@@ -5,6 +5,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
#include "jv_alloc.h"
|
#include "jv_alloc.h"
|
||||||
|
#include "builtin.h"
|
||||||
#define YYMALLOC jv_mem_alloc
|
#define YYMALLOC jv_mem_alloc
|
||||||
#define YYFREE jv_mem_free
|
#define YYFREE jv_mem_free
|
||||||
%}
|
%}
|
||||||
@@ -202,64 +203,31 @@ static block constant_fold(block a, block b, int op) {
|
|||||||
if (!block_is_single(a) || !block_is_const(a) ||
|
if (!block_is_single(a) || !block_is_const(a) ||
|
||||||
!block_is_single(b) || !block_is_const(b))
|
!block_is_single(b) || !block_is_const(b))
|
||||||
return gen_noop();
|
return gen_noop();
|
||||||
if (op == '+') {
|
|
||||||
if (block_const_kind(a) == JV_KIND_NULL) {
|
jv jv_a = block_const(a);
|
||||||
block_free(a);
|
block_free(a);
|
||||||
return b;
|
jv jv_b = block_const(b);
|
||||||
}
|
|
||||||
if (block_const_kind(b) == JV_KIND_NULL) {
|
|
||||||
block_free(b);
|
block_free(b);
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (block_const_kind(a) != block_const_kind(b))
|
|
||||||
return gen_noop();
|
|
||||||
|
|
||||||
jv res = jv_invalid();
|
jv res = jv_invalid();
|
||||||
|
|
||||||
if (block_const_kind(a) == JV_KIND_NUMBER) {
|
|
||||||
jv jv_a = block_const(a);
|
|
||||||
jv jv_b = block_const(b);
|
|
||||||
|
|
||||||
double na = jv_number_value(jv_a);
|
|
||||||
double nb = jv_number_value(jv_b);
|
|
||||||
|
|
||||||
int cmp = jv_cmp(jv_a, jv_b);
|
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case '+': res = jv_number(na + nb); break;
|
case '+': res = binop_plus(jv_a, jv_b); break;
|
||||||
case '-': res = jv_number(na - nb); break;
|
case '-': res = binop_minus(jv_a, jv_b); break;
|
||||||
case '*': res = jv_number(na * nb); break;
|
case '*': res = binop_multiply(jv_a, jv_b); break;
|
||||||
case '/':
|
case '/': res = binop_divide(jv_a, jv_b); break;
|
||||||
if (nb == 0.0) return gen_noop();
|
case '%': res = binop_mod(jv_a, jv_b); break;
|
||||||
res = jv_number(na / nb);
|
case EQ: res = binop_equal(jv_a, jv_b); break;
|
||||||
break;
|
case NEQ: res = binop_notequal(jv_a, jv_b); break;
|
||||||
case '%':
|
case '<': res = binop_less(jv_a, jv_b); break;
|
||||||
#define is_unsafe_to_int_cast(n) (isnan(n) || (n) < INTMAX_MIN || -(n) < INTMAX_MIN)
|
case '>': res = binop_greater(jv_a, jv_b); break;
|
||||||
if (is_unsafe_to_int_cast(na) || is_unsafe_to_int_cast(nb) || (intmax_t)nb == 0) return gen_noop();
|
case LESSEQ: res = binop_lesseq(jv_a, jv_b); break;
|
||||||
#undef is_unsafe_to_int_cast
|
case GREATEREQ: res = binop_greatereq(jv_a, jv_b); break;
|
||||||
res = jv_number((intmax_t)na % (intmax_t)nb);
|
|
||||||
break;
|
|
||||||
case EQ: res = (cmp == 0 ? jv_true() : jv_false()); break;
|
|
||||||
case NEQ: res = (cmp != 0 ? jv_true() : jv_false()); break;
|
|
||||||
case '<': res = (cmp < 0 ? jv_true() : jv_false()); break;
|
|
||||||
case '>': res = (cmp > 0 ? jv_true() : jv_false()); break;
|
|
||||||
case LESSEQ: res = (cmp <= 0 ? jv_true() : jv_false()); break;
|
|
||||||
case GREATEREQ: res = (cmp >= 0 ? jv_true() : jv_false()); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
} else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) {
|
|
||||||
res = jv_string_concat(block_const(a), block_const(b));
|
|
||||||
} else {
|
|
||||||
return gen_noop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jv_get_kind(res) == JV_KIND_INVALID)
|
if (jv_is_valid(res))
|
||||||
return gen_noop();
|
|
||||||
|
|
||||||
block_free(a);
|
|
||||||
block_free(b);
|
|
||||||
return gen_const(res);
|
return gen_const(res);
|
||||||
|
|
||||||
|
return gen_error(jv_invalid_get_msg(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
static block gen_binop(block a, block b, int op) {
|
static block gen_binop(block a, block b, int op) {
|
||||||
|
|||||||
Reference in new issue
Block a user