Зачем нужны виртуальные функции

в 11:36, , рубрики: c++, виртуальные функции, Программирование, стили программирования, чистый код

Привет. Если ты знаешь ответ на вопрос в заголовке, – поздравляю, эта статья тебе не нужна. Она адресуется новичкам в программировании, вроде меня, которые не всегда могут самостоятельно разобраться со всеми тонкостями C++ и других типизированных языков, а если и могут, лучше всё равно учиться на чужих ошибках.
В данной статье я не просто отвечу на вопрос "Зачем нужны виртуальные функции в C++", а приведу пример из своей практики. Для краткого ответа можно обратиться к поисковикам, которые выдают примерно следующее: "Виртуальные функции нужны для обеспечения полиморфизма — одного из трёх китов ООП. Благодаря им машина может сама определить тип объекта по указателю, не загружая этой задачей программиста". О'кей, но вопрос «зачем» остался, хотя теперь он значит немного другое: "Зачем полагаться на машину, тратить лишние время и память, если можно самостоятельно подкастовать указатель, ведь тип объекта, на который он ссылается, почти всегда известен?" Действительно, кастование на первый взгляд оставляет виртуальные функции без работы, и именно оно становится причиной заблуждений и плохого кода. В мелких проектах проигрыш незаметен, но, как вы скоро убедитесь, с ростом программы касты увеличивают листинг в почти геометрической прогрессии.

Для начала вспомним, где вообще могут понадобиться касты и виртуальные функции. Тип теряется, когда объекту, объявленному с типом A, операцией new выделяется память под объект типа B, совместимого с типом A, обычно наследуемого от A. Чаще всего объект не один, а целый массив. Массив указателей одного типа, каждый из которых ждёт присвоение области памяти с объектами совершенно других типов. Вот такой пример мы и рассмотрим.
Долго не буду тянуть, задача была такова: на основе документа, размеченного языком гипертекстовой разметки Markedit (про него можете почитать тут), построить синтаксическое дерево разбора и создать файл, содержащий тот же документ в разметке HTML. Моё решение состоит из трёх последовательных подпрограмм: разбор исходного текста на токены, построение из токенов синтаксического дерева и построение на его основе документа HTML. Нас интересует вторая часть. Дело в том, что узлы конечного дерева имеют разные типы (раздел, параграф, текстовый узел, ссылка, сноска итд.), но для узлов-родителей указатели на узлы-детей хранятся в массиве, и потому имеют один тип – Node.

Сам парсер в упрощенной форме работает так: создаётся «корень» синтаксического дерева tree с типом Root, объявляется указатель open_node общего типа Node, которому тут же присваивается адрес tree, и переменная type перечислимого типа Node_type, а затем начинается цикл, перебирающий токены от самого первого до последнего. На каждой итерации первым делом в переменную type заносится тип открытой ноды open_node (типы в виде перечисления хранятся в структуре нод), после чего следует оператор выбора switch, проверяющий тип очередного токена (типы токенов уже заботливо предоставлены лексером). В каждой ветке switch-а представлено ещё одно ветвление, проверяющее переменную type, где, как мы помним, содержится тип открытого узла. В зависимости от её значения выполняются разные действия, например: добавить в открытый узел узел-лист определённого типа, открыть в открытом узле другой узел определённого типа и передать его адрес в open_node, закрыть открытый узел, выбросить исключение. Применимо к теме статьи, нас интересует второй пример. Каждый открытый узел (и вообще каждый узел, который можно открыть) уже содержит массив указателей на узлы типа Node. Поэтому, когда мы открываем в открытом узле новый узел (присваиваем очередному указателю массива область памяти для объекта другого типа), для семантического анализатора языка C++ он остаётся экземпляром типа Node, не приобретая новых полей и методов. Указатель на него теперь присваивается переменной open_node, не теряя типа Node. Но как работать с указателем общего типа Node, когда нужно вызвать метод, например, абзаца? Например, open_bold(), открывающий в нём узел полужирного шрифта? Ведь open_bold() объявлен и определён как метод класса Paragraph, и Node совершенно о нём не знает. Вдобавок, open_node тоже объявлена как указатель на Node, а методы должна принимать ото всех типов открывающихся узлов.

