One-pass augment expansion algorithm: First observation: since we
    validate each imported module, all nodes that are augmented by
    other modules already exist.  For each node in the path to the
    target node, if it does not exist, it might get created by an
    augment later in this module.  This only applies to nodes defined
    in our namespace (since all other modules already are validated).
    For each such node, we add a temporary Statement instance, and
    store a pointer to it.  If we find such a temporary node in the
    nodes we add, we replace it with our real node, and delete it from
    the list of temporary nodes created.  When we're done with all
    augment statements, the list of temporary nodes should be empty,
    otherwise it is an error.

def v_expand_2_augment(ctx, stmt):
    """
    One-pass augment expansion algorithm: First observation: since we
    validate each imported module, all nodes that are augmented by
    other modules already exist.  For each node in the path to the
    target node, if it does not exist, it might get created by an
    augment later in this module.  This only applies to nodes defined
    in our namespace (since all other modules already are validated).
    For each such node, we add a temporary Statement instance, and
    store a pointer to it.  If we find such a temporary node in the
    nodes we add, we replace it with our real node, and delete it from
    the list of temporary nodes created.  When we're done with all
    augment statements, the list of temporary nodes should be empty,
    otherwise it is an error.
    """
    if hasattr(stmt, 'i_target_node'):
        # already expanded
        return
    stmt.i_target_node = find_target_node(ctx, stmt, is_augment=True)

    if stmt.i_target_node is None:
        return
    if not hasattr(stmt.i_target_node, 'i_children'):
        err_add(ctx.errors, stmt.pos, 'BAD_NODE_IN_AUGMENT',
                (stmt.i_target_node.i_module.arg, stmt.i_target_node.arg,
                 stmt.i_target_node.keyword))
        return

    def chk_mandatory(s):
        if s.keyword == 'leaf':
            m = s.search_one('mandatory')
            if m is not None and m.arg == 'true':
                err_add(ctx.errors, m.pos, 'AUGMENT_MANDATORY', s.arg)
        elif s.keyword == 'list' or s.keyword == 'leaf-list':
            m = s.search_one('min-elements')
            if m is not None and int(m.arg) >= 1:
                err_add(ctx.errors, m.pos, 'AUGMENT_MANDATORY', s.arg)
        elif s.keyword == 'container':
            p = s.search_one('presence')
            if p == None:
                for sc in s.i_children:
                    chk_mandatory(sc)
    # if we're augmenting another module, make sure we're not
    # trying to add a mandatory node
    if stmt.i_module.i_modulename != stmt.i_target_node.i_module.i_modulename:
        # 1.1 allows mandatory augment if the augment is conditional
        if stmt.i_module.i_version == '1' or stmt.search_one('when') is None:
            for sc in stmt.i_children:
                chk_mandatory(sc)

    # copy the expanded children into the target node
    def add_tmp_children(node, tmp_children):
        for tmp in tmp_children:
            ch = search_child(node.i_children, stmt.i_module.i_modulename,
                              tmp.arg)
            if ch is not None:
                del ch.i_module.i_undefined_augment_nodes[tmp]
                if not hasattr(ch, 'i_children'):
                    err_add(ctx.errors, tmp.pos, 'BAD_NODE_IN_AUGMENT',
                            (stmt.i_module.i_modulename, ch.arg,
                             ch.keyword))
                    raise Abort
                add_tmp_children(ch, tmp.i_children)
            elif node.keyword == 'choice' and tmp.keyword != 'case':
                # create an artifical case node for the shorthand
                new_case = create_new_case(ctx, node, tmp, expand=False)
                new_case.parent = node
            else:
                node.i_children.append(tmp)
                tmp.parent = node

    for c in stmt.i_children:
        c.i_augment = stmt

        ch = search_child(stmt.i_target_node.i_children,
                          stmt.i_module.i_modulename, c.arg)
        if ch is not None:
            if ch.keyword == '__tmp_augment__':
                # replace this node with the proper one,
                # and also do this recursively
                del ch.i_module.i_undefined_augment_nodes[ch]
                if not hasattr(c, 'i_children'):
                    err_add(ctx.errors, stmt.pos, 'BAD_NODE_IN_AUGMENT',
                            (stmt.i_module.i_modulename, c.arg,
                             c.keyword))
                    return
                idx = stmt.i_target_node.i_children.index(ch)
                stmt.i_target_node.i_children[idx] = c
                c.parent = stmt.i_target_node
                try:
                    add_tmp_children(c, ch.i_children)
                except Abort:
                    return
            else:
                err_add(ctx.errors, c.pos, 'DUPLICATE_CHILD_NAME',
                        (stmt.arg, stmt.pos, c.arg, ch.pos))
                return
        elif stmt.i_target_node.keyword == 'choice' and c.keyword != 'case':
            # create an artifical case node for the shorthand
            new_case = create_new_case(ctx, stmt.i_target_node, c, expand=False)
            new_case.parent = stmt.i_target_node
            v_inherit_properties(ctx, stmt.i_target_node, new_case)
        else:
            stmt.i_target_node.i_children.append(c)
            c.parent = stmt.i_target_node
            v_inherit_properties(ctx, stmt.i_target_node, c)
    for s in stmt.substmts:
        if s.keyword in _copy_augment_keywords:
            stmt.i_target_node.substmts.append(s)
            s.parent = stmt.i_target_node