1
0
mirror of https://github.com/stedolan/jq.git synced 2024-05-11 05:55:39 +00:00

Merge pull request #36 from stroan/contains-operator

Implemented contains operator
This commit is contained in:
Stephen Dolan
2012-10-25 11:51:55 -07:00
12 changed files with 2118 additions and 1807 deletions

View File

@@ -161,6 +161,21 @@ static void f_greatereq(jv input[], jv output[]) {
order_cmp(input, output, CMP_OP_GREATEREQ);
}
static void f_contains(jv input[], jv output[]) {
jv_free(input[0]);
jv a = input[2];
jv b = input[1];
jv_kind akind = jv_get_kind(a);
if (akind == jv_get_kind(b)) {
output[0] = jv_bool(jv_contains(a, b));
} else {
output[0] = jv_invalid_with_msg(jv_string_fmt("Can only check containment of values of the same type."));
jv_free(a);
jv_free(b);
}
}
static void f_tonumber(jv input[], jv output[]) {
if (jv_get_kind(input[0]) == JV_KIND_NUMBER) {
output[0] = input[0];
@@ -294,6 +309,7 @@ static struct cfunction function_list[] = {
{f_greater, "_greater", CALL_BUILTIN_3_1},
{f_lesseq, "_lesseq", CALL_BUILTIN_3_1},
{f_greatereq, "_greatereq", CALL_BUILTIN_3_1},
{f_contains, "_contains", CALL_BUILTIN_3_1},
{f_length, "length", CALL_BUILTIN_1_1},
{f_type, "type", CALL_BUILTIN_1_1},
{f_add, "add", CALL_BUILTIN_1_1},

View File

@@ -522,7 +522,33 @@ sections:
- program: '.[] == 1'
input: '[1, 1.0, "1", "banana"]'
output: ['[true, true, false, false]']
- title: `contains`
body: |
The expression 'a contains b' will produce true if b is completely
contained within a. A string B is contained in a string A if B is a
substring of A. An array B is contained in an array A is all elements
in B are contained in any element in A. An object B is contained in
object A if all of the values in B are contained in the value in A with
the same key. All other types are assumed to be contained in each other
if they are equal.
examples:
- program: '. == contains "bar"'
input: '"foobar"'
output: ['true']
- program: '. contains ["baz", "bar"]'
input: '["foobar", "foobaz", "blarp"]'
output: ['true']
- program: '. contains ["bazzzzz", "bar"]'
input: '["foobar", "foobaz", "blarp"]'
output: ['false']
- program: '. contains {foo: 12, bar: [{barp: 12}]}'
input: '{foo: 12, bar:[1,2,{barp:12, blip:13}]}'
output: ['true']
- program: '. contains {foo: 12, bar: [{barp: 15}]}'
input: '{foo: 12, bar:[1,2,{barp:12, blip:13}]}'
output: ['false']
- title: if-then-else
body: |

61
jv.c
View File

@@ -329,6 +329,28 @@ jv jv_array_slice(jv a, int start, int end) {
return a;
}
int jv_array_contains(jv a, jv b) {
int r = 1;
int a_length = jv_array_length(jv_copy(a));
int b_length = jv_array_length(jv_copy(b));
for (int bi = 0; bi < b_length; bi++) {
int ri = 0;
for (int ai = 0; ai < a_length; ai++) {
if (jv_contains(jv_array_get(jv_copy(a), ai), jv_array_get(jv_copy(b), bi))) {
ri = 1;
break;
}
}
if (!ri) {
r = 0;
break;
}
}
jv_free(a);
jv_free(b);
return r;
}
/*
* Strings (internal helpers)
@@ -863,6 +885,27 @@ jv jv_object_merge(jv a, jv b) {
return a;
}
int jv_object_contains(jv a, jv b) {
assert(jv_get_kind(a) == JV_KIND_OBJECT);
assert(jv_get_kind(b) == JV_KIND_OBJECT);
int r = 1;
jv_object_foreach(i, b) {
jv key = jv_object_iter_key(b, i);
jv a_val = jv_object_get(jv_copy(a), jv_copy(key));
jv b_val = jv_object_get(jv_copy(b), jv_copy(key));
r = jv_contains(a_val, b_val);
jv_free(key);
if (!r) break;
}
jv_free(a);
jv_free(b);
return r;
}
/*
* Object iteration (internal helpers)
*/
@@ -977,3 +1020,21 @@ int jv_equal(jv a, jv b) {
jv_free(b);
return r;
}
int jv_contains(jv a, jv b) {
int r = 1;
if (jv_get_kind(a) != jv_get_kind(b)) {
r = 0;
} else if (jv_get_kind(a) == JV_KIND_OBJECT) {
r = jv_object_contains(jv_copy(a), jv_copy(b));
} else if (jv_get_kind(a) == JV_KIND_ARRAY) {
r = jv_array_contains(jv_copy(a), jv_copy(b));
} else if (jv_get_kind(a) == JV_KIND_STRING) {
r = strstr(jv_string_value(a), jv_string_value(b)) != 0;
} else {
r = jv_equal(jv_copy(a), jv_copy(b));
}
jv_free(a);
jv_free(b);
return r;
}

1
jv.h
View File

@@ -48,6 +48,7 @@ jv jv_copy(jv);
void jv_free(jv);
int jv_equal(jv, jv);
int jv_contains(jv, jv);
jv jv_invalid();
jv jv_invalid_with_msg(jv);

View File

@@ -358,8 +358,8 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
#define YY_NUM_RULES 35
#define YY_END_OF_BUFFER 36
#define YY_NUM_RULES 36
#define YY_END_OF_BUFFER 37
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@@ -367,17 +367,19 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
static yyconst flex_int16_t yy_accept[88] =
static yyconst flex_int16_t yy_accept[96] =
{ 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 36, 34, 33, 33, 34, 26, 1, 22,
23, 24, 22, 22, 22, 22, 22, 25, 22, 22,
22, 32, 32, 32, 32, 32, 32, 32, 22, 30,
30, 28, 31, 33, 2, 1, 17, 15, 25, 16,
0, 13, 18, 20, 3, 21, 32, 32, 4, 32,
32, 32, 6, 11, 32, 14, 30, 29, 27, 29,
0, 25, 19, 10, 5, 32, 32, 12, 32, 0,
29, 9, 8, 7, 29, 29, 0
0, 0, 37, 35, 34, 34, 35, 27, 1, 23,
24, 25, 23, 23, 23, 23, 23, 26, 23, 23,
23, 33, 33, 33, 33, 33, 33, 33, 33, 23,
31, 31, 29, 32, 34, 2, 1, 17, 15, 26,
16, 0, 13, 18, 20, 3, 21, 33, 33, 4,
33, 33, 33, 33, 6, 11, 33, 14, 31, 30,
28, 30, 0, 26, 19, 10, 33, 5, 33, 33,
12, 33, 0, 30, 33, 9, 8, 7, 30, 33,
30, 33, 33, 22, 0
} ;
static yyconst flex_int32_t yy_ec[256] =
@@ -391,11 +393,11 @@ static yyconst flex_int32_t yy_ec[256] =
21, 22, 1, 1, 23, 23, 23, 23, 24, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
25, 26, 27, 1, 28, 1, 29, 23, 23, 30,
25, 26, 27, 1, 28, 1, 29, 23, 30, 31,
31, 32, 23, 33, 34, 23, 23, 35, 23, 36,
37, 23, 23, 38, 39, 40, 41, 23, 23, 23,
23, 23, 42, 43, 44, 1, 1, 1, 1, 1,
32, 33, 23, 34, 35, 23, 23, 36, 23, 37,
38, 23, 23, 39, 40, 41, 42, 23, 23, 23,
23, 23, 43, 44, 45, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -412,91 +414,97 @@ static yyconst flex_int32_t yy_ec[256] =
1, 1, 1, 1, 1
} ;
static yyconst flex_int32_t yy_meta[45] =
static yyconst flex_int32_t yy_meta[46] =
{ 0,
1, 1, 2, 2, 1, 3, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 4, 1, 1, 1,
1, 1, 4, 4, 1, 5, 1, 6, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 1, 1, 1
4, 4, 1, 1, 1
} ;
static yyconst flex_int16_t yy_base[97] =
static yyconst flex_int16_t yy_base[105] =
{ 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42, 43, 144, 145, 48, 50, 122, 145, 0, 145,
145, 145, 121, 120, 39, 40, 42, 50, 119, 118,
117, 0, 23, 91, 37, 88, 78, 77, 80, 0,
0, 145, 38, 73, 145, 0, 145, 145, 63, 145,
71, 79, 145, 145, 145, 145, 0, 69, 0, 66,
27, 67, 0, 0, 65, 145, 0, 69, 145, 67,
75, 74, 145, 0, 0, 58, 58, 0, 50, 61,
58, 0, 0, 0, 56, 51, 145, 102, 108, 111,
117, 123, 126, 128, 130, 132
43, 44, 152, 153, 49, 51, 130, 153, 0, 153,
153, 153, 129, 128, 40, 41, 43, 51, 127, 126,
125, 0, 23, 107, 112, 35, 110, 88, 91, 100,
0, 0, 153, 39, 74, 153, 0, 153, 153, 63,
153, 72, 94, 153, 153, 153, 153, 0, 75, 0,
68, 71, 27, 72, 0, 0, 70, 153, 0, 75,
153, 74, 82, 81, 153, 0, 56, 0, 63, 62,
0, 56, 65, 66, 62, 0, 0, 0, 64, 53,
59, 45, 39, 0, 153, 107, 113, 116, 122, 128,
131, 133, 135, 137
} ;
static yyconst flex_int16_t yy_def[97] =
static yyconst flex_int16_t yy_def[105] =
{ 0,
87, 1, 1, 1, 1, 1, 1, 1, 1, 1,
88, 88, 87, 87, 87, 87, 87, 87, 89, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 90, 90, 90, 90, 90, 90, 90, 87, 91,
91, 87, 92, 87, 87, 89, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 90, 90, 90, 90,
90, 90, 90, 90, 90, 87, 91, 87, 87, 93,
87, 87, 87, 90, 90, 90, 90, 90, 90, 92,
94, 90, 90, 90, 95, 96, 0, 87, 87, 87,
87, 87, 87, 87, 87, 87
95, 1, 1, 1, 1, 1, 1, 1, 1, 1,
96, 96, 95, 95, 95, 95, 95, 95, 97, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 98, 98, 98, 98, 98, 98, 98, 98, 95,
99, 99, 95, 100, 95, 95, 97, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 98, 98, 98,
98, 98, 98, 98, 98, 98, 98, 95, 99, 95,
95, 101, 95, 95, 95, 98, 98, 98, 98, 98,
98, 98, 100, 102, 98, 98, 98, 98, 103, 98,
104, 98, 98, 98, 0, 95, 95, 95, 95, 95,
95, 95, 95, 95
} ;
static yyconst flex_int16_t yy_nxt[190] =
static yyconst flex_int16_t yy_nxt[199] =
{ 0,
14, 15, 16, 14, 17, 18, 19, 20, 21, 22,
23, 24, 20, 25, 26, 27, 28, 20, 20, 29,
30, 31, 32, 32, 21, 14, 22, 32, 33, 34,
35, 32, 32, 36, 32, 32, 37, 32, 32, 38,
32, 21, 39, 22, 41, 41, 69, 42, 42, 44,
44, 44, 44, 49, 49, 49, 49, 52, 58, 50,
76, 59, 53, 51, 49, 77, 49, 43, 43, 87,
51, 61, 62, 51, 44, 44, 80, 49, 70, 49,
51, 80, 71, 80, 71, 84, 51, 72, 83, 82,
72, 72, 80, 51, 80, 79, 78, 75, 74, 73,
35, 36, 32, 32, 37, 32, 32, 38, 32, 32,
39, 32, 21, 40, 22, 42, 42, 71, 43, 43,
45, 45, 45, 45, 50, 50, 50, 50, 53, 59,
51, 79, 60, 54, 52, 50, 80, 50, 44, 44,
63, 64, 52, 95, 52, 45, 45, 50, 94, 50,
72, 93, 52, 73, 83, 73, 52, 92, 74, 83,
90, 83, 88, 87, 52, 86, 85, 74, 74, 83,
83, 82, 81, 78, 77, 76, 72, 41, 41, 41,
41, 41, 41, 47, 75, 47, 47, 47, 47, 58,
68, 58, 69, 69, 67, 69, 66, 69, 70, 70,
70, 70, 70, 70, 84, 84, 89, 89, 91, 91,
70, 70, 65, 62, 61, 57, 56, 55, 49, 48,
46, 95, 13, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95
66, 70, 40, 40, 40, 40, 40, 40, 46, 65,
46, 46, 46, 46, 57, 64, 57, 67, 67, 63,
67, 60, 67, 68, 68, 68, 68, 68, 68, 81,
81, 85, 85, 86, 86, 68, 68, 56, 55, 54,
48, 47, 45, 87, 13, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87
} ;
static yyconst flex_int16_t yy_chk[190] =
static yyconst flex_int16_t yy_chk[199] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 11, 12, 43, 11, 12, 15,
15, 16, 16, 25, 26, 25, 26, 27, 33, 25,
61, 33, 27, 26, 28, 61, 28, 11, 12, 80,
26, 35, 35, 28, 44, 44, 86, 49, 43, 49,
28, 85, 51, 81, 51, 79, 49, 51, 77, 76,
72, 71, 70, 49, 68, 65, 62, 60, 58, 52,
1, 1, 1, 1, 1, 11, 12, 44, 11, 12,
15, 15, 16, 16, 25, 26, 25, 26, 27, 33,
25, 63, 33, 27, 26, 28, 63, 28, 11, 12,
36, 36, 26, 83, 28, 45, 45, 50, 93, 50,
44, 92, 28, 52, 91, 52, 50, 90, 52, 89,
85, 84, 82, 80, 50, 79, 77, 74, 73, 72,
70, 67, 64, 62, 61, 59, 83, 96, 96, 96,
96, 96, 96, 97, 53, 97, 97, 97, 97, 98,
40, 98, 99, 99, 39, 99, 38, 99, 100, 100,
100, 100, 100, 100, 101, 101, 102, 102, 103, 103,
104, 104, 37, 35, 34, 31, 30, 29, 24, 23,
17, 13, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95
39, 80, 88, 88, 88, 88, 88, 88, 89, 38,
89, 89, 89, 89, 90, 37, 90, 91, 91, 36,
91, 34, 91, 92, 92, 92, 92, 92, 92, 93,
93, 94, 94, 95, 95, 96, 96, 31, 30, 29,
24, 23, 17, 13, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
87, 87, 87, 87, 87, 87, 87, 87, 87
} ;
/* The intent behind this definition is that it'll catch
@@ -530,7 +538,7 @@ struct lexer_param;
static int enter(int opening, int state, yyscan_t yyscanner);
static int try_exit(int closing, int state, yyscan_t yyscanner);
#define YY_NO_INPUT 1
#line 534 "lexer.gen.c"
#line 542 "lexer.gen.c"
#define INITIAL 0
#define IN_PAREN 1
@@ -786,7 +794,7 @@ YY_DECL
#line 35 "lexer.l"
#line 790 "lexer.gen.c"
#line 798 "lexer.gen.c"
yylval = yylval_param;
@@ -843,13 +851,13 @@ yy_match:
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 88 )
if ( yy_current_state >= 96 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
++yy_cp;
}
while ( yy_base[yy_current_state] != 145 );
while ( yy_base[yy_current_state] != 153 );
yy_find_action:
yy_act = yy_accept[yy_current_state];
@@ -981,57 +989,62 @@ YY_RULE_SETUP
case 22:
YY_RULE_SETUP
#line 59 "lexer.l"
{ return yytext[0];}
{ return CONTAINS; }
YY_BREAK
case 23:
YY_RULE_SETUP
#line 61 "lexer.l"
#line 60 "lexer.l"
{ return yytext[0];}
YY_BREAK
case 24:
YY_RULE_SETUP
#line 62 "lexer.l"
{
return enter(yytext[0], YY_START, yyscanner);
}
YY_BREAK
case 24:
case 25:
YY_RULE_SETUP
#line 65 "lexer.l"
#line 66 "lexer.l"
{
return try_exit(yytext[0], YY_START, yyscanner);
}
YY_BREAK
case 25:
case 26:
YY_RULE_SETUP
#line 70 "lexer.l"
#line 71 "lexer.l"
{
yylval->literal = jv_parse_sized(yytext, yyleng); return LITERAL;
}
YY_BREAK
case 26:
case 27:
YY_RULE_SETUP
#line 74 "lexer.l"
#line 75 "lexer.l"
{
yy_push_state(IN_QQSTRING, yyscanner);
return QQSTRING_START;
}
YY_BREAK
case 27:
case 28:
YY_RULE_SETUP
#line 80 "lexer.l"
#line 81 "lexer.l"
{
return enter(QQSTRING_INTERP_START, YY_START, yyscanner);
}
YY_BREAK
case 28:
case 29:
YY_RULE_SETUP
#line 83 "lexer.l"
#line 84 "lexer.l"
{
yy_pop_state(yyscanner);
return QQSTRING_END;
}
YY_BREAK
case 29:
/* rule 29 can match eol */
case 30:
/* rule 30 can match eol */
YY_RULE_SETUP
#line 87 "lexer.l"
#line 88 "lexer.l"
{
/* pass escapes to the json parser */
jv escapes = jv_string_fmt("\"%.*s\"", yyleng, yytext);
@@ -1040,45 +1053,45 @@ YY_RULE_SETUP
return QQSTRING_TEXT;
}
YY_BREAK
case 30:
/* rule 30 can match eol */
case 31:
/* rule 31 can match eol */
YY_RULE_SETUP
#line 94 "lexer.l"
#line 95 "lexer.l"
{
yylval->literal = jv_string_sized(yytext, yyleng);
return QQSTRING_TEXT;
}
YY_BREAK
case 31:
case 32:
YY_RULE_SETUP
#line 98 "lexer.l"
#line 99 "lexer.l"
{
return INVALID_CHARACTER;
}
YY_BREAK
case 32:
case 33:
YY_RULE_SETUP
#line 104 "lexer.l"
#line 105 "lexer.l"
{ yylval->literal = jv_string(yytext); return IDENT;}
YY_BREAK
case 33:
/* rule 33 can match eol */
YY_RULE_SETUP
#line 106 "lexer.l"
{}
YY_BREAK
case 34:
/* rule 34 can match eol */
YY_RULE_SETUP
#line 108 "lexer.l"
{ return INVALID_CHARACTER; }
#line 107 "lexer.l"
{}
YY_BREAK
case 35:
YY_RULE_SETUP
#line 110 "lexer.l"
#line 109 "lexer.l"
{ return INVALID_CHARACTER; }
YY_BREAK
case 36:
YY_RULE_SETUP
#line 111 "lexer.l"
YY_FATAL_ERROR( "flex scanner jammed" );
YY_BREAK
#line 1082 "lexer.gen.c"
#line 1095 "lexer.gen.c"
case YY_STATE_EOF(INITIAL):
case YY_STATE_EOF(IN_PAREN):
case YY_STATE_EOF(IN_BRACKET):
@@ -1377,7 +1390,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 88 )
if ( yy_current_state >= 96 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1406,11 +1419,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 88 )
if ( yy_current_state >= 96 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
yy_is_jam = (yy_current_state == 87);
yy_is_jam = (yy_current_state == 95);
return yy_is_jam ? 0 : yy_current_state;
}
@@ -2264,7 +2277,7 @@ void jq_yyfree (void * ptr , yyscan_t yyscanner)
#define YYTABLES_NAME "yytables"
#line 110 "lexer.l"
#line 111 "lexer.l"
/* perhaps these should be calls... */

