<commit_before><?php
/**
 *
 */

namespace Mvc5\Route\Definition;

use InvalidArgumentException;
use Mvc5\Arg;

/**
 * Portions copyright (c) 2013 <NAME> 'DASPRiD'. (http://github.com/DASPRiD/Dash)
 * under the Simplified BSD License (http://opensource.org/licenses/BSD-2-Clause).
 */
trait Compile
{
    /**
     * @param array $tokens
     * @param array $params
     * @param array $defaults
     * @param bool|false $wildcard
     * @return string
     * @throws InvalidArgumentException
     */
    protected function compile(array $tokens, array $params, array $defaults = null, $wildcard = false)
    {
        $current = [
            'is_optional' => false,
            'skip'        => true,
            'skippable'   => false,
            'path'        => '',
        ];

        $stack = [];

        foreach($tokens as $part) {
            if ('literal' === $part[Dash::TYPE]) {
                $current['path'] .= $part[Dash::LITERAL];
                continue;
            }

            if ('param' === $part[Dash::TYPE]) {
                $current['skippable'] = true;

                if (!$part[Dash::NAME]) {
                    continue;
                }

                if (!isset($params[$part[Dash::NAME]])) {
                    if (!$current['is_optional']) {
                        throw new InvalidArgumentException(sprintf('Missing parameter "%s"', $part[Dash::NAME]));
                    }

                    continue;
                }

                (!$current['is_optional'] || !isset($defaults[$part[Dash::NAME]])
                    || $defaults[$part[Dash::NAME]] !== $params[$part[Dash::NAME]]) &&
                        $current['skip'] = false;

                $current['path'] .= $params[$part[Dash::NAME]];

                unset($params[$part[Dash::NAME]]);

                continue;
            }

            if ('optional-start' === $part[Dash::TYPE]) {

                $stack[] = $current;

                $current = [
                    'is_optional' => true,
                    'skip'        => true,
                    'skippable'   => false,
                    'path'        => '',
                ];

                continue;
            }

            if ('optional-end' === $part[Dash::TYPE]) {

                $parent = array_pop($stack);

                if ($current['path'] === '' || !$current['is_optional'] || !$current['skippable'] || !$current['skip']) {
                    $parent['path'] .= $current['path'];
                    $parent['skip'] = false;
                }

                $current = $parent;

                continue;
            }
        }

        if ($wildcard && $params) {
            $current['path'] = rtrim($current['path'], Arg::SEPARATOR);

            foreach($params as $key => $value) {
                null !== $value && $current['path'] .= Arg::SEPARATOR . $key . Arg::SEPARATOR . $value;
            }
        }

        return $current['path'];
    }
}<commit_msg>use default value when param is required and not provided, use callback for wildcard params<commit_after><?php
/**
 *
 */

namespace Mvc5\Route\Definition;

use InvalidArgumentException;
use Mvc5\Arg;

/**
 * Portions copyright (c) 2013 <NAME> 'DASPRiD'. (http://github.com/DASPRiD/Dash)
 * under the Simplified BSD License (http://opensource.org/licenses/BSD-2-Clause).
 */
trait Compile
{
    /**
     * @param array $tokens
     * @param array $params
     * @param array $defaults
     * @param callable $wildcard
     * @return string
     * @throws InvalidArgumentException
     */
    protected function compile(array $tokens, array $params, array $defaults = null, callable $wildcard = null)
    {
        $current = [
            'is_optional' => false,
            'skip'        => true,
            'skippable'   => false,
            'path'        => '',
        ];

        $stack = [];

        foreach($tokens as $part) {
            if ('literal' === $part[Dash::TYPE]) {
                $current['path'] .= $part[Dash::LITERAL];
                continue;
            }

            if ('param' === $part[Dash::TYPE]) {
                $current['skippable'] = true;

                if (!$part[Dash::NAME]) {
                    continue;
                }

                $default = isset($defaults[$part[Dash::NAME]]) ? $defaults[$part[Dash::NAME]] : null;
                $path    = isset($params[$part[Dash::NAME]])   ? $params[$part[Dash::NAME]]   : null;

                if (!$path) {
                    if ($current['is_optional']) {
                        continue;
                    }

                    if (!$default) {
                        throw new InvalidArgumentException(sprintf('Missing parameter "%s"', $part[Dash::NAME]));
                    }

                    $path = $default;
                }

                (!$current['is_optional'] || !$default || $default !== $path)
                    && $current['skip'] = false;

                $current['path'] .= $path;

                unset($params[$part[Dash::NAME]]);

                continue;
            }

            if ('optional-start' === $part[Dash::TYPE]) {

                $stack[] = $current;

                $current = [
                    'is_optional' => true,
                    'skip'        => true,
                    'skippable'   => false,
                    'path'        => '',
                ];

                continue;
            }

            if ('optional-end' === $part[Dash::TYPE]) {

                $parent = array_pop($stack);

                if ($current['path'] === '' || !$current['is_optional'] || !$current['skippable'] || !$current['skip']) {
                    $parent['path'] .= $current['path'];
                    $parent['skip'] = false;
                }

                $current = $parent;

                continue;
            }
        }

        return $wildcard && $params ? $wildcard(rtrim($current['path'], Arg::SEPARATOR), $params) : $current['path'];
    }
}
{'fraction_non_alphanumeric': 0.16364543489106936, 'fraction_numerical': 0.002328288707799767, 'mean_word_length': 1.4230459307010475, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6323057', 'n_tokens_mistral': 1715, 'n_tokens_neox': 1597, 'n_words': 307}