public function render(TokenList $tokenList, $skin = 'default') {
        if (!array_key_exists($skin, $this->ruleRenderers)) {
            throw new ParserException('Skin ' . $skin . ' not found');
        }

        if (!array_key_exists($skin, $this->tokenRenderers)) {
            $this->tokenRenderers[$skin] = [];
            foreach ($this->ruleRenderers[$skin] as $ruleRenderer) {
                $ruleRenderer->register($this);
            }
        }

        \Event::fire('vikon.parser.before.render', [$tokenList]);

        $output = '';

        foreach ($tokenList->getTokens() as $token) {
            \Event::fire('vikon.parser.token.render.' . $token->getName(), [$token, $tokenList]);

            if (array_key_exists($token->getName(), $this->tokenRenderers[$skin])) {
                $output .= $this->tokenRenderers[$skin][$token->getName()]($token, $tokenList);
            } else {
                $output .= (string)$token;
            }
        }

        return $output;
    }