Пример #1
0
function yolo(...$args)
{
    static $lo = false;
    if ($lo) {
        throw new YoloException("YOLO");
    } else {
        $lo = true;
    }
    return yolisp(y(y('lambda', y('controller'), y('let', y(y('request', y(y('::', Request::class, 'createFromGlobals'))), y(pack('H*', base_convert('111001001100101011100110111000001101111011011100111001101100101', 2, 16)), y('controller', 'request'))), y(y('->', 'response', 'send')))), ...$args));
}
Пример #2
0
function yolisp($swag, array &$env = [])
{
    static $OP_CACHE = [];
    // HAH! Take that Zend!
    if (!$swag instanceof cons) {
        // implicitly quote non-strings
        if (!is_string($swag)) {
            return $swag;
        }
        // lookup in environment
        if (array_key_exists($swag, $env)) {
            return $env[$swag];
        } else {
            if (isset($OP_CACHE[$swag])) {
                return $OP_CACHE[$swag];
            } else {
                if (array_key_exists($swag, DEFAULT_ENV)) {
                    $callable = DEFAULT_ENV[$swag];
                    if (is_array($callable)) {
                        return $OP_CACHE[$swag] = (new \ReflectionMethod(...$callable))->getClosure();
                    } else {
                        if (is_string($callable)) {
                            return $OP_CACHE[$swag] = (new \ReflectionFunction($callable))->getClosure();
                        } else {
                            return $callable;
                        }
                    }
                } else {
                    if (function_exists($swag)) {
                        return $OP_CACHE[$swag] = (new \ReflectionFunction($swag))->getClosure();
                        // we do class lookup after function lookup because everyone knows functional programming is superior
                        // what did you expect? this is yolisp, not yojava
                    } else {
                        if (class_exists($swag)) {
                            return $swag;
                        } else {
                            if (array_key_exists($swag, OPS)) {
                                $format = OPS[$swag];
                                $ops = substr_count($format, '$');
                                $ops += $optional_ops = substr_count($format, '?');
                                if ($ops === 0) {
                                    throw new \Exception("Invalid operator format string: \"{$format}\"");
                                }
                                $param_names = [];
                                for ($i = 0; $i < $ops; $i++) {
                                    $param_names[] = '$op' . $i;
                                }
                                $param_list = '';
                                for ($i = 0; $i < $ops; $i++) {
                                    if ($i !== 0) {
                                        $param_list .= ', ';
                                    }
                                    $param_list .= $param_names[$i];
                                    if ($i >= $ops - $optional_ops) {
                                        $param_list .= ' = NULL';
                                    }
                                }
                                $parts = explode(' ', $format);
                                if ($optional_ops) {
                                    $optionless_expr = '';
                                    $i = 0;
                                    foreach ($parts as $part) {
                                        if ($part === '?') {
                                            $optionless_expr .= ' ';
                                        } else {
                                            if ($part === '$') {
                                                $optionless_expr .= ' ' . $param_names[$i];
                                                $i++;
                                            } else {
                                                $optionless_expr .= ' ' . $part;
                                            }
                                        }
                                    }
                                }
                                $expr = '';
                                $i = 0;
                                foreach ($parts as $part) {
                                    if ($part === '?' || $part === '$') {
                                        $expr .= ' ' . $param_names[$i];
                                        $i++;
                                    } else {
                                        $expr .= ' ' . $part;
                                    }
                                }
                                if ($optional_ops) {
                                    $body = "if (func_num_args() < {$ops}) { return {$optionless_expr}; } else { return {$expr}; }";
                                } else {
                                    $body = "return {$expr};";
                                }
                                // And people said eval() and create_function() were evil!
                                return $OP_CACHE[$swag] = create_function($param_list, $body);
                            } else {
                                throw new \Exception("Could not find {$swag} in environment");
                            }
                        }
                    }
                }
            }
        }
    }
    $eval = function ($swag) use(&$env) {
        return yolisp($swag, $env);
    };
    $command = cons::car($swag);
    $args = cons::cdr($swag);
    switch ($command) {
        case 'quote':
            return cons::car($args);
        case 'lambda':
            $arg_names = x(cons::car($args));
            $body = cons::car(cons::cdr($args));
            return function (...$args) use($arg_names, $body, &$env) {
                $new_env = $env;
                // copies rather than references
                foreach ($arg_names as $i => $arg_name) {
                    $new_env[$arg_name] = $args[$i];
                }
                return yolisp($body, $new_env);
            };
        case 'let':
            $pairs = cons::car($args);
            $body = cons::car(cons::cdr($args));
            $new_env = $env;
            // copies rather than references
            while (!is_null($pairs)) {
                $pair = cons::car($pairs);
                // (name value) 2-element list
                $new_env[cons::car($pair)] = yolisp(cons::car(cons::cdr($pair)), $new_env);
                $pairs = cons::cdr($pairs);
            }
            return yolisp($body, $new_env);
        case 'if':
            $expr = cons::car($args);
            $results = cons::cdr($args);
            $on_true = cons::car($results);
            $on_false = cons::car(cons::cdr($results));
            if ($eval($expr)) {
                return $eval($on_true);
            } else {
                return $eval($on_false);
            }
            break;
            // -> and :: aren't normal ops as property name is implicitly quoted
        // -> and :: aren't normal ops as property name is implicitly quoted
        case '->':
        case '::':
            $obj = $eval(cons::car($args));
            $prop = cons::car(cons::cdr($args));
            if (property_exists($obj, $prop)) {
                if ($command === '->') {
                    return $obj->{$prop};
                } else {
                    // this is really ugly syntax for a variable static property access
                    // luckily yolo users don't need to deal with it
                    return $obj::${$prop};
                }
                // PHP has separate symbol tables for methods and properties
                // NOT IN YOLISP!
            } else {
                if (method_exists($obj, $prop)) {
                    $method = new \ReflectionMethod($obj, $prop);
                    if ($command === '->') {
                        return $method->getClosure($obj);
                    } else {
                        return $method->getClosure();
                    }
                } else {
                    throw new \Exception("No property/method {$command}{$prop} in {$obj}");
                }
            }
            break;
        case 'new':
            $class = cons::car($args);
            $constructor_args = cons::cdr($args);
            $class_name = $eval($class);
            $evaluated_args = array_map($eval, x($constructor_args));
            return new $class_name(...$evaluated_args);
        default:
            $func = $eval($command);
            $evaluated_args = array_map($eval, x($args));
            return $func(...$evaluated_args);
    }
}