View File

@@ -354,7 +354,7 @@ extern int jq_yylex \
#undef YY_DECL
#endif
#line 110 "lexer.l"
#line 111 "lexer.l"
#line 361 "lexer.gen.h"

View File

@@ -56,6 +56,7 @@ struct lexer_param;
"//=" { return SETDEFINEDOR; }
"<=" { return LESSEQ; }
">=" { return GREATEREQ; }
"contains" { return CONTAINS; }
"."|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"\$"|"<"|">" { return yytext[0];}
"["|"{"|"(" {

File diff suppressed because it is too large Load Diff

View File

@@ -82,11 +82,12 @@
SETDEFINEDOR = 278,
LESSEQ = 279,
GREATEREQ = 280,
QQSTRING_START = 281,
QQSTRING_TEXT = 282,
QQSTRING_INTERP_START = 283,
QQSTRING_INTERP_END = 284,
QQSTRING_END = 285
CONTAINS = 281,
QQSTRING_START = 282,
QQSTRING_TEXT = 283,
QQSTRING_INTERP_START = 284,
QQSTRING_INTERP_END = 285,
QQSTRING_END = 286
};
#endif
@@ -105,7 +106,7 @@ typedef union YYSTYPE
/* Line 2068 of yacc.c */
#line 109 "parser.gen.h"
#line 110 "parser.gen.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */

