File manager - Edit - /home/opticamezl/www/newok/mtdowling.tar
Back
jmespath.php/.travis.yml 0000604 00000000323 15173423247 0011260 0 ustar 00 language: php php: - 5.4 - 5.5 - 5.6 - hhvm before_script: - composer install script: make test after_script: - make perf - JP_PHP_COMPILE=on make perf - JP_PHP_COMPILE=on CACHE=on make perf jmespath.php/composer.json 0000604 00000001200 15173423247 0011664 0 ustar 00 { "name": "mtdowling/jmespath.php", "description": "Declaratively specify how to extract elements from a JSON document", "keywords": ["json", "jsonpath"], "license": "MIT", "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" } ], "require": { "php": ">=5.4.0" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "autoload": { "psr-4": { "JmesPath\\": "src/" }, "files": ["src/JmesPath.php"] }, "bin": ["bin/jp.php"], "extra": { "branch-alias": { "dev-master": "2.0-dev" } } } jmespath.php/README.rst 0000604 00000007701 15173423247 0010645 0 ustar 00 ============ jmespath.php ============ JMESPath (pronounced "jaymz path") allows you to declaratively specify how to extract elements from a JSON document. *jmespath.php* allows you to use JMESPath in PHP applications with PHP data structures. It requires PHP 5.4 or greater and can be installed through `Composer <http://getcomposer.org/doc/00-intro.md>`_ using the ``mtdowling/jmespath.php`` package. .. code-block:: php require 'vendor/autoload.php'; $expression = 'foo.*.baz'; $data = [ 'foo' => [ 'bar' => ['baz' => 1], 'bam' => ['baz' => 2], 'boo' => ['baz' => 3] ] ]; JmesPath\search($expression, $data); // Returns: [1, 2, 3] - `JMESPath Tutorial <http://jmespath.org/tutorial.html>`_ - `JMESPath Grammar <http://jmespath.org/specification.html#grammar>`_ - `JMESPath Python library <https://github.com/jmespath/jmespath.py>`_ PHP Usage ========= The ``JmesPath\search`` function can be used in most cases when using the library. This function utilizes a JMESPath runtime based on your environment. The runtime utilized can be configured using environment variables and may at some point in the future automatically utilize a C extension if available. .. code-block:: php $result = JmesPath\search($expression, $data); // or, if you require PSR-4 compliance. $result = JmesPath\Env::search($expression, $data); Runtimes -------- jmespath.php utilizes *runtimes*. There are currently two runtimes: AstRuntime and CompilerRuntime. AstRuntime is utilized by ``JmesPath\search()`` and ``JmesPath\Env::search()`` by default. AstRuntime ~~~~~~~~~~ The AstRuntime will parse an expression, cache the resulting AST in memory, and interpret the AST using an external tree visitor. AstRuntime provides a good general approach for interpreting JMESPath expressions that have a low to moderate level of reuse. .. code-block:: php $runtime = new JmesPath\AstRuntime(); $runtime('foo.bar', ['foo' => ['bar' => 'baz']]); // > 'baz' CompilerRuntime ~~~~~~~~~~~~~~~ ``JmesPath\CompilerRuntime`` provides the most performance for applications that have a moderate to high level of reuse of JMESPath expressions. The CompilerRuntime will walk a JMESPath AST and emit PHP source code, resulting in anywhere from 7x to 60x speed improvements. Compiling JMESPath expressions to source code is a slower process than just walking and interpreting a JMESPath AST (via the AstRuntime). However, running the compiled JMESPath code results in much better performance than walking an AST. This essentially means that there is a warm-up period when using the ``CompilerRuntime``, but after the warm-up period, it will provide much better performance. Use the CompilerRuntime if you know that you will be executing JMESPath expressions more than once or if you can pre-compile JMESPath expressions before executing them (for example, server-side applications). .. code-block:: php // Note: The cache directory argument is optional. $runtime = new JmesPath\CompilerRuntime('/path/to/compile/folder'); $runtime('foo.bar', ['foo' => ['bar' => 'baz']]); // > 'baz' Environment Variables ^^^^^^^^^^^^^^^^^^^^^ You can utilize the CompilerRuntime in ``JmesPath\search()`` by setting the ``JP_PHP_COMPILE`` environment variable to "on" or to a directory on disk used to store cached expressions. Testing ======= A comprehensive list of test cases can be found at https://github.com/jmespath/jmespath.php/tree/master/tests/compliance. These compliance tests are utilized by jmespath.php to ensure consistency with other implementations, and can serve as examples of the language. jmespath.php is tested using PHPUnit. In order to run the tests, you need to first install the dependencies using Composer as described in the *Installation* section. Next you just need to run the tests via make: .. code-block:: bash make test You can run a suite of performance tests as well: .. code-block:: bash make perf jmespath.php/Makefile 0000604 00000000411 15173423247 0010605 0 ustar 00 all: clean coverage test: vendor/bin/phpunit coverage: vendor/bin/phpunit --coverage-html=artifacts/coverage view-coverage: open artifacts/coverage/index.html clean: rm -rf artifacts/* rm -rf compiled/* perf: php bin/perf.php .PHONY: test coverage perf jmespath.php/.gitignore 0000604 00000000065 15173423247 0011142 0 ustar 00 vendor composer.lock phpunit.xml compiled artifacts/ jmespath.php/phpunit.xml.dist 0000604 00000000546 15173423247 0012331 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="./vendor/autoload.php" colors="true"> <testsuites> <testsuite> <directory>tests</directory> </testsuite> </testsuites> <filter> <whitelist> <directory suffix=".php">src</directory> </whitelist> </filter> </phpunit> jmespath.php/src/DebugRuntime.php 0000604 00000006160 15173423247 0013046 0 ustar 00 <?php namespace JmesPath; /** * Provides CLI debugging information for the AST and Compiler runtimes. */ class DebugRuntime { private $runtime; private $out; private $lexer; private $parser; public function __construct(callable $runtime, $output = null) { $this->runtime = $runtime; $this->out = $output ?: STDOUT; $this->lexer = new Lexer(); $this->parser = new Parser($this->lexer); } public function __invoke($expression, $data) { if ($this->runtime instanceof CompilerRuntime) { return $this->debugCompiled($expression, $data); } return $this->debugInterpreted($expression, $data); } private function debugInterpreted($expression, $data) { return $this->debugCallback( function () use ($expression, $data) { $runtime = $this->runtime; return $runtime($expression, $data); }, $expression, $data ); } private function debugCompiled($expression, $data) { $result = $this->debugCallback( function () use ($expression, $data) { $runtime = $this->runtime; return $runtime($expression, $data); }, $expression, $data ); $this->dumpCompiledCode($expression); return $result; } private function dumpTokens($expression) { $lexer = new Lexer(); fwrite($this->out, "Tokens\n======\n\n"); $tokens = $lexer->tokenize($expression); foreach ($tokens as $t) { fprintf( $this->out, "%3d %-13s %s\n", $t['pos'], $t['type'], json_encode($t['value']) ); } fwrite($this->out, "\n"); } private function dumpAst($expression) { $parser = new Parser(); $ast = $parser->parse($expression); fwrite($this->out, "AST\n========\n\n"); fwrite($this->out, json_encode($ast, JSON_PRETTY_PRINT) . "\n"); } private function dumpCompiledCode($expression) { fwrite($this->out, "Code\n========\n\n"); $dir = sys_get_temp_dir(); $hash = md5($expression); $functionName = "jmespath_{$hash}"; $filename = "{$dir}/{$functionName}.php"; fwrite($this->out, "File: {$filename}\n\n"); fprintf($this->out, file_get_contents($filename)); } private function debugCallback(callable $debugFn, $expression, $data) { fprintf($this->out, "Expression\n==========\n\n%s\n\n", $expression); $this->dumpTokens($expression); $this->dumpAst($expression); fprintf($this->out, "\nData\n====\n\n%s\n\n", json_encode($data, JSON_PRETTY_PRINT)); $startTime = microtime(true); $result = $debugFn(); $total = microtime(true) - $startTime; fprintf($this->out, "\nResult\n======\n\n%s\n\n", json_encode($result, JSON_PRETTY_PRINT)); fwrite($this->out, "Time\n====\n\n"); fprintf($this->out, "Total time: %f ms\n\n", $total); return $result; } } jmespath.php/src/AstRuntime.php 0000604 00000002673 15173423247 0012554 0 ustar 00 <?php namespace JmesPath; /** * Uses an external tree visitor to interpret an AST. */ class AstRuntime { private $parser; private $interpreter; private $cache = []; private $cachedCount = 0; public function __construct( Parser $parser = null, callable $fnDispatcher = null ) { $fnDispatcher = $fnDispatcher ?: FnDispatcher::getInstance(); $this->interpreter = new TreeInterpreter($fnDispatcher); $this->parser = $parser ?: new Parser(); } /** * Returns data from the provided input that matches a given JMESPath * expression. * * @param string $expression JMESPath expression to evaluate * @param mixed $data Data to search. This data should be data that * is similar to data returned from json_decode * using associative arrays rather than objects. * * @return mixed|null Returns the matching data or null */ public function __invoke($expression, $data) { if (!isset($this->cache[$expression])) { // Clear the AST cache when it hits 1024 entries if (++$this->cachedCount > 1024) { $this->cache = []; $this->cachedCount = 0; } $this->cache[$expression] = $this->parser->parse($expression); } return $this->interpreter->visit($this->cache[$expression], $data); } } jmespath.php/src/TreeInterpreter.php 0000604 00000017217 15173423247 0013604 0 ustar 00 <?php namespace JmesPath; /** * Tree visitor used to evaluates JMESPath AST expressions. */ class TreeInterpreter { /** @var callable */ private $fnDispatcher; /** * @param callable $fnDispatcher Function dispatching function that accepts * a function name argument and an array of * function arguments and returns the result. */ public function __construct(callable $fnDispatcher = null) { $this->fnDispatcher = $fnDispatcher ?: FnDispatcher::getInstance(); } /** * Visits each node in a JMESPath AST and returns the evaluated result. * * @param array $node JMESPath AST node * @param mixed $data Data to evaluate * * @return mixed */ public function visit(array $node, $data) { return $this->dispatch($node, $data); } /** * Recursively traverses an AST using depth-first, pre-order traversal. * The evaluation logic for each node type is embedded into a large switch * statement to avoid the cost of "double dispatch". * @return mixed */ private function dispatch(array $node, $value) { $dispatcher = $this->fnDispatcher; switch ($node['type']) { case 'field': if (is_array($value) || $value instanceof \ArrayAccess) { return isset($value[$node['value']]) ? $value[$node['value']] : null; } elseif ($value instanceof \stdClass) { return isset($value->{$node['value']}) ? $value->{$node['value']} : null; } return null; case 'subexpression': return $this->dispatch( $node['children'][1], $this->dispatch($node['children'][0], $value) ); case 'index': if (!Utils::isArray($value)) { return null; } $idx = $node['value'] >= 0 ? $node['value'] : $node['value'] + count($value); return isset($value[$idx]) ? $value[$idx] : null; case 'projection': $left = $this->dispatch($node['children'][0], $value); switch ($node['from']) { case 'object': if (!Utils::isObject($left)) { return null; } break; case 'array': if (!Utils::isArray($left)) { return null; } break; default: if (!is_array($left) || !($left instanceof \stdClass)) { return null; } } $collected = []; foreach ((array) $left as $val) { $result = $this->dispatch($node['children'][1], $val); if ($result !== null) { $collected[] = $result; } } return $collected; case 'flatten': static $skipElement = []; $value = $this->dispatch($node['children'][0], $value); if (!Utils::isArray($value)) { return null; } $merged = []; foreach ($value as $values) { // Only merge up arrays lists and not hashes if (is_array($values) && isset($values[0])) { $merged = array_merge($merged, $values); } elseif ($values !== $skipElement) { $merged[] = $values; } } return $merged; case 'literal': return $node['value']; case 'current': return $value; case 'or': $result = $this->dispatch($node['children'][0], $value); return Utils::isTruthy($result) ? $result : $this->dispatch($node['children'][1], $value); case 'and': $result = $this->dispatch($node['children'][0], $value); return Utils::isTruthy($result) ? $this->dispatch($node['children'][1], $value) : $result; case 'not': return !Utils::isTruthy( $this->dispatch($node['children'][0], $value) ); case 'pipe': return $this->dispatch( $node['children'][1], $this->dispatch($node['children'][0], $value) ); case 'multi_select_list': if ($value === null) { return null; } $collected = []; foreach ($node['children'] as $node) { $collected[] = $this->dispatch($node, $value); } return $collected; case 'multi_select_hash': if ($value === null) { return null; } $collected = []; foreach ($node['children'] as $node) { $collected[$node['value']] = $this->dispatch( $node['children'][0], $value ); } return $collected; case 'comparator': $left = $this->dispatch($node['children'][0], $value); $right = $this->dispatch($node['children'][1], $value); if ($node['value'] == '==') { return Utils::isEqual($left, $right); } elseif ($node['value'] == '!=') { return !Utils::isEqual($left, $right); } else { return self::relativeCmp($left, $right, $node['value']); } case 'condition': return Utils::isTruthy($this->dispatch($node['children'][0], $value)) ? $this->dispatch($node['children'][1], $value) : null; case 'function': $args = []; foreach ($node['children'] as $arg) { $args[] = $this->dispatch($arg, $value); } return $dispatcher($node['value'], $args); case 'slice': return is_string($value) || Utils::isArray($value) ? Utils::slice( $value, $node['value'][0], $node['value'][1], $node['value'][2] ) : null; case 'expref': $apply = $node['children'][0]; return function ($value) use ($apply) { return $this->visit($apply, $value); }; default: throw new \RuntimeException("Unknown node type: {$node['type']}"); } } /** * @return bool */ private static function relativeCmp($left, $right, $cmp) { if (!(is_int($left) || is_float($left)) || !(is_int($right) || is_float($right))) { return false; } switch ($cmp) { case '>': return $left > $right; case '>=': return $left >= $right; case '<': return $left < $right; case '<=': return $left <= $right; default: throw new \RuntimeException("Invalid comparison: $cmp"); } } } jmespath.php/src/Utils.php 0000604 00000015274 15173423247 0011562 0 ustar 00 <?php namespace JmesPath; class Utils { static $typeMap = [ 'boolean' => 'boolean', 'string' => 'string', 'NULL' => 'null', 'double' => 'number', 'float' => 'number', 'integer' => 'number' ]; /** * Returns true if the value is truthy * * @param mixed $value Value to check * * @return bool */ public static function isTruthy($value) { if (!$value) { return $value === 0 || $value === '0'; } elseif ($value instanceof \stdClass) { return (bool) get_object_vars($value); } else { return true; } } /** * Gets the JMESPath type equivalent of a PHP variable. * * @param mixed $arg PHP variable * @return string Returns the JSON data type * @throws \InvalidArgumentException when an unknown type is given. */ public static function type($arg) { $type = gettype($arg); if (isset(self::$typeMap[$type])) { return self::$typeMap[$type]; } elseif ($type === 'array') { if (empty($arg)) { return 'array'; } reset($arg); return key($arg) === 0 ? 'array' : 'object'; } elseif ($arg instanceof \stdClass) { return 'object'; } elseif ($arg instanceof \Closure) { return 'expression'; } elseif ($arg instanceof \ArrayAccess && $arg instanceof \Countable ) { return count($arg) == 0 || $arg->offsetExists(0) ? 'array' : 'object'; } elseif (method_exists($arg, '__toString')) { return 'string'; } throw new \InvalidArgumentException( 'Unable to determine JMESPath type from ' . get_class($arg) ); } /** * Determine if the provided value is a JMESPath compatible object. * * @param mixed $value * * @return bool */ public static function isObject($value) { if (is_array($value)) { return !$value || array_keys($value)[0] !== 0; } // Handle array-like values. Must be empty or offset 0 does not exist return $value instanceof \Countable && $value instanceof \ArrayAccess ? count($value) == 0 || !$value->offsetExists(0) : $value instanceof \stdClass; } /** * Determine if the provided value is a JMESPath compatible array. * * @param mixed $value * * @return bool */ public static function isArray($value) { if (is_array($value)) { return !$value || array_keys($value)[0] === 0; } // Handle array-like values. Must be empty or offset 0 exists. return $value instanceof \Countable && $value instanceof \ArrayAccess ? count($value) == 0 || $value->offsetExists(0) : false; } /** * JSON aware value comparison function. * * @param mixed $a First value to compare * @param mixed $b Second value to compare * * @return bool */ public static function isEqual($a, $b) { if ($a === $b) { return true; } elseif ($a instanceof \stdClass) { return self::isEqual((array) $a, $b); } elseif ($b instanceof \stdClass) { return self::isEqual($a, (array) $b); } else { return false; } } /** * JMESPath requires a stable sorting algorithm, so here we'll implement * a simple Schwartzian transform that uses array index positions as tie * breakers. * * @param array $data List or map of data to sort * @param callable $sortFn Callable used to sort values * * @return array Returns the sorted array * @link http://en.wikipedia.org/wiki/Schwartzian_transform */ public static function stableSort(array $data, callable $sortFn) { // Decorate each item by creating an array of [value, index] array_walk($data, function (&$v, $k) { $v = [$v, $k]; }); // Sort by the sort function and use the index as a tie-breaker uasort($data, function ($a, $b) use ($sortFn) { return $sortFn($a[0], $b[0]) ?: ($a[1] < $b[1] ? -1 : 1); }); // Undecorate each item and return the resulting sorted array return array_map(function ($v) { return $v[0]; }, array_values($data)); } /** * Creates a Python-style slice of a string or array. * * @param array|string $value Value to slice * @param int|null $start Starting position * @param int|null $stop Stop position * @param int $step Step (1, 2, -1, -2, etc.) * * @return array|string * @throws \InvalidArgumentException */ public static function slice($value, $start = null, $stop = null, $step = 1) { if (!is_array($value) && !is_string($value)) { throw new \InvalidArgumentException('Expects string or array'); } return self::sliceIndices($value, $start, $stop, $step); } private static function adjustEndpoint($length, $endpoint, $step) { if ($endpoint < 0) { $endpoint += $length; if ($endpoint < 0) { $endpoint = $step < 0 ? -1 : 0; } } elseif ($endpoint >= $length) { $endpoint = $step < 0 ? $length - 1 : $length; } return $endpoint; } private static function adjustSlice($length, $start, $stop, $step) { if ($step === null) { $step = 1; } elseif ($step === 0) { throw new \RuntimeException('step cannot be 0'); } if ($start === null) { $start = $step < 0 ? $length - 1 : 0; } else { $start = self::adjustEndpoint($length, $start, $step); } if ($stop === null) { $stop = $step < 0 ? -1 : $length; } else { $stop = self::adjustEndpoint($length, $stop, $step); } return [$start, $stop, $step]; } private static function sliceIndices($subject, $start, $stop, $step) { $type = gettype($subject); $len = $type == 'string' ? strlen($subject) : count($subject); list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step); $result = []; if ($step > 0) { for ($i = $start; $i < $stop; $i += $step) { $result[] = $subject[$i]; } } else { for ($i = $start; $i > $stop; $i += $step) { $result[] = $subject[$i]; } } return $type == 'string' ? implode($result, '') : $result; } } jmespath.php/src/FnDispatcher.php 0000604 00000027776 15173423247 0013046 0 ustar 00 <?php namespace JmesPath; /** * Dispatches to named JMESPath functions using a single function that has the * following signature: * * mixed $result = fn(string $function_name, array $args) */ class FnDispatcher { /** * Gets a cached instance of the default function implementations. * * @return FnDispatcher */ public static function getInstance() { static $instance = null; if (!$instance) { $instance = new self(); } return $instance; } /** * @param string $fn Function name. * @param array $args Function arguments. * * @return mixed */ public function __invoke($fn, array $args) { return $this->{'fn_' . $fn}($args); } private function fn_abs(array $args) { $this->validate('abs', $args, [['number']]); return abs($args[0]); } private function fn_avg(array $args) { $this->validate('avg', $args, [['array']]); $sum = $this->reduce('avg:0', $args[0], ['number'], function ($a, $b) { return $a + $b; }); return $args[0] ? ($sum / count($args[0])) : null; } private function fn_ceil(array $args) { $this->validate('ceil', $args, [['number']]); return ceil($args[0]); } private function fn_contains(array $args) { $this->validate('contains', $args, [['string', 'array'], ['any']]); if (is_array($args[0])) { return in_array($args[1], $args[0]); } elseif (is_string($args[1])) { return strpos($args[0], $args[1]) !== false; } else { return null; } } private function fn_ends_with(array $args) { $this->validate('ends_with', $args, [['string'], ['string']]); list($search, $suffix) = $args; return $suffix === '' || substr($search, -strlen($suffix)) === $suffix; } private function fn_floor(array $args) { $this->validate('floor', $args, [['number']]); return floor($args[0]); } private function fn_not_null(array $args) { if (!$args) { throw new \RuntimeException( "not_null() expects 1 or more arguments, 0 were provided" ); } return array_reduce($args, function ($carry, $item) { return $carry !== null ? $carry : $item; }); } private function fn_join(array $args) { $this->validate('join', $args, [['string'], ['array']]); $fn = function ($a, $b, $i) use ($args) { return $i ? ($a . $args[0] . $b) : $b; }; return $this->reduce('join:0', $args[1], ['string'], $fn); } private function fn_keys(array $args) { $this->validate('keys', $args, [['object']]); return array_keys((array) $args[0]); } private function fn_length(array $args) { $this->validate('length', $args, [['string', 'array', 'object']]); return is_string($args[0]) ? strlen($args[0]) : count((array) $args[0]); } private function fn_max(array $args) { $this->validate('max', $args, [['array']]); $fn = function ($a, $b) { return $a >= $b ? $a : $b; }; return $this->reduce('max:0', $args[0], ['number', 'string'], $fn); } private function fn_max_by(array $args) { $this->validate('max_by', $args, [['array'], ['expression']]); $expr = $this->wrapExpression('max_by:1', $args[1], ['number', 'string']); $fn = function ($carry, $item, $index) use ($expr) { return $index ? ($expr($carry) >= $expr($item) ? $carry : $item) : $item; }; return $this->reduce('max_by:1', $args[0], ['any'], $fn); } private function fn_min(array $args) { $this->validate('min', $args, [['array']]); $fn = function ($a, $b, $i) { return $i && $a <= $b ? $a : $b; }; return $this->reduce('min:0', $args[0], ['number', 'string'], $fn); } private function fn_min_by(array $args) { $this->validate('min_by', $args, [['array'], ['expression']]); $expr = $this->wrapExpression('min_by:1', $args[1], ['number', 'string']); $i = -1; $fn = function ($a, $b) use ($expr, &$i) { return ++$i ? ($expr($a) <= $expr($b) ? $a : $b) : $b; }; return $this->reduce('min_by:1', $args[0], ['any'], $fn); } private function fn_reverse(array $args) { $this->validate('reverse', $args, [['array', 'string']]); if (is_array($args[0])) { return array_reverse($args[0]); } elseif (is_string($args[0])) { return strrev($args[0]); } else { throw new \RuntimeException('Cannot reverse provided argument'); } } private function fn_sum(array $args) { $this->validate('sum', $args, [['array']]); $fn = function ($a, $b) { return $a + $b; }; return $this->reduce('sum:0', $args[0], ['number'], $fn); } private function fn_sort(array $args) { $this->validate('sort', $args, [['array']]); $valid = ['string', 'number']; return Utils::stableSort($args[0], function ($a, $b) use ($valid) { $this->validateSeq('sort:0', $valid, $a, $b); return strnatcmp($a, $b); }); } private function fn_sort_by(array $args) { $this->validate('sort_by', $args, [['array'], ['expression']]); $expr = $args[1]; $valid = ['string', 'number']; return Utils::stableSort( $args[0], function ($a, $b) use ($expr, $valid) { $va = $expr($a); $vb = $expr($b); $this->validateSeq('sort_by:0', $valid, $va, $vb); return strnatcmp($va, $vb); } ); } private function fn_starts_with(array $args) { $this->validate('starts_with', $args, [['string'], ['string']]); list($search, $prefix) = $args; return $prefix === '' || strpos($search, $prefix) === 0; } private function fn_type(array $args) { $this->validateArity('type', count($args), 1); return Utils::type($args[0]); } private function fn_to_string(array $args) { $this->validateArity('to_string', count($args), 1); $v = $args[0]; if (is_string($v)) { return $v; } elseif (is_object($v) && !($v instanceof \JsonSerializable) && method_exists($v, '__toString') ) { return (string) $v; } return json_encode($v); } private function fn_to_number(array $args) { $this->validateArity('to_number', count($args), 1); $value = $args[0]; $type = Utils::type($value); if ($type == 'number') { return $value; } elseif ($type == 'string' && is_numeric($value)) { return strpos($value, '.') ? (float) $value : (int) $value; } else { return null; } } private function fn_values(array $args) { $this->validate('values', $args, [['array', 'object']]); return array_values((array) $args[0]); } private function fn_merge(array $args) { if (!$args) { throw new \RuntimeException( "merge() expects 1 or more arguments, 0 were provided" ); } return call_user_func_array('array_replace', $args); } private function fn_to_array(array $args) { $this->validate('to_array', $args, [['any']]); return Utils::isArray($args[0]) ? $args[0] : [$args[0]]; } private function fn_map(array $args) { $this->validate('map', $args, [['expression'], ['any']]); $result = []; foreach ($args[1] as $a) { $result[] = $args[0]($a); } return $result; } private function typeError($from, $msg) { if (strpos($from, ':')) { list($fn, $pos) = explode(':', $from); throw new \RuntimeException( sprintf('Argument %d of %s %s', $pos, $fn, $msg) ); } else { throw new \RuntimeException( sprintf('Type error: %s %s', $from, $msg) ); } } private function validateArity($from, $given, $expected) { if ($given != $expected) { $err = "%s() expects {$expected} arguments, {$given} were provided"; throw new \RuntimeException(sprintf($err, $from)); } } private function validate($from, $args, $types = []) { $this->validateArity($from, count($args), count($types)); foreach ($args as $index => $value) { if (!isset($types[$index]) || !$types[$index]) { continue; } $this->validateType("{$from}:{$index}", $value, $types[$index]); } } private function validateType($from, $value, array $types) { if ($types[0] == 'any' || in_array(Utils::type($value), $types) || ($value === [] && in_array('object', $types)) ) { return; } $msg = 'must be one of the following types: ' . implode(', ', $types) . '. ' . Utils::type($value) . ' found'; $this->typeError($from, $msg); } /** * Validates value A and B, ensures they both are correctly typed, and of * the same type. * * @param string $from String of function:argument_position * @param array $types Array of valid value types. * @param mixed $a Value A * @param mixed $b Value B */ private function validateSeq($from, array $types, $a, $b) { $ta = Utils::type($a); $tb = Utils::type($b); if ($ta !== $tb) { $msg = "encountered a type mismatch in sequence: {$ta}, {$tb}"; $this->typeError($from, $msg); } $typeMatch = ($types && $types[0] == 'any') || in_array($ta, $types); if (!$typeMatch) { $msg = 'encountered a type error in sequence. The argument must be ' . 'an array of ' . implode('|', $types) . ' types. ' . "Found {$ta}, {$tb}."; $this->typeError($from, $msg); } } /** * Reduces and validates an array of values to a single value using a fn. * * @param string $from String of function:argument_position * @param array $values Values to reduce. * @param array $types Array of valid value types. * @param callable $reduce Reduce function that accepts ($carry, $item). * * @return mixed */ private function reduce($from, array $values, array $types, callable $reduce) { $i = -1; return array_reduce( $values, function ($carry, $item) use ($from, $types, $reduce, &$i) { if (++$i > 0) { $this->validateSeq($from, $types, $carry, $item); } return $reduce($carry, $item, $i); } ); } /** * Validates the return values of expressions as they are applied. * * @param string $from Function name : position * @param callable $expr Expression function to validate. * @param array $types Array of acceptable return type values. * * @return callable Returns a wrapped function */ private function wrapExpression($from, callable $expr, array $types) { list($fn, $pos) = explode(':', $from); $from = "The expression return value of argument {$pos} of {$fn}"; return function ($value) use ($from, $expr, $types) { $value = $expr($value); $this->validateType($from, $value, $types); return $value; }; } /** @internal Pass function name validation off to runtime */ public function __call($name, $args) { $name = str_replace('fn_', '', $name); throw new \RuntimeException("Call to undefined function {$name}"); } } jmespath.php/src/SyntaxErrorException.php 0000604 00000002146 15173423247 0014633 0 ustar 00 <?php namespace JmesPath; /** * Syntax errors raise this exception that gives context */ class SyntaxErrorException extends \InvalidArgumentException { /** * @param string $expectedTypesOrMessage Expected array of tokens or message * @param array $token Current token * @param string $expression Expression input */ public function __construct( $expectedTypesOrMessage, array $token, $expression ) { $message = "Syntax error at character {$token['pos']}\n" . $expression . "\n" . str_repeat(' ', $token['pos']) . "^\n"; $message .= !is_array($expectedTypesOrMessage) ? $expectedTypesOrMessage : $this->createTokenMessage($token, $expectedTypesOrMessage); parent::__construct($message); } private function createTokenMessage(array $token, array $valid) { return sprintf( 'Expected one of the following: %s; found %s "%s"', implode(', ', array_keys($valid)), $token['type'], $token['value'] ); } } jmespath.php/src/TreeCompiler.php 0000604 00000031432 15173423247 0013046 0 ustar 00 <?php namespace JmesPath; /** * Tree visitor used to compile JMESPath expressions into native PHP code. */ class TreeCompiler { private $indentation; private $source; private $vars; /** * @param array $ast AST to compile. * @param string $fnName The name of the function to generate. * @param string $expr Expression being compiled. * * @return string */ public function visit(array $ast, $fnName, $expr) { $this->vars = []; $this->source = $this->indentation = ''; $this->write("<?php\n") ->write('use JmesPath\\TreeInterpreter as Ti;') ->write('use JmesPath\\FnDispatcher as Fn;') ->write('use JmesPath\\Utils;') ->write('') ->write('function %s(Ti $interpreter, $value) {', $fnName) ->indent() ->dispatch($ast) ->write('') ->write('return $value;') ->outdent() ->write('}'); return $this->source; } /** * @param array $node * @return mixed */ private function dispatch(array $node) { return $this->{"visit_{$node['type']}"}($node); } /** * Creates a monotonically incrementing unique variable name by prefix. * * @param string $prefix Variable name prefix * * @return string */ private function makeVar($prefix) { if (!isset($this->vars[$prefix])) { $this->vars[$prefix] = 0; return '$' . $prefix; } return '$' . $prefix . ++$this->vars[$prefix]; } /** * Writes the given line of source code. Pass positional arguments to write * that match the format of sprintf. * * @param string $str String to write * @return $this */ private function write($str) { $this->source .= $this->indentation; if (func_num_args() == 1) { $this->source .= $str . "\n"; return $this; } $this->source .= vsprintf($str, array_slice(func_get_args(), 1)) . "\n"; return $this; } /** * Decreases the indentation level of code being written * @return $this */ private function outdent() { $this->indentation = substr($this->indentation, 0, -4); return $this; } /** * Increases the indentation level of code being written * @return $this */ private function indent() { $this->indentation .= ' '; return $this; } private function visit_or(array $node) { $a = $this->makeVar('beforeOr'); return $this ->write('%s = $value;', $a) ->dispatch($node['children'][0]) ->write('if (!$value && $value !== "0" && $value !== 0) {') ->indent() ->write('$value = %s;', $a) ->dispatch($node['children'][1]) ->outdent() ->write('}'); } private function visit_and(array $node) { $a = $this->makeVar('beforeAnd'); return $this ->write('%s = $value;', $a) ->dispatch($node['children'][0]) ->write('if ($value || $value === "0" || $value === 0) {') ->indent() ->write('$value = %s;', $a) ->dispatch($node['children'][1]) ->outdent() ->write('}'); } private function visit_not(array $node) { return $this ->write('// Visiting not node') ->dispatch($node['children'][0]) ->write('// Applying boolean not to result of not node') ->write('$value = !Utils::isTruthy($value);'); } private function visit_subexpression(array $node) { return $this ->dispatch($node['children'][0]) ->write('if ($value !== null) {') ->indent() ->dispatch($node['children'][1]) ->outdent() ->write('}'); } private function visit_field(array $node) { $arr = '$value[' . var_export($node['value'], true) . ']'; $obj = '$value->{' . var_export($node['value'], true) . '}'; $this->write('if (is_array($value) || $value instanceof \\ArrayAccess) {') ->indent() ->write('$value = isset(%s) ? %s : null;', $arr, $arr) ->outdent() ->write('} elseif ($value instanceof \\stdClass) {') ->indent() ->write('$value = isset(%s) ? %s : null;', $obj, $obj) ->outdent() ->write("} else {") ->indent() ->write('$value = null;') ->outdent() ->write("}"); return $this; } private function visit_index(array $node) { if ($node['value'] >= 0) { $check = '$value[' . $node['value'] . ']'; return $this->write( '$value = (is_array($value) || $value instanceof \\ArrayAccess)' . ' && isset(%s) ? %s : null;', $check, $check ); } $a = $this->makeVar('count'); return $this ->write('if (is_array($value) || ($value instanceof \\ArrayAccess && $value instanceof \\Countable)) {') ->indent() ->write('%s = count($value) + %s;', $a, $node['value']) ->write('$value = isset($value[%s]) ? $value[%s] : null;', $a, $a) ->outdent() ->write('} else {') ->indent() ->write('$value = null;') ->outdent() ->write('}'); } private function visit_literal(array $node) { return $this->write('$value = %s;', var_export($node['value'], true)); } private function visit_pipe(array $node) { return $this ->dispatch($node['children'][0]) ->dispatch($node['children'][1]); } private function visit_multi_select_list(array $node) { return $this->visit_multi_select_hash($node); } private function visit_multi_select_hash(array $node) { $listVal = $this->makeVar('list'); $value = $this->makeVar('prev'); $this->write('if ($value !== null) {') ->indent() ->write('%s = [];', $listVal) ->write('%s = $value;', $value); $first = true; foreach ($node['children'] as $child) { if (!$first) { $this->write('$value = %s;', $value); } $first = false; if ($node['type'] == 'multi_select_hash') { $this->dispatch($child['children'][0]); $key = var_export($child['value'], true); $this->write('%s[%s] = $value;', $listVal, $key); } else { $this->dispatch($child); $this->write('%s[] = $value;', $listVal); } } return $this ->write('$value = %s;', $listVal) ->outdent() ->write('}'); } private function visit_function(array $node) { $value = $this->makeVar('val'); $args = $this->makeVar('args'); $this->write('%s = $value;', $value) ->write('%s = [];', $args); foreach ($node['children'] as $arg) { $this->dispatch($arg); $this->write('%s[] = $value;', $args) ->write('$value = %s;', $value); } return $this->write( '$value = Fn::getInstance()->__invoke("%s", %s);', $node['value'], $args ); } private function visit_slice(array $node) { return $this ->write('$value = !is_string($value) && !Utils::isArray($value)') ->write(' ? null : Utils::slice($value, %s, %s, %s);', var_export($node['value'][0], true), var_export($node['value'][1], true), var_export($node['value'][2], true) ); } private function visit_current(array $node) { return $this->write('// Visiting current node (no-op)'); } private function visit_expref(array $node) { $child = var_export($node['children'][0], true); return $this->write('$value = function ($value) use ($interpreter) {') ->indent() ->write('return $interpreter->visit(%s, $value);', $child) ->outdent() ->write('};'); } private function visit_flatten(array $node) { $this->dispatch($node['children'][0]); $merged = $this->makeVar('merged'); $val = $this->makeVar('val'); $this ->write('// Visiting merge node') ->write('if (!Utils::isArray($value)) {') ->indent() ->write('$value = null;') ->outdent() ->write('} else {') ->indent() ->write('%s = [];', $merged) ->write('foreach ($value as %s) {', $val) ->indent() ->write('if (is_array(%s) && isset(%s[0])) {', $val, $val) ->indent() ->write('%s = array_merge(%s, %s);', $merged, $merged, $val) ->outdent() ->write('} elseif (%s !== []) {', $val) ->indent() ->write('%s[] = %s;', $merged, $val) ->outdent() ->write('}') ->outdent() ->write('}') ->write('$value = %s;', $merged) ->outdent() ->write('}'); return $this; } private function visit_projection(array $node) { $val = $this->makeVar('val'); $collected = $this->makeVar('collected'); $this->write('// Visiting projection node') ->dispatch($node['children'][0]) ->write(''); if (!isset($node['from'])) { $this->write('if (!is_array($value) || !($value instanceof \stdClass)) { $value = null; }'); } elseif ($node['from'] == 'object') { $this->write('if (!Utils::isObject($value)) { $value = null; }'); } elseif ($node['from'] == 'array') { $this->write('if (!Utils::isArray($value)) { $value = null; }'); } $this->write('if ($value !== null) {') ->indent() ->write('%s = [];', $collected) ->write('foreach ((array) $value as %s) {', $val) ->indent() ->write('$value = %s;', $val) ->dispatch($node['children'][1]) ->write('if ($value !== null) {') ->indent() ->write('%s[] = $value;', $collected) ->outdent() ->write('}') ->outdent() ->write('}') ->write('$value = %s;', $collected) ->outdent() ->write('}'); return $this; } private function visit_condition(array $node) { $value = $this->makeVar('beforeCondition'); return $this ->write('%s = $value;', $value) ->write('// Visiting condition node') ->dispatch($node['children'][0]) ->write('// Checking result of condition node') ->write('if (Utils::isTruthy($value)) {') ->indent() ->write('$value = %s;', $value) ->dispatch($node['children'][1]) ->outdent() ->write('} else {') ->indent() ->write('$value = null;') ->outdent() ->write('}'); } private function visit_comparator(array $node) { $value = $this->makeVar('val'); $a = $this->makeVar('left'); $b = $this->makeVar('right'); $this ->write('// Visiting comparator node') ->write('%s = $value;', $value) ->dispatch($node['children'][0]) ->write('%s = $value;', $a) ->write('$value = %s;', $value) ->dispatch($node['children'][1]) ->write('%s = $value;', $b); if ($node['value'] == '==') { $this->write('$value = Utils::isEqual(%s, %s);', $a, $b); } elseif ($node['value'] == '!=') { $this->write('$value = !Utils::isEqual(%s, %s);', $a, $b); } else { $this->write( '$value = (is_int(%s) || is_float(%s)) && (is_int(%s) || is_float(%s)) && %s %s %s;', $a, $a, $b, $b, $a, $node['value'], $b ); } return $this; } /** @internal */ public function __call($method, $args) { throw new \RuntimeException( sprintf('Invalid node encountered: %s', json_encode($args[0])) ); } } jmespath.php/src/Parser.php 0000604 00000033612 15173423247 0011712 0 ustar 00 <?php namespace JmesPath; use JmesPath\Lexer as T; /** * JMESPath Pratt parser * @link http://hall.org.ua/halls/wizzard/pdf/Vaughan.Pratt.TDOP.pdf */ class Parser { /** @var Lexer */ private $lexer; private $tokens; private $token; private $tpos; private $expression; private static $nullToken = ['type' => T::T_EOF]; private static $currentNode = ['type' => T::T_CURRENT]; private static $bp = [ T::T_EOF => 0, T::T_QUOTED_IDENTIFIER => 0, T::T_IDENTIFIER => 0, T::T_RBRACKET => 0, T::T_RPAREN => 0, T::T_COMMA => 0, T::T_RBRACE => 0, T::T_NUMBER => 0, T::T_CURRENT => 0, T::T_EXPREF => 0, T::T_COLON => 0, T::T_PIPE => 1, T::T_OR => 2, T::T_AND => 3, T::T_COMPARATOR => 5, T::T_FLATTEN => 9, T::T_STAR => 20, T::T_FILTER => 21, T::T_DOT => 40, T::T_NOT => 45, T::T_LBRACE => 50, T::T_LBRACKET => 55, T::T_LPAREN => 60, ]; /** @var array Acceptable tokens after a dot token */ private static $afterDot = [ T::T_IDENTIFIER => true, // foo.bar T::T_QUOTED_IDENTIFIER => true, // foo."bar" T::T_STAR => true, // foo.* T::T_LBRACE => true, // foo[1] T::T_LBRACKET => true, // foo{a: 0} T::T_FILTER => true, // foo.[?bar==10] ]; /** * @param Lexer $lexer Lexer used to tokenize expressions */ public function __construct(Lexer $lexer = null) { $this->lexer = $lexer ?: new Lexer(); } /** * Parses a JMESPath expression into an AST * * @param string $expression JMESPath expression to compile * * @return array Returns an array based AST * @throws SyntaxErrorException */ public function parse($expression) { $this->expression = $expression; $this->tokens = $this->lexer->tokenize($expression); $this->tpos = -1; $this->next(); $result = $this->expr(); if ($this->token['type'] === T::T_EOF) { return $result; } throw $this->syntax('Did not reach the end of the token stream'); } /** * Parses an expression while rbp < lbp. * * @param int $rbp Right bound precedence * * @return array */ private function expr($rbp = 0) { $left = $this->{"nud_{$this->token['type']}"}(); while ($rbp < self::$bp[$this->token['type']]) { $left = $this->{"led_{$this->token['type']}"}($left); } return $left; } private function nud_identifier() { $token = $this->token; $this->next(); return ['type' => 'field', 'value' => $token['value']]; } private function nud_quoted_identifier() { $token = $this->token; $this->next(); $this->assertNotToken(T::T_LPAREN); return ['type' => 'field', 'value' => $token['value']]; } private function nud_current() { $this->next(); return self::$currentNode; } private function nud_literal() { $token = $this->token; $this->next(); return ['type' => 'literal', 'value' => $token['value']]; } private function nud_expref() { $this->next(); return ['type' => T::T_EXPREF, 'children' => [$this->expr(self::$bp[T::T_EXPREF])]]; } private function nud_not() { $this->next(); return ['type' => T::T_NOT, 'children' => [$this->expr(self::$bp[T::T_NOT])]]; } private function nud_lparen() { $this->next(); $result = $this->expr(0); if ($this->token['type'] !== T::T_RPAREN) { throw $this->syntax('Unclosed `(`'); } $this->next(); return $result; } private function nud_lbrace() { static $validKeys = [T::T_QUOTED_IDENTIFIER => true, T::T_IDENTIFIER => true]; $this->next($validKeys); $pairs = []; do { $pairs[] = $this->parseKeyValuePair(); if ($this->token['type'] == T::T_COMMA) { $this->next($validKeys); } } while ($this->token['type'] !== T::T_RBRACE); $this->next(); return['type' => 'multi_select_hash', 'children' => $pairs]; } private function nud_flatten() { return $this->led_flatten(self::$currentNode); } private function nud_filter() { return $this->led_filter(self::$currentNode); } private function nud_star() { return $this->parseWildcardObject(self::$currentNode); } private function nud_lbracket() { $this->next(); $type = $this->token['type']; if ($type == T::T_NUMBER || $type == T::T_COLON) { return $this->parseArrayIndexExpression(); } elseif ($type == T::T_STAR && $this->lookahead() == T::T_RBRACKET) { return $this->parseWildcardArray(); } else { return $this->parseMultiSelectList(); } } private function led_lbracket(array $left) { static $nextTypes = [T::T_NUMBER => true, T::T_COLON => true, T::T_STAR => true]; $this->next($nextTypes); switch ($this->token['type']) { case T::T_NUMBER: case T::T_COLON: return [ 'type' => 'subexpression', 'children' => [$left, $this->parseArrayIndexExpression()] ]; default: return $this->parseWildcardArray($left); } } private function led_flatten(array $left) { $this->next(); return [ 'type' => 'projection', 'from' => 'array', 'children' => [ ['type' => T::T_FLATTEN, 'children' => [$left]], $this->parseProjection(self::$bp[T::T_FLATTEN]) ] ]; } private function led_dot(array $left) { $this->next(self::$afterDot); if ($this->token['type'] == T::T_STAR) { return $this->parseWildcardObject($left); } return [ 'type' => 'subexpression', 'children' => [$left, $this->parseDot(self::$bp[T::T_DOT])] ]; } private function led_or(array $left) { $this->next(); return [ 'type' => T::T_OR, 'children' => [$left, $this->expr(self::$bp[T::T_OR])] ]; } private function led_and(array $left) { $this->next(); return [ 'type' => T::T_AND, 'children' => [$left, $this->expr(self::$bp[T::T_AND])] ]; } private function led_pipe(array $left) { $this->next(); return [ 'type' => T::T_PIPE, 'children' => [$left, $this->expr(self::$bp[T::T_PIPE])] ]; } private function led_lparen(array $left) { $args = []; $this->next(); while ($this->token['type'] != T::T_RPAREN) { $args[] = $this->expr(0); if ($this->token['type'] == T::T_COMMA) { $this->next(); } } $this->next(); return [ 'type' => 'function', 'value' => $left['value'], 'children' => $args ]; } private function led_filter(array $left) { $this->next(); $expression = $this->expr(); if ($this->token['type'] != T::T_RBRACKET) { throw $this->syntax('Expected a closing rbracket for the filter'); } $this->next(); $rhs = $this->parseProjection(self::$bp[T::T_FILTER]); return [ 'type' => 'projection', 'from' => 'array', 'children' => [ $left ?: self::$currentNode, [ 'type' => 'condition', 'children' => [$expression, $rhs] ] ] ]; } private function led_comparator(array $left) { $token = $this->token; $this->next(); return [ 'type' => T::T_COMPARATOR, 'value' => $token['value'], 'children' => [$left, $this->expr(self::$bp[T::T_COMPARATOR])] ]; } private function parseProjection($bp) { $type = $this->token['type']; if (self::$bp[$type] < 10) { return self::$currentNode; } elseif ($type == T::T_DOT) { $this->next(self::$afterDot); return $this->parseDot($bp); } elseif ($type == T::T_LBRACKET || $type == T::T_FILTER) { return $this->expr($bp); } throw $this->syntax('Syntax error after projection'); } private function parseDot($bp) { if ($this->token['type'] == T::T_LBRACKET) { $this->next(); return $this->parseMultiSelectList(); } return $this->expr($bp); } private function parseKeyValuePair() { static $validColon = [T::T_COLON => true]; $key = $this->token['value']; $this->next($validColon); $this->next(); return [ 'type' => 'key_val_pair', 'value' => $key, 'children' => [$this->expr()] ]; } private function parseWildcardObject(array $left = null) { $this->next(); return [ 'type' => 'projection', 'from' => 'object', 'children' => [ $left ?: self::$currentNode, $this->parseProjection(self::$bp[T::T_STAR]) ] ]; } private function parseWildcardArray(array $left = null) { static $getRbracket = [T::T_RBRACKET => true]; $this->next($getRbracket); $this->next(); return [ 'type' => 'projection', 'from' => 'array', 'children' => [ $left ?: self::$currentNode, $this->parseProjection(self::$bp[T::T_STAR]) ] ]; } /** * Parses an array index expression (e.g., [0], [1:2:3] */ private function parseArrayIndexExpression() { static $matchNext = [ T::T_NUMBER => true, T::T_COLON => true, T::T_RBRACKET => true ]; $pos = 0; $parts = [null, null, null]; $expected = $matchNext; do { if ($this->token['type'] == T::T_COLON) { $pos++; $expected = $matchNext; } elseif ($this->token['type'] == T::T_NUMBER) { $parts[$pos] = $this->token['value']; $expected = [T::T_COLON => true, T::T_RBRACKET => true]; } $this->next($expected); } while ($this->token['type'] != T::T_RBRACKET); // Consume the closing bracket $this->next(); if ($pos === 0) { // No colons were found so this is a simple index extraction return ['type' => 'index', 'value' => $parts[0]]; } if ($pos > 2) { throw $this->syntax('Invalid array slice syntax: too many colons'); } // Sliced array from start (e.g., [2:]) return [ 'type' => 'projection', 'from' => 'array', 'children' => [ ['type' => 'slice', 'value' => $parts], $this->parseProjection(self::$bp[T::T_STAR]) ] ]; } private function parseMultiSelectList() { $nodes = []; do { $nodes[] = $this->expr(); if ($this->token['type'] == T::T_COMMA) { $this->next(); $this->assertNotToken(T::T_RBRACKET); } } while ($this->token['type'] !== T::T_RBRACKET); $this->next(); return ['type' => 'multi_select_list', 'children' => $nodes]; } private function syntax($msg) { return new SyntaxErrorException($msg, $this->token, $this->expression); } private function lookahead() { return (!isset($this->tokens[$this->tpos + 1])) ? T::T_EOF : $this->tokens[$this->tpos + 1]['type']; } private function next(array $match = null) { if (!isset($this->tokens[$this->tpos + 1])) { $this->token = self::$nullToken; } else { $this->token = $this->tokens[++$this->tpos]; } if ($match && !isset($match[$this->token['type']])) { throw $this->syntax($match); } } private function assertNotToken($type) { if ($this->token['type'] == $type) { throw $this->syntax("Token {$this->tpos} not allowed to be $type"); } } /** * @internal Handles undefined tokens without paying the cost of validation */ public function __call($method, $args) { $prefix = substr($method, 0, 4); if ($prefix == 'nud_' || $prefix == 'led_') { $token = substr($method, 4); $message = "Unexpected \"$token\" token ($method). Expected one of" . " the following tokens: " . implode(', ', array_map(function ($i) { return '"' . substr($i, 4) . '"'; }, array_filter( get_class_methods($this), function ($i) use ($prefix) { return strpos($i, $prefix) === 0; } ))); throw $this->syntax($message); } throw new \BadMethodCallException("Call to undefined method $method"); } } jmespath.php/src/Env.php 0000604 00000003653 15173423247 0011210 0 ustar 00 <?php namespace JmesPath; /** * Provides a simple environment based search. * * The runtime utilized by the Env class can be customized via environment * variables. If the JP_PHP_COMPILE environment variable is specified, then the * CompilerRuntime will be utilized. If set to "on", JMESPath expressions will * be cached to the system's temp directory. Set the environment variable to * a string to cache expressions to a specific directory. */ final class Env { const COMPILE_DIR = 'JP_PHP_COMPILE'; /** * Returns data from the input array that matches a JMESPath expression. * * @param string $expression JMESPath expression to evaluate * @param mixed $data JSON-like data to search * * @return mixed|null Returns the matching data or null */ public static function search($expression, $data) { static $runtime; if (!$runtime) { $runtime = Env::createRuntime(); } return $runtime($expression, $data); } /** * Creates a JMESPath runtime based on environment variables and extensions * available on a system. * * @return callable */ public static function createRuntime() { switch ($compileDir = getenv(self::COMPILE_DIR)) { case false: return new AstRuntime(); case 'on': return new CompilerRuntime(); default: return new CompilerRuntime($compileDir); } } /** * Delete all previously compiled JMESPath files from the JP_COMPILE_DIR * directory or sys_get_temp_dir(). * * @return int Returns the number of deleted files. */ public static function cleanCompileDir() { $total = 0; $compileDir = getenv(self::COMPILE_DIR) ?: sys_get_temp_dir(); foreach (glob("{$compileDir}/jmespath_*.php") as $file) { $total++; unlink($file); } return $total; } } jmespath.php/src/Lexer.php 0000604 00000035622 15173423247 0011540 0 ustar 00 <?php namespace JmesPath; /** * Tokenizes JMESPath expressions */ class Lexer { const T_DOT = 'dot'; const T_STAR = 'star'; const T_COMMA = 'comma'; const T_COLON = 'colon'; const T_CURRENT = 'current'; const T_EXPREF = 'expref'; const T_LPAREN = 'lparen'; const T_RPAREN = 'rparen'; const T_LBRACE = 'lbrace'; const T_RBRACE = 'rbrace'; const T_LBRACKET = 'lbracket'; const T_RBRACKET = 'rbracket'; const T_FLATTEN = 'flatten'; const T_IDENTIFIER = 'identifier'; const T_NUMBER = 'number'; const T_QUOTED_IDENTIFIER = 'quoted_identifier'; const T_UNKNOWN = 'unknown'; const T_PIPE = 'pipe'; const T_OR = 'or'; const T_AND = 'and'; const T_NOT = 'not'; const T_FILTER = 'filter'; const T_LITERAL = 'literal'; const T_EOF = 'eof'; const T_COMPARATOR = 'comparator'; const STATE_IDENTIFIER = 0; const STATE_NUMBER = 1; const STATE_SINGLE_CHAR = 2; const STATE_WHITESPACE = 3; const STATE_STRING_LITERAL = 4; const STATE_QUOTED_STRING = 5; const STATE_JSON_LITERAL = 6; const STATE_LBRACKET = 7; const STATE_PIPE = 8; const STATE_LT = 9; const STATE_GT = 10; const STATE_EQ = 11; const STATE_NOT = 12; const STATE_AND = 13; /** @var array We know what token we are consuming based on each char */ private static $transitionTable = [ '<' => self::STATE_LT, '>' => self::STATE_GT, '=' => self::STATE_EQ, '!' => self::STATE_NOT, '[' => self::STATE_LBRACKET, '|' => self::STATE_PIPE, '&' => self::STATE_AND, '`' => self::STATE_JSON_LITERAL, '"' => self::STATE_QUOTED_STRING, "'" => self::STATE_STRING_LITERAL, '-' => self::STATE_NUMBER, '0' => self::STATE_NUMBER, '1' => self::STATE_NUMBER, '2' => self::STATE_NUMBER, '3' => self::STATE_NUMBER, '4' => self::STATE_NUMBER, '5' => self::STATE_NUMBER, '6' => self::STATE_NUMBER, '7' => self::STATE_NUMBER, '8' => self::STATE_NUMBER, '9' => self::STATE_NUMBER, ' ' => self::STATE_WHITESPACE, "\t" => self::STATE_WHITESPACE, "\n" => self::STATE_WHITESPACE, "\r" => self::STATE_WHITESPACE, '.' => self::STATE_SINGLE_CHAR, '*' => self::STATE_SINGLE_CHAR, ']' => self::STATE_SINGLE_CHAR, ',' => self::STATE_SINGLE_CHAR, ':' => self::STATE_SINGLE_CHAR, '@' => self::STATE_SINGLE_CHAR, '(' => self::STATE_SINGLE_CHAR, ')' => self::STATE_SINGLE_CHAR, '{' => self::STATE_SINGLE_CHAR, '}' => self::STATE_SINGLE_CHAR, '_' => self::STATE_IDENTIFIER, 'A' => self::STATE_IDENTIFIER, 'B' => self::STATE_IDENTIFIER, 'C' => self::STATE_IDENTIFIER, 'D' => self::STATE_IDENTIFIER, 'E' => self::STATE_IDENTIFIER, 'F' => self::STATE_IDENTIFIER, 'G' => self::STATE_IDENTIFIER, 'H' => self::STATE_IDENTIFIER, 'I' => self::STATE_IDENTIFIER, 'J' => self::STATE_IDENTIFIER, 'K' => self::STATE_IDENTIFIER, 'L' => self::STATE_IDENTIFIER, 'M' => self::STATE_IDENTIFIER, 'N' => self::STATE_IDENTIFIER, 'O' => self::STATE_IDENTIFIER, 'P' => self::STATE_IDENTIFIER, 'Q' => self::STATE_IDENTIFIER, 'R' => self::STATE_IDENTIFIER, 'S' => self::STATE_IDENTIFIER, 'T' => self::STATE_IDENTIFIER, 'U' => self::STATE_IDENTIFIER, 'V' => self::STATE_IDENTIFIER, 'W' => self::STATE_IDENTIFIER, 'X' => self::STATE_IDENTIFIER, 'Y' => self::STATE_IDENTIFIER, 'Z' => self::STATE_IDENTIFIER, 'a' => self::STATE_IDENTIFIER, 'b' => self::STATE_IDENTIFIER, 'c' => self::STATE_IDENTIFIER, 'd' => self::STATE_IDENTIFIER, 'e' => self::STATE_IDENTIFIER, 'f' => self::STATE_IDENTIFIER, 'g' => self::STATE_IDENTIFIER, 'h' => self::STATE_IDENTIFIER, 'i' => self::STATE_IDENTIFIER, 'j' => self::STATE_IDENTIFIER, 'k' => self::STATE_IDENTIFIER, 'l' => self::STATE_IDENTIFIER, 'm' => self::STATE_IDENTIFIER, 'n' => self::STATE_IDENTIFIER, 'o' => self::STATE_IDENTIFIER, 'p' => self::STATE_IDENTIFIER, 'q' => self::STATE_IDENTIFIER, 'r' => self::STATE_IDENTIFIER, 's' => self::STATE_IDENTIFIER, 't' => self::STATE_IDENTIFIER, 'u' => self::STATE_IDENTIFIER, 'v' => self::STATE_IDENTIFIER, 'w' => self::STATE_IDENTIFIER, 'x' => self::STATE_IDENTIFIER, 'y' => self::STATE_IDENTIFIER, 'z' => self::STATE_IDENTIFIER, ]; /** @var array Valid identifier characters after first character */ private $validIdentifier = [ 'A' => true, 'B' => true, 'C' => true, 'D' => true, 'E' => true, 'F' => true, 'G' => true, 'H' => true, 'I' => true, 'J' => true, 'K' => true, 'L' => true, 'M' => true, 'N' => true, 'O' => true, 'P' => true, 'Q' => true, 'R' => true, 'S' => true, 'T' => true, 'U' => true, 'V' => true, 'W' => true, 'X' => true, 'Y' => true, 'Z' => true, 'a' => true, 'b' => true, 'c' => true, 'd' => true, 'e' => true, 'f' => true, 'g' => true, 'h' => true, 'i' => true, 'j' => true, 'k' => true, 'l' => true, 'm' => true, 'n' => true, 'o' => true, 'p' => true, 'q' => true, 'r' => true, 's' => true, 't' => true, 'u' => true, 'v' => true, 'w' => true, 'x' => true, 'y' => true, 'z' => true, '_' => true, '0' => true, '1' => true, '2' => true, '3' => true, '4' => true, '5' => true, '6' => true, '7' => true, '8' => true, '9' => true, ]; /** @var array Valid number characters after the first character */ private $numbers = [ '0' => true, '1' => true, '2' => true, '3' => true, '4' => true, '5' => true, '6' => true, '7' => true, '8' => true, '9' => true ]; /** @var array Map of simple single character tokens */ private $simpleTokens = [ '.' => self::T_DOT, '*' => self::T_STAR, ']' => self::T_RBRACKET, ',' => self::T_COMMA, ':' => self::T_COLON, '@' => self::T_CURRENT, '(' => self::T_LPAREN, ')' => self::T_RPAREN, '{' => self::T_LBRACE, '}' => self::T_RBRACE, ]; /** * Tokenize the JMESPath expression into an array of tokens hashes that * contain a 'type', 'value', and 'key'. * * @param string $input JMESPath input * * @return array * @throws SyntaxErrorException */ public function tokenize($input) { $tokens = []; if ($input === '') { goto eof; } $chars = str_split($input); while (false !== ($current = current($chars))) { // Every character must be in the transition character table. if (!isset(self::$transitionTable[$current])) { $tokens[] = [ 'type' => self::T_UNKNOWN, 'pos' => key($chars), 'value' => $current ]; next($chars); continue; } $state = self::$transitionTable[$current]; if ($state === self::STATE_SINGLE_CHAR) { // Consume simple tokens like ".", ",", "@", etc. $tokens[] = [ 'type' => $this->simpleTokens[$current], 'pos' => key($chars), 'value' => $current ]; next($chars); } elseif ($state === self::STATE_IDENTIFIER) { // Consume identifiers $start = key($chars); $buffer = ''; do { $buffer .= $current; $current = next($chars); } while ($current !== false && isset($this->validIdentifier[$current])); $tokens[] = [ 'type' => self::T_IDENTIFIER, 'value' => $buffer, 'pos' => $start ]; } elseif ($state === self::STATE_WHITESPACE) { // Skip whitespace next($chars); } elseif ($state === self::STATE_LBRACKET) { // Consume "[", "[?", and "[]" $position = key($chars); $actual = next($chars); if ($actual === ']') { next($chars); $tokens[] = [ 'type' => self::T_FLATTEN, 'pos' => $position, 'value' => '[]' ]; } elseif ($actual === '?') { next($chars); $tokens[] = [ 'type' => self::T_FILTER, 'pos' => $position, 'value' => '[?' ]; } else { $tokens[] = [ 'type' => self::T_LBRACKET, 'pos' => $position, 'value' => '[' ]; } } elseif ($state === self::STATE_STRING_LITERAL) { // Consume raw string literals $t = $this->inside($chars, "'", self::T_LITERAL); $t['value'] = str_replace("\\'", "'", $t['value']); $tokens[] = $t; } elseif ($state === self::STATE_PIPE) { // Consume pipe and OR $tokens[] = $this->matchOr($chars, '|', '|', self::T_OR, self::T_PIPE); } elseif ($state == self::STATE_JSON_LITERAL) { // Consume JSON literals $token = $this->inside($chars, '`', self::T_LITERAL); if ($token['type'] === self::T_LITERAL) { $token['value'] = str_replace('\\`', '`', $token['value']); $token = $this->parseJson($token); } $tokens[] = $token; } elseif ($state == self::STATE_NUMBER) { // Consume numbers $start = key($chars); $buffer = ''; do { $buffer .= $current; $current = next($chars); } while ($current !== false && isset($this->numbers[$current])); $tokens[] = [ 'type' => self::T_NUMBER, 'value' => (int)$buffer, 'pos' => $start ]; } elseif ($state === self::STATE_QUOTED_STRING) { // Consume quoted identifiers $token = $this->inside($chars, '"', self::T_QUOTED_IDENTIFIER); if ($token['type'] === self::T_QUOTED_IDENTIFIER) { $token['value'] = '"' . $token['value'] . '"'; $token = $this->parseJson($token); } $tokens[] = $token; } elseif ($state === self::STATE_EQ) { // Consume equals $tokens[] = $this->matchOr($chars, '=', '=', self::T_COMPARATOR, self::T_UNKNOWN); } elseif ($state == self::STATE_AND) { $tokens[] = $this->matchOr($chars, '&', '&', self::T_AND, self::T_EXPREF); } elseif ($state === self::STATE_NOT) { // Consume not equal $tokens[] = $this->matchOr($chars, '!', '=', self::T_COMPARATOR, self::T_NOT); } else { // either '<' or '>' // Consume less than and greater than $tokens[] = $this->matchOr($chars, $current, '=', self::T_COMPARATOR, self::T_COMPARATOR); } } eof: $tokens[] = [ 'type' => self::T_EOF, 'pos' => strlen($input), 'value' => null ]; return $tokens; } /** * Returns a token based on whether or not the next token matches the * expected value. If it does, a token of "$type" is returned. Otherwise, * a token of "$orElse" type is returned. * * @param array $chars Array of characters by reference. * @param string $current The current character. * @param string $expected Expected character. * @param string $type Expected result type. * @param string $orElse Otherwise return a token of this type. * * @return array Returns a conditional token. */ private function matchOr(array &$chars, $current, $expected, $type, $orElse) { if (next($chars) === $expected) { next($chars); return [ 'type' => $type, 'pos' => key($chars) - 1, 'value' => $current . $expected ]; } return [ 'type' => $orElse, 'pos' => key($chars) - 1, 'value' => $current ]; } /** * Returns a token the is the result of consuming inside of delimiter * characters. Escaped delimiters will be adjusted before returning a * value. If the token is not closed, "unknown" is returned. * * @param array $chars Array of characters by reference. * @param string $delim The delimiter character. * @param string $type Token type. * * @return array Returns the consumed token. */ private function inside(array &$chars, $delim, $type) { $position = key($chars); $current = next($chars); $buffer = ''; while ($current !== $delim) { if ($current === '\\') { $buffer .= '\\'; $current = next($chars); } if ($current === false) { // Unclosed delimiter return [ 'type' => self::T_UNKNOWN, 'value' => $buffer, 'pos' => $position ]; } $buffer .= $current; $current = next($chars); } next($chars); return ['type' => $type, 'value' => $buffer, 'pos' => $position]; } /** * Parses a JSON token or sets the token type to "unknown" on error. * * @param array $token Token that needs parsing. * * @return array Returns a token with a parsed value. */ private function parseJson(array $token) { $value = json_decode($token['value'], true); if ($error = json_last_error()) { // Legacy support for elided quotes. Try to parse again by adding // quotes around the bad input value. $value = json_decode('"' . $token['value'] . '"', true); if ($error = json_last_error()) { $token['type'] = self::T_UNKNOWN; return $token; } } $token['value'] = $value; return $token; } } jmespath.php/src/CompilerRuntime.php 0000604 00000005064 15173423247 0013574 0 ustar 00 <?php namespace JmesPath; /** * Compiles JMESPath expressions to PHP source code and executes it. * * JMESPath file names are stored in the cache directory using the following * logic to determine the filename: * * 1. Start with the string "jmespath_" * 2. Append the MD5 checksum of the expression. * 3. Append ".php" */ class CompilerRuntime { private $parser; private $compiler; private $cacheDir; private $interpreter; /** * @param string $dir Directory used to store compiled PHP files. * @param Parser $parser JMESPath parser to utilize * @throws \RuntimeException if the cache directory cannot be created */ public function __construct($dir = null, Parser $parser = null) { $this->parser = $parser ?: new Parser(); $this->compiler = new TreeCompiler(); $dir = $dir ?: sys_get_temp_dir(); if (!is_dir($dir) && !mkdir($dir, 0755, true)) { throw new \RuntimeException("Unable to create cache directory: $dir"); } $this->cacheDir = realpath($dir); $this->interpreter = new TreeInterpreter(); } /** * Returns data from the provided input that matches a given JMESPath * expression. * * @param string $expression JMESPath expression to evaluate * @param mixed $data Data to search. This data should be data that * is similar to data returned from json_decode * using associative arrays rather than objects. * * @return mixed|null Returns the matching data or null * @throws \RuntimeException */ public function __invoke($expression, $data) { $functionName = 'jmespath_' . md5($expression); if (!function_exists($functionName)) { $filename = "{$this->cacheDir}/{$functionName}.php"; if (!file_exists($filename)) { $this->compile($filename, $expression, $functionName); } require $filename; } return $functionName($this->interpreter, $data); } private function compile($filename, $expression, $functionName) { $code = $this->compiler->visit( $this->parser->parse($expression), $functionName, $expression ); if (!file_put_contents($filename, $code)) { throw new \RuntimeException(sprintf( 'Unable to write the compiled PHP code to: %s (%s)', $filename, var_export(error_get_last(), true) )); } } } jmespath.php/src/JmesPath.php 0000604 00000000572 15173423247 0012170 0 ustar 00 <?php namespace JmesPath; /** * Returns data from the input array that matches a JMESPath expression. * * @param string $expression Expression to search. * @param mixed $data Data to search. * * @return mixed|null */ if (!function_exists(__NAMESPACE__ . '\search')) { function search($expression, $data) { return Env::search($expression, $data); } } jmespath.php/LICENSE 0000604 00000002101 15173423247 0010150 0 ustar 00 Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jmespath.php/bin/jp.php 0000604 00000004025 15173423247 0011044 0 ustar 00 #!/usr/bin/env php <?php require __DIR__ . '/../vendor/autoload.php'; use JmesPath\Env; use JmesPath\DebugRuntime; $description = <<<EOT Runs a JMESPath expression on the provided input or a test case. Provide the JSON input and expression: echo '{}' | jp.php expression Or provide the path to a compliance script, a suite, and test case number: jp.php --script path_to_script --suite test_suite_number --case test_case_number [expression] EOT; $args = []; $currentKey = null; for ($i = 1, $total = count($argv); $i < $total; $i++) { if ($i % 2) { if (substr($argv[$i], 0, 2) == '--') { $currentKey = str_replace('--', '', $argv[$i]); } else { $currentKey = trim($argv[$i]); } } else { $args[$currentKey] = $argv[$i]; $currentKey = null; } } $expression = $currentKey; if (isset($args['file']) || isset($args['suite']) || isset($args['case'])) { if (!isset($args['file']) || !isset($args['suite']) || !isset($args['case'])) { die($description); } // Manually run a compliance test $path = realpath($args['file']); file_exists($path) or die('File not found at ' . $path); $json = json_decode(file_get_contents($path), true); $set = $json[$args['suite']]; $data = $set['given']; if (!isset($expression)) { $expression = $set['cases'][$args['case']]['expression']; echo "Expects\n=======\n"; if (isset($set['cases'][$args['case']]['result'])) { echo json_encode($set['cases'][$args['case']]['result'], JSON_PRETTY_PRINT) . "\n\n"; } elseif (isset($set['cases'][$args['case']]['error'])) { echo "{$set['cases'][$argv['case']]['error']} error\n\n"; } else { echo "NULL\n\n"; } } } elseif (isset($expression)) { // Pass in an expression and STDIN as a standalone argument $data = json_decode(stream_get_contents(STDIN), true); } else { die($description); } $runtime = new DebugRuntime(Env::createRuntime()); $runtime($expression, $data); jmespath.php/bin/perf.php 0000604 00000002732 15173423247 0011372 0 ustar 00 #!/usr/bin/env php <?php require __DIR__ . '/../vendor/autoload.php'; $dir = isset($argv[1]) ? $argv[1] : __DIR__ . '/../tests/compliance/perf'; is_dir($dir) or die('Dir not found: ' . $dir); // Warm up the runner \JmesPath\Env::search('foo', []); $total = 0; foreach (glob($dir . '/*.json') as $file) { $total += runSuite($file); } echo "\nTotal time: {$total}\n"; function runSuite($file) { $contents = file_get_contents($file); $json = json_decode($contents, true); $total = 0; foreach ($json as $suite) { foreach ($suite['cases'] as $case) { $total += runCase( $suite['given'], $case['expression'], $case['name'] ); } } return $total; } function runCase($given, $expression, $name) { $best = 99999; $runtime = \JmesPath\Env::createRuntime(); for ($i = 0; $i < 100; $i++) { $t = microtime(true); $runtime($expression, $given); $tryTime = (microtime(true) - $t) * 1000; if ($tryTime < $best) { $best = $tryTime; } if (!getenv('CACHE')) { $runtime = \JmesPath\Env::createRuntime(); // Delete compiled scripts if not caching. if ($runtime instanceof \JmesPath\CompilerRuntime) { array_map('unlink', glob(sys_get_temp_dir() . '/jmespath_*.php')); } } } printf("time: %07.4fms name: %s\n", $best, $name); return $best; } jmespath.php/tests/FnDispatcherTest.php 0000604 00000001741 15173423247 0014241 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\FnDispatcher; class FnDispatcherTest extends \PHPUnit_Framework_TestCase { public function testConvertsToString() { $fn = new FnDispatcher(); $this->assertEquals('foo', $fn('to_string', ['foo'])); $this->assertEquals('1', $fn('to_string', [1])); $this->assertEquals('["foo"]', $fn('to_string', [['foo']])); $std = new \stdClass(); $std->foo = 'bar'; $this->assertEquals('{"foo":"bar"}', $fn('to_string', [$std])); $this->assertEquals('foo', $fn('to_string', [new _TestStringClass()])); $this->assertEquals('"foo"', $fn('to_string', [new _TestJsonStringClass()])); } } class _TestStringClass { public function __toString() { return 'foo'; } } class _TestJsonStringClass implements \JsonSerializable { public function __toString() { return 'no!'; } public function jsonSerialize() { return 'foo'; } } jmespath.php/tests/LexerTest.php 0000604 00000005311 15173423247 0012743 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\Lexer; use JmesPath\SyntaxErrorException; /** * @covers JmesPath\Lexer */ class LexerTest extends \PHPUnit_Framework_TestCase { public function inputProvider() { return array( array('0', 'number'), array('1', 'number'), array('2', 'number'), array('3', 'number'), array('4', 'number'), array('5', 'number'), array('6', 'number'), array('7', 'number'), array('8', 'number'), array('9', 'number'), array('-1', 'number'), array('-1.5', 'number'), array('109.5', 'number'), array('.', 'dot'), array('{', 'lbrace'), array('}', 'rbrace'), array('[', 'lbracket'), array(']', 'rbracket'), array(':', 'colon'), array(',', 'comma'), array('||', 'or'), array('*', 'star'), array('foo', 'identifier'), array('"foo"', 'quoted_identifier'), array('`true`', 'literal'), array('`false`', 'literal'), array('`null`', 'literal'), array('`"true"`', 'literal') ); } /** * @dataProvider inputProvider */ public function testTokenizesInput($input, $type) { $l = new Lexer(); $tokens = $l->tokenize($input); $this->assertEquals($tokens[0]['type'], $type); } public function testTokenizesJsonLiterals() { $l = new Lexer(); $tokens = $l->tokenize('`null`, `false`, `true`, `"abc"`, `"ab\\"c"`,' . '`0`, `0.45`, `-0.5`'); $this->assertNull($tokens[0]['value']); $this->assertFalse($tokens[2]['value']); $this->assertTrue($tokens[4]['value']); $this->assertEquals('abc', $tokens[6]['value']); $this->assertEquals('ab"c', $tokens[8]['value']); $this->assertSame(0, $tokens[10]['value']); $this->assertSame(0.45, $tokens[12]['value']); $this->assertSame(-0.5, $tokens[14]['value']); } public function testTokenizesJsonNumbers() { $l = new Lexer(); $tokens = $l->tokenize('`10`, `1.2`, `-10.20e-10`, `1.2E+2`'); $this->assertEquals(10, $tokens[0]['value']); $this->assertEquals(1.2, $tokens[2]['value']); $this->assertEquals(-1.02E-9, $tokens[4]['value']); $this->assertEquals(120, $tokens[6]['value']); } public function testCanWorkWithElidedJsonLiterals() { $l = new Lexer(); $tokens = $l->tokenize('`foo`'); $this->assertEquals('foo', $tokens[0]['value']); $this->assertEquals('literal', $tokens[0]['type']); } } jmespath.php/tests/SyntaxErrorExceptionTest.php 0000604 00000001716 15173423247 0016050 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\SyntaxErrorException; /** * @covers JmesPath\SyntaxErrorException */ class SyntaxErrorExceptionTest extends \PHPUnit_Framework_TestCase { public function testCreatesWithNoArray() { $e = new SyntaxErrorException( 'Found comma', ['type' => 'comma', 'pos' => 3, 'value' => ','], 'abc,def' ); $expected = <<<EOT Syntax error at character 3 abc,def ^ Found comma EOT; $this->assertContains($expected, $e->getMessage()); } public function testCreatesWithArray() { $e = new SyntaxErrorException( ['dot' => true, 'eof' => true], ['type' => 'comma', 'pos' => 3, 'value' => ','], 'abc,def' ); $expected = <<<EOT Syntax error at character 3 abc,def ^ Expected one of the following: dot, eof; found comma "," EOT; $this->assertContains($expected, $e->getMessage()); } } jmespath.php/tests/UtilsTest.php 0000604 00000006612 15173423247 0012771 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\Utils; class UtilsTest extends \PHPUnit_Framework_TestCase { public function typeProvider() { return [ ['a', 'string'], [10, 'number'], [1.0, 'number'], [true, 'boolean'], [false, 'boolean'], [[], 'array'], [[1, 2], 'array'], [['a' => 1], 'object'], [new \stdClass(), 'object'], [function () {}, 'expression'], [new \ArrayObject(), 'array'], [new \ArrayObject([1, 2]), 'array'], [new \ArrayObject(['foo' => 'bar']), 'object'], [new _TestStr(), 'string'] ]; } /** * @dataProvider typeProvider */ public function testGetsTypes($given, $type) { $this->assertEquals($type, Utils::type($given)); } /** * @expectedException \InvalidArgumentException */ public function testThrowsForInvalidArg() { Utils::type(new _TestClass()); } public function isArrayProvider() { return [ [[], true], [[1, 2], true], [['a' => 1], false], [new _TestClass(), false], [new \ArrayObject(['a' => 'b']), false], [new \ArrayObject([1]), true], [new \stdClass(), false] ]; } /** * @dataProvider isArrayProvider */ public function testChecksIfArray($given, $result) { $this->assertSame($result, Utils::isArray($given)); } public function isObjectProvider() { return [ [[], true], [[1, 2], false], [['a' => 1], true], [new _TestClass(), false], [new \ArrayObject(['a' => 'b']), true], [new \ArrayObject([1]), false], [new \stdClass(), true] ]; } /** * @dataProvider isObjectProvider */ public function testChecksIfObject($given, $result) { $this->assertSame($result, Utils::isObject($given)); } public function testHasStableSort() { $data = [new _TestStr(), new _TestStr(), 0, 10, 2]; $result = Utils::stableSort($data, function ($a, $b) { $a = (int) (string) $a; $b = (int) (string) $b; return $a > $b ? -1 : ($a == $b ? 0 : 1); }); $this->assertSame($data[0], $result[0]); $this->assertSame($data[1], $result[1]); $this->assertEquals(10, $result[2]); $this->assertEquals(2, $result[3]); $this->assertEquals(0, $result[4]); } public function testSlicesArrays() { $this->assertEquals([3, 2, 1], Utils::slice([1, 2, 3], null, null, -1)); $this->assertEquals([1, 3], Utils::slice([1, 2, 3], null, null, 2)); $this->assertEquals([2, 3], Utils::slice([1, 2, 3], 1)); } public function testSlicesStrings() { $this->assertEquals('cba', Utils::slice('abc', null, null, -1)); $this->assertEquals('ac', Utils::slice('abc', null, null, 2)); $this->assertEquals('bc', Utils::slice('abc', 1)); } } class _TestClass implements \ArrayAccess { public function offsetExists($offset) {} public function offsetGet($offset) {} public function offsetSet($offset, $value) {} public function offsetUnset($offset) {} } class _TestStr { public function __toString() { return '100'; } } jmespath.php/tests/compliance/slice.json 0000604 00000007067 15173423247 0014431 0 ustar 00 [{ "given": { "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "bar": { "baz": 1 } }, "cases": [ { "expression": "bar[0:10]", "result": null }, { "expression": "foo[0:10:1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0:10]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0:10:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0::1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0::]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[:10:1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[::1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[:10:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[::]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[1:9]", "result": [1, 2, 3, 4, 5, 6, 7, 8] }, { "expression": "foo[0:10:2]", "result": [0, 2, 4, 6, 8] }, { "expression": "foo[5:]", "result": [5, 6, 7, 8, 9] }, { "expression": "foo[5::2]", "result": [5, 7, 9] }, { "expression": "foo[::2]", "result": [0, 2, 4, 6, 8] }, { "expression": "foo[::-1]", "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] }, { "expression": "foo[1::2]", "result": [1, 3, 5, 7, 9] }, { "expression": "foo[10:0:-1]", "result": [9, 8, 7, 6, 5, 4, 3, 2, 1] }, { "expression": "foo[10:5:-1]", "result": [9, 8, 7, 6] }, { "expression": "foo[8:2:-2]", "result": [8, 6, 4] }, { "expression": "foo[0:20]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[10:-20:-1]", "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] }, { "expression": "foo[10:-20]", "result": [] }, { "expression": "foo[-4:-1]", "result": [6, 7, 8] }, { "expression": "foo[:-5:-1]", "result": [9, 8, 7, 6] }, { "expression": "foo[8:2:0]", "error": "invalid-value" }, { "expression": "foo[8:2:0:1]", "error": "syntax" }, { "expression": "foo[8:2&]", "error": "syntax" }, { "expression": "foo[2:a:3]", "error": "syntax" } ] }, { "given": { "foo": [{"a": 1}, {"a": 2}, {"a": 3}], "bar": [{"a": {"b": 1}}, {"a": {"b": 2}}, {"a": {"b": 3}}], "baz": 50 }, "cases": [ { "expression": "foo[:2].a", "result": [1, 2] }, { "expression": "foo[:2].b", "result": [] }, { "expression": "foo[:2].a.b", "result": [] }, { "expression": "bar[::-1].a.b", "result": [3, 2, 1] }, { "expression": "bar[:2].a.b", "result": [1, 2] }, { "expression": "baz[:2].a", "result": null } ] }, { "given": [{"a": 1}, {"a": 2}, {"a": 3}], "cases": [ { "expression": "[:]", "result": [{"a": 1}, {"a": 2}, {"a": 3}] }, { "expression": "[:2].a", "result": [1, 2] }, { "expression": "[::-1].a", "result": [3, 2, 1] }, { "expression": "[:2].b", "result": [] } ] }] jmespath.php/tests/compliance/literal.json 0000604 00000011363 15173423247 0014760 0 ustar 00 [ { "given": { "foo": [{"name": "a"}, {"name": "b"}], "bar": {"baz": "qux"} }, "cases": [ { "expression": "`\"foo\"`", "result": "foo" }, { "comment": "Interpret escaped unicode.", "expression": "`\"\\u03a6\"`", "result": "Φ" }, { "expression": "`\"✓\"`", "result": "✓" }, { "expression": "`[1, 2, 3]`", "result": [1, 2, 3] }, { "expression": "`{\"a\": \"b\"}`", "result": {"a": "b"} }, { "expression": "`true`", "result": true }, { "expression": "`false`", "result": false }, { "expression": "`null`", "result": null }, { "expression": "`0`", "result": 0 }, { "expression": "`1`", "result": 1 }, { "expression": "`2`", "result": 2 }, { "expression": "`3`", "result": 3 }, { "expression": "`4`", "result": 4 }, { "expression": "`5`", "result": 5 }, { "expression": "`6`", "result": 6 }, { "expression": "`7`", "result": 7 }, { "expression": "`8`", "result": 8 }, { "expression": "`9`", "result": 9 }, { "comment": "Escaping a backtick in quotes", "expression": "`\"foo\\`bar\"`", "result": "foo`bar" }, { "comment": "Double quote in literal", "expression": "`\"foo\\\"bar\"`", "result": "foo\"bar" }, { "expression": "`\"1\\`\"`", "result": "1`" }, { "comment": "Multiple literal expressions with escapes", "expression": "`\"\\\\\"`.{a:`\"b\"`}", "result": {"a": "b"} }, { "comment": "literal . identifier", "expression": "`{\"a\": \"b\"}`.a", "result": "b" }, { "comment": "literal . identifier . identifier", "expression": "`{\"a\": {\"b\": \"c\"}}`.a.b", "result": "c" }, { "comment": "literal . identifier bracket-expr", "expression": "`[0, 1, 2]`[1]", "result": 1 } ] }, { "comment": "Literals", "given": {"type": "object"}, "cases": [ { "comment": "Literal with leading whitespace", "expression": "` {\"foo\": true}`", "result": {"foo": true} }, { "comment": "Literal with trailing whitespace", "expression": "`{\"foo\": true} `", "result": {"foo": true} }, { "comment": "Literal on RHS of subexpr not allowed", "expression": "foo.`\"bar\"`", "error": "syntax" } ] }, { "comment": "Raw String Literals", "given": {}, "cases": [ { "expression": "'foo'", "result": "foo" }, { "expression": "' foo '", "result": " foo " }, { "expression": "'0'", "result": "0" }, { "expression": "'newline\n'", "result": "newline\n" }, { "expression": "'\n'", "result": "\n" }, { "expression": "'✓'", "result": "✓" }, { "expression": "'𝄞'", "result": "𝄞" }, { "expression": "' [foo] '", "result": " [foo] " }, { "expression": "'[foo]'", "result": "[foo]" }, { "comment": "Do not interpret escaped unicode.", "expression": "'\\u03a6'", "result": "\\u03a6" }, { "comment": "Can escape the single quote", "expression": "'foo\\'bar'", "result": "foo'bar" } ] } ] jmespath.php/tests/compliance/indices.json 0000604 00000021137 15173423247 0014742 0 ustar 00 [{ "given": {"foo": {"bar": ["zero", "one", "two"]}}, "cases": [ { "expression": "foo.bar[0]", "result": "zero" }, { "expression": "foo.bar[1]", "result": "one" }, { "expression": "foo.bar[2]", "result": "two" }, { "expression": "foo.bar[3]", "result": null }, { "expression": "foo.bar[-1]", "result": "two" }, { "expression": "foo.bar[-2]", "result": "one" }, { "expression": "foo.bar[-3]", "result": "zero" }, { "expression": "foo.bar[-4]", "result": null } ] }, { "given": {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, "cases": [ { "expression": "foo.bar", "result": null }, { "expression": "foo[0].bar", "result": "one" }, { "expression": "foo[1].bar", "result": "two" }, { "expression": "foo[2].bar", "result": "three" }, { "expression": "foo[3].notbar", "result": "four" }, { "expression": "foo[3].bar", "result": null }, { "expression": "foo[0]", "result": {"bar": "one"} }, { "expression": "foo[1]", "result": {"bar": "two"} }, { "expression": "foo[2]", "result": {"bar": "three"} }, { "expression": "foo[3]", "result": {"notbar": "four"} }, { "expression": "foo[4]", "result": null } ] }, { "given": [ "one", "two", "three" ], "cases": [ { "expression": "[0]", "result": "one" }, { "expression": "[1]", "result": "two" }, { "expression": "[2]", "result": "three" }, { "expression": "[-1]", "result": "three" }, { "expression": "[-2]", "result": "two" }, { "expression": "[-3]", "result": "one" } ] }, { "given": {"reservations": [ {"instances": [{"foo": 1}, {"foo": 2}]} ]}, "cases": [ { "expression": "reservations[].instances[].foo", "result": [1, 2] }, { "expression": "reservations[].instances[].bar", "result": [] }, { "expression": "reservations[].notinstances[].foo", "result": [] }, { "expression": "reservations[].notinstances[].foo", "result": [] } ] }, { "given": {"reservations": [{ "instances": [ {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, {"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, {"foo": "bar"}, {"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]}, {"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]}, {"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]}, {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} ], "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} }, { "instances": [ {"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, {"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, {"c": "bar"}, {"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]}, {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} ], "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} } ]}, "cases": [ { "expression": "reservations[].instances[].foo[].bar", "result": [1, 2, 4, 5, 6, 8] }, { "expression": "reservations[].instances[].foo[].baz", "result": [] }, { "expression": "reservations[].instances[].notfoo[].bar", "result": [20, 21, 22, 23, 24, 25] }, { "expression": "reservations[].instances[].notfoo[].notbar", "result": [[7], [7]] }, { "expression": "reservations[].notinstances[].foo", "result": [] }, { "expression": "reservations[].instances[].foo[].notbar", "result": [3, [7]] }, { "expression": "reservations[].instances[].bar[].baz", "result": [[1], [2], [3], [4]] }, { "expression": "reservations[].instances[].baz[].baz", "result": [[1, 2], [], [], [3, 4]] }, { "expression": "reservations[].instances[].qux[].baz", "result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []] }, { "expression": "reservations[].instances[].qux[].baz[]", "result": [1, 2, 3, 4, 1, 2, 3, 4] } ] }, { "given": { "foo": [ [["one", "two"], ["three", "four"]], [["five", "six"], ["seven", "eight"]], [["nine"], ["ten"]] ] }, "cases": [ { "expression": "foo[]", "result": [["one", "two"], ["three", "four"], ["five", "six"], ["seven", "eight"], ["nine"], ["ten"]] }, { "expression": "foo[][0]", "result": ["one", "three", "five", "seven", "nine", "ten"] }, { "expression": "foo[][1]", "result": ["two", "four", "six", "eight"] }, { "expression": "foo[][0][0]", "result": [] }, { "expression": "foo[][2][2]", "result": [] }, { "expression": "foo[][0][0][100]", "result": [] } ] }, { "given": { "foo": [{ "bar": [ { "qux": 2, "baz": 1 }, { "qux": 4, "baz": 3 } ] }, { "bar": [ { "qux": 6, "baz": 5 }, { "qux": 8, "baz": 7 } ] } ] }, "cases": [ { "expression": "foo", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[]", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[].bar", "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] }, { "expression": "foo[].bar[]", "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] }, { "expression": "foo[].bar[].baz", "result": [1, 3, 5, 7] } ] }, { "given": { "string": "string", "hash": {"foo": "bar", "bar": "baz"}, "number": 23, "nullvalue": null }, "cases": [ { "expression": "string[]", "result": null }, { "expression": "hash[]", "result": null }, { "expression": "number[]", "result": null }, { "expression": "nullvalue[]", "result": null }, { "expression": "string[].foo", "result": null }, { "expression": "hash[].foo", "result": null }, { "expression": "number[].foo", "result": null }, { "expression": "nullvalue[].foo", "result": null }, { "expression": "nullvalue[].foo[].bar", "result": null } ] } ] jmespath.php/tests/compliance/syntax.json 0000604 00000032204 15173423247 0014647 0 ustar 00 [{ "comment": "Dot syntax", "given": {"type": "object"}, "cases": [ { "expression": "foo.bar", "result": null }, { "expression": "foo.1", "error": "syntax" }, { "expression": "foo.-11", "error": "syntax" }, { "expression": "foo", "result": null }, { "expression": "foo.", "error": "syntax" }, { "expression": "foo.", "error": "syntax" }, { "expression": ".foo", "error": "syntax" }, { "expression": "foo..bar", "error": "syntax" }, { "expression": "foo.bar.", "error": "syntax" }, { "expression": "foo[.]", "error": "syntax" } ] }, { "comment": "Simple token errors", "given": {"type": "object"}, "cases": [ { "expression": ".", "error": "syntax" }, { "expression": ":", "error": "syntax" }, { "expression": ",", "error": "syntax" }, { "expression": "]", "error": "syntax" }, { "expression": "[", "error": "syntax" }, { "expression": "}", "error": "syntax" }, { "expression": "{", "error": "syntax" }, { "expression": ")", "error": "syntax" }, { "expression": "(", "error": "syntax" }, { "expression": "((&", "error": "syntax" }, { "expression": "a[", "error": "syntax" }, { "expression": "a]", "error": "syntax" }, { "expression": "a][", "error": "syntax" }, { "expression": "!", "error": "syntax" } ] }, { "comment": "Boolean syntax errors", "given": {"type": "object"}, "cases": [ { "expression": "![!(!", "error": "syntax" } ] }, { "comment": "Wildcard syntax", "given": {"type": "object"}, "cases": [ { "expression": "*", "result": ["object"] }, { "expression": "*.*", "result": [] }, { "expression": "*.foo", "result": [] }, { "expression": "*[0]", "result": [] }, { "expression": ".*", "error": "syntax" }, { "expression": "*foo", "error": "syntax" }, { "expression": "*0", "error": "syntax" }, { "expression": "foo[*]bar", "error": "syntax" }, { "expression": "foo[*]*", "error": "syntax" } ] }, { "comment": "Flatten syntax", "given": {"type": "object"}, "cases": [ { "expression": "[]", "result": null } ] }, { "comment": "Simple bracket syntax", "given": {"type": "object"}, "cases": [ { "expression": "[0]", "result": null }, { "expression": "[*]", "result": null }, { "expression": "*.[0]", "error": "syntax" }, { "expression": "*.[\"0\"]", "result": [[null]] }, { "expression": "[*].bar", "result": null }, { "expression": "[*][0]", "result": null }, { "expression": "foo[#]", "error": "syntax" } ] }, { "comment": "Multi-select list syntax", "given": {"type": "object"}, "cases": [ { "expression": "foo[0]", "result": null }, { "comment": "Valid multi-select of a list", "expression": "foo[0, 1]", "error": "syntax" }, { "expression": "foo.[0]", "error": "syntax" }, { "expression": "foo.[*]", "result": null }, { "comment": "Multi-select of a list with trailing comma", "expression": "foo[0, ]", "error": "syntax" }, { "comment": "Multi-select of a list with trailing comma and no close", "expression": "foo[0,", "error": "syntax" }, { "comment": "Multi-select of a list with trailing comma and no close", "expression": "foo.[a", "error": "syntax" }, { "comment": "Multi-select of a list with extra comma", "expression": "foo[0,, 1]", "error": "syntax" }, { "comment": "Multi-select of a list using an identifier index", "expression": "foo[abc]", "error": "syntax" }, { "comment": "Multi-select of a list using identifier indices", "expression": "foo[abc, def]", "error": "syntax" }, { "comment": "Multi-select of a list using an identifier index", "expression": "foo[abc, 1]", "error": "syntax" }, { "comment": "Multi-select of a list using an identifier index with trailing comma", "expression": "foo[abc, ]", "error": "syntax" }, { "comment": "Valid multi-select of a hash using an identifier index", "expression": "foo.[abc]", "result": null }, { "comment": "Valid multi-select of a hash", "expression": "foo.[abc, def]", "result": null }, { "comment": "Multi-select of a hash using a numeric index", "expression": "foo.[abc, 1]", "error": "syntax" }, { "comment": "Multi-select of a hash with a trailing comma", "expression": "foo.[abc, ]", "error": "syntax" }, { "comment": "Multi-select of a hash with extra commas", "expression": "foo.[abc,, def]", "error": "syntax" }, { "comment": "Multi-select of a hash using number indices", "expression": "foo.[0, 1]", "error": "syntax" } ] }, { "comment": "Multi-select hash syntax", "given": {"type": "object"}, "cases": [ { "comment": "No key or value", "expression": "a{}", "error": "syntax" }, { "comment": "No closing token", "expression": "a{", "error": "syntax" }, { "comment": "Not a key value pair", "expression": "a{foo}", "error": "syntax" }, { "comment": "Missing value and closing character", "expression": "a{foo:", "error": "syntax" }, { "comment": "Missing closing character", "expression": "a{foo: 0", "error": "syntax" }, { "comment": "Missing value", "expression": "a{foo:}", "error": "syntax" }, { "comment": "Trailing comma and no closing character", "expression": "a{foo: 0, ", "error": "syntax" }, { "comment": "Missing value with trailing comma", "expression": "a{foo: ,}", "error": "syntax" }, { "comment": "Accessing Array using an identifier", "expression": "a{foo: bar}", "error": "syntax" }, { "expression": "a{foo: 0}", "error": "syntax" }, { "comment": "Missing key-value pair", "expression": "a.{}", "error": "syntax" }, { "comment": "Not a key-value pair", "expression": "a.{foo}", "error": "syntax" }, { "comment": "Missing value", "expression": "a.{foo:}", "error": "syntax" }, { "comment": "Missing value with trailing comma", "expression": "a.{foo: ,}", "error": "syntax" }, { "comment": "Valid multi-select hash extraction", "expression": "a.{foo: bar}", "result": null }, { "comment": "Valid multi-select hash extraction", "expression": "a.{foo: bar, baz: bam}", "result": null }, { "comment": "Trailing comma", "expression": "a.{foo: bar, }", "error": "syntax" }, { "comment": "Missing key in second key-value pair", "expression": "a.{foo: bar, baz}", "error": "syntax" }, { "comment": "Missing value in second key-value pair", "expression": "a.{foo: bar, baz:}", "error": "syntax" }, { "comment": "Trailing comma", "expression": "a.{foo: bar, baz: bam, }", "error": "syntax" }, { "comment": "Nested multi select", "expression": "{\"\\\\\":{\" \":*}}", "result": {"\\": {" ": ["object"]}} } ] }, { "comment": "Or expressions", "given": {"type": "object"}, "cases": [ { "expression": "foo || bar", "result": null }, { "expression": "foo ||", "error": "syntax" }, { "expression": "foo.|| bar", "error": "syntax" }, { "expression": " || foo", "error": "syntax" }, { "expression": "foo || || foo", "error": "syntax" }, { "expression": "foo.[a || b]", "result": null }, { "expression": "foo.[a ||]", "error": "syntax" }, { "expression": "\"foo", "error": "syntax" } ] }, { "comment": "Filter expressions", "given": {"type": "object"}, "cases": [ { "expression": "foo[?bar==`\"baz\"`]", "result": null }, { "expression": "foo[? bar == `\"baz\"` ]", "result": null }, { "expression": "foo[ ?bar==`\"baz\"`]", "error": "syntax" }, { "expression": "foo[?bar==]", "error": "syntax" }, { "expression": "foo[?==]", "error": "syntax" }, { "expression": "foo[?==bar]", "error": "syntax" }, { "expression": "foo[?bar==baz?]", "error": "syntax" }, { "expression": "foo[?a.b.c==d.e.f]", "result": null }, { "expression": "foo[?bar==`[0, 1, 2]`]", "result": null }, { "expression": "foo[?bar==`[\"a\", \"b\", \"c\"]`]", "result": null }, { "comment": "Literal char not escaped", "expression": "foo[?bar==`[\"foo`bar\"]`]", "error": "syntax" }, { "comment": "Literal char escaped", "expression": "foo[?bar==`[\"foo\\`bar\"]`]", "result": null }, { "comment": "Unknown comparator", "expression": "foo[?bar<>baz]", "error": "syntax" }, { "comment": "Unknown comparator", "expression": "foo[?bar^baz]", "error": "syntax" }, { "expression": "foo[bar==baz]", "error": "syntax" }, { "comment": "Quoted identifier in filter expression no spaces", "expression": "[?\"\\\\\">`\"foo\"`]", "result": null }, { "comment": "Quoted identifier in filter expression with spaces", "expression": "[?\"\\\\\" > `\"foo\"`]", "result": null } ] }, { "comment": "Filter expression errors", "given": {"type": "object"}, "cases": [ { "expression": "bar.`\"anything\"`", "error": "syntax" }, { "expression": "bar.baz.noexists.`\"literal\"`", "error": "syntax" }, { "comment": "Literal wildcard projection", "expression": "foo[*].`\"literal\"`", "error": "syntax" }, { "expression": "foo[*].name.`\"literal\"`", "error": "syntax" }, { "expression": "foo[].name.`\"literal\"`", "error": "syntax" }, { "expression": "foo[].name.`\"literal\"`.`\"subliteral\"`", "error": "syntax" }, { "comment": "Projecting a literal onto an empty list", "expression": "foo[*].name.noexist.`\"literal\"`", "error": "syntax" }, { "expression": "foo[].name.noexist.`\"literal\"`", "error": "syntax" }, { "expression": "twolen[*].`\"foo\"`", "error": "syntax" }, { "comment": "Two level projection of a literal", "expression": "twolen[*].threelen[*].`\"bar\"`", "error": "syntax" }, { "comment": "Two level flattened projection of a literal", "expression": "twolen[].threelen[].`\"bar\"`", "error": "syntax" } ] }, { "comment": "Identifiers", "given": {"type": "object"}, "cases": [ { "expression": "foo", "result": null }, { "expression": "\"foo\"", "result": null }, { "expression": "\"\\\\\"", "result": null } ] }, { "comment": "Combined syntax", "given": [], "cases": [ { "expression": "*||*|*|*", "result": [] }, { "expression": "*[]||[*]", "result": [] }, { "expression": "[*.*]", "result": [[]] } ] } ] jmespath.php/tests/compliance/functions.json 0000604 00000043105 15173423247 0015333 0 ustar 00 [{ "given": { "foo": -1, "zero": 0, "numbers": [-1, 3, 4, 5], "array": [-1, 3, 4, 5, "a", "100"], "strings": ["a", "b", "c"], "decimals": [1.01, 1.2, -1.5], "str": "Str", "false": false, "empty_list": [], "empty_hash": {}, "objects": {"foo": "bar", "bar": "baz"}, "null_key": null }, "cases": [ { "expression": "abs(foo)", "result": 1 }, { "expression": "abs(foo)", "result": 1 }, { "expression": "abs(str)", "error": "invalid-type" }, { "expression": "abs(array[1])", "result": 3 }, { "expression": "abs(array[1])", "result": 3 }, { "expression": "abs(`false`)", "error": "invalid-type" }, { "expression": "abs(`-24`)", "result": 24 }, { "expression": "abs(`-24`)", "result": 24 }, { "expression": "abs(`1`, `2`)", "error": "invalid-arity" }, { "expression": "abs()", "error": "invalid-arity" }, { "expression": "unknown_function(`1`, `2`)", "error": "unknown-function" }, { "expression": "avg(numbers)", "result": 2.75 }, { "expression": "avg(array)", "error": "invalid-type" }, { "expression": "avg('abc')", "error": "invalid-type" }, { "expression": "avg(foo)", "error": "invalid-type" }, { "expression": "avg(@)", "error": "invalid-type" }, { "expression": "avg(strings)", "error": "invalid-type" }, { "expression": "ceil(`1.2`)", "result": 2 }, { "expression": "ceil(decimals[0])", "result": 2 }, { "expression": "ceil(decimals[1])", "result": 2 }, { "expression": "ceil(decimals[2])", "result": -1 }, { "expression": "ceil('string')", "error": "invalid-type" }, { "expression": "contains('abc', 'a')", "result": true }, { "expression": "contains('abc', 'd')", "result": false }, { "expression": "contains(`false`, 'd')", "error": "invalid-type" }, { "expression": "contains(strings, 'a')", "result": true }, { "expression": "contains(decimals, `1.2`)", "result": true }, { "expression": "contains(decimals, `false`)", "result": false }, { "expression": "ends_with(str, 'r')", "result": true }, { "expression": "ends_with(str, 'tr')", "result": true }, { "expression": "ends_with(str, 'Str')", "result": true }, { "expression": "ends_with(str, 'SStr')", "result": false }, { "expression": "ends_with(str, 'foo')", "result": false }, { "expression": "ends_with(str, `0`)", "error": "invalid-type" }, { "expression": "floor(`1.2`)", "result": 1 }, { "expression": "floor('string')", "error": "invalid-type" }, { "expression": "floor(decimals[0])", "result": 1 }, { "expression": "floor(foo)", "result": -1 }, { "expression": "floor(str)", "error": "invalid-type" }, { "expression": "length('abc')", "result": 3 }, { "expression": "length('')", "result": 0 }, { "expression": "length(@)", "result": 12 }, { "expression": "length(strings[0])", "result": 1 }, { "expression": "length(str)", "result": 3 }, { "expression": "length(array)", "result": 6 }, { "expression": "length(objects)", "result": 2 }, { "expression": "length(`false`)", "error": "invalid-type" }, { "expression": "length(foo)", "error": "invalid-type" }, { "expression": "length(strings[0])", "result": 1 }, { "expression": "max(numbers)", "result": 5 }, { "expression": "max(decimals)", "result": 1.2 }, { "expression": "max(strings)", "result": "c" }, { "expression": "max(abc)", "error": "invalid-type" }, { "expression": "max(array)", "error": "invalid-type" }, { "expression": "max(decimals)", "result": 1.2 }, { "expression": "max(empty_list)", "result": null }, { "expression": "merge(`{}`)", "result": {} }, { "expression": "merge(`{}`, `{}`)", "result": {} }, { "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)", "result": {"a": 1, "b": 2} }, { "expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)", "result": {"a": 2} }, { "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)", "result": {"a": 2, "b": 2, "c": 3, "d": 4} }, { "expression": "min(numbers)", "result": -1 }, { "expression": "min(decimals)", "result": -1.5 }, { "expression": "min(abc)", "error": "invalid-type" }, { "expression": "min(array)", "error": "invalid-type" }, { "expression": "min(empty_list)", "result": null }, { "expression": "min(decimals)", "result": -1.5 }, { "expression": "min(strings)", "result": "a" }, { "expression": "type('abc')", "result": "string" }, { "expression": "type(`1.0`)", "result": "number" }, { "expression": "type(`2`)", "result": "number" }, { "expression": "type(`true`)", "result": "boolean" }, { "expression": "type(`false`)", "result": "boolean" }, { "expression": "type(`null`)", "result": "null" }, { "expression": "type(`[0]`)", "result": "array" }, { "expression": "type(`{\"a\": \"b\"}`)", "result": "object" }, { "expression": "type(@)", "result": "object" }, { "expression": "sort(keys(objects))", "result": ["bar", "foo"] }, { "expression": "keys(foo)", "error": "invalid-type" }, { "expression": "keys(strings)", "error": "invalid-type" }, { "expression": "keys(`false`)", "error": "invalid-type" }, { "expression": "sort(values(objects))", "result": ["bar", "baz"] }, { "expression": "keys(empty_hash)", "result": [] }, { "expression": "values(foo)", "error": "invalid-type" }, { "expression": "join(', ', strings)", "result": "a, b, c" }, { "expression": "join(', ', strings)", "result": "a, b, c" }, { "expression": "join(',', `[\"a\", \"b\"]`)", "result": "a,b" }, { "expression": "join(',', `[\"a\", 0]`)", "error": "invalid-type" }, { "expression": "join(', ', str)", "error": "invalid-type" }, { "expression": "join('|', strings)", "result": "a|b|c" }, { "expression": "join(`2`, strings)", "error": "invalid-type" }, { "expression": "join('|', decimals)", "error": "invalid-type" }, { "expression": "join('|', decimals[].to_string(@))", "result": "1.01|1.2|-1.5" }, { "expression": "join('|', empty_list)", "result": "" }, { "expression": "reverse(numbers)", "result": [5, 4, 3, -1] }, { "expression": "reverse(array)", "result": ["100", "a", 5, 4, 3, -1] }, { "expression": "reverse(`[]`)", "result": [] }, { "expression": "reverse('')", "result": "" }, { "expression": "reverse('hello world')", "result": "dlrow olleh" }, { "expression": "starts_with(str, 'S')", "result": true }, { "expression": "starts_with(str, 'St')", "result": true }, { "expression": "starts_with(str, 'Str')", "result": true }, { "expression": "starts_with(str, 'String')", "result": false }, { "expression": "starts_with(str, `0`)", "error": "invalid-type" }, { "expression": "sum(numbers)", "result": 11 }, { "expression": "sum(decimals)", "result": 0.71 }, { "expression": "sum(array)", "error": "invalid-type" }, { "expression": "sum(array[].to_number(@))", "result": 111 }, { "expression": "sum(`[]`)", "result": 0 }, { "expression": "to_array('foo')", "result": ["foo"] }, { "expression": "to_array(`0`)", "result": [0] }, { "expression": "to_array(objects)", "result": [{"foo": "bar", "bar": "baz"}] }, { "expression": "to_array(`[1, 2, 3]`)", "result": [1, 2, 3] }, { "expression": "to_array(false)", "result": [false] }, { "expression": "to_string('foo')", "result": "foo" }, { "expression": "to_string(`1.2`)", "result": "1.2" }, { "expression": "to_string(`[0, 1]`)", "result": "[0,1]" }, { "expression": "to_number('1.0')", "result": 1.0 }, { "expression": "to_number('1.1')", "result": 1.1 }, { "expression": "to_number('4')", "result": 4 }, { "expression": "to_number('notanumber')", "result": null }, { "expression": "to_number(`false`)", "result": null }, { "expression": "to_number(`null`)", "result": null }, { "expression": "to_number(`[0]`)", "result": null }, { "expression": "to_number(`{\"foo\": 0}`)", "result": null }, { "expression": "\"to_string\"(`1.0`)", "error": "syntax" }, { "expression": "sort(numbers)", "result": [-1, 3, 4, 5] }, { "expression": "sort(strings)", "result": ["a", "b", "c"] }, { "expression": "sort(decimals)", "result": [-1.5, 1.01, 1.2] }, { "expression": "sort(array)", "error": "invalid-type" }, { "expression": "sort(abc)", "error": "invalid-type" }, { "expression": "sort(empty_list)", "result": [] }, { "expression": "sort(@)", "error": "invalid-type" }, { "expression": "not_null(unknown_key, str)", "result": "Str" }, { "expression": "not_null(unknown_key, foo.bar, empty_list, str)", "result": [] }, { "expression": "not_null(unknown_key, null_key, empty_list, str)", "result": [] }, { "expression": "not_null(all, expressions, are_null)", "result": null }, { "expression": "not_null()", "error": "invalid-arity" }, { "description": "function projection on single arg function", "expression": "numbers[].to_string(@)", "result": ["-1", "3", "4", "5"] }, { "description": "function projection on single arg function", "expression": "array[].to_number(@)", "result": [-1, 3, 4, 5, 100] } ] }, { "given": { "foo": [ {"b": "b", "a": "a"}, {"c": "c", "b": "b"}, {"d": "d", "c": "c"}, {"e": "e", "d": "d"}, {"f": "f", "e": "e"} ] }, "cases": [ { "description": "function projection on variadic function", "expression": "foo[].not_null(f, e, d, c, b, a)", "result": ["b", "c", "d", "e", "f"] } ] }, { "given": { "people": [ {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"}, {"age": 10, "age_str": "10", "bool": true, "name": 3} ] }, "cases": [ { "description": "sort by field expression", "expression": "sort_by(people, &age)", "result": [ {"age": 10, "age_str": "10", "bool": true, "name": 3}, {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"} ] }, { "expression": "sort_by(people, &age_str)", "result": [ {"age": 10, "age_str": "10", "bool": true, "name": 3}, {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"} ] }, { "description": "sort by function expression", "expression": "sort_by(people, &to_number(age_str))", "result": [ {"age": 10, "age_str": "10", "bool": true, "name": 3}, {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"} ] }, { "description": "function projection on sort_by function", "expression": "sort_by(people, &age)[].name", "result": [3, "a", "c", "b", "d"] }, { "expression": "sort_by(people, &extra)", "error": "invalid-type" }, { "expression": "sort_by(people, &bool)", "error": "invalid-type" }, { "expression": "sort_by(people, &name)", "error": "invalid-type" }, { "expression": "sort_by(people, name)", "error": "invalid-type" }, { "expression": "sort_by(people, &age)[].extra", "result": ["foo", "bar"] }, { "expression": "sort_by(`[]`, &age)", "result": [] }, { "expression": "max_by(people, &age)", "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} }, { "expression": "max_by(people, &age_str)", "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} }, { "expression": "max_by(people, &bool)", "error": "invalid-type" }, { "expression": "max_by(people, &extra)", "error": "invalid-type" }, { "expression": "max_by(people, &to_number(age_str))", "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} }, { "expression": "min_by(people, &age)", "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} }, { "expression": "min_by(people, &age_str)", "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} }, { "expression": "min_by(people, &bool)", "error": "invalid-type" }, { "expression": "min_by(people, &extra)", "error": "invalid-type" }, { "expression": "min_by(people, &to_number(age_str))", "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} } ] }, { "given": { "people": [ {"age": 10, "order": "1"}, {"age": 10, "order": "2"}, {"age": 10, "order": "3"}, {"age": 10, "order": "4"}, {"age": 10, "order": "5"}, {"age": 10, "order": "6"}, {"age": 10, "order": "7"}, {"age": 10, "order": "8"}, {"age": 10, "order": "9"}, {"age": 10, "order": "10"}, {"age": 10, "order": "11"} ] }, "cases": [ { "description": "stable sort order", "expression": "sort_by(people, &age)", "result": [ {"age": 10, "order": "1"}, {"age": 10, "order": "2"}, {"age": 10, "order": "3"}, {"age": 10, "order": "4"}, {"age": 10, "order": "5"}, {"age": 10, "order": "6"}, {"age": 10, "order": "7"}, {"age": 10, "order": "8"}, {"age": 10, "order": "9"}, {"age": 10, "order": "10"}, {"age": 10, "order": "11"} ] } ] }, { "given": { "people": [ {"a": 10, "b": 1, "c": "z"}, {"a": 10, "b": 2, "c": null}, {"a": 10, "b": 3}, {"a": 10, "b": 4, "c": "z"}, {"a": 10, "b": 5, "c": null}, {"a": 10, "b": 6}, {"a": 10, "b": 7, "c": "z"}, {"a": 10, "b": 8, "c": null}, {"a": 10, "b": 9} ], "empty": [] }, "cases": [ { "expression": "map(&a, people)", "result": [10, 10, 10, 10, 10, 10, 10, 10, 10] }, { "expression": "map(&c, people)", "result": ["z", null, null, "z", null, null, "z", null, null] }, { "expression": "map(&a, badkey)", "error": "invalid-type" }, { "expression": "map(&foo, empty)", "result": [] } ] }, { "given": { "array": [ { "foo": {"bar": "yes1"} }, { "foo": {"bar": "yes2"} }, { "foo1": {"bar": "no"} } ]}, "cases": [ { "expression": "map(&foo.bar, array)", "result": ["yes1", "yes2", null] }, { "expression": "map(&foo1.bar, array)", "result": [null, null, "no"] }, { "expression": "map(&foo.bar.baz, array)", "result": [null, null, null] } ] }, { "given": { "array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]] }, "cases": [ { "expression": "map(&[], array)", "result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]] } ] } ] jmespath.php/tests/compliance/pipe.json 0000604 00000004451 15173423247 0014261 0 ustar 00 [{ "given": { "foo": { "bar": { "baz": "subkey" }, "other": { "baz": "subkey" }, "other2": { "baz": "subkey" }, "other3": { "notbaz": ["a", "b", "c"] }, "other4": { "notbaz": ["a", "b", "c"] } } }, "cases": [ { "expression": "foo.*.baz | [0]", "result": "subkey" }, { "expression": "foo.*.baz | [1]", "result": "subkey" }, { "expression": "foo.*.baz | [2]", "result": "subkey" }, { "expression": "foo.bar.* | [0]", "result": "subkey" }, { "expression": "foo.*.notbaz | [*]", "result": [["a", "b", "c"], ["a", "b", "c"]] }, { "expression": "{\"a\": foo.bar, \"b\": foo.other} | *.baz", "result": ["subkey", "subkey"] } ] }, { "given": { "foo": { "bar": { "baz": "one" }, "other": { "baz": "two" }, "other2": { "baz": "three" }, "other3": { "notbaz": ["a", "b", "c"] }, "other4": { "notbaz": ["d", "e", "f"] } } }, "cases": [ { "expression": "foo | bar", "result": {"baz": "one"} }, { "expression": "foo | bar | baz", "result": "one" }, { "expression": "foo|bar| baz", "result": "one" }, { "expression": "not_there | [0]", "result": null }, { "expression": "not_there | [0]", "result": null }, { "expression": "[foo.bar, foo.other] | [0]", "result": {"baz": "one"} }, { "expression": "{\"a\": foo.bar, \"b\": foo.other} | a", "result": {"baz": "one"} }, { "expression": "{\"a\": foo.bar, \"b\": foo.other} | b", "result": {"baz": "two"} }, { "expression": "foo.bam || foo.bar | baz", "result": "one" }, { "expression": "foo | not_there || bar", "result": {"baz": "one"} } ] }, { "given": { "foo": [{ "bar": [{ "baz": "one" }, { "baz": "two" }] }, { "bar": [{ "baz": "three" }, { "baz": "four" }] }] }, "cases": [ { "expression": "foo[*].bar[*] | [0][0]", "result": {"baz": "one"} } ] }] jmespath.php/tests/compliance/basic.json 0000604 00000003767 15173423247 0014416 0 ustar 00 [{ "given": {"foo": {"bar": {"baz": "correct"}}}, "cases": [ { "expression": "foo", "result": {"bar": {"baz": "correct"}} }, { "expression": "foo.bar", "result": {"baz": "correct"} }, { "expression": "foo.bar.baz", "result": "correct" }, { "expression": "foo\n.\nbar\n.baz", "result": "correct" }, { "expression": "foo.bar.baz.bad", "result": null }, { "expression": "foo.bar.bad", "result": null }, { "expression": "foo.bad", "result": null }, { "expression": "bad", "result": null }, { "expression": "bad.morebad.morebad", "result": null } ] }, { "given": {"foo": {"bar": ["one", "two", "three"]}}, "cases": [ { "expression": "foo", "result": {"bar": ["one", "two", "three"]} }, { "expression": "foo.bar", "result": ["one", "two", "three"] } ] }, { "given": ["one", "two", "three"], "cases": [ { "expression": "one", "result": null }, { "expression": "two", "result": null }, { "expression": "three", "result": null }, { "expression": "one.two", "result": null } ] }, { "given": {"foo": {"1": ["one", "two", "three"], "-1": "bar"}}, "cases": [ { "expression": "foo.\"1\"", "result": ["one", "two", "three"] }, { "expression": "foo.\"1\"[0]", "result": "one" }, { "expression": "foo.\"-1\"", "result": "bar" } ] } ] jmespath.php/tests/compliance/identifiers.json 0000604 00000060232 15173423247 0015630 0 ustar 00 [ { "given": { "__L": true }, "cases": [ { "expression": "__L", "result": true } ] }, { "given": { "!\r": true }, "cases": [ { "expression": "\"!\\r\"", "result": true } ] }, { "given": { "Y_1623": true }, "cases": [ { "expression": "Y_1623", "result": true } ] }, { "given": { "x": true }, "cases": [ { "expression": "x", "result": true } ] }, { "given": { "\tF\uCebb": true }, "cases": [ { "expression": "\"\\tF\\uCebb\"", "result": true } ] }, { "given": { " \t": true }, "cases": [ { "expression": "\" \\t\"", "result": true } ] }, { "given": { " ": true }, "cases": [ { "expression": "\" \"", "result": true } ] }, { "given": { "v2": true }, "cases": [ { "expression": "v2", "result": true } ] }, { "given": { "\t": true }, "cases": [ { "expression": "\"\\t\"", "result": true } ] }, { "given": { "_X": true }, "cases": [ { "expression": "_X", "result": true } ] }, { "given": { "\t4\ud9da\udd15": true }, "cases": [ { "expression": "\"\\t4\\ud9da\\udd15\"", "result": true } ] }, { "given": { "v24_W": true }, "cases": [ { "expression": "v24_W", "result": true } ] }, { "given": { "H": true }, "cases": [ { "expression": "\"H\"", "result": true } ] }, { "given": { "\f": true }, "cases": [ { "expression": "\"\\f\"", "result": true } ] }, { "given": { "E4": true }, "cases": [ { "expression": "\"E4\"", "result": true } ] }, { "given": { "!": true }, "cases": [ { "expression": "\"!\"", "result": true } ] }, { "given": { "tM": true }, "cases": [ { "expression": "tM", "result": true } ] }, { "given": { " [": true }, "cases": [ { "expression": "\" [\"", "result": true } ] }, { "given": { "R!": true }, "cases": [ { "expression": "\"R!\"", "result": true } ] }, { "given": { "_6W": true }, "cases": [ { "expression": "_6W", "result": true } ] }, { "given": { "\uaBA1\r": true }, "cases": [ { "expression": "\"\\uaBA1\\r\"", "result": true } ] }, { "given": { "tL7": true }, "cases": [ { "expression": "tL7", "result": true } ] }, { "given": { "<<U\t": true }, "cases": [ { "expression": "\"<<U\\t\"", "result": true } ] }, { "given": { "\ubBcE\ufAfB": true }, "cases": [ { "expression": "\"\\ubBcE\\ufAfB\"", "result": true } ] }, { "given": { "sNA_": true }, "cases": [ { "expression": "sNA_", "result": true } ] }, { "given": { "9": true }, "cases": [ { "expression": "\"9\"", "result": true } ] }, { "given": { "\\\b\ud8cb\udc83": true }, "cases": [ { "expression": "\"\\\\\\b\\ud8cb\\udc83\"", "result": true } ] }, { "given": { "r": true }, "cases": [ { "expression": "\"r\"", "result": true } ] }, { "given": { "Q": true }, "cases": [ { "expression": "Q", "result": true } ] }, { "given": { "_Q__7GL8": true }, "cases": [ { "expression": "_Q__7GL8", "result": true } ] }, { "given": { "\\": true }, "cases": [ { "expression": "\"\\\\\"", "result": true } ] }, { "given": { "RR9_": true }, "cases": [ { "expression": "RR9_", "result": true } ] }, { "given": { "\r\f:": true }, "cases": [ { "expression": "\"\\r\\f:\"", "result": true } ] }, { "given": { "r7": true }, "cases": [ { "expression": "r7", "result": true } ] }, { "given": { "-": true }, "cases": [ { "expression": "\"-\"", "result": true } ] }, { "given": { "p9": true }, "cases": [ { "expression": "p9", "result": true } ] }, { "given": { "__": true }, "cases": [ { "expression": "__", "result": true } ] }, { "given": { "\b\t": true }, "cases": [ { "expression": "\"\\b\\t\"", "result": true } ] }, { "given": { "O_": true }, "cases": [ { "expression": "O_", "result": true } ] }, { "given": { "_r_8": true }, "cases": [ { "expression": "_r_8", "result": true } ] }, { "given": { "_j": true }, "cases": [ { "expression": "_j", "result": true } ] }, { "given": { ":": true }, "cases": [ { "expression": "\":\"", "result": true } ] }, { "given": { "\rB": true }, "cases": [ { "expression": "\"\\rB\"", "result": true } ] }, { "given": { "Obf": true }, "cases": [ { "expression": "Obf", "result": true } ] }, { "given": { "\n": true }, "cases": [ { "expression": "\"\\n\"", "result": true } ] }, { "given": { "\f\udb54\udf33": true }, "cases": [ { "expression": "\"\\f\udb54\udf33\"", "result": true } ] }, { "given": { "\\\u4FDc": true }, "cases": [ { "expression": "\"\\\\\\u4FDc\"", "result": true } ] }, { "given": { "\r": true }, "cases": [ { "expression": "\"\\r\"", "result": true } ] }, { "given": { "m_": true }, "cases": [ { "expression": "m_", "result": true } ] }, { "given": { "\r\fB ": true }, "cases": [ { "expression": "\"\\r\\fB \"", "result": true } ] }, { "given": { "+\"\"": true }, "cases": [ { "expression": "\"+\\\"\\\"\"", "result": true } ] }, { "given": { "Mg": true }, "cases": [ { "expression": "Mg", "result": true } ] }, { "given": { "\"!\/": true }, "cases": [ { "expression": "\"\\\"!\\/\"", "result": true } ] }, { "given": { "7\"": true }, "cases": [ { "expression": "\"7\\\"\"", "result": true } ] }, { "given": { "\\\udb3a\udca4S": true }, "cases": [ { "expression": "\"\\\\\udb3a\udca4S\"", "result": true } ] }, { "given": { "\"": true }, "cases": [ { "expression": "\"\\\"\"", "result": true } ] }, { "given": { "Kl": true }, "cases": [ { "expression": "Kl", "result": true } ] }, { "given": { "\b\b": true }, "cases": [ { "expression": "\"\\b\\b\"", "result": true } ] }, { "given": { ">": true }, "cases": [ { "expression": "\">\"", "result": true } ] }, { "given": { "hvu": true }, "cases": [ { "expression": "hvu", "result": true } ] }, { "given": { "; !": true }, "cases": [ { "expression": "\"; !\"", "result": true } ] }, { "given": { "hU": true }, "cases": [ { "expression": "hU", "result": true } ] }, { "given": { "!I\n\/": true }, "cases": [ { "expression": "\"!I\\n\\/\"", "result": true } ] }, { "given": { "\uEEbF": true }, "cases": [ { "expression": "\"\\uEEbF\"", "result": true } ] }, { "given": { "U)\t": true }, "cases": [ { "expression": "\"U)\\t\"", "result": true } ] }, { "given": { "fa0_9": true }, "cases": [ { "expression": "fa0_9", "result": true } ] }, { "given": { "/": true }, "cases": [ { "expression": "\"/\"", "result": true } ] }, { "given": { "Gy": true }, "cases": [ { "expression": "Gy", "result": true } ] }, { "given": { "\b": true }, "cases": [ { "expression": "\"\\b\"", "result": true } ] }, { "given": { "<": true }, "cases": [ { "expression": "\"<\"", "result": true } ] }, { "given": { "\t": true }, "cases": [ { "expression": "\"\\t\"", "result": true } ] }, { "given": { "\t&\\\r": true }, "cases": [ { "expression": "\"\\t&\\\\\\r\"", "result": true } ] }, { "given": { "#": true }, "cases": [ { "expression": "\"#\"", "result": true } ] }, { "given": { "B__": true }, "cases": [ { "expression": "B__", "result": true } ] }, { "given": { "\nS \n": true }, "cases": [ { "expression": "\"\\nS \\n\"", "result": true } ] }, { "given": { "Bp": true }, "cases": [ { "expression": "Bp", "result": true } ] }, { "given": { ",\t;": true }, "cases": [ { "expression": "\",\\t;\"", "result": true } ] }, { "given": { "B_q": true }, "cases": [ { "expression": "B_q", "result": true } ] }, { "given": { "\/+\t\n\b!Z": true }, "cases": [ { "expression": "\"\\/+\\t\\n\\b!Z\"", "result": true } ] }, { "given": { "\udadd\udfc7\\ueFAc": true }, "cases": [ { "expression": "\"\udadd\udfc7\\\\ueFAc\"", "result": true } ] }, { "given": { ":\f": true }, "cases": [ { "expression": "\":\\f\"", "result": true } ] }, { "given": { "\/": true }, "cases": [ { "expression": "\"\\/\"", "result": true } ] }, { "given": { "_BW_6Hg_Gl": true }, "cases": [ { "expression": "_BW_6Hg_Gl", "result": true } ] }, { "given": { "\udbcf\udc02": true }, "cases": [ { "expression": "\"\udbcf\udc02\"", "result": true } ] }, { "given": { "zs1DC": true }, "cases": [ { "expression": "zs1DC", "result": true } ] }, { "given": { "__434": true }, "cases": [ { "expression": "__434", "result": true } ] }, { "given": { "\udb94\udd41": true }, "cases": [ { "expression": "\"\udb94\udd41\"", "result": true } ] }, { "given": { "Z_5": true }, "cases": [ { "expression": "Z_5", "result": true } ] }, { "given": { "z_M_": true }, "cases": [ { "expression": "z_M_", "result": true } ] }, { "given": { "YU_2": true }, "cases": [ { "expression": "YU_2", "result": true } ] }, { "given": { "_0": true }, "cases": [ { "expression": "_0", "result": true } ] }, { "given": { "\b+": true }, "cases": [ { "expression": "\"\\b+\"", "result": true } ] }, { "given": { "\"": true }, "cases": [ { "expression": "\"\\\"\"", "result": true } ] }, { "given": { "D7": true }, "cases": [ { "expression": "D7", "result": true } ] }, { "given": { "_62L": true }, "cases": [ { "expression": "_62L", "result": true } ] }, { "given": { "\tK\t": true }, "cases": [ { "expression": "\"\\tK\\t\"", "result": true } ] }, { "given": { "\n\\\f": true }, "cases": [ { "expression": "\"\\n\\\\\\f\"", "result": true } ] }, { "given": { "I_": true }, "cases": [ { "expression": "I_", "result": true } ] }, { "given": { "W_a0_": true }, "cases": [ { "expression": "W_a0_", "result": true } ] }, { "given": { "BQ": true }, "cases": [ { "expression": "BQ", "result": true } ] }, { "given": { "\tX$\uABBb": true }, "cases": [ { "expression": "\"\\tX$\\uABBb\"", "result": true } ] }, { "given": { "Z9": true }, "cases": [ { "expression": "Z9", "result": true } ] }, { "given": { "\b%\"\uda38\udd0f": true }, "cases": [ { "expression": "\"\\b%\\\"\uda38\udd0f\"", "result": true } ] }, { "given": { "_F": true }, "cases": [ { "expression": "_F", "result": true } ] }, { "given": { "!,": true }, "cases": [ { "expression": "\"!,\"", "result": true } ] }, { "given": { "\"!": true }, "cases": [ { "expression": "\"\\\"!\"", "result": true } ] }, { "given": { "Hh": true }, "cases": [ { "expression": "Hh", "result": true } ] }, { "given": { "&": true }, "cases": [ { "expression": "\"&\"", "result": true } ] }, { "given": { "9\r\\R": true }, "cases": [ { "expression": "\"9\\r\\\\R\"", "result": true } ] }, { "given": { "M_k": true }, "cases": [ { "expression": "M_k", "result": true } ] }, { "given": { "!\b\n\udb06\ude52\"\"": true }, "cases": [ { "expression": "\"!\\b\\n\udb06\ude52\\\"\\\"\"", "result": true } ] }, { "given": { "6": true }, "cases": [ { "expression": "\"6\"", "result": true } ] }, { "given": { "_7": true }, "cases": [ { "expression": "_7", "result": true } ] }, { "given": { "0": true }, "cases": [ { "expression": "\"0\"", "result": true } ] }, { "given": { "\\8\\": true }, "cases": [ { "expression": "\"\\\\8\\\\\"", "result": true } ] }, { "given": { "b7eo": true }, "cases": [ { "expression": "b7eo", "result": true } ] }, { "given": { "xIUo9": true }, "cases": [ { "expression": "xIUo9", "result": true } ] }, { "given": { "5": true }, "cases": [ { "expression": "\"5\"", "result": true } ] }, { "given": { "?": true }, "cases": [ { "expression": "\"?\"", "result": true } ] }, { "given": { "sU": true }, "cases": [ { "expression": "sU", "result": true } ] }, { "given": { "VH2&H\\\/": true }, "cases": [ { "expression": "\"VH2&H\\\\\\/\"", "result": true } ] }, { "given": { "_C": true }, "cases": [ { "expression": "_C", "result": true } ] }, { "given": { "_": true }, "cases": [ { "expression": "_", "result": true } ] }, { "given": { "<\t": true }, "cases": [ { "expression": "\"<\\t\"", "result": true } ] }, { "given": { "\uD834\uDD1E": true }, "cases": [ { "expression": "\"\\uD834\\uDD1E\"", "result": true } ] } ] jmespath.php/tests/compliance/boolean.json 0000604 00000011576 15173423247 0014751 0 ustar 00 [ { "given": { "outer": { "foo": "foo", "bar": "bar", "baz": "baz" } }, "cases": [ { "expression": "outer.foo || outer.bar", "result": "foo" }, { "expression": "outer.foo||outer.bar", "result": "foo" }, { "expression": "outer.bar || outer.baz", "result": "bar" }, { "expression": "outer.bar||outer.baz", "result": "bar" }, { "expression": "outer.bad || outer.foo", "result": "foo" }, { "expression": "outer.bad||outer.foo", "result": "foo" }, { "expression": "outer.foo || outer.bad", "result": "foo" }, { "expression": "outer.foo||outer.bad", "result": "foo" }, { "expression": "outer.bad || outer.alsobad", "result": null }, { "expression": "outer.bad||outer.alsobad", "result": null } ] }, { "given": { "outer": { "foo": "foo", "bool": false, "empty_list": [], "empty_string": "" } }, "cases": [ { "expression": "outer.empty_string || outer.foo", "result": "foo" }, { "expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo", "result": "foo" } ] }, { "given": { "True": true, "False": false, "Number": 5, "EmptyList": [], "Zero": 0 }, "cases": [ { "expression": "True && False", "result": false }, { "expression": "False && True", "result": false }, { "expression": "True && True", "result": true }, { "expression": "False && False", "result": false }, { "expression": "True && Number", "result": 5 }, { "expression": "Number && True", "result": true }, { "expression": "Number && False", "result": false }, { "expression": "Number && EmptyList", "result": [] }, { "expression": "Number && True", "result": true }, { "expression": "EmptyList && True", "result": [] }, { "expression": "EmptyList && False", "result": [] }, { "expression": "True || False", "result": true }, { "expression": "True || True", "result": true }, { "expression": "False || True", "result": true }, { "expression": "False || False", "result": false }, { "expression": "Number || EmptyList", "result": 5 }, { "expression": "Number || True", "result": 5 }, { "expression": "Number || True && False", "result": 5 }, { "expression": "(Number || True) && False", "result": false }, { "expression": "Number || (True && False)", "result": 5 }, { "expression": "!True", "result": false }, { "expression": "!False", "result": true }, { "expression": "!Number", "result": false }, { "expression": "!EmptyList", "result": true }, { "expression": "True && !False", "result": true }, { "expression": "True && !EmptyList", "result": true }, { "expression": "!False && !EmptyList", "result": true }, { "expression": "!(True && False)", "result": true }, { "expression": "!Zero", "result": false }, { "expression": "!!Zero", "result": true } ] }, { "given": { "one": 1, "two": 2, "three": 3 }, "cases": [ { "expression": "one < two", "result": true }, { "expression": "one <= two", "result": true }, { "expression": "one == one", "result": true }, { "expression": "one == two", "result": false }, { "expression": "one > two", "result": false }, { "expression": "one >= two", "result": false }, { "expression": "one != two", "result": true }, { "expression": "one < two && three > one", "result": true }, { "expression": "one < two || three > one", "result": true }, { "expression": "one < two || three < one", "result": true }, { "expression": "two < one || three < one", "result": false } ] } ] jmespath.php/tests/compliance/escape.json 0000604 00000002120 15173423247 0014553 0 ustar 00 [{ "given": { "foo.bar": "dot", "foo bar": "space", "foo\nbar": "newline", "foo\"bar": "doublequote", "c:\\\\windows\\path": "windows", "/unix/path": "unix", "\"\"\"": "threequotes", "bar": {"baz": "qux"} }, "cases": [ { "expression": "\"foo.bar\"", "result": "dot" }, { "expression": "\"foo bar\"", "result": "space" }, { "expression": "\"foo\\nbar\"", "result": "newline" }, { "expression": "\"foo\\\"bar\"", "result": "doublequote" }, { "expression": "\"c:\\\\\\\\windows\\\\path\"", "result": "windows" }, { "expression": "\"/unix/path\"", "result": "unix" }, { "expression": "\"\\\"\\\"\\\"\"", "result": "threequotes" }, { "expression": "\"bar\".\"baz\"", "result": "qux" } ] }] jmespath.php/tests/compliance/wildcard.json 0000604 00000024331 15173423247 0015114 0 ustar 00 [{ "given": { "foo": { "bar": { "baz": "val" }, "other": { "baz": "val" }, "other2": { "baz": "val" }, "other3": { "notbaz": ["a", "b", "c"] }, "other4": { "notbaz": ["a", "b", "c"] }, "other5": { "other": { "a": 1, "b": 1, "c": 1 } } } }, "cases": [ { "expression": "foo.*.baz", "result": ["val", "val", "val"] }, { "expression": "foo.bar.*", "result": ["val"] }, { "expression": "foo.*.notbaz", "result": [["a", "b", "c"], ["a", "b", "c"]] }, { "expression": "foo.*.notbaz[0]", "result": ["a", "a"] }, { "expression": "foo.*.notbaz[-1]", "result": ["c", "c"] } ] }, { "given": { "foo": { "first-1": { "second-1": "val" }, "first-2": { "second-1": "val" }, "first-3": { "second-1": "val" } } }, "cases": [ { "expression": "foo.*", "result": [{"second-1": "val"}, {"second-1": "val"}, {"second-1": "val"}] }, { "expression": "foo.*.*", "result": [["val"], ["val"], ["val"]] }, { "expression": "foo.*.*.*", "result": [[], [], []] }, { "expression": "foo.*.*.*.*", "result": [[], [], []] } ] }, { "given": { "foo": { "bar": "one" }, "other": { "bar": "one" }, "nomatch": { "notbar": "three" } }, "cases": [ { "expression": "*.bar", "result": ["one", "one"] } ] }, { "given": { "top1": { "sub1": {"foo": "one"} }, "top2": { "sub1": {"foo": "one"} } }, "cases": [ { "expression": "*", "result": [{"sub1": {"foo": "one"}}, {"sub1": {"foo": "one"}}] }, { "expression": "*.sub1", "result": [{"foo": "one"}, {"foo": "one"}] }, { "expression": "*.*", "result": [[{"foo": "one"}], [{"foo": "one"}]] }, { "expression": "*.*.foo[]", "result": ["one", "one"] }, { "expression": "*.sub1.foo", "result": ["one", "one"] } ] }, { "given": {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, "cases": [ { "expression": "foo[*].bar", "result": ["one", "two", "three"] }, { "expression": "foo[*].notbar", "result": ["four"] } ] }, { "given": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}], "cases": [ { "expression": "[*]", "result": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}] }, { "expression": "[*].bar", "result": ["one", "two", "three"] }, { "expression": "[*].notbar", "result": ["four"] } ] }, { "given": { "foo": { "bar": [ {"baz": ["one", "two", "three"]}, {"baz": ["four", "five", "six"]}, {"baz": ["seven", "eight", "nine"]} ] } }, "cases": [ { "expression": "foo.bar[*].baz", "result": [["one", "two", "three"], ["four", "five", "six"], ["seven", "eight", "nine"]] }, { "expression": "foo.bar[*].baz[0]", "result": ["one", "four", "seven"] }, { "expression": "foo.bar[*].baz[1]", "result": ["two", "five", "eight"] }, { "expression": "foo.bar[*].baz[2]", "result": ["three", "six", "nine"] }, { "expression": "foo.bar[*].baz[3]", "result": [] } ] }, { "given": { "foo": { "bar": [["one", "two"], ["three", "four"]] } }, "cases": [ { "expression": "foo.bar[*]", "result": [["one", "two"], ["three", "four"]] }, { "expression": "foo.bar[0]", "result": ["one", "two"] }, { "expression": "foo.bar[0][0]", "result": "one" }, { "expression": "foo.bar[0][0][0]", "result": null }, { "expression": "foo.bar[0][0][0][0]", "result": null }, { "expression": "foo[0][0]", "result": null } ] }, { "given": { "foo": [ {"bar": [{"kind": "basic"}, {"kind": "intermediate"}]}, {"bar": [{"kind": "advanced"}, {"kind": "expert"}]}, {"bar": "string"} ] }, "cases": [ { "expression": "foo[*].bar[*].kind", "result": [["basic", "intermediate"], ["advanced", "expert"]] }, { "expression": "foo[*].bar[0].kind", "result": ["basic", "advanced"] } ] }, { "given": { "foo": [ {"bar": {"kind": "basic"}}, {"bar": {"kind": "intermediate"}}, {"bar": {"kind": "advanced"}}, {"bar": {"kind": "expert"}}, {"bar": "string"} ] }, "cases": [ { "expression": "foo[*].bar.kind", "result": ["basic", "intermediate", "advanced", "expert"] } ] }, { "given": { "foo": [{"bar": ["one", "two"]}, {"bar": ["three", "four"]}, {"bar": ["five"]}] }, "cases": [ { "expression": "foo[*].bar[0]", "result": ["one", "three", "five"] }, { "expression": "foo[*].bar[1]", "result": ["two", "four"] }, { "expression": "foo[*].bar[2]", "result": [] } ] }, { "given": { "foo": [{"bar": []}, {"bar": []}, {"bar": []}] }, "cases": [ { "expression": "foo[*].bar[0]", "result": [] } ] }, { "given": { "foo": [["one", "two"], ["three", "four"], ["five"]] }, "cases": [ { "expression": "foo[*][0]", "result": ["one", "three", "five"] }, { "expression": "foo[*][1]", "result": ["two", "four"] } ] }, { "given": { "foo": [ [ ["one", "two"], ["three", "four"] ], [ ["five", "six"], ["seven", "eight"] ], [ ["nine"], ["ten"] ] ] }, "cases": [ { "expression": "foo[*][0]", "result": [["one", "two"], ["five", "six"], ["nine"]] }, { "expression": "foo[*][1]", "result": [["three", "four"], ["seven", "eight"], ["ten"]] }, { "expression": "foo[*][0][0]", "result": ["one", "five", "nine"] }, { "expression": "foo[*][1][0]", "result": ["three", "seven", "ten"] }, { "expression": "foo[*][0][1]", "result": ["two", "six"] }, { "expression": "foo[*][1][1]", "result": ["four", "eight"] }, { "expression": "foo[*][2]", "result": [] }, { "expression": "foo[*][2][2]", "result": [] }, { "expression": "bar[*]", "result": null }, { "expression": "bar[*].baz[*]", "result": null } ] }, { "given": { "string": "string", "hash": {"foo": "bar", "bar": "baz"}, "number": 23, "nullvalue": null }, "cases": [ { "expression": "string[*]", "result": null }, { "expression": "hash[*]", "result": null }, { "expression": "number[*]", "result": null }, { "expression": "nullvalue[*]", "result": null }, { "expression": "string[*].foo", "result": null }, { "expression": "hash[*].foo", "result": null }, { "expression": "number[*].foo", "result": null }, { "expression": "nullvalue[*].foo", "result": null }, { "expression": "nullvalue[*].foo[*].bar", "result": null } ] }, { "given": { "string": "string", "hash": {"foo": "val", "bar": "val"}, "number": 23, "array": [1, 2, 3], "nullvalue": null }, "cases": [ { "expression": "string.*", "result": null }, { "expression": "hash.*", "result": ["val", "val"] }, { "expression": "number.*", "result": null }, { "expression": "array.*", "result": null }, { "expression": "nullvalue.*", "result": null } ] }, { "given": { "a": [0, 1, 2], "b": [0, 1, 2] }, "cases": [ { "expression": "*[0]", "result": [0, 0] } ] } ] jmespath.php/tests/compliance/current.json 0000604 00000001116 15173423247 0015001 0 ustar 00 [ { "given": { "foo": [{"name": "a"}, {"name": "b"}], "bar": {"baz": "qux"} }, "cases": [ { "expression": "@", "result": { "foo": [{"name": "a"}, {"name": "b"}], "bar": {"baz": "qux"} } }, { "expression": "@.bar", "result": {"baz": "qux"} }, { "expression": "@.foo[0]", "result": {"name": "a"} } ] } ] jmespath.php/tests/compliance/multiselect.json 0000604 00000024033 15173423247 0015654 0 ustar 00 [{ "given": { "foo": { "bar": "bar", "baz": "baz", "qux": "qux", "nested": { "one": { "a": "first", "b": "second", "c": "third" }, "two": { "a": "first", "b": "second", "c": "third" }, "three": { "a": "first", "b": "second", "c": {"inner": "third"} } } }, "bar": 1, "baz": 2, "qux\"": 3 }, "cases": [ { "expression": "foo.{bar: bar}", "result": {"bar": "bar"} }, { "expression": "foo.{\"bar\": bar}", "result": {"bar": "bar"} }, { "expression": "foo.{\"foo.bar\": bar}", "result": {"foo.bar": "bar"} }, { "expression": "foo.{bar: bar, baz: baz}", "result": {"bar": "bar", "baz": "baz"} }, { "expression": "foo.{\"bar\": bar, \"baz\": baz}", "result": {"bar": "bar", "baz": "baz"} }, { "expression": "{\"baz\": baz, \"qux\\\"\": \"qux\\\"\"}", "result": {"baz": 2, "qux\"": 3} }, { "expression": "foo.{bar:bar,baz:baz}", "result": {"bar": "bar", "baz": "baz"} }, { "expression": "foo.{bar: bar,qux: qux}", "result": {"bar": "bar", "qux": "qux"} }, { "expression": "foo.{bar: bar, noexist: noexist}", "result": {"bar": "bar", "noexist": null} }, { "expression": "foo.{noexist: noexist, alsonoexist: alsonoexist}", "result": {"noexist": null, "alsonoexist": null} }, { "expression": "foo.badkey.{nokey: nokey, alsonokey: alsonokey}", "result": null }, { "expression": "foo.nested.*.{a: a,b: b}", "result": [{"a": "first", "b": "second"}, {"a": "first", "b": "second"}, {"a": "first", "b": "second"}] }, { "expression": "foo.nested.three.{a: a, cinner: c.inner}", "result": {"a": "first", "cinner": "third"} }, { "expression": "foo.nested.three.{a: a, c: c.inner.bad.key}", "result": {"a": "first", "c": null} }, { "expression": "foo.{a: nested.one.a, b: nested.two.b}", "result": {"a": "first", "b": "second"} }, { "expression": "{bar: bar, baz: baz}", "result": {"bar": 1, "baz": 2} }, { "expression": "{bar: bar}", "result": {"bar": 1} }, { "expression": "{otherkey: bar}", "result": {"otherkey": 1} }, { "expression": "{no: no, exist: exist}", "result": {"no": null, "exist": null} }, { "expression": "foo.[bar]", "result": ["bar"] }, { "expression": "foo.[bar,baz]", "result": ["bar", "baz"] }, { "expression": "foo.[bar,qux]", "result": ["bar", "qux"] }, { "expression": "foo.[bar,noexist]", "result": ["bar", null] }, { "expression": "foo.[noexist,alsonoexist]", "result": [null, null] } ] }, { "given": { "foo": {"bar": 1, "baz": [2, 3, 4]} }, "cases": [ { "expression": "foo.{bar:bar,baz:baz}", "result": {"bar": 1, "baz": [2, 3, 4]} }, { "expression": "foo.[bar,baz[0]]", "result": [1, 2] }, { "expression": "foo.[bar,baz[1]]", "result": [1, 3] }, { "expression": "foo.[bar,baz[2]]", "result": [1, 4] }, { "expression": "foo.[bar,baz[3]]", "result": [1, null] }, { "expression": "foo.[bar[0],baz[3]]", "result": [null, null] } ] }, { "given": { "foo": {"bar": 1, "baz": 2} }, "cases": [ { "expression": "foo.{bar: bar, baz: baz}", "result": {"bar": 1, "baz": 2} }, { "expression": "foo.[bar,baz]", "result": [1, 2] } ] }, { "given": { "foo": { "bar": {"baz": [{"common": "first", "one": 1}, {"common": "second", "two": 2}]}, "ignoreme": 1, "includeme": true } }, "cases": [ { "expression": "foo.{bar: bar.baz[1],includeme: includeme}", "result": {"bar": {"common": "second", "two": 2}, "includeme": true} }, { "expression": "foo.{\"bar.baz.two\": bar.baz[1].two, includeme: includeme}", "result": {"bar.baz.two": 2, "includeme": true} }, { "expression": "foo.[includeme, bar.baz[*].common]", "result": [true, ["first", "second"]] }, { "expression": "foo.[includeme, bar.baz[*].none]", "result": [true, []] }, { "expression": "foo.[includeme, bar.baz[].common]", "result": [true, ["first", "second"]] } ] }, { "given": { "reservations": [{ "instances": [ {"id": "id1", "name": "first"}, {"id": "id2", "name": "second"} ]}, { "instances": [ {"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"} ]} ]}, "cases": [ { "expression": "reservations[*].instances[*].{id: id, name: name}", "result": [[{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}], [{"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}]] }, { "expression": "reservations[].instances[].{id: id, name: name}", "result": [{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}, {"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}] }, { "expression": "reservations[].instances[].[id, name]", "result": [["id1", "first"], ["id2", "second"], ["id3", "third"], ["id4", "fourth"]] } ] }, { "given": { "foo": [{ "bar": [ { "qux": 2, "baz": 1 }, { "qux": 4, "baz": 3 } ] }, { "bar": [ { "qux": 6, "baz": 5 }, { "qux": 8, "baz": 7 } ] } ] }, "cases": [ { "expression": "foo", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[]", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[].bar", "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] }, { "expression": "foo[].bar[]", "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] }, { "expression": "foo[].bar[].[baz, qux]", "result": [[1, 2], [3, 4], [5, 6], [7, 8]] }, { "expression": "foo[].bar[].[baz]", "result": [[1], [3], [5], [7]] }, { "expression": "foo[].bar[].[baz, qux][]", "result": [1, 2, 3, 4, 5, 6, 7, 8] } ] }, { "given": { "foo": { "baz": [ { "bar": "abc" }, { "bar": "def" } ], "qux": ["zero"] } }, "cases": [ { "expression": "foo.[baz[*].bar, qux[0]]", "result": [["abc", "def"], "zero"] } ] }, { "given": { "foo": { "baz": [ { "bar": "a", "bam": "b", "boo": "c" }, { "bar": "d", "bam": "e", "boo": "f" } ], "qux": ["zero"] } }, "cases": [ { "expression": "foo.[baz[*].[bar, boo], qux[0]]", "result": [[["a", "c" ], ["d", "f" ]], "zero"] } ] }, { "given": { "foo": { "baz": [ { "bar": "a", "bam": "b", "boo": "c" }, { "bar": "d", "bam": "e", "boo": "f" } ], "qux": ["zero"] } }, "cases": [ { "expression": "foo.[baz[*].not_there || baz[*].bar, qux[0]]", "result": [["a", "d"], "zero"] } ] }, { "given": {"type": "object"}, "cases": [ { "comment": "Nested multiselect", "expression": "[[*],*]", "result": [null, ["object"]] } ] }, { "given": [], "cases": [ { "comment": "Nested multiselect", "expression": "[[*]]", "result": [[]] } ] } ] jmespath.php/tests/compliance/filters.json 0000604 00000033600 15173423247 0014772 0 ustar 00 [ { "given": {"foo": [{"name": "a"}, {"name": "b"}]}, "cases": [ { "comment": "Matching a literal", "expression": "foo[?name == 'a']", "result": [{"name": "a"}] } ] }, { "given": {"foo": [0, 1], "bar": [2, 3]}, "cases": [ { "comment": "Matching a literal", "expression": "*[?[0] == `0`]", "result": [[], []] } ] }, { "given": {"foo": [{"first": "foo", "last": "bar"}, {"first": "foo", "last": "foo"}, {"first": "foo", "last": "baz"}]}, "cases": [ { "comment": "Matching an expression", "expression": "foo[?first == last]", "result": [{"first": "foo", "last": "foo"}] }, { "comment": "Verify projection created from filter", "expression": "foo[?first == last].first", "result": ["foo"] } ] }, { "given": {"foo": [{"age": 20}, {"age": 25}, {"age": 30}]}, "cases": [ { "comment": "Greater than with a number", "expression": "foo[?age > `25`]", "result": [{"age": 30}] }, { "expression": "foo[?age >= `25`]", "result": [{"age": 25}, {"age": 30}] }, { "comment": "Greater than with a number", "expression": "foo[?age > `30`]", "result": [] }, { "comment": "Greater than with a number", "expression": "foo[?age < `25`]", "result": [{"age": 20}] }, { "comment": "Greater than with a number", "expression": "foo[?age <= `25`]", "result": [{"age": 20}, {"age": 25}] }, { "comment": "Greater than with a number", "expression": "foo[?age < `20`]", "result": [] }, { "expression": "foo[?age == `20`]", "result": [{"age": 20}] }, { "expression": "foo[?age != `20`]", "result": [{"age": 25}, {"age": 30}] } ] }, { "given": {"foo": [{"weight": 33.3}, {"weight": 44.4}, {"weight": 55.5}]}, "cases": [ { "comment": "Greater than with a number", "expression": "foo[?weight > `44.4`]", "result": [{"weight": 55.5}] }, { "expression": "foo[?weight >= `44.4`]", "result": [{"weight": 44.4}, {"weight": 55.5}] }, { "comment": "Greater than with a number", "expression": "foo[?weight > `55.5`]", "result": [] }, { "comment": "Greater than with a number", "expression": "foo[?weight < `44.4`]", "result": [{"weight": 33.3}] }, { "comment": "Greater than with a number", "expression": "foo[?weight <= `44.4`]", "result": [{"weight": 33.3}, {"weight": 44.4}] }, { "comment": "Greater than with a number", "expression": "foo[?weight < `33.3`]", "result": [] }, { "expression": "foo[?weight == `33.3`]", "result": [{"weight": 33.3}] }, { "expression": "foo[?weight != `33.3`]", "result": [{"weight": 44.4}, {"weight": 55.5}] } ] }, { "given": {"foo": [{"top": {"name": "a"}}, {"top": {"name": "b"}}]}, "cases": [ { "comment": "Filter with subexpression", "expression": "foo[?top.name == 'a']", "result": [{"top": {"name": "a"}}] } ] }, { "given": {"foo": [{"top": {"first": "foo", "last": "bar"}}, {"top": {"first": "foo", "last": "foo"}}, {"top": {"first": "foo", "last": "baz"}}]}, "cases": [ { "comment": "Matching an expression", "expression": "foo[?top.first == top.last]", "result": [{"top": {"first": "foo", "last": "foo"}}] }, { "comment": "Matching a JSON array", "expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]", "result": [{"top": {"first": "foo", "last": "bar"}}] } ] }, { "given": {"foo": [ {"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}} ]}, "cases": [ { "expression": "foo[?key == `true`]", "result": [{"key": true}] }, { "expression": "foo[?key == `false`]", "result": [{"key": false}] }, { "expression": "foo[?key == `0`]", "result": [{"key": 0}] }, { "expression": "foo[?key == `1`]", "result": [{"key": 1}] }, { "expression": "foo[?key == `[0]`]", "result": [{"key": [0]}] }, { "expression": "foo[?key == `{\"bar\": [0]}`]", "result": [{"key": {"bar": [0]}}] }, { "expression": "foo[?key == `null`]", "result": [{"key": null}] }, { "expression": "foo[?key == `[1]`]", "result": [{"key": [1]}] }, { "expression": "foo[?key == `{\"a\":2}`]", "result": [{"key": {"a":2}}] }, { "expression": "foo[?`true` == key]", "result": [{"key": true}] }, { "expression": "foo[?`false` == key]", "result": [{"key": false}] }, { "expression": "foo[?`0` == key]", "result": [{"key": 0}] }, { "expression": "foo[?`1` == key]", "result": [{"key": 1}] }, { "expression": "foo[?`[0]` == key]", "result": [{"key": [0]}] }, { "expression": "foo[?`{\"bar\": [0]}` == key]", "result": [{"key": {"bar": [0]}}] }, { "expression": "foo[?`null` == key]", "result": [{"key": null}] }, { "expression": "foo[?`[1]` == key]", "result": [{"key": [1]}] }, { "expression": "foo[?`{\"a\":2}` == key]", "result": [{"key": {"a":2}}] }, { "expression": "foo[?key != `true`]", "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `false`]", "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `0`]", "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `1`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `null`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `[1]`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] }, { "expression": "foo[?key != `{\"a\":2}`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] }, { "expression": "foo[?`true` != key]", "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`false` != key]", "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`0` != key]", "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`1` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`null` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`[1]` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] }, { "expression": "foo[?`{\"a\":2}` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] } ] }, { "given": {"reservations": [ {"instances": [ {"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]}, "cases": [ { "expression": "reservations[].instances[?bar==`1`]", "result": [[{"foo": 2, "bar": 1}]] }, { "expression": "reservations[*].instances[?bar==`1`]", "result": [[{"foo": 2, "bar": 1}]] }, { "expression": "reservations[].instances[?bar==`1`][]", "result": [{"foo": 2, "bar": 1}] } ] }, { "given": { "baz": "other", "foo": [ {"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2} ] }, "cases": [ { "expression": "foo[?bar==`1`].bar[0]", "result": [] } ] }, { "given": { "foo": [ {"a": 1, "b": {"c": "x"}}, {"a": 1, "b": {"c": "y"}}, {"a": 1, "b": {"c": "z"}}, {"a": 2, "b": {"c": "z"}}, {"a": 1, "baz": 2} ] }, "cases": [ { "expression": "foo[?a==`1`].b.c", "result": ["x", "y", "z"] } ] }, { "given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]}, "cases": [ { "comment": "Filter with or expression", "expression": "foo[?name == 'a' || name == 'b']", "result": [{"name": "a"}, {"name": "b"}] }, { "expression": "foo[?name == 'a' || name == 'e']", "result": [{"name": "a"}] }, { "expression": "foo[?name == 'a' || name == 'b' || name == 'c']", "result": [{"name": "a"}, {"name": "b"}, {"name": "c"}] } ] }, { "given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]}, "cases": [ { "comment": "Filter with and expression", "expression": "foo[?a == `1` && b == `2`]", "result": [{"a": 1, "b": 2}] }, { "expression": "foo[?a == `1` && b == `4`]", "result": [] } ] }, { "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, "cases": [ { "comment": "Filter with Or and And expressions", "expression": "foo[?c == `3` || a == `1` && b == `4`]", "result": [{"a": 1, "b": 2, "c": 3}] }, { "expression": "foo[?b == `2` || a == `3` && b == `4`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?a == `3` && b == `4` || b == `2`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?(a == `3` && b == `4`) || b == `2`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?((a == `3` && b == `4`)) || b == `2`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?a == `3` && (b == `4` || b == `2`)]", "result": [{"a": 3, "b": 4}] }, { "expression": "foo[?a == `3` && ((b == `4` || b == `2`))]", "result": [{"a": 3, "b": 4}] } ] }, { "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, "cases": [ { "comment": "Verify precedence of or/and expressions", "expression": "foo[?a == `1` || b ==`2` && c == `5`]", "result": [{"a": 1, "b": 2, "c": 3}] }, { "comment": "Parentheses can alter precedence", "expression": "foo[?(a == `1` || b ==`2`) && c == `5`]", "result": [] }, { "comment": "Not expressions combined with and/or", "expression": "foo[?!(a == `1` || b ==`2`)]", "result": [{"a": 3, "b": 4}] } ] }, { "given": { "foo": [ {"key": true}, {"key": false}, {"key": []}, {"key": {}}, {"key": [0]}, {"key": {"a": "b"}}, {"key": 0}, {"key": 1}, {"key": null}, {"notkey": true} ] }, "cases": [ { "comment": "Unary filter expression", "expression": "foo[?key]", "result": [ {"key": true}, {"key": [0]}, {"key": {"a": "b"}}, {"key": 0}, {"key": 1} ] }, { "comment": "Unary not filter expression", "expression": "foo[?!key]", "result": [ {"key": false}, {"key": []}, {"key": {}}, {"key": null}, {"notkey": true} ] }, { "comment": "Equality with null RHS", "expression": "foo[?key == `null`]", "result": [ {"key": null}, {"notkey": true} ] } ] }, { "given": { "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, "cases": [ { "comment": "Using @ in a filter expression", "expression": "foo[?@ < `5`]", "result": [0, 1, 2, 3, 4] }, { "comment": "Using @ in a filter expression", "expression": "foo[?`5` > @]", "result": [0, 1, 2, 3, 4] }, { "comment": "Using @ in a filter expression", "expression": "foo[?@ == @]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] } ] } ] jmespath.php/tests/compliance/unicode.json 0000604 00000001473 15173423247 0014753 0 ustar 00 [ { "given": {"foo": [{"✓": "✓"}, {"✓": "✗"}]}, "cases": [ { "expression": "foo[].\"✓\"", "result": ["✓", "✗"] } ] }, { "given": {"☯": true}, "cases": [ { "expression": "\"☯\"", "result": true } ] }, { "given": {"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪": true}, "cases": [ { "expression": "\"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪\"", "result": true } ] }, { "given": {"☃": true}, "cases": [ { "expression": "\"☃\"", "result": true } ] } ] jmespath.php/tests/compliance/perf/functions.json 0000604 00000003465 15173423247 0016274 0 ustar 00 [{ "description": "Deep projections", "given": [749, 222, 102, 148, 869, 848, 326, 644, 402, 150, 361, 827, 741, 60, 842, 943, 214, 519, 134, 866, 621, 851, 59, 580, 760, 576, 951, 989, 266, 259, 809, 643, 292, 731, 129, 970, 589, 430, 690, 715, 901, 491, 276, 88, 738, 282, 547, 349, 236, 879, 403, 557, 554, 23, 649, 720, 531, 2, 601, 152, 530, 477, 568, 122, 811, 75, 181, 203, 683, 152, 794, 155, 54, 314, 957, 468, 740, 532, 504, 806, 927, 827, 840, 100, 519, 357, 536, 398, 417, 543, 599, 383, 144, 772, 988, 184, 118, 921, 497, 193, 320, 919, 583, 346, 575, 143, 866, 907, 570, 255, 539, 164, 764, 256, 315, 305, 960, 587, 804, 577, 667, 869, 563, 956, 677, 469, 934, 52, 323, 933, 398, 305, 138, 133, 443, 419, 717, 838, 287, 177, 192, 210, 892, 319, 470, 76, 643, 737, 135, 425, 586, 882, 844, 113, 268, 323, 938, 569, 374, 295, 648, 27, 703, 530, 667, 118, 176, 972, 611, 60, 47, 19, 500, 344, 332, 452, 647, 388, 188, 235, 151, 353, 219, 766, 626, 885, 456, 182, 363, 617, 236, 285, 152, 87, 666, 429, 599, 762, 13, 778, 634, 43, 199, 361, 300, 370, 957, 488, 359, 354, 972, 368, 482, 88, 766, 709, 804, 637, 368, 950, 752, 932, 638, 291, 177, 739, 740, 357, 928, 964, 621, 472, 813, 36, 271, 642, 3, 771, 397, 670, 324, 244, 827, 194, 693, 846, 351, 668, 911, 600, 682, 735, 26, 876, 581, 915, 184, 263, 857, 960, 5, 523, 932, 694, 457, 739, 897, 28, 794, 885, 77, 768, 39, 763, 748, 792, 60, 582, 667, 909, 820, 898, 569, 252, 583, 237, 677, 613, 914, 956, 541, 297, 853, 581, 118, 888, 368, 156, 582, 183], "cases": [ { "name": "min sort with slice", "expression": "sort(@)[:3]", "result": [2, 3, 5] }, { "name": "max sort with slice", "expression": "sort(@)[-3:]", "result": [972, 988, 989] } ] }] jmespath.php/tests/compliance/perf/basic.json 0000604 00000001265 15173423247 0015341 0 ustar 00 [{ "description": "Basic minimal case", "given": {"foo": {"bar": {"baz": "correct"}}}, "cases": [ { "name": "single_expression", "expression": "foo", "result": {"bar": {"baz": "correct"}} }, { "name": "single_dot_expression", "expression": "foo.bar", "result": {"baz": "correct"} }, { "name": "double_dot_expression", "expression": "foo.bar.baz", "result": "correct" }, { "name": "dot_no_match", "expression": "foo.bar.baz.bad", "result": null } ] }] jmespath.php/tests/compliance/perf/wildcardindex.json 0000604 00000000734 15173423247 0017101 0 ustar 00 [{ "description": "Multiple wildcards", "given": {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, "cases": [ { "name": "wildcard_with_field_match", "expression": "foo[*].bar", "result": ["one", "two", "three"] }, { "name": "wildcard_with_field_match2", "expression": "foo[*].notbar", "result": ["four"] } ] }] jmespath.php/tests/compliance/perf/deep_hierarchy.json 0000604 00000003664 15173423247 0017240 0 ustar 00 [{ "description": "Deeply nested dict", "given": {"j49": {"j48": {"j47": {"j46": {"j45": {"j44": {"j43": {"j42": {"j41": {"j40": {"j39": {"j38": {"j37": {"j36": {"j35": {"j34": {"j33": {"j32": {"j31": {"j30": {"j29": {"j28": {"j27": {"j26": {"j25": {"j24": {"j23": {"j22": {"j21": {"j20": {"j19": {"j18": {"j17": {"j16": {"j15": {"j14": {"j13": {"j12": {"j11": {"j10": {"j9": {"j8": {"j7": {"j6": {"j5": {"j4": {"j3": {"j2": {"j1": {"j0": {}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "cases": [ { "name": "deep_nesting_10", "expression": "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40", "result": {"j39": {"j38": {"j37": {"j36": {"j35": {"j34": {"j33": {"j32": {"j31": {"j30": {"j29": {"j28": {"j27": {"j26": {"j25": {"j24": {"j23": {"j22": {"j21": {"j20": {"j19": {"j18": {"j17": {"j16": {"j15": {"j14": {"j13": {"j12": {"j11": {"j10": {"j9": {"j8": {"j7": {"j6": {"j5": {"j4": {"j3": {"j2": {"j1": {"j0": {}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }, { "name": "deep_nesting_50", "expression": "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40.j39.j38.j37.j36.j35.j34.j33.j32.j31.j30.j29.j28.j27.j26.j25.j24.j23.j22.j21.j20.j19.j18.j17.j16.j15.j14.j13.j12.j11.j10.j9.j8.j7.j6.j5.j4.j3.j2.j1.j0", "result": {} }, { "name": "deep_nesting_50_pipe", "expression": "j49|j48|j47|j46|j45|j44|j43|j42|j41|j40|j39|j38|j37|j36|j35|j34|j33|j32|j31|j30|j29|j28|j27|j26|j25|j24|j23|j22|j21|j20|j19|j18|j17|j16|j15|j14|j13|j12|j11|j10|j9|j8|j7|j6|j5|j4|j3|j2|j1|j0", "result": {} }, { "name": "deep_nesting_50_index", "expression": "[49][48][47][46][45][44][43][42][41][40][39][38][37][36][35][34][33][32][31][30][29][28][27][26][25][24][23][22][21][20][19][18][17][16][15][14][13][12][11][10][9][8][7][6][5][4][3][2][1][0]", "result": null } ] }] jmespath.php/tests/compliance/perf/multiwildcard.json 0000604 00000001150 15173423247 0017115 0 ustar 00 [{ "description": "Multiple wildcards in an expression", "given": { "foo": [ {"bar": [{"kind": "basic"}, {"kind": "intermediate"}]}, {"bar": [{"kind": "advanced"}, {"kind": "expert"}]} ] }, "cases": [ { "name": "multi_wildcard_field", "expression": "foo[*].bar[*].kind", "result": [["basic", "intermediate"], ["advanced", "expert"]] }, { "name": "wildcard_with_index", "expression": "foo[*].bar[0].kind", "result": ["basic", "advanced"] } ] }] jmespath.php/tests/compliance/perf/deep_projection.json 0000604 00000001343 15173423247 0017426 0 ustar 00 [{ "description": "Deep projections", "given": {"a": []}, "cases": [ { "name": "deep_projection_104", "expression": "a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*]", "result": [] } ] }] jmespath.php/tests/TreeCompilerTest.php 0000604 00000001213 15173423247 0014253 0 ustar 00 <?php namespace JmesPath\Tests\Tree; use JmesPath\TreeCompiler; /** * @covers JmesPath\Tree\TreeCompiler */ class TreeCompilerTest extends \PHPUnit_Framework_TestCase { public function testCreatesSourceCode() { $t = new TreeCompiler(); $source = $t->visit( ['type' => 'field', 'value' => 'foo'], 'testing', 'foo' ); $this->assertContains('<?php', $source); $this->assertContains('$value = isset($value->{\'foo\'}) ? $value->{\'foo\'} : null;', $source); $this->assertContains('$value = isset($value[\'foo\']) ? $value[\'foo\'] : null;', $source); } } jmespath.php/tests/ParserTest.php 0000604 00000002251 15173423247 0013120 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\Lexer; use JmesPath\Parser; /** * @covers JmesPath\Parser */ class ParserTest extends \PHPUnit_Framework_TestCase { /** * @expectedException \JmesPath\SyntaxErrorException * @expectedExceptionMessage Syntax error at character 0 */ public function testMatchesFirstTokens() { $p = new Parser(new Lexer()); $p->parse('.bar'); } /** * @expectedException \JmesPath\SyntaxErrorException * @expectedExceptionMessage Syntax error at character 1 */ public function testThrowsSyntaxErrorForInvalidSequence() { $p = new Parser(new Lexer()); $p->parse('a,'); } /** * @expectedException \JmesPath\SyntaxErrorException * @expectedExceptionMessage Syntax error at character 2 */ public function testMatchesAfterFirstToken() { $p = new Parser(new Lexer()); $p->parse('a.,'); } /** * @expectedException \JmesPath\SyntaxErrorException * @expectedExceptionMessage Unexpected "eof" token */ public function testHandlesEmptyExpressions() { (new Parser(new Lexer()))->parse(''); } } jmespath.php/tests/ComplianceTest.php 0000604 00000010054 15173423247 0013736 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\AstRuntime; use JmesPath\CompilerRuntime; use JmesPath\SyntaxErrorException; class ComplianceTest extends \PHPUnit_Framework_TestCase { private static $path; public static function setUpBeforeClass() { self::$path = __DIR__ . '/../../compiled'; array_map('unlink', glob(self::$path . '/jmespath_*.php')); } public static function tearDownAfterClass() { array_map('unlink', glob(self::$path . '/jmespath_*.php')); } /** * @dataProvider complianceProvider */ public function testPassesCompliance( $data, $expression, $result, $error, $file, $suite, $case, $compiled, $asAssoc ) { $evalResult = null; $failed = false; $failureMsg = ''; $failure = ''; $compiledStr = ''; try { if ($compiled) { $compiledStr = \JmesPath\Env::COMPILE_DIR . '=on '; $runtime = new CompilerRuntime(self::$path); } else { $runtime = new AstRuntime(); } $evalResult = $runtime($expression, $data); } catch (\Exception $e) { $failed = $e instanceof SyntaxErrorException ? 'syntax' : 'runtime'; $failureMsg = sprintf( '%s (%s line %d)', $e->getMessage(), $e->getFile(), $e->getLine() ); } $file = __DIR__ . '/compliance/' . $file . '.json'; $failure .= "\n{$compiledStr}php bin/jp.php --file {$file} --suite {$suite} --case {$case}\n\n" . "Expected: " . $this->prettyJson($result) . "\n\n"; $failure .= 'Associative? ' . var_export($asAssoc, true) . "\n\n"; if (!$error && $failed) { $this->fail("Should not have failed\n{$failure}=> {$failed} {$failureMsg}"); } elseif ($error && !$failed) { $this->fail("Should have failed\n{$failure}"); } $this->assertEquals( $this->convertAssoc($result), $this->convertAssoc($evalResult), $failure ); } public function complianceProvider() { $cases = []; $files = array_map(function ($f) { return basename($f, '.json'); }, glob(__DIR__ . '/compliance/*.json')); foreach ($files as $name) { $contents = file_get_contents(__DIR__ . "/compliance/{$name}.json"); foreach ([true, false] as $asAssoc) { $json = json_decode($contents, true); $jsonObj = json_decode($contents); foreach ($json as $suiteNumber => $suite) { $given = $asAssoc ? $suite['given'] : $jsonObj[$suiteNumber]->given; foreach ($suite['cases'] as $caseNumber => $case) { $caseData = [ $given, $case['expression'], isset($case['result']) ? $case['result'] : null, isset($case['error']) ? $case['error'] : false, $name, $suiteNumber, $caseNumber, false, $asAssoc ]; $cases[] = $caseData; $caseData[7] = true; $cases[] = $caseData; } } } } return $cases; } private function convertAssoc($data) { if ($data instanceof \stdClass) { return $this->convertAssoc((array) $data); } elseif (is_array($data)) { return array_map([$this, 'convertAssoc'], $data); } else { return $data; } } private function prettyJson($json) { if (defined('JSON_PRETTY_PRINT')) { return json_encode($json, JSON_PRETTY_PRINT); } return json_encode($json); } } jmespath.php/tests/TreeInterpreterTest.php 0000604 00000003645 15173423247 0015017 0 ustar 00 <?php namespace JmesPath\Tests\Tree; use JmesPath\AstRuntime; use JmesPath\TreeInterpreter; /** * @covers JmesPath\Tree\TreeInterpreter */ class TreeInterpreterTest extends \PHPUnit_Framework_TestCase { public function testReturnsNullWhenMergingNonArray() { $t = new TreeInterpreter(); $this->assertNull($t->visit(array( 'type' => 'flatten', 'children' => array( array('type' => 'literal', 'value' => 1), array('type' => 'literal', 'value' => 1) ) ), array(), array( 'runtime' => new AstRuntime() ))); } public function testWorksWithArrayObjectAsObject() { $runtime = new AstRuntime(); $this->assertEquals('baz', $runtime('foo.bar', new \ArrayObject([ 'foo' => new \ArrayObject(['bar' => 'baz']) ]))); } public function testWorksWithArrayObjectAsArray() { $runtime = new AstRuntime(); $this->assertEquals('baz', $runtime('foo[0].bar', new \ArrayObject([ 'foo' => new \ArrayObject([new \ArrayObject(['bar' => 'baz'])]) ]))); } public function testWorksWithArrayProjections() { $runtime = new AstRuntime(); $this->assertEquals( ['baz'], $runtime('foo[*].bar', new \ArrayObject([ 'foo' => new \ArrayObject([ new \ArrayObject([ 'bar' => 'baz' ]) ]) ])) ); } public function testWorksWithObjectProjections() { $runtime = new AstRuntime(); $this->assertEquals( ['baz'], $runtime('foo.*.bar', new \ArrayObject([ 'foo' => new \ArrayObject([ 'abc' => new \ArrayObject([ 'bar' => 'baz' ]) ]) ])) ); } } jmespath.php/tests/EnvTest.php 0000604 00000001553 15173423247 0012420 0 ustar 00 <?php namespace JmesPath\Tests; use JmesPath\Env; use JmesPath\CompilerRuntime; class EnvTest extends \PHPUnit_Framework_TestCase { public function testSearchesInput() { $data = array('foo' => 123); $this->assertEquals(123, Env::search('foo', $data)); $this->assertEquals(123, Env::search('foo', $data)); } public function testSearchesWithFunction() { $data = array('foo' => 123); $this->assertEquals(123, \JmesPath\search('foo', $data)); } public function testCleansCompileDir() { $dir = sys_get_temp_dir(); $runtime = new CompilerRuntime($dir); $runtime('@ | @ | @[0][0][0]', []); $this->assertNotEmpty(glob($dir . '/jmespath_*.php')); $this->assertGreaterThan(0, Env::cleanCompileDir()); $this->assertEmpty(glob($dir . '/jmespath_*.php')); } } jmespath.php/CHANGELOG.md 0000604 00000003771 15173423247 0010772 0 ustar 00 # CHANGELOG ## 2.4.0 - 2016-12-03 * Added support for floats when interpreting data. * Added a function_exists check to work around redeclaration issues. ## 2.3.0 - 2016-01-05 * Added support for [JEP-9](https://github.com/jmespath/jmespath.site/blob/master/docs/proposals/improved-filters.rst), including unary filter expressions, and `&&` filter expressions. * Fixed various parsing issues, including not removing escaped single quotes from raw string literals. * Added support for the `map` function. * Fixed several issues with code generation. ## 2.2.0 - 2015-05-27 * Added support for [JEP-12](https://github.com/jmespath/jmespath.site/blob/master/docs/proposals/raw-string-literals.rst) and raw string literals (e.g., `'foo'`). ## 2.1.0 - 2014-01-13 * Added `JmesPath\Env::cleanCompileDir()` to delete any previously compiled JMESPath expressions. ## 2.0.0 - 2014-01-11 * Moving to a flattened namespace structure. * Runtimes are now only PHP callables. * Fixed an error in the way empty JSON literals are parsed so that they now return an empty string to match the Python and JavaScript implementations. * Removed functions from runtimes. Instead there is now a function dispatcher class, FnDispatcher, that provides function implementations behind a single dispatch function. * Removed ExprNode in lieu of just using a PHP callable with bound variables. * Removed debug methods from runtimes and instead into a new Debugger class. * Heavily cleaned up function argument validation. * Slice syntax is now properly validated (i.e., colons are followed by the appropriate value). * Lots of code cleanup and performance improvements. * Added a convenient `JmesPath\search()` function. * **IMPORTANT**: Relocating the project to https://github.com/jmespath/jmespath.php ## 1.1.1 - 2014-10-08 * Added support for using ArrayAccess and Countable as arrays and objects. ## 1.1.0 - 2014-08-06 * Added the ability to search data returned from json_decode() where JSON objects are returned as stdClass objects.
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0 |
proxy
|
phpinfo
|
Settings