void HighlightStateBuilder::build(DelimitedLangElem *elem,
        HighlightState *state) {
    const string &name = elem->getName();

    StringDef *start = elem->getStart();
    StringDef *end = elem->getEnd();
    StringDef *escape = elem->getEscape();

    string start_string = (start ? start->toString() : "");
    string end_string = (end ? end->toString() : "");
    string escape_string = (escape ? escape->toString() : "");

    if (elem->isNested() && start_string == end_string) {
        // the two delimiters must be different for nested elements
        throw HighlightBuilderException(
                "delimiters must be different for nested elements", elem);
    }

    bool end_string_has_references = false;
    // check possible back reference markers and their correctness
    if (end && end->hasBackRef() && end_string.size()) {
        backreference_info ref_info = RegexPreProcessor::num_of_references(
                end_string);
        subexpressions_info info =
                RegexPreProcessor::num_of_marked_subexpressions(start_string,
                        true, true);

        // possible errors, e.g., unbalanced parenthesis
        if (info.errors.size()) {
            throw HighlightBuilderException(info.errors, elem);
        }

        // check that there are enough subexpressions as requested by the maximal
        // back reference number
        unsigned int max = ref_info.second;
        if (max > info.marked) {
            std::ostringstream error;
            error << max << " subexpressions requested, but only "
                    << info.marked << " found";
            throw HighlightBuilderException(error.str(), elem);
        }

        end_string_has_references = true;
    }

    HighlightRulePtr rule;

    // if this element starts a new state/environment, we must split it
    if (elem->getStateLangElem() || elem->isMultiline()
            || end_string_has_references) {
        rule = HighlightRulePtr(highlightRuleFactory->createMultiLineRule(name,
                start_string, end_string, escape_string, elem->isNested()));

        if (end_string_has_references) {
            // record that the state (and the rule representing the end)
            // need to have dynamic back references replaced
            rule->getNextState()->setNeedsReferenceReplacement();
            rule->getNextState()->getRuleList().front()->setNeedsReferenceReplacement();

            // and that the starting rule has sub expressions
            // (that will be used for replacing dynamic back references)
            rule->setHasSubexpressions();

            // if the element is nested, then the last rule is a sort of copy
            // of the first one, so we need to record that it has subexpressions too
            if (elem->isNested()) {
                rule->getNextState()->getRuleList().back()->setHasSubexpressions();
            }
        }
    } else {
        rule = HighlightRulePtr(highlightRuleFactory->createLineRule(name,
                start_string, end_string, escape_string, elem->isNested()));
    }

    rule->setAdditionalInfo(elem->toStringParserInfo());
    state->addRule(rule);

    if (rule->getNextState().get()) {
        // as for exit level, if the rule was split using states, we must set
        // the exit level of the first rule of the next state (i.e., the end expression) of the rule
        // this exit level must be incremented by one: 1 is for exiting the inner state
        // of the rule, and 1 for exiting the state this rule belongs to
        setExitLevel(elem, rule->getNextState()->getRuleList().front().get(), 1);

        // adjust the additional info of the exiting rule
        rule->getNextState()->getRuleList().front()->setAdditionalInfo(
                elem->toStringParserInfo());

        // since this is a delimited element, we must set the default element for
        // the inner state to the name of the element itself
        rule->getNextState()->setDefaultElement(name);
    } else {
        setExitLevel(elem, rule.get());
    }
}