Здесь есть два решения: очевидное и правильное. Очевидным для новичка является static_cast, а правильным — виртуальные функции. Давайте сначала рассмотрим одну ветвь switch-а парсера, написанного с помощью первого способа:

        case Lexer::BOLD_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_bold();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_bold();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_bold();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_bold();
        break; }

Неплохо. А теперь, не буду долго тянуть, покажу тот же участок кода, написанный при помощи виртуальных функций:

        case Lexer::BOLD_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_bold();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_bold();

            } else if (type == Node::UNORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_bold();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_bold();
        break; }

Выигрыш очевиден, но действительно ли он нам нужен? Ведь тогда придётся объявлять в классе Node все методы всех производных классов как виртуальные и в каждом производном классе их как-то реализовывать. Ответ — да, действительно. Методов конкретно в этой программе не так уж и много (29), а их реализация в производных классах, не относящихся к ним, состоит всего из одной строчки: throw string(«error!»);. Можно включить творческий режим и придумать для каждого выброса исключения уникальную строку. Но самое главное — из-за сокращения кода уменьшилось число ошибок в нём. Кастование — одна из самых главных причин ошибок в коде. Потому что после применения static_cast компилятор перестаёт ругаться, если в приведённом классе содержится вызываемый метод. А между тем, в разных классах могут содержаться разные методы с одним названием. В моём случае в коде запряталось 6!!! ошибок, при том одна из них дублировалась в нескольких ветвях switch. Вот она:

