Skip to content

Commit 3d760fa

Browse files
Merge pull request #54 from GrahamCampbell/compliance
Latest compliance
2 parents 1271bc2 + bcce266 commit 3d760fa

File tree

11 files changed

+168
-25
lines changed

11 files changed

+168
-25
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
],
1414

1515
"require": {
16-
"php": ">=5.4.0"
16+
"php": ">=5.4.0",
17+
"symfony/polyfill-mbstring": "^1.4"
1718
},
1819

1920
"require-dev": {

src/FnDispatcher.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private function fn_contains(array $args)
6262
if (is_array($args[0])) {
6363
return in_array($args[1], $args[0]);
6464
} elseif (is_string($args[1])) {
65-
return strpos($args[0], $args[1]) !== false;
65+
return mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false;
6666
} else {
6767
return null;
6868
}
@@ -72,7 +72,7 @@ private function fn_ends_with(array $args)
7272
{
7373
$this->validate('ends_with', $args, [['string'], ['string']]);
7474
list($search, $suffix) = $args;
75-
return $suffix === '' || substr($search, -strlen($suffix)) === $suffix;
75+
return $suffix === '' || mb_substr($search, -mb_strlen($suffix, 'UTF-8'), null, 'UTF-8') === $suffix;
7676
}
7777

7878
private function fn_floor(array $args)
@@ -112,7 +112,7 @@ private function fn_keys(array $args)
112112
private function fn_length(array $args)
113113
{
114114
$this->validate('length', $args, [['string', 'array', 'object']]);
115-
return is_string($args[0]) ? strlen($args[0]) : count((array) $args[0]);
115+
return is_string($args[0]) ? mb_strlen($args[0], 'UTF-8') : count((array) $args[0]);
116116
}
117117

118118
private function fn_max(array $args)
@@ -207,7 +207,7 @@ private function fn_starts_with(array $args)
207207
{
208208
$this->validate('starts_with', $args, [['string'], ['string']]);
209209
list($search, $prefix) = $args;
210-
return $prefix === '' || strpos($search, $prefix) === 0;
210+
return $prefix === '' || mb_strpos($search, $prefix, 0, 'UTF-8') === 0;
211211
}
212212

213213
private function fn_type(array $args)
@@ -240,7 +240,7 @@ private function fn_to_number(array $args)
240240
if ($type == 'number') {
241241
return $value;
242242
} elseif ($type == 'string' && is_numeric($value)) {
243-
return strpos($value, '.') ? (float) $value : (int) $value;
243+
return mb_strpos($value, '.', 0, 'UTF-8') ? (float) $value : (int) $value;
244244
} else {
245245
return null;
246246
}
@@ -282,7 +282,7 @@ private function fn_map(array $args)
282282

283283
private function typeError($from, $msg)
284284
{
285-
if (strpos($from, ':')) {
285+
if (mb_strpos($from, ':', 0, 'UTF-8')) {
286286
list($fn, $pos) = explode(':', $from);
287287
throw new \RuntimeException(
288288
sprintf('Argument %d of %s %s', $pos, $fn, $msg)

src/Lexer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public function tokenize($input)
340340
eof:
341341
$tokens[] = [
342342
'type' => self::T_EOF,
343-
'pos' => strlen($input),
343+
'pos' => mb_strlen($input, 'UTF-8'),
344344
'value' => null
345345
];
346346

src/Utils.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ private static function adjustSlice($length, $start, $stop, $step)
214214
private static function sliceIndices($subject, $start, $stop, $step)
215215
{
216216
$type = gettype($subject);
217-
$len = $type == 'string' ? strlen($subject) : count($subject);
217+
$len = $type == 'string' ? mb_strlen($subject, 'UTF-8') : count($subject);
218218
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
219219

220220
$result = [];

tests/ComplianceTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public function testPassesCompliance(
6161

6262
$file = __DIR__ . '/compliance/' . $file . '.json';
6363
$failure .= "\n{$compiledStr}php bin/jp.php --file {$file} --suite {$suite} --case {$case}\n\n"
64+
. "Result: " . $this->prettyJson($evalResult) . "\n\n"
6465
. "Expected: " . $this->prettyJson($result) . "\n\n";
6566
$failure .= 'Associative? ' . var_export($asAssoc, true) . "\n\n";
6667

tests/compliance/basic.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@
1818
"expression": "foo\n.\nbar\n.baz",
1919
"result": "correct"
2020
},
21+
{
22+
"expression": "foo . bar . baz",
23+
"result": "correct"
24+
},
25+
{
26+
"expression": "foo\t.\tbar\t.\tbaz",
27+
"result": "correct"
28+
},
29+
{
30+
"expression": "foo\r.\rbar\r.\rbaz",
31+
"result": "correct"
32+
},
33+
{
34+
"expression": "foo\r\n.\r\nbar\r\n.\r\nbaz",
35+
"result": "correct"
36+
},
2137
{
2238
"expression": "foo.bar.baz.bad",
2339
"result": null

tests/compliance/boolean.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@
205205
"given": {
206206
"one": 1,
207207
"two": 2,
208-
"three": 3
208+
"three": 3,
209+
"emptylist": [],
210+
"boolvalue": false
209211
},
210212
"cases": [
211213
{
@@ -236,6 +238,22 @@
236238
"expression": "one != two",
237239
"result": true
238240
},
241+
{
242+
"expression": "emptylist < one",
243+
"result": null
244+
},
245+
{
246+
"expression": "emptylist < nullvalue",
247+
"result": null
248+
},
249+
{
250+
"expression": "emptylist < boolvalue",
251+
"result": null
252+
},
253+
{
254+
"expression": "one < boolvalue",
255+
"result": null
256+
},
239257
{
240258
"expression": "one < two && three > one",
241259
"result": true

tests/compliance/functions.json

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
"expression": "avg(strings)",
8484
"error": "invalid-type"
8585
},
86+
{
87+
"expression": "avg(empty_list)",
88+
"result": null
89+
},
8690
{
8791
"expression": "ceil(`1.2`)",
8892
"result": 2
@@ -175,6 +179,10 @@
175179
"expression": "length('abc')",
176180
"result": 3
177181
},
182+
{
183+
"expression": "length('✓foo')",
184+
"result": 4
185+
},
178186
{
179187
"expression": "length('')",
180188
"result": 0
@@ -568,12 +576,12 @@
568576
"error": "invalid-arity"
569577
},
570578
{
571-
"description": "function projection on single arg function",
579+
"comment": "function projection on single arg function",
572580
"expression": "numbers[].to_string(@)",
573581
"result": ["-1", "3", "4", "5"]
574582
},
575583
{
576-
"description": "function projection on single arg function",
584+
"comment": "function projection on single arg function",
577585
"expression": "array[].to_number(@)",
578586
"result": [-1, 3, 4, 5, 100]
579587
}
@@ -591,7 +599,7 @@
591599
},
592600
"cases": [
593601
{
594-
"description": "function projection on variadic function",
602+
"comment": "function projection on variadic function",
595603
"expression": "foo[].not_null(f, e, d, c, b, a)",
596604
"result": ["b", "c", "d", "e", "f"]
597605
}
@@ -609,7 +617,7 @@
609617
},
610618
"cases": [
611619
{
612-
"description": "sort by field expression",
620+
"comment": "sort by field expression",
613621
"expression": "sort_by(people, &age)",
614622
"result": [
615623
{"age": 10, "age_str": "10", "bool": true, "name": 3},
@@ -630,7 +638,7 @@
630638
]
631639
},
632640
{
633-
"description": "sort by function expression",
641+
"comment": "sort by function expression",
634642
"expression": "sort_by(people, &to_number(age_str))",
635643
"result": [
636644
{"age": 10, "age_str": "10", "bool": true, "name": 3},
@@ -641,7 +649,7 @@
641649
]
642650
},
643651
{
644-
"description": "function projection on sort_by function",
652+
"comment": "function projection on sort_by function",
645653
"expression": "sort_by(people, &age)[].name",
646654
"result": [3, "a", "c", "b", "d"]
647655
},
@@ -689,6 +697,10 @@
689697
"expression": "max_by(people, &to_number(age_str))",
690698
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
691699
},
700+
{
701+
"expression": "max_by(`[]`, &age)",
702+
"result": null
703+
},
692704
{
693705
"expression": "min_by(people, &age)",
694706
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
@@ -708,6 +720,10 @@
708720
{
709721
"expression": "min_by(people, &to_number(age_str))",
710722
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
723+
},
724+
{
725+
"expression": "min_by(`[]`, &age)",
726+
"result": null
711727
}
712728
]
713729
}, {
@@ -729,7 +745,7 @@
729745
},
730746
"cases": [
731747
{
732-
"description": "stable sort order",
748+
"comment": "stable sort order",
733749
"expression": "sort_by(people, &age)",
734750
"result": [
735751
{"age": 10, "order": "1"},

tests/compliance/literal.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,16 @@
184184
"comment": "Can escape the single quote",
185185
"expression": "'foo\\'bar'",
186186
"result": "foo'bar"
187+
},
188+
{
189+
"comment": "Backslash not followed by single quote is treated as any other character",
190+
"expression": "'\\z'",
191+
"result": "\\z"
192+
},
193+
{
194+
"comment": "Backslash not followed by single quote is treated as any other character",
195+
"expression": "'\\\\'",
196+
"result": "\\\\"
187197
}
188198
]
189199
}

tests/compliance/multiselect.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,11 @@
387387
"comment": "Nested multiselect",
388388
"expression": "[[*]]",
389389
"result": [[]]
390+
},
391+
{
392+
"comment": "Select on null",
393+
"expression": "missing.{foo: bar}",
394+
"result": null
390395
}
391396
]
392397
}

0 commit comments

Comments
 (0)