File diff suppressed because it is too large Load Diff

View File

@@ -65,6 +65,7 @@ struct lexer_param;
%token SETDEFINEDOR "//="
%token LESSEQ "<="
%token GREATEREQ ">="
%token CONTAINS "contains"
%token QQSTRING_START
%token <literal> QQSTRING_TEXT
@@ -80,7 +81,7 @@ struct lexer_param;
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETDEFINEDOR
%left OR
%left AND
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ CONTAINS
%left '+' '-'
%left '*' '/'
@@ -152,6 +153,7 @@ static block gen_binop(block a, block b, int op) {
case '>': funcname = "_greater"; break;
case LESSEQ: funcname = "_lesseq"; break;
case GREATEREQ: funcname = "_greatereq"; break;
case CONTAINS: funcname = "_contains"; break;
}
assert(funcname);
@@ -308,6 +310,10 @@ Exp ">=" Exp {
$$ = gen_binop($1, $3, GREATEREQ);
} |
Exp "contains" Exp {
$$ = gen_binop($1, $3, CONTAINS);
} |
Term {
$$ = $1;
}

View File

@@ -395,3 +395,28 @@ def inc(x): x |= .+1; inc(.[].a)
[{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}]
{}
[true,false]
# containment operator
["foo" contains "foo", "foobar" contains "foo", "foo" contains "foobar"]
{}
[true, true, false]
[[] contains [], [1,2,3] contains [1,2], [1,2,3] contains [3,1], [1,2,3] contains [4], [1,2,3] contains [1,4]]
{}
[true, true, true, false, false]
[["foobar", "foobaz"] contains ["baz", "bar"], ["foobar", "foobaz"] contains ["foo"], ["foobar", "foobaz"] contains ["blap"]]
{}
[true, true, false]
[{foo: 12, bar:13} contains {foo: 12}, {foo: 12} contains {}, {foo: 12, bar:13} contains {baz:14}]
{}
[true, true, false]
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} contains {bar: 14, foo: {blap: {}}}
{}
true
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} contains {bar: 14, foo: {blap: {bar: 14}}}
{}
false