else if (type == Node::
    open_node = static_cast<Title*>(open_node)->open_italic();

Далее под спойлерами привожу полные листинги первой и второй версии парсера.

Парсер с кастованием

Root * Parser::parse (const Lexer &lexer) {
    Node * open_node(tree);
    Node::Node_type type;

    for (unsigned long i(0), len(lexer.count()); i < len; i++) {
        type = open_node->get_type();
        if (type == Node::CITE || type == Node::TEXT || type == Node::NEWLINE || type == Node::NOTIFICATION || type == Node::IMAGE)
            throw string("error!");

        switch (lexer[i].type) {
        case Lexer::NEWLINE: {
            if (type == Node::ROOT || type == Node::SECTION)
                ;

            else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->add_text("n");

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->add_text("n");

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->add_text("n");

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }

            } else if (type == Node::LINK) {
                open_node = static_cast<Link*>(open_node)->add_text(lexer[i].lexeme);

            } else // INLINE
                open_node = static_cast<Inline*>(open_node)->add_text(lexer[i].lexeme);
        break; }
        case Lexer::DOUBLE_NEWLINE: {
            if (type == Node::ROOT || type == Node::SECTION)
                ;

            else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }

            } else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }

            } else
                throw string("unexpected double newline!");
        break; }
        case Lexer::UNDERLINE: {
            if (type == Node::ROOT)
                open_node = tree->add_line();

            else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->add_line();

            } else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->add_line();

            } else if (type == Node::TITLE)
                throw string("unexpected underline inside title!");

            else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->add_line();

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->add_line();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->add_line();

            } else // INLINE
                throw string("unexpected underline inside inline span!");
        break; }
        case Lexer::TITLE_START: {
            if (lexer[i].lexeme.size() > 7)
                throw string("invalid title: "" + lexer[i].lexeme + ""!");

            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::SECTION)
                open_node = static_cast<Section*>(open_node)->open_title(lexer[i].lexeme.size()-1);

            else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::TITLE)
                throw string("title can't contain another title!");

            else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::LINK)
                throw string("link can't contain a title!");

            else // INLINE
                throw string("inline span can't contain a title!");
        break; }
        case Lexer::BOLD_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_bold();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_bold();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_bold();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_bold();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_bold();
        break; }
        case Lexer::ITALIC_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_italic();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_italic();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_italic();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_italic();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_italic();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_italic();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_italic();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_italic();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_italic();
        break; }
        case Lexer::UNDERLINED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_underlined();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_underlined();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_underlined();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_underlined();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_underlined();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_underlined();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_underlined();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_underlined();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_underlined();
        break; }
        case Lexer::OVERLINED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_overlined();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_overlined();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_overlined();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_overlined();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_overlined();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_overlined();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_overlined();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_overlined();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_overlined();
        break; }
        case Lexer::THROWLINED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_throwlined();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_throwlined();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_throwlined();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_throwlined();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_throwlined();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_throwlined();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_throwlined();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_throwlined();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_throwlined();
        break; }
        case Lexer::SUBSCRIPT_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_subscript();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_subscript();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_subscript();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_subscript();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_subscript();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_subscript();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_subscript();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_subscript();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_subscript();
        break; }
        case Lexer::SUPERSCRIPT_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_superscript();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_superscript();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_superscript();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_superscript();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_superscript();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_superscript();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_superscript();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_superscript();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_superscript();
        break; }
        case Lexer::MARKED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_marked();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_marked();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_marked();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_marked();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_marked();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_marked();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_marked();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_marked();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_marked();
        break; }
        case Lexer::MONOSPACE_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_monospace();

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_monospace();

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_monospace();

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_monospace();

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_monospace();

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_monospace();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_monospace();

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_monospace();

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_monospace();
        break; }
        case Lexer::SPAN_OR_IMAGE_FINISH: {
            if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->close();

            else if (type == Node::BOLD || type == Node::ITALIC || type == Node::UNDERLINED || type == Node::OVERLINED || type == Node::THROWLINED || type == Node::SUBSCRIPT || type == Node::SUPERSCRIPT || type == Node::MARKED || type == Node::MONOSPACE)
                open_node = static_cast<Inline*>(open_node)->close();

            else if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text("]");

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text("]");

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->add_text("]");

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->add_text("]");

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->add_text("]");

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text("]");

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text("]");

            } else // INLINE
                open_node = static_cast<Inline*>(open_node)->add_text(">");
        break; }
        case Lexer::LINK_START: {
            if (i > len-3 || lexer[++i].type != Lexer::TEXT || lexer[++i].type != Lexer::LINK_FINISH)
                throw string("unclosed link!");

            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_link(lexer[i-1].lexeme);

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_link(lexer[i-1].lexeme);

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->open_link(lexer[i-1].lexeme);

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->open_link(lexer[i-1].lexeme);

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->open_link(lexer[i-1].lexeme);

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_link(lexer[i-1].lexeme);

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->open_link(lexer[i-1].lexeme);

            } else if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->open_link(lexer[i-1].lexeme);

            else // INLINE
                open_node = static_cast<Inline*>(open_node)->open_link(lexer[i-1].lexeme);
        break; }
        case Lexer::LINK_FINISH: {
            if (type == Node::LINK)
                open_node = static_cast<Link*>(open_node)->close();

            else if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(">");

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section>(open_node).open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(">");

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->add_text(">");

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->add_text(">");

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->add_text(">");

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(">");

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(">");

            } else // INLINE
                open_node = static_cast<Inline*>(open_node)->add_text(">");

        break; }
        case Lexer::IMAGE_START: {
            if (i > len-5 || lexer[++i].type != Lexer::TEXT || lexer[++i].type != Lexer::LINK_FINISH || (lexer[++i].type != Lexer::TEXT && lexer[i].type != Lexer::SPAN_OR_IMAGE_FINISH) || (lexer[i].type == Lexer::TEXT && lexer[i+1].type != Lexer::SPAN_OR_IMAGE_FINISH))
                throw string("unclosed image defintion!");

            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::TITLE) {
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::LINK) {
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");

            } else { // INLINE
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = static_cast<Paragraph*>(open_node)->add_image(lexer[i-2].lexeme, "");
            }
        break; }
        case Lexer::CITE: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::SECTION)
                open_node = static_cast<Section*>(open_node)->add_cite(atoi(lexer[i].lexeme.c_str()));

            else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->add_image(lexer[i-3].lexeme, lexer[i-1].lexeme);

            else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::LINK)
                throw string("link can't contain a cite!");

            else // INLINE
                throw string("inline span can't contain a cite!");
        break; }
        case Lexer::QUOTE_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else if (type == Node::SECTION)
                open_node = static_cast<Section*>(open_node)->open_quote();

            else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else if (type == Node::TITLE) {
                open_node = static_cast<Title*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else if (type == Node::LINK) {
                open_node = static_cast<Link*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();

            } else { // INLINE
                open_node = static_cast<Inline*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_quote();
            }
        break; }
        case Lexer::NOTIFICATION: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::TITLE) {
                open_node = static_cast<Title*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else if (type == Node::LINK) {
                open_node = static_cast<Link*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);

            } else { // INLINE
                open_node = static_cast<Inline*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_notification(lexer[i].lexeme);
            }
        break; }
        case Lexer::TEXT: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(lexer[i].lexeme);

            } else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(lexer[i].lexeme);

            } else if (type == Node::PARAGRAPH)
                open_node = static_cast<Paragraph*>(open_node)->add_text(lexer[i].lexeme);

            else if (type == Node::TITLE)
                open_node = static_cast<Title*>(open_node)->add_text(lexer[i].lexeme);

            else if (type == Node::QUOTE)
                open_node = static_cast<Quote*>(open_node)->add_text(lexer[i].lexeme);

            else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(lexer[i].lexeme);

            } else if (type == Node::ORDERED_LIST) {
                open_node = static_cast<Ordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->open_paragraph();
                open_node = static_cast<Paragraph*>(open_node)->add_text(lexer[i].lexeme);

            } else if (type == Node::LINK) {
                open_node = static_cast<Link*>(open_node)->add_text(lexer[i].lexeme);

            } else // INLINE
                open_node = static_cast<Inline*>(open_node)->add_text(lexer[i].lexeme);
        break; }
        case Lexer::UNORDERED_LIST_ITEM_MARKER: {

        break; }
        case Lexer::ORDERED_LIST_ITEM_MARKER: {

        break; }
        case Lexer::END: {
            if (type == Node::ROOT)
                open_node = tree->close();

            else if (type == Node::SECTION) {
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->close();

            } else if (type == Node::PARAGRAPH) {
                open_node = static_cast<Paragraph*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->close();

            } else if (type == Node::QUOTE) {
                open_node = static_cast<Quote*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->close();

            } else if (type == Node::UNORDERED_LIST) {
                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->close();

            } else if (type == Node::ORDERED_LIST) {

                open_node = static_cast<Unordered_list*>(open_node)->close();
                while (open_node->get_type() != Node::SECTION) {
                    if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::UNORDERED_LIST)
                        open_node = static_cast<Unordered_list*>(open_node)->close();
                    else if (open_node->get_type() == Node::PARAGRAPH)
                        open_node = static_cast<Paragraph*>(open_node)->close();
                }
                open_node = static_cast<Section*>(open_node)->close();
                open_node = tree->close();

            } else // LINK || INLINE
                throw string("unexpected ending!");
            ///    ROOT,
            ///    SECTION,
            ///    PARAGRAPH, TITLE, QUOTE, UNORDERED_LIST, ORDERED_LIST,
            ///    BOLD, ITALIC, UNDERLINED, OVERLINED, THROWLINED, SUBSCRIPT, SUPERSCRIPT, MARKED, MONOSPACE,
            ///    LINK
        break; }
        }
    }
    concatenate();
    return tree;
}

