2014-06-13 17:51:41 -05:00
# include <assert.h>
# include <limits.h>
2013-06-17 20:50:45 -05:00
# include <math.h>
2014-06-18 19:49:38 -04:00
# include <oniguruma.h>
2013-05-17 03:03:42 +10:00
# include <stdlib.h>
2012-09-18 10:17:38 +01:00
# include <string.h>
2012-08-16 01:00:30 +01:00
# include "builtin.h"
2012-09-17 20:59:34 +01:00
# include "compile.h"
2012-11-28 01:08:23 -06:00
# include "jq_parser.h"
2013-06-21 12:06:28 +01:00
# include "bytecode.h"
2012-09-18 10:17:38 +01:00
# include "locfile.h"
2012-12-27 20:49:34 +00:00
# include "jv_unicode.h"
2012-09-18 10:17:38 +01:00
2012-12-02 22:12:08 +00:00
static jv type_error ( jv bad , const char * msg ) {
jv err = jv_invalid_with_msg ( jv_string_fmt ( " %s %s " ,
jv_kind_name ( jv_get_kind ( bad ) ) ,
msg ) ) ;
jv_free ( bad ) ;
return err ;
}
static jv type_error2 ( jv bad1 , jv bad2 , const char * msg ) {
jv err = jv_invalid_with_msg ( jv_string_fmt ( " %s and %s %s " ,
jv_kind_name ( jv_get_kind ( bad1 ) ) ,
jv_kind_name ( jv_get_kind ( bad2 ) ) ,
msg ) ) ;
jv_free ( bad1 ) ;
jv_free ( bad2 ) ;
return err ;
}
static jv f_plus ( jv input , jv a , jv b ) {
jv_free ( input ) ;
2012-12-29 16:50:58 +00:00
if ( jv_get_kind ( a ) = = JV_KIND_NULL ) {
jv_free ( a ) ;
return b ;
} else if ( jv_get_kind ( b ) = = JV_KIND_NULL ) {
jv_free ( b ) ;
return a ;
} else if ( jv_get_kind ( a ) = = JV_KIND_NUMBER & & jv_get_kind ( b ) = = JV_KIND_NUMBER ) {
2012-12-02 22:12:08 +00:00
return jv_number ( jv_number_value ( a ) +
jv_number_value ( b ) ) ;
2012-09-10 16:16:39 +01:00
} else if ( jv_get_kind ( a ) = = JV_KIND_STRING & & jv_get_kind ( b ) = = JV_KIND_STRING ) {
2012-12-02 22:12:08 +00:00
return jv_string_concat ( a , b ) ;
2012-09-02 16:31:59 +01:00
} else if ( jv_get_kind ( a ) = = JV_KIND_ARRAY & & jv_get_kind ( b ) = = JV_KIND_ARRAY ) {
2012-12-02 22:12:08 +00:00
return jv_array_concat ( a , b ) ;
2012-09-09 19:17:07 +01:00
} else if ( jv_get_kind ( a ) = = JV_KIND_OBJECT & & jv_get_kind ( b ) = = JV_KIND_OBJECT ) {
2012-12-02 22:12:08 +00:00
return jv_object_merge ( a , b ) ;
2012-08-16 01:16:08 +01:00
} else {
2012-12-02 22:12:08 +00:00
return type_error2 ( a , b , " cannot be added " ) ;
2012-08-16 01:16:08 +01:00
}
}
2014-02-21 08:46:56 +00:00
# define LIBM_DD(name) \
static jv f_ # # name ( jv input ) { \
if ( jv_get_kind ( input ) ! = JV_KIND_NUMBER ) { \
return type_error ( input , " number required " ) ; \
} \
jv ret = jv_number ( name ( jv_number_value ( input ) ) ) ; \
jv_free ( input ) ; \
return ret ; \
}
# include "libm.h"
# undef LIBM_DD
2013-09-11 20:22:56 -04:00
2013-01-03 12:53:06 +00:00
static jv f_negate ( jv input ) {
if ( jv_get_kind ( input ) ! = JV_KIND_NUMBER ) {
return type_error ( input , " cannot be negated " ) ;
}
jv ret = jv_number ( - jv_number_value ( input ) ) ;
jv_free ( input ) ;
return ret ;
}
2013-11-28 15:17:32 -06:00
static jv f_startswith ( jv a , jv b ) {
int alen = jv_string_length_bytes ( jv_copy ( a ) ) ;
int blen = jv_string_length_bytes ( jv_copy ( b ) ) ;
jv ret ;
if ( blen < = alen & & memcmp ( jv_string_value ( a ) , jv_string_value ( b ) , blen ) = = 0 )
ret = jv_true ( ) ;
else
ret = jv_false ( ) ;
jv_free ( a ) ;
jv_free ( b ) ;
return ret ;
}
static jv f_endswith ( jv a , jv b ) {
const char * astr = jv_string_value ( a ) ;
const char * bstr = jv_string_value ( b ) ;
size_t alen = jv_string_length_bytes ( jv_copy ( a ) ) ;
size_t blen = jv_string_length_bytes ( jv_copy ( b ) ) ;
jv ret ; ;
if ( alen < blen | |
memcmp ( astr + ( alen - blen ) , bstr , blen ) ! = 0 )
ret = jv_false ( ) ;
else
ret = jv_true ( ) ;
jv_free ( a ) ;
jv_free ( b ) ;
return ret ;
}
2013-11-30 00:44:14 -06:00
static jv f_ltrimstr ( jv input , jv left ) {
if ( jv_get_kind ( f_startswith ( jv_copy ( input ) , jv_copy ( left ) ) ) ! = JV_KIND_TRUE ) {
jv_free ( left ) ;
return input ;
}
/*
* FIXME It ' d be better to share the suffix with the original input - -
* that we could do , we just can ' t share prefixes .
*/
int prefixlen = jv_string_length_bytes ( left ) ;
jv res = jv_string_sized ( jv_string_value ( input ) + prefixlen ,
jv_string_length_bytes ( jv_copy ( input ) ) - prefixlen ) ;
jv_free ( input ) ;
return res ;
}
static jv f_rtrimstr ( jv input , jv right ) {
if ( jv_get_kind ( f_endswith ( jv_copy ( input ) , jv_copy ( right ) ) ) = = JV_KIND_TRUE ) {
jv res = jv_string_sized ( jv_string_value ( input ) ,
jv_string_length_bytes ( jv_copy ( input ) ) - jv_string_length_bytes ( right ) ) ;
jv_free ( input ) ;
return res ;
}
jv_free ( right ) ;
return input ;
}
2012-12-02 22:12:08 +00:00
static jv f_minus ( jv input , jv a , jv b ) {
jv_free ( input ) ;
2012-09-10 16:49:25 +01:00
if ( jv_get_kind ( a ) = = JV_KIND_NUMBER & & jv_get_kind ( b ) = = JV_KIND_NUMBER ) {
2012-12-02 22:12:08 +00:00
return jv_number ( jv_number_value ( a ) - jv_number_value ( b ) ) ;
2012-09-10 16:49:25 +01:00
} else if ( jv_get_kind ( a ) = = JV_KIND_ARRAY & & jv_get_kind ( b ) = = JV_KIND_ARRAY ) {
jv out = jv_array ( ) ;
2012-12-31 23:27:00 +00:00
jv_array_foreach ( a , i , x ) {
2012-09-10 16:49:25 +01:00
int include = 1 ;
2012-12-31 23:27:00 +00:00
jv_array_foreach ( b , j , y ) {
if ( jv_equal ( jv_copy ( x ) , y ) ) {
2012-09-10 16:49:25 +01:00
include = 0 ;
break ;
}
}
if ( include )
out = jv_array_append ( out , jv_copy ( x ) ) ;
jv_free ( x ) ;
}
jv_free ( a ) ;
jv_free ( b ) ;
2012-12-02 22:12:08 +00:00
return out ;
2012-09-10 16:49:25 +01:00
} else {
2012-12-02 22:12:08 +00:00
return type_error2 ( a , b , " cannot be subtracted " ) ;
2012-09-10 16:49:25 +01:00
}
}
2012-12-02 22:12:08 +00:00
static jv f_multiply ( jv input , jv a , jv b ) {
2014-06-07 21:43:30 -05:00
jv_kind ak = jv_get_kind ( a ) ;
jv_kind bk = jv_get_kind ( b ) ;
2012-12-02 22:12:08 +00:00
jv_free ( input ) ;
2014-06-07 21:43:30 -05:00
if ( ak = = JV_KIND_NUMBER & & bk = = JV_KIND_NUMBER ) {
2012-12-02 22:12:08 +00:00
return jv_number ( jv_number_value ( a ) * jv_number_value ( b ) ) ;
2014-06-07 21:43:30 -05:00
} else if ( ( ak = = JV_KIND_STRING & & bk = = JV_KIND_NUMBER ) | |
( ak = = JV_KIND_NUMBER & & bk = = JV_KIND_STRING ) ) {
jv str = a ;
jv num = b ;
if ( ak = = JV_KIND_NUMBER ) {
str = b ;
num = a ;
}
2013-11-29 15:49:11 -06:00
int n ;
2014-06-07 21:43:30 -05:00
size_t alen = jv_string_length_bytes ( jv_copy ( str ) ) ;
jv res = str ;
2013-11-29 15:49:11 -06:00
2014-06-07 21:43:30 -05:00
for ( n = jv_number_value ( num ) - 1 ; n > 0 ; n - - )
res = jv_string_append_buf ( res , jv_string_value ( str ) , alen ) ;
2013-11-29 15:49:11 -06:00
2014-06-07 21:43:30 -05:00
jv_free ( num ) ;
2013-11-29 15:49:11 -06:00
if ( n < 0 ) {
2014-06-07 21:43:30 -05:00
jv_free ( str ) ;
2013-11-29 15:49:11 -06:00
return jv_null ( ) ;
}
return res ;
2014-06-07 21:43:30 -05:00
} else if ( ak = = JV_KIND_OBJECT & & bk = = JV_KIND_OBJECT ) {
2014-03-08 03:56:05 +01:00
return jv_object_merge_recursive ( a , b ) ;
2012-09-10 16:57:17 +01:00
} else {
2012-12-02 22:12:08 +00:00
return type_error2 ( a , b , " cannot be multiplied " ) ;
2012-09-10 16:57:17 +01:00
}
}
2012-12-02 22:12:08 +00:00
static jv f_divide ( jv input , jv a , jv b ) {
jv_free ( input ) ;
2012-09-10 16:57:17 +01:00
if ( jv_get_kind ( a ) = = JV_KIND_NUMBER & & jv_get_kind ( b ) = = JV_KIND_NUMBER ) {
2012-12-02 22:12:08 +00:00
return jv_number ( jv_number_value ( a ) / jv_number_value ( b ) ) ;
2013-11-28 22:27:23 -06:00
} else if ( jv_get_kind ( a ) = = JV_KIND_STRING & & jv_get_kind ( b ) = = JV_KIND_STRING ) {
return jv_string_split ( a , b ) ;
2012-09-10 16:57:17 +01:00
} else {
2012-12-02 22:12:08 +00:00
return type_error2 ( a , b , " cannot be divided " ) ;
2012-09-10 16:57:17 +01:00
}
}
2013-06-17 20:21:37 -05:00
static jv f_mod ( jv input , jv a , jv b ) {
jv_free ( input ) ;
if ( jv_get_kind ( a ) = = JV_KIND_NUMBER & & jv_get_kind ( b ) = = JV_KIND_NUMBER ) {
return jv_number ( ( intmax_t ) jv_number_value ( a ) % ( intmax_t ) jv_number_value ( b ) ) ;
} else {
return type_error2 ( a , b , " cannot be divided " ) ;
}
}
2012-12-02 22:12:08 +00:00
static jv f_equal ( jv input , jv a , jv b ) {
jv_free ( input ) ;
return jv_bool ( jv_equal ( a , b ) ) ;
2012-09-10 18:08:00 +01:00
}
2012-12-02 22:12:08 +00:00
static jv f_notequal ( jv input , jv a , jv b ) {
jv_free ( input ) ;
return jv_bool ( ! jv_equal ( a , b ) ) ;
2012-10-23 17:01:39 +02:00
}
2012-12-02 22:12:08 +00:00
enum cmp_op {
CMP_OP_LESS ,
CMP_OP_GREATER ,
CMP_OP_LESSEQ ,
CMP_OP_GREATEREQ
} ;
static jv order_cmp ( jv input , jv a , jv b , enum cmp_op op ) {
jv_free ( input ) ;
int r = jv_cmp ( a , b ) ;
return jv_bool ( ( op = = CMP_OP_LESS & & r < 0 ) | |
( op = = CMP_OP_LESSEQ & & r < = 0 ) | |
( op = = CMP_OP_GREATEREQ & & r > = 0 ) | |
( op = = CMP_OP_GREATER & & r > 0 ) ) ;
2012-10-07 22:34:12 +01:00
}
2012-12-02 22:12:08 +00:00
static jv f_less ( jv input , jv a , jv b ) {
return order_cmp ( input , a , b , CMP_OP_LESS ) ;
2012-10-07 22:34:12 +01:00
}
2012-12-02 22:12:08 +00:00
static jv f_greater ( jv input , jv a , jv b ) {
return order_cmp ( input , a , b , CMP_OP_GREATER ) ;
2012-10-07 22:34:12 +01:00
}
2012-12-02 22:12:08 +00:00
static jv f_lesseq ( jv input , jv a , jv b ) {
return order_cmp ( input , a , b , CMP_OP_LESSEQ ) ;
2012-10-07 22:34:12 +01:00
}
2012-12-02 22:12:08 +00:00
static jv f_greatereq ( jv input , jv a , jv b ) {
return order_cmp ( input , a , b , CMP_OP_GREATEREQ ) ;
2012-10-07 22:34:12 +01:00
}
2012-12-02 22:24:02 +00:00
static jv f_contains ( jv a , jv b ) {
2012-12-31 23:27:00 +00:00
if ( jv_get_kind ( a ) = = jv_get_kind ( b ) ) {
2012-12-02 22:12:08 +00:00
return jv_bool ( jv_contains ( a , b ) ) ;
} else {
return type_error2 ( a , b , " cannot have their containment checked " ) ;
2012-10-24 11:42:25 -07:00
}
}
2013-11-29 16:16:52 -06:00
static jv f_dump ( jv input ) {
return jv_dump_string ( input , 0 ) ;
}
static jv f_json_parse ( jv input ) {
if ( jv_get_kind ( input ) ! = JV_KIND_STRING )
return type_error ( input , " only strings can be parsed " ) ;
jv res = jv_parse_sized ( jv_string_value ( input ) ,
jv_string_length_bytes ( jv_copy ( input ) ) ) ;
jv_free ( input ) ;
return res ;
}
2012-12-02 22:12:08 +00:00
static jv f_tonumber ( jv input ) {
if ( jv_get_kind ( input ) = = JV_KIND_NUMBER ) {
return input ;
}
if ( jv_get_kind ( input ) = = JV_KIND_STRING ) {
jv parsed = jv_parse ( jv_string_value ( input ) ) ;
if ( ! jv_is_valid ( parsed ) | | jv_get_kind ( parsed ) = = JV_KIND_NUMBER ) {
jv_free ( input ) ;
return parsed ;
2012-09-10 17:08:13 +01:00
}
}
2012-12-02 22:12:08 +00:00
return type_error ( input , " cannot be parsed as a number " ) ;
2012-09-10 17:08:13 +01:00
}
2012-12-02 22:12:08 +00:00
static jv f_length ( jv input ) {
if ( jv_get_kind ( input ) = = JV_KIND_ARRAY ) {
return jv_number ( jv_array_length ( input ) ) ;
} else if ( jv_get_kind ( input ) = = JV_KIND_OBJECT ) {
return jv_number ( jv_object_length ( input ) ) ;
} else if ( jv_get_kind ( input ) = = JV_KIND_STRING ) {
2013-05-15 00:37:38 +01:00
return jv_number ( jv_string_length_codepoints ( input ) ) ;
2013-11-30 02:29:21 -06:00
} else if ( jv_get_kind ( input ) = = JV_KIND_NUMBER ) {
return jv_number ( fabs ( jv_number_value ( input ) ) ) ;
2012-12-16 13:10:48 +00:00
} else if ( jv_get_kind ( input ) = = JV_KIND_NULL ) {
jv_free ( input ) ;
return jv_number ( 0 ) ;
2012-09-16 11:08:42 +01:00
} else {
2012-12-02 22:12:08 +00:00
return type_error ( input , " has no length " ) ;
2012-09-16 11:08:42 +01:00
}
}
2012-12-02 22:12:08 +00:00
static jv f_tostring ( jv input ) {
if ( jv_get_kind ( input ) = = JV_KIND_STRING ) {
return input ;
2012-09-17 20:14:07 +01:00
} else {
2012-12-02 22:12:08 +00:00
return jv_dump_string ( input , 0 ) ;
2012-09-17 20:14:07 +01:00
}
}
2012-12-27 20:49:34 +00:00
# define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
static jv escape_string ( jv input , const char * escapings ) {
assert ( jv_get_kind ( input ) = = JV_KIND_STRING ) ;
const char * lookup [ 128 ] = { 0 } ;
const char * p = escapings ;
while ( * p ) {
lookup [ ( int ) * p ] = p + 1 ;
p + + ;
p + = strlen ( p ) ;
p + + ;
}
jv ret = jv_string ( " " ) ;
const char * i = jv_string_value ( input ) ;
2013-05-15 00:37:38 +01:00
const char * end = i + jv_string_length_bytes ( jv_copy ( input ) ) ;
2012-12-27 20:49:34 +00:00
const char * cstart ;
int c = 0 ;
while ( ( i = jvp_utf8_next ( ( cstart = i ) , end , & c ) ) ) {
assert ( c ! = - 1 ) ;
if ( c < 128 & & lookup [ c ] ) {
ret = jv_string_append_str ( ret , lookup [ c ] ) ;
} else {
ret = jv_string_append_buf ( ret , cstart , i - cstart ) ;
}
}
jv_free ( input ) ;
return ret ;
}
static jv f_format ( jv input , jv fmt ) {
if ( jv_get_kind ( fmt ) ! = JV_KIND_STRING ) {
jv_free ( input ) ;
return type_error ( fmt , " is not a valid format " ) ;
}
const char * fmt_s = jv_string_value ( fmt ) ;
if ( ! strcmp ( fmt_s , " json " ) ) {
jv_free ( fmt ) ;
return jv_dump_string ( input , 0 ) ;
} else if ( ! strcmp ( fmt_s , " text " ) ) {
jv_free ( fmt ) ;
return f_tostring ( input ) ;
} else if ( ! strcmp ( fmt_s , " csv " ) ) {
jv_free ( fmt ) ;
if ( jv_get_kind ( input ) ! = JV_KIND_ARRAY )
return type_error ( input , " cannot be csv-formatted, only array " ) ;
jv line = jv_string ( " " ) ;
2012-12-31 23:27:00 +00:00
jv_array_foreach ( input , i , x ) {
2012-12-27 20:49:34 +00:00
if ( i ) line = jv_string_append_str ( line , " , " ) ;
switch ( jv_get_kind ( x ) ) {
case JV_KIND_NULL :
/* null rendered as empty string */
jv_free ( x ) ;
break ;
case JV_KIND_TRUE :
case JV_KIND_FALSE :
line = jv_string_concat ( line , jv_dump_string ( x , 0 ) ) ;
break ;
case JV_KIND_NUMBER :
if ( jv_number_value ( x ) ! = jv_number_value ( x ) ) {
/* NaN, render as empty string */
jv_free ( x ) ;
} else {
line = jv_string_concat ( line , jv_dump_string ( x , 0 ) ) ;
}
break ;
case JV_KIND_STRING : {
line = jv_string_append_str ( line , " \" " ) ;
line = jv_string_concat ( line , escape_string ( x , " \" \" \" \0 " ) ) ;
line = jv_string_append_str ( line , " \" " ) ;
break ;
}
default :
jv_free ( input ) ;
jv_free ( line ) ;
return type_error ( x , " is not valid in a csv row " ) ;
}
}
jv_free ( input ) ;
return line ;
} else if ( ! strcmp ( fmt_s , " html " ) ) {
jv_free ( fmt ) ;
return escape_string ( f_tostring ( input ) , " && ; \ 0 < & lt ; \ 0 > & gt ; \ 0 ' & apos ; \ 0 \ " " \0 " ) ;
} else if ( ! strcmp ( fmt_s , " uri " ) ) {
jv_free ( fmt ) ;
input = f_tostring ( input ) ;
int unreserved [ 128 ] = { 0 } ;
const char * p = CHARS_ALPHANUM " -_.!~*'() " ;
while ( * p ) unreserved [ ( int ) * p + + ] = 1 ;
jv line = jv_string ( " " ) ;
const char * s = jv_string_value ( input ) ;
2013-05-15 00:37:38 +01:00
for ( int i = 0 ; i < jv_string_length_bytes ( jv_copy ( input ) ) ; i + + ) {
2012-12-29 01:37:22 +00:00
unsigned ch = ( unsigned ) ( unsigned char ) * s ;
2012-12-27 20:49:34 +00:00
if ( ch < 128 & & unreserved [ ch ] ) {
line = jv_string_append_buf ( line , s , 1 ) ;
} else {
2014-06-29 19:48:01 -07:00
line = jv_string_concat ( line , jv_string_fmt ( " %%%02X " , ch ) ) ;
2012-12-27 20:49:34 +00:00
}
s + + ;
}
jv_free ( input ) ;
return line ;
} else if ( ! strcmp ( fmt_s , " sh " ) ) {
jv_free ( fmt ) ;
if ( jv_get_kind ( input ) ! = JV_KIND_ARRAY )
input = jv_array_set ( jv_array ( ) , 0 , input ) ;
jv line = jv_string ( " " ) ;
2012-12-31 23:27:00 +00:00
jv_array_foreach ( input , i , x ) {
2012-12-27 20:49:34 +00:00
if ( i ) line = jv_string_append_str ( line , " " ) ;
switch ( jv_get_kind ( x ) ) {
case JV_KIND_NULL :
case JV_KIND_TRUE :
case JV_KIND_FALSE :
case JV_KIND_NUMBER :
line = jv_string_concat ( line , jv_dump_string ( x , 0 ) ) ;
break ;
case JV_KIND_STRING : {
line = jv_string_append_str ( line , " ' " ) ;
line = jv_string_concat ( line , escape_string ( x , " '' \\ '' \0 " ) ) ;
line = jv_string_append_str ( line , " ' " ) ;
break ;
}
default :
jv_free ( input ) ;
jv_free ( line ) ;
return type_error ( x , " can not be escaped for shell " ) ;
}
}
jv_free ( input ) ;
return line ;
} else if ( ! strcmp ( fmt_s , " base64 " ) ) {
jv_free ( fmt ) ;
input = f_tostring ( input ) ;
jv line = jv_string ( " " ) ;
const char b64 [ 64 + 1 ] = CHARS_ALPHANUM " +/ " ;
2014-07-01 13:17:48 -04:00
const unsigned char * data = ( const unsigned char * ) jv_string_value ( input ) ;
2013-05-15 00:37:38 +01:00
int len = jv_string_length_bytes ( jv_copy ( input ) ) ;
2012-12-27 20:49:34 +00:00
for ( int i = 0 ; i < len ; i + = 3 ) {
uint32_t code = 0 ;
int n = len - i > = 3 ? 3 : len - i ;
for ( int j = 0 ; j < 3 ; j + + ) {
code < < = 8 ;
code | = j < n ? ( unsigned ) data [ i + j ] : 0 ;
}
char buf [ 4 ] ;
for ( int j = 0 ; j < 4 ; j + + ) {
buf [ j ] = b64 [ ( code > > ( 18 - j * 6 ) ) & 0x3f ] ;
}
if ( n < 3 ) buf [ 3 ] = ' = ' ;
if ( n < 2 ) buf [ 2 ] = ' = ' ;
line = jv_string_append_buf ( line , buf , sizeof ( buf ) ) ;
}
jv_free ( input ) ;
return line ;
} else {
jv_free ( input ) ;
return jv_invalid_with_msg ( jv_string_concat ( fmt , jv_string ( " is not a valid format " ) ) ) ;
}
}
2012-12-02 22:12:08 +00:00
static jv f_keys ( jv input ) {
if ( jv_get_kind ( input ) = = JV_KIND_OBJECT | | jv_get_kind ( input ) = = JV_KIND_ARRAY ) {
return jv_keys ( input ) ;
2012-10-20 00:26:37 +01:00
} else {
2012-12-02 22:12:08 +00:00
return type_error ( input , " has no keys " ) ;
2012-10-20 00:26:37 +01:00
}
}
2012-12-02 22:12:08 +00:00
static jv f_sort ( jv input ) {
if ( jv_get_kind ( input ) = = JV_KIND_ARRAY ) {
return jv_sort ( input , jv_copy ( input ) ) ;
2012-11-30 20:27:16 +00:00
} else {
2012-12-02 22:12:08 +00:00
return type_error ( input , " cannot be sorted, as it is not an array " ) ;
2012-11-30 20:27:16 +00:00
}
}
2012-12-02 23:22:15 +00:00
static jv f_sort_by_impl ( jv input , jv keys ) {
if ( jv_get_kind ( input ) = = JV_KIND_ARRAY & &
jv_get_kind ( keys ) = = JV_KIND_ARRAY & &
jv_array_length ( jv_copy ( input ) ) = = jv_array_length ( jv_copy ( keys ) ) ) {
return jv_sort ( input , keys ) ;
} else {
return type_error2 ( input , keys , " cannot be sorted, as they are not both arrays " ) ;
}
}
static jv f_group_by_impl ( jv input , jv keys ) {
if ( jv_get_kind ( input ) = = JV_KIND_ARRAY & &
jv_get_kind ( keys ) = = JV_KIND_ARRAY & &
jv_array_length ( jv_copy ( input ) ) = = jv_array_length ( jv_copy ( keys ) ) ) {
return jv_group ( input , keys ) ;
} else {
return type_error2 ( input , keys , " cannot be sorted, as they are not both arrays " ) ;
}
}
2014-06-18 19:49:38 -04:00
static int f_match_name_iter ( const UChar * name , const UChar * name_end , int ngroups ,
int * groups , regex_t * reg , void * arg ) {
jv captures = * ( jv * ) arg ;
for ( int i = 0 ; i < ngroups ; + + i ) {
jv cap = jv_array_get ( jv_copy ( captures ) , groups [ i ] - 1 ) ;
if ( jv_get_kind ( cap ) = = JV_KIND_OBJECT ) {
cap = jv_object_set ( cap , jv_string ( " name " ) , jv_string_sized ( ( const char * ) name , name_end - name ) ) ;
captures = jv_array_set ( captures , groups [ i ] - 1 , cap ) ;
} else {
jv_free ( cap ) ;
}
}
* ( jv * ) arg = captures ;
return 0 ;
}
static jv f_match ( jv input , jv regex , jv modifiers , jv testmode ) {
int test = jv_equal ( testmode , jv_true ( ) ) ;
jv result ;
int onigret ;
int global = 0 ;
regex_t * reg ;
OnigErrorInfo einfo ;
OnigRegion * region ;
if ( jv_get_kind ( input ) ! = JV_KIND_STRING ) {
jv_free ( regex ) ;
jv_free ( modifiers ) ;
return type_error ( input , " cannot be matched, as it is not a string " ) ;
}
if ( jv_get_kind ( regex ) ! = JV_KIND_STRING ) {
jv_free ( input ) ;
jv_free ( modifiers ) ;
return type_error ( regex , " is not a string " ) ;
}
OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP ;
if ( jv_get_kind ( modifiers ) = = JV_KIND_STRING ) {
jv modarray = jv_string_explode ( jv_copy ( modifiers ) ) ;
jv_array_foreach ( modarray , i , mod ) {
switch ( ( int ) jv_number_value ( mod ) ) {
case ' g ' :
global = 1 ;
break ;
case ' i ' :
options | = ONIG_OPTION_IGNORECASE ;
break ;
case ' x ' :
options | = ONIG_OPTION_EXTEND ;
break ;
case ' m ' :
options | = ONIG_OPTION_MULTILINE ;
break ;
case ' s ' :
options | = ONIG_OPTION_SINGLELINE ;
break ;
case ' p ' :
options | = ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE ;
break ;
case ' l ' :
options | = ONIG_OPTION_FIND_LONGEST ;
break ;
case ' n ' :
options | = ONIG_OPTION_FIND_NOT_EMPTY ;
break ;
default :
jv_free ( input ) ;
jv_free ( regex ) ;
jv_free ( modarray ) ;
return jv_invalid_with_msg ( jv_string_concat ( modifiers ,
jv_string ( " is not a valid modifier string " ) ) ) ;
}
}
jv_free ( modarray ) ;
} else if ( jv_get_kind ( modifiers ) ! = JV_KIND_NULL ) {
// If it isn't a string or null, then it is the wrong type...
jv_free ( input ) ;
jv_free ( regex ) ;
return type_error ( modifiers , " is not a string " ) ;
}
jv_free ( modifiers ) ;
onigret = onig_new ( & reg , ( const UChar * ) jv_string_value ( regex ) ,
( const UChar * ) ( jv_string_value ( regex ) + jv_string_length_bytes ( jv_copy ( regex ) ) ) ,
options , ONIG_ENCODING_UTF8 , ONIG_SYNTAX_PERL_NG , & einfo ) ;
if ( onigret ! = ONIG_NORMAL ) {
UChar ebuf [ ONIG_MAX_ERROR_MESSAGE_LEN ] ;
onig_error_code_to_str ( ebuf , onigret , einfo ) ;
jv_free ( input ) ;
jv_free ( regex ) ;
return jv_invalid_with_msg ( jv_string_concat ( jv_string ( " Regex failure: " ) ,
jv_string ( ( char * ) ebuf ) ) ) ;
}
if ( ! test )
result = jv_array ( ) ;
const char * input_string = jv_string_value ( input ) ;
const UChar * start = ( const UChar * ) jv_string_value ( input ) ;
const unsigned long length = jv_string_length_bytes ( jv_copy ( input ) ) ;
const UChar * end = start + length ;
region = onig_region_new ( ) ;
do {
onigret = onig_search ( reg ,
( const UChar * ) jv_string_value ( input ) , end , /* string boundaries */
start , end , /* search boundaries */
region , ONIG_OPTION_NONE ) ;
if ( onigret > = 0 ) {
if ( test ) {
result = jv_true ( ) ;
break ;
}
// Zero-width match
if ( region - > end [ 0 ] = = region - > beg [ 0 ] ) {
unsigned long idx ;
const char * fr = ( const char * ) input_string ;
for ( idx = 0 ; fr ! = input_string + region - > beg [ 0 ] ; idx + + ) {
fr + = jvp_utf8_decode_length ( * fr ) ;
}
jv match = jv_object_set ( jv_object ( ) , jv_string ( " offset " ) , jv_number ( idx ) ) ;
match = jv_object_set ( match , jv_string ( " length " ) , jv_number ( 0 ) ) ;
match = jv_object_set ( match , jv_string ( " string " ) , jv_string ( " " ) ) ;
match = jv_object_set ( match , jv_string ( " captures " ) , jv_array ( ) ) ;
result = jv_array_append ( result , match ) ;
start + = 1 ;
continue ;
}
unsigned long idx ;
unsigned long len ;
const char * fr = ( const char * ) input_string ;
for ( idx = len = 0 ; fr ! = input_string + region - > end [ 0 ] ; len + + ) {
if ( fr = = input_string + region - > beg [ 0 ] ) idx = len , len = 0 ;
fr + = jvp_utf8_decode_length ( * fr ) ;
}
jv match = jv_object_set ( jv_object ( ) , jv_string ( " offset " ) , jv_number ( idx ) ) ;
unsigned long blen = region - > end [ 0 ] - region - > beg [ 0 ] ;
match = jv_object_set ( match , jv_string ( " length " ) , jv_number ( len ) ) ;
match = jv_object_set ( match , jv_string ( " string " ) , jv_string_sized ( input_string + region - > beg [ 0 ] , blen ) ) ;
jv captures = jv_array ( ) ;
for ( int i = 1 ; i < region - > num_regs ; + + i ) {
// Empty capture.
if ( region - > beg [ i ] = = region - > end [ i ] ) {
// Didn't match.
jv cap ;
if ( region - > beg [ i ] = = - 1 ) {
cap = jv_object_set ( jv_object ( ) , jv_string ( " offset " ) , jv_number ( - 1 ) ) ;
cap = jv_object_set ( cap , jv_string ( " string " ) , jv_null ( ) ) ;
} else {
fr = input_string ;
for ( idx = 0 ; fr ! = input_string + region - > beg [ i ] ; idx + + ) {
fr + = jvp_utf8_decode_length ( * fr ) ;
}
cap = jv_object_set ( jv_object ( ) , jv_string ( " offset " ) , jv_number ( idx ) ) ;
cap = jv_object_set ( cap , jv_string ( " string " ) , jv_string ( " " ) ) ;
}
cap = jv_object_set ( cap , jv_string ( " length " ) , jv_number ( 0 ) ) ;
cap = jv_object_set ( cap , jv_string ( " name " ) , jv_null ( ) ) ;
captures = jv_array_append ( captures , cap ) ;
continue ;
}
fr = input_string ;
for ( idx = len = 0 ; fr ! = input_string + region - > end [ i ] ; len + + ) {
if ( fr = = input_string + region - > beg [ i ] ) idx = len , len = 0 ;
fr + = jvp_utf8_decode_length ( * fr ) ;
}
blen = region - > end [ i ] - region - > beg [ i ] ;
jv cap = jv_object_set ( jv_object ( ) , jv_string ( " offset " ) , jv_number ( idx ) ) ;
cap = jv_object_set ( cap , jv_string ( " length " ) , jv_number ( len ) ) ;
cap = jv_object_set ( cap , jv_string ( " string " ) , jv_string_sized ( input_string + region - > beg [ i ] , blen ) ) ;
cap = jv_object_set ( cap , jv_string ( " name " ) , jv_null ( ) ) ;
captures = jv_array_append ( captures , cap ) ;
}
onig_foreach_name ( reg , f_match_name_iter , & captures ) ;
match = jv_object_set ( match , jv_string ( " captures " ) , captures ) ;
result = jv_array_append ( result , match ) ;
start = ( const UChar * ) ( input_string + region - > end [ 0 ] ) ;
onig_region_free ( region , 0 ) ;
} else if ( onigret = = ONIG_MISMATCH ) {
if ( test )
result = jv_false ( ) ;
break ;
} else { /* Error */
UChar ebuf [ ONIG_MAX_ERROR_MESSAGE_LEN ] ;
onig_error_code_to_str ( ebuf , onigret , einfo ) ;
jv_free ( result ) ;
result = jv_invalid_with_msg ( jv_string_concat ( jv_string ( " Regex failure: " ) ,
jv_string ( ( char * ) ebuf ) ) ) ;
break ;
}
} while ( global & & start ! = end ) ;
onig_region_free ( region , 1 ) ;
region = NULL ;
if ( region )
onig_region_free ( region , 1 ) ;
onig_free ( reg ) ;
jv_free ( input ) ;
jv_free ( regex ) ;
return result ;
}
2012-12-04 22:45:03 +00:00
static jv minmax_by ( jv values , jv keys , int is_min ) {
if ( jv_get_kind ( values ) ! = JV_KIND_ARRAY )
return type_error2 ( values , keys , " cannot be iterated over " ) ;
if ( jv_get_kind ( keys ) ! = JV_KIND_ARRAY )
return type_error2 ( values , keys , " cannot be iterated over " ) ;
if ( jv_array_length ( jv_copy ( values ) ) ! = jv_array_length ( jv_copy ( keys ) ) )
return type_error2 ( values , keys , " have wrong length " ) ;
if ( jv_array_length ( jv_copy ( values ) ) = = 0 ) {
jv_free ( values ) ;
jv_free ( keys ) ;
return jv_null ( ) ;
}
jv ret = jv_array_get ( jv_copy ( values ) , 0 ) ;
jv retkey = jv_array_get ( jv_copy ( keys ) , 0 ) ;
for ( int i = 1 ; i < jv_array_length ( jv_copy ( values ) ) ; i + + ) {
jv item = jv_array_get ( jv_copy ( keys ) , i ) ;
int cmp = jv_cmp ( jv_copy ( item ) , jv_copy ( retkey ) ) ;
if ( ( cmp < 0 ) = = ( is_min = = 1 ) ) {
jv_free ( retkey ) ;
retkey = item ;
jv_free ( ret ) ;
ret = jv_array_get ( jv_copy ( values ) , i ) ;
} else {
jv_free ( item ) ;
}
}
jv_free ( values ) ;
jv_free ( keys ) ;
jv_free ( retkey ) ;
return ret ;
}
static jv f_min ( jv x ) {
return minmax_by ( x , jv_copy ( x ) , 1 ) ;
}
static jv f_max ( jv x ) {
return minmax_by ( x , jv_copy ( x ) , 0 ) ;
}
static jv f_min_by_impl ( jv x , jv y ) {
return minmax_by ( x , y , 1 ) ;
}
static jv f_max_by_impl ( jv x , jv y ) {
return minmax_by ( x , y , 0 ) ;
}
2012-12-02 22:12:08 +00:00
static jv f_type ( jv input ) {
jv out = jv_string ( jv_kind_name ( jv_get_kind ( input ) ) ) ;
jv_free ( input ) ;
return out ;
2012-09-17 23:06:48 +01:00
}
2012-12-10 22:30:09 +00:00
static jv f_error ( jv input , jv msg ) {
jv_free ( input ) ;
return jv_invalid_with_msg ( msg ) ;
}
2014-06-16 22:33:24 -05:00
// FIXME Should autoconf check for this!
# ifndef WIN32
2014-06-13 17:51:41 -05:00
extern const char * * environ ;
2014-06-16 22:33:24 -05:00
# endif
2014-06-13 17:51:41 -05:00
static jv f_env ( jv input ) {
jv_free ( input ) ;
jv env = jv_object ( ) ;
const char * var , * val ;
for ( const char * * e = environ ; * e ! = NULL ; e + + ) {
var = e [ 0 ] ;
val = strchr ( e [ 0 ] , ' = ' ) ;
if ( val = = NULL )
env = jv_object_set ( env , jv_string ( var ) , jv_null ( ) ) ;
else if ( var - val < INT_MAX )
env = jv_object_set ( env , jv_string_sized ( var , val - var ) , jv_string ( val + 1 ) ) ;
}
return env ;
}
2014-02-21 08:46:56 +00:00
# define LIBM_DD(name) \
{ ( cfunction_ptr ) f_ # # name , " _ " # name , 1 } ,
2013-05-05 22:37:46 +01:00
static const struct cfunction function_list [ ] = {
2014-02-21 08:46:56 +00:00
# include "libm.h"
2012-12-02 22:12:08 +00:00
{ ( cfunction_ptr ) f_plus , " _plus " , 3 } ,
2013-01-03 12:53:06 +00:00
{ ( cfunction_ptr ) f_negate , " _negate " , 1 } ,
2012-12-02 22:12:08 +00:00
{ ( cfunction_ptr ) f_minus , " _minus " , 3 } ,
{ ( cfunction_ptr ) f_multiply , " _multiply " , 3 } ,
{ ( cfunction_ptr ) f_divide , " _divide " , 3 } ,
2013-06-17 20:21:37 -05:00
{ ( cfunction_ptr ) f_mod , " _mod " , 3 } ,
2013-11-29 16:16:52 -06:00
{ ( cfunction_ptr ) f_dump , " tojson " , 1 } ,
{ ( cfunction_ptr ) f_json_parse , " fromjson " , 1 } ,
2012-12-02 22:12:08 +00:00
{ ( cfunction_ptr ) f_tonumber , " tonumber " , 1 } ,
{ ( cfunction_ptr ) f_tostring , " tostring " , 1 } ,
{ ( cfunction_ptr ) f_keys , " keys " , 1 } ,
2013-11-28 15:17:32 -06:00
{ ( cfunction_ptr ) f_startswith , " startswith " , 2 } ,
{ ( cfunction_ptr ) f_endswith , " endswith " , 2 } ,
2013-11-30 00:44:14 -06:00
{ ( cfunction_ptr ) f_ltrimstr , " ltrimstr " , 2 } ,
{ ( cfunction_ptr ) f_rtrimstr , " rtrimstr " , 2 } ,
2013-11-28 22:27:23 -06:00
{ ( cfunction_ptr ) jv_string_split , " split " , 2 } ,
2013-11-28 14:53:50 -06:00
{ ( cfunction_ptr ) jv_string_explode , " explode " , 1 } ,
{ ( cfunction_ptr ) jv_string_implode , " implode " , 1 } ,
2012-12-28 16:08:29 +00:00
{ ( cfunction_ptr ) jv_setpath , " setpath " , 3 } , // FIXME typechecking
{ ( cfunction_ptr ) jv_getpath , " getpath " , 2 } ,
2012-12-29 16:13:06 +00:00
{ ( cfunction_ptr ) jv_delpaths , " delpaths " , 2 } ,
2013-05-06 13:46:16 +01:00
{ ( cfunction_ptr ) jv_has , " has " , 2 } ,
2012-12-02 22:12:08 +00:00
{ ( cfunction_ptr ) f_equal , " _equal " , 3 } ,
{ ( cfunction_ptr ) f_notequal , " _notequal " , 3 } ,
{ ( cfunction_ptr ) f_less , " _less " , 3 } ,
{ ( cfunction_ptr ) f_greater , " _greater " , 3 } ,
{ ( cfunction_ptr ) f_lesseq , " _lesseq " , 3 } ,
{ ( cfunction_ptr ) f_greatereq , " _greatereq " , 3 } ,
2012-12-02 22:24:02 +00:00
{ ( cfunction_ptr ) f_contains , " contains " , 2 } ,
2012-12-02 22:12:08 +00:00
{ ( cfunction_ptr ) f_length , " length " , 1 } ,
{ ( cfunction_ptr ) f_type , " type " , 1 } ,
{ ( cfunction_ptr ) f_sort , " sort " , 1 } ,
2012-12-02 23:22:15 +00:00
{ ( cfunction_ptr ) f_sort_by_impl , " _sort_by_impl " , 2 } ,
{ ( cfunction_ptr ) f_group_by_impl , " _group_by_impl " , 2 } ,
2012-12-04 22:45:03 +00:00
{ ( cfunction_ptr ) f_min , " min " , 1 } ,
{ ( cfunction_ptr ) f_max , " max " , 1 } ,
{ ( cfunction_ptr ) f_min_by_impl , " _min_by_impl " , 2 } ,
{ ( cfunction_ptr ) f_max_by_impl , " _max_by_impl " , 2 } ,
2012-12-10 22:30:09 +00:00
{ ( cfunction_ptr ) f_error , " error " , 2 } ,
2012-12-27 20:49:34 +00:00
{ ( cfunction_ptr ) f_format , " format " , 2 } ,
2014-06-13 17:51:41 -05:00
{ ( cfunction_ptr ) f_env , " env " , 1 } ,
2014-06-18 19:49:38 -04:00
{ ( cfunction_ptr ) f_match , " _match_impl " , 4 } ,
2012-08-16 01:00:30 +01:00
} ;
2014-02-21 08:46:56 +00:00
# undef LIBM_DD
2012-09-17 20:59:34 +01:00
2012-11-26 18:53:47 +00:00
struct bytecoded_builtin { const char * name ; block code ; } ;
static block bind_bytecoded_builtins ( block b ) {
block builtins = gen_noop ( ) ;
2012-12-28 16:08:29 +00:00
{
struct bytecoded_builtin builtin_defs [ ] = {
{ " empty " , gen_op_simple ( BACKTRACK ) } ,
{ " false " , gen_const ( jv_false ( ) ) } ,
{ " true " , gen_const ( jv_true ( ) ) } ,
{ " null " , gen_const ( jv_null ( ) ) } ,
{ " not " , gen_condbranch ( gen_const ( jv_false ( ) ) ,
gen_const ( jv_true ( ) ) ) }
} ;
for ( unsigned i = 0 ; i < sizeof ( builtin_defs ) / sizeof ( builtin_defs [ 0 ] ) ; i + + ) {
builtins = BLOCK ( builtins , gen_function ( builtin_defs [ i ] . name , gen_noop ( ) ,
builtin_defs [ i ] . code ) ) ;
}
}
{
struct bytecoded_builtin builtin_def_1arg [ ] = {
2013-05-13 15:00:05 +01:00
{ " path " , BLOCK ( gen_op_simple ( PATH_BEGIN ) ,
2012-12-28 16:08:29 +00:00
gen_call ( " arg " , gen_noop ( ) ) ,
2013-05-13 15:00:05 +01:00
gen_op_simple ( PATH_END ) ) } ,
2012-12-28 16:08:29 +00:00
} ;
for ( unsigned i = 0 ; i < sizeof ( builtin_def_1arg ) / sizeof ( builtin_def_1arg [ 0 ] ) ; i + + ) {
builtins = BLOCK ( builtins , gen_function ( builtin_def_1arg [ i ] . name ,
2013-06-18 01:07:18 +01:00
gen_param ( " arg " ) ,
2012-12-28 16:08:29 +00:00
builtin_def_1arg [ i ] . code ) ) ;
}
2012-11-26 18:53:47 +00:00
}
2013-05-14 16:09:10 +01:00
{
2014-07-02 21:22:53 -05:00
// Note that we can now define `range` as a jq-coded function
2013-06-18 01:07:18 +01:00
block rangevar = gen_op_var_fresh ( STOREV , " rangevar " ) ;
2013-05-14 16:09:10 +01:00
block init = BLOCK ( gen_op_simple ( DUP ) , gen_call ( " start " , gen_noop ( ) ) , rangevar ) ;
block range = BLOCK ( init ,
gen_call ( " end " , gen_noop ( ) ) ,
2013-06-18 01:07:18 +01:00
gen_op_bound ( RANGE , rangevar ) ) ;
2013-05-14 16:09:10 +01:00
builtins = BLOCK ( builtins , gen_function ( " range " ,
2013-06-18 01:07:18 +01:00
BLOCK ( gen_param ( " start " ) , gen_param ( " end " ) ) ,
2013-05-14 16:09:10 +01:00
range ) ) ;
}
2012-12-28 16:08:29 +00:00
2013-05-15 01:37:15 +01:00
return block_bind_referenced ( builtins , b , OP_IS_CALL_PSEUDO ) ;
2012-11-26 18:53:47 +00:00
}
2012-09-17 20:59:34 +01:00
2014-02-21 08:46:56 +00:00
# define LIBM_DD(name) "def " #name ": _" #name ";",
2013-05-05 22:37:46 +01:00
static const char * const jq_builtins [ ] = {
2014-07-07 18:48:13 -05:00
" def break: error( \" break \" ); " ,
2012-09-18 12:58:39 +01:00
" def map(f): [.[] | f]; " ,
" def select(f): if f then . else empty end; " ,
2014-06-18 00:15:22 +02:00
" def sort(f): _sort_by_impl(map([f])); " ,
" def sort_by(f): sort(f); " ,
" def group(f): _group_by_impl(map([f])); " ,
" def group_by(f): group(f); " ,
" def unique: group(.) | map(.[0]); " ,
" def unique(f): group(f) | map(.[0]); " ,
" def unique_by(f): unique(f); " ,
" def max(f): _max_by_impl(map([f])); " ,
" def min(f): _min_by_impl(map([f])); " ,
" def max_by(f): max(f); " ,
" def min_by(f): min(f); " ,
2014-02-21 08:46:56 +00:00
# include "libm.h"
2013-05-16 15:07:53 +01:00
" def add: reduce .[] as $x (null; . + $x); " ,
2012-12-29 16:13:06 +00:00
" def del(f): delpaths([path(f)]); " ,
2013-05-16 15:07:53 +01:00
" def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v)); " ,
" def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update)); " ,
2012-12-29 22:59:07 +00:00
" def recurse(f): ., (f | select(. != null) | recurse(f)); " ,
2014-06-18 00:15:22 +02:00
" def recurse: recurse(.[]?); " ,
" def recurse_down: recurse; " ,
2013-05-13 16:15:49 +01:00
" def to_entries: [keys[] as $k | {key: $k, value: .[$k]}]; " ,
2014-06-16 22:40:44 -05:00
" def from_entries: map({(.key): .value}) | add | .//={}; " ,
2013-05-13 16:15:49 +01:00
" def with_entries(f): to_entries | map(f) | from_entries; " ,
2013-05-16 14:25:28 +01:00
" def reverse: [.[length - 1 - range(0;length)]]; " ,
2014-06-08 02:01:44 -05:00
" def indices(i): if type == \" array \" and (i|type) == \" array \" then .[i] elif type == \" array \" then .[[i]] else .[i] end; " ,
" def index(i): if type == \" array \" and (i|type) == \" array \" then .[i] elif type == \" array \" then .[[i]] else .[i] end | .[0]; " ,
" def rindex(i): if type == \" array \" and (i|type) == \" array \" then .[i] elif type == \" array \" then .[[i]] else .[i] end | .[-1:][0]; " ,
2014-02-05 15:23:32 -06:00
" def paths: path(recurse(if (type|. == \" array \" or . == \" object \" ) then .[] else empty end))|select(length > 0); " ,
2014-06-18 00:15:22 +02:00
" def paths(node_filter): . as $dot|paths|select(. as $p|$dot|getpath($p)|node_filter); " ,
2014-02-17 04:01:32 +01:00
" def any: reduce .[] as $i (false; . or $i); " ,
" def all: reduce .[] as $i (true; . and $i); " ,
2014-07-06 20:08:06 -05:00
" def any(condition): reduce .[] as $i (false; . or ($i|condition)); " ,
2014-07-07 22:25:34 -05:00
" def any(generator; condition): "
" [false, "
" foreach generator as $i "
" (false; "
" if . then break elif $i | condition then true else . end; "
" if . then . else empty end)] | any; " ,
2014-07-06 20:08:06 -05:00
" def all(condition): reduce .[] as $i (true; . and ($i|condition)); " ,
2014-07-07 22:25:34 -05:00
" def all(generator; condition): "
" [true, "
" foreach generator as $i "
" (true; "
" if .|not then break elif $i | condition then . else false end; "
" if .|not then . else empty end)]|all; " ,
2014-03-10 01:35:44 +01:00
" def arrays: select(type == \" array \" ); " ,
" def objects: select(type == \" object \" ); " ,
" def iterables: arrays, objects; " ,
" def booleans: select(type == \" boolean \" ); " ,
" def numbers: select(type == \" number \" ); " ,
" def strings: select(type == \" string \" ); " ,
" def nulls: select(type == \" null \" ); " ,
" def values: arrays, objects, booleans, numbers, strings; " ,
2014-03-13 00:16:33 -05:00
" def scalars: select(. == null or . == true or . == false or type == \" number \" or type == \" string \" ); " ,
2014-06-18 00:15:22 +02:00
" def leaf_paths: paths(scalars); " ,
2014-05-30 00:45:16 +02:00
" def join(x): reduce .[] as $i ( \" \" ; . + (if . == \" \" then $i else x + $i end)); " ,
2014-06-16 02:34:18 +02:00
" def flatten: reduce .[] as $i ([]; if $i | type == \" array \" then . + ($i | flatten) else . + [$i] end); " ,
" def flatten(x): reduce .[] as $i ([]; if $i | type == \" array \" and x > 0 then . + ($i | flatten(x-1)) else . + [$i] end); " ,
2014-06-18 00:15:22 +02:00
" def range(x): range(0;x); " ,
2014-06-18 19:49:38 -04:00
" def match(re; mode): _match_impl(re; mode; false)|.[]; " ,
" def match(val): if val | type == \" string \" then match(val; null) elif val | type == \" array \" and (val | length) > 1 then match(val[0]; val[1]) elif val | type == \" array \" and (val | length > 0) then match(val[0]; null) else error((val | type) + \" not a string or array \" ) end; " ,
" def test(re; mode): _match_impl(re; mode; true); " ,
" def test(val): if val |type == \" string \" then test(val; null) elif val | type == \" array \" and (val | length) > 1 then test(val[0]; val[1]) elif val | type == \" array \" and (val | length > 0) then test(val[0]; null) else error((val | type) + \" not a string or array \" ) end; " ,
// "def test(re): _match(re; null; 1);",
2014-07-02 21:22:53 -05:00
// range/3, with a `by` expression argument
" def range(init; upto; by): "
" def _range: "
" if (by > 0 and . < upto) or (by < 0 and . > upto) then ., ((.+by)|_range) else . end; "
" if by == 0 then init else init|_range end | select((by > 0 and . < upto) or (by < 0 and . > upto)); " ,
2014-07-02 21:45:49 -05:00
// generic iterator/generator
" def while(cond; update): "
" def _while: "
" if cond then ., (update | _while) else empty end; "
" _while; " ,
2014-07-07 22:26:07 -05:00
" def limit(n; exp): if n < 0 then exp else foreach exp as $item ([n, null]; if .[0] < 1 then break else [.[0] -1, $item] end; .[1]) end; " ,
2012-09-18 10:17:38 +01:00
} ;
2014-02-21 08:46:56 +00:00
# undef LIBM_DD
2012-09-18 10:17:38 +01:00
2012-09-17 20:59:34 +01:00
2013-11-30 02:05:42 -06:00
static int builtins_bind_one ( jq_state * jq , block * bb , const char * code ) {
2013-05-17 03:03:42 +10:00
struct locfile src ;
2013-11-30 02:05:42 -06:00
locfile_init ( & src , jq , code , strlen ( code ) ) ;
2013-05-17 03:03:42 +10:00
block funcs ;
int nerrors = jq_parse_library ( & src , & funcs ) ;
if ( nerrors = = 0 ) {
* bb = block_bind_referenced ( funcs , * bb , OP_IS_CALL_PSEUDO ) ;
}
locfile_free ( & src ) ;
return nerrors ;
}
2013-11-30 02:05:42 -06:00
static int slurp_lib ( jq_state * jq , block * bb ) {
2013-05-17 03:03:42 +10:00
int nerrors = 0 ;
char * home = getenv ( " HOME " ) ;
if ( home ) { // silently ignore no $HOME
jv filename = jv_string_append_str ( jv_string ( home ) , " /.jq " ) ;
2013-06-15 00:08:59 -05:00
jv data = jv_load_file ( jv_string_value ( filename ) , 1 ) ;
2013-05-17 03:03:42 +10:00
if ( jv_is_valid ( data ) ) {
2013-11-30 02:05:42 -06:00
nerrors = builtins_bind_one ( jq , bb , jv_string_value ( data ) ) ;
2013-05-17 03:03:42 +10:00
}
jv_free ( filename ) ;
jv_free ( data ) ;
}
return nerrors ;
}
2013-11-30 02:05:42 -06:00
int builtins_bind ( jq_state * jq , block * bb ) {
int nerrors = slurp_lib ( jq , bb ) ;
2013-05-17 03:03:42 +10:00
if ( nerrors ) {
block_free ( * bb ) ;
return nerrors ;
}
2012-09-18 12:58:39 +01:00
for ( int i = ( int ) ( sizeof ( jq_builtins ) / sizeof ( jq_builtins [ 0 ] ) ) - 1 ; i > = 0 ; i - - ) {
2013-11-30 02:05:42 -06:00
nerrors = builtins_bind_one ( jq , bb , jq_builtins [ i ] ) ;
2012-09-18 10:17:38 +01:00
assert ( ! nerrors ) ;
2012-09-17 20:59:34 +01:00
}
2013-05-17 03:03:42 +10:00
* bb = bind_bytecoded_builtins ( * bb ) ;
* bb = gen_cbinding ( function_list , sizeof ( function_list ) / sizeof ( function_list [ 0 ] ) , * bb ) ;
return nerrors ;
2012-09-16 17:08:56 +01:00
}