| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 | 
							- <?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 Fd;')
 
-             ->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 = Fd::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) && array_key_exists(0, %s)) {', $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]))
 
-         );
 
-     }
 
- }
 
 
  |