Парсер с обращением к виртуальным методам
Root * Parser::parse (const Lexer &lexer) {
    Node * open_node(tree);
    Node::Node_type type;

    for (unsigned long i(0), len(lexer.count()); i < len; i++) {
        type = open_node->get_type();
        if (type == Node::CITE || type == Node::TEXT || type == Node::NEWLINE || type == Node::NOTIFICATION || type == Node::IMAGE)
            throw string("error!");

        switch (lexer[i].type) {
        case Lexer::NEWLINE: {
            if (type == Node::ROOT || type == Node::SECTION)
                ;

            else if (type == Node::PARAGRAPH || type == Node::TITLE || type == Node::QUOTE || type == Node::TITLE || type == Node::QUOTE)
                open_node = open_node->add_text("n");

            else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();

            } else // LINK, INLINE
                open_node = open_node->add_text(lexer[i].lexeme);
        break; }
        case Lexer::DOUBLE_NEWLINE: {
            if (type == Node::ROOT || type == Node::SECTION)
                ;

            else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();

            } else
                throw string("unexpected double newline!");
        break; }
        case Lexer::UNDERLINE: {
            if (type == Node::ROOT)
                open_node = tree->add_line();

            else if (type == Node::SECTION) {
                open_node = open_node->close();
                open_node = tree->add_line();

            } else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->close();
                open_node = tree->add_line();

            } else if (type == Node::TITLE)
                throw string("unexpected underline inside title!");

            else if (type == Node::LINK)
                throw string("unexpected underline inside link!");

            else // INLINE
                throw string("unexpected underline inside inline span!");
        break; }
        case Lexer::TITLE_START: {
            if (lexer[i].lexeme.size() > 7)
                throw string("invalid title: "" + lexer[i].lexeme + ""!");

            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::SECTION)
                open_node = open_node->open_title(lexer[i].lexeme.size()-1);

            else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_title(lexer[i].lexeme.size()-1);

            } else if (type == Node::TITLE)
                throw string("title can't contain another title!");

            else if (type == Node::LINK)
                throw string("link can't contain a title!");

            else // INLINE
                throw string("inline span can't contain a title!");
        break; }
        case Lexer::BOLD_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_bold();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_bold();

            } else if (type == Node::UNORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_bold();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_bold();
        break; }
        case Lexer::ITALIC_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_italic();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_italic();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_italic();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_italic();
        break; }
        case Lexer::UNDERLINED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_underlined();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_underlined();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_underlined();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_underlined();
        break; }
        case Lexer::OVERLINED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_overlined();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_overlined();

            } else if (type == Node::PARAGRAPH)
                open_node = open_node->open_overlined();

            else if (type == Node::TITLE)
                open_node = open_node->open_overlined();

            else if (type == Node::QUOTE)
                open_node = open_node->open_overlined();

            else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_overlined();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_overlined();
        break; }
        case Lexer::THROWLINED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_throwlined();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_throwlined();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_throwlined();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_throwlined();
        break; }
        case Lexer::SUBSCRIPT_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_subscript();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_subscript();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_subscript();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_subscript();
        break; }
        case Lexer::SUPERSCRIPT_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_superscript();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_superscript();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_superscript();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_superscript();
        break; }
        case Lexer::MARKED_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_marked();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_marked();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_marked();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_marked();
        break; }
        case Lexer::MONOSPACE_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_monospace();

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_monospace();

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_monospace();

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_monospace();
        break; }
        case Lexer::SPAN_OR_IMAGE_FINISH: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text("]");

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text("]");

            } else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::LINK)
                open_node = open_node->add_text("]");

            else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text("]");

            } else // TITLE, INLINE
                open_node = open_node->close();
        break; }
        case Lexer::LINK_START: {
            if (i > len-3 || lexer[++i].type != Lexer::TEXT || lexer[++i].type != Lexer::LINK_FINISH)
                throw string("unclosed link!");

            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_link(lexer[i-1].lexeme);

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->open_link(lexer[i-1].lexeme);

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->open_link(lexer[i-1].lexeme);

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->open_link(lexer[i-1].lexeme);
        break; }
        case Lexer::LINK_FINISH: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text(">");

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text(">");

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text(">");

            } else if (type == Node::LINK)
                open_node = open_node->close();

            else // PARAGRAPH, TITLE, QUOTE, INLINE
                open_node = open_node->add_text(">");

        break; }
        case Lexer::IMAGE_START: {
            if (i > len-5 || lexer[++i].type != Lexer::TEXT || lexer[++i].type != Lexer::LINK_FINISH || (lexer[++i].type != Lexer::TEXT && lexer[i].type != Lexer::SPAN_OR_IMAGE_FINISH) || (lexer[i].type == Lexer::TEXT && lexer[i+1].type != Lexer::SPAN_OR_IMAGE_FINISH))
                throw string("unclosed image defintion!");

            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = open_node->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = open_node->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = open_node->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = open_node->add_image(lexer[i-2].lexeme, "");

            } else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = open_node->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = open_node->add_image(lexer[i-2].lexeme, "");

            } else { // TITLE, LINK, INLINE
                if (lexer[i].type == Lexer::TEXT) {
                    open_node = open_node->add_image(lexer[i-2].lexeme, lexer[i].lexeme);
                    i++;
                } else
                    open_node = open_node->add_image(lexer[i-2].lexeme, "");
            }
        break; }
        case Lexer::CITE: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::SECTION)
                open_node = open_node->add_cite(atoi(lexer[i].lexeme.c_str()));

            else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->add_cite(atoi(lexer[i].lexeme.c_str()));

            } else if (type == Node::TITLE)
                throw string("title cant't contain a cite!");

            else if (type == Node::LINK)
                throw string("link can't contain a cite!");

            else // INLINE
                throw string("inline span can't contain a cite!");
        break; }
        case Lexer::QUOTE_START: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_quote();

            } else if (type == Node::SECTION)
                open_node = open_node->open_quote();

            else {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_quote();
            }
        break; }
        case Lexer::NOTIFICATION: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_notification(lexer[i].lexeme);

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->add_notification(lexer[i].lexeme);

            } else {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_notification(lexer[i].lexeme);
            }
        break; }
        case Lexer::TEXT: {
            if (type == Node::ROOT) {
                open_node = tree->open_section();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text(lexer[i].lexeme);

            } else if (type == Node::SECTION) {
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text(lexer[i].lexeme);

            } else if (type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->open_paragraph();
                open_node = open_node->add_text(lexer[i].lexeme);

            } else // PARAGRAPH, TITLE, QUOTE, LINK, INLINE
                open_node = open_node->add_text(lexer[i].lexeme);
        break; }
        case Lexer::UNORDERED_LIST_ITEM_MARKER: {

        break; }
        case Lexer::ORDERED_LIST_ITEM_MARKER: {

        break; }
        case Lexer::END: {
            if (type == Node::ROOT)
                open_node = tree->close();

            else if (type == Node::SECTION) {
                open_node = open_node->close();
                open_node = tree->close();

            } else if (type == Node::PARAGRAPH || type == Node::QUOTE || type == Node::UNORDERED_LIST || type == Node::ORDERED_LIST) {
                open_node = open_node->close();
                while (open_node->get_type() != Node::SECTION)
                    open_node = open_node->close();
                open_node = open_node->close();
                open_node = tree->close();

            } else // LINK || INLINE
                throw string("unexpected ending!");
        break; }
        }
    }
    concatenate();
    return tree;
}

С 1357 строк код сократился до 487 – почти в три раза, не считая длину строк!

Остался один вопрос: а что же со временем выполнения? Сколько миллисекунд мы должны заплатить за то, чтобы компьютер сам определял тип открытого узла? Я провёл эксперимент – зафиксировал время работы парсера в миллисекундах в первом и втором случаях для одного и того же документа на своём домашнем компьютере. Вот результат:
Кастование – 538 мс.
Виртуальные функции – 1174 мс.
Итого, 636 мс – плата за компактность кода и отсутствие ошибок. Много это? Возможно. Но если нам нужна программа, работающая с максимально возможной скоростью и требующая минимально возможное количество памяти, мы бы не стали обращаться к ООП и вообще написали бы её на языке ассемблера, потратив неделю времени и рискуя допустить огромное число ошибок. Так что мой выбор – везде, где в программе встретится static_cast и dynamic_cast, заменить их виртуальными функциями. А какое ваше мнение?

Автор: James Soifer

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js