HelenOS sources

root/uspace/app/sbi/src/ancr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ancr_module_process
  2. ancr_csi_dfs
  3. ancr_csi_process
  4. ancr_csi_get_pred
  5. ancr_csi_print_cycle

/*
 * Copyright (c) 2010 Jiri Svoboda
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @file Ancestry resolver.
 *
 * A chicken-and-egg problem is that in order to match identifiers to CSI
 * definitions we need to know CSI ancestry. To know CSI ancestry we need
 * to match identifiers to CSI definitions. Thus both must be done at the
 * same time. Once we know the ancestry of some CSI, we are able to resolve
 * symbols referenced within the scope of that CSI (but not in nested scopes).
 *
 * Here lies probably the most complicated (although not so complicated)
 * algorithm. To process node N we must first process outer(N). This allows
 * us to find all base(N) nodes and process them.
 *
 * To ensure all nodes get processed correctly, we use a two-layer walk.
 * In the lower layer (ancr_csi_process) we follow the dependencies.
 * ancr_csi_process(N) ensures N (and possibly other nodes) get resolved.
 *
 * In the second layer we simply do a DFS of the CSI tree, calling
 * ancr_csi_process() on each node. This ensures that eventually all
 * nodes get processed.
 */

#include <stdlib.h>
#include <assert.h>
#include "builtin.h"
#include "cspan.h"
#include "list.h"
#include "mytypes.h"
#include "stree.h"
#include "strtab.h"
#include "symbol.h"

#include "ancr.h"

static void ancr_csi_dfs(stree_program_t *prog, stree_csi_t *csi);
static void ancr_csi_process(stree_program_t *prog, stree_csi_t *node);
static stree_csi_t *ancr_csi_get_pred(stree_program_t *prog, stree_csi_t *csi,
    stree_texpr_t *pred_ref);
static void ancr_csi_print_cycle(stree_program_t *prog, stree_csi_t *node);

/** Process ancestry of all CSIs in a module.
 *
 * Note that currently we expect there to be exactly one module in the
 * whole program.
 *
 * @param prog          Program being processed.
 * @param module        Module to process.
 */
void ancr_module_process(stree_program_t *prog, stree_module_t *module)
{
        list_node_t *node;
        stree_modm_t *modm;

        (void) module;
        node = list_first(&prog->module->members);

        while (node != NULL) {
                modm = list_node_data(node, stree_modm_t *);

                switch (modm->mc) {
                case mc_csi:
                        ancr_csi_dfs(prog, modm->u.csi);
                        break;
                case mc_enum:
                        break;
                }

                node = list_next(&prog->module->members, node);
        }
}

/** Walk CSI node tree depth-first.
 *
 * This is the outer depth-first walk whose purpose is to eventually
 * process all CSI nodes by calling ancr_csi_process() on them.
 * (Which causes that and possibly some other nodes to be processed).
 *
 * @param prog          Program being processed.
 * @param csi           CSI node to visit.
 */
static void ancr_csi_dfs(stree_program_t *prog, stree_csi_t *csi)
{
        list_node_t *node;
        stree_csimbr_t *csimbr;

        /* Process this node first. */
        ancr_csi_process(prog, csi);

        /* Now visit all children. */
        node = list_first(&csi->members);
        while (node != NULL) {
                csimbr = list_node_data(node, stree_csimbr_t *);
                if (csimbr->cc == csimbr_csi)
                        ancr_csi_dfs(prog, csimbr->u.csi);

                node = list_next(&csi->members, node);
        }
}

/** Process csi node.
 *
 * Fist processes the pre-required nodes (outer CSI and base CSIs),
 * then processes @a node. This is the core 'outward-and-baseward' walk.
 *
 * @param prog          Program being processed.
 * @param csi           CSI node to process.
 */
static void ancr_csi_process(stree_program_t *prog, stree_csi_t *csi)
{
        stree_csi_t *base_csi, *outer_csi;
        stree_csi_t *gf_class;

        list_node_t *pred_n;
        stree_texpr_t *pred;
        stree_csi_t *pred_csi;

        if (csi->ancr_state == ws_visited) {
                /* Node already processed */
                return;
        }

        if (csi->ancr_state == ws_active) {
                /* Error, closed reference loop. */
                printf("Error: Circular class, struct or interface chain: ");
                ancr_csi_print_cycle(prog, csi);
                printf(".\n");
                exit(1);
        }

        csi->ancr_state = ws_active;

        outer_csi = csi_to_symbol(csi)->outer_csi;
        gf_class = builtin_get_gf_class(prog->builtin);

        if (csi != gf_class) {
                /* Implicit inheritance from grandfather class. */
                base_csi = gf_class;
        } else {
                /* Grandfather class has no base class. */
                base_csi = NULL;
        }

        /* Process outer CSI */
        if (outer_csi != NULL)
                ancr_csi_process(prog, outer_csi);

        /*
         * Process inheritance list.
         */
        pred_n = list_first(&csi->inherit);

        /* For a class node, the first entry can be a class. */
        if (csi->cc == csi_class && pred_n != NULL) {
                pred = list_node_data(pred_n, stree_texpr_t *);
                pred_csi = ancr_csi_get_pred(prog, csi, pred);
                assert(pred_csi != NULL);

                if (pred_csi->cc == csi_class) {
                        /* Process base class */
                        base_csi = pred_csi;
                        ancr_csi_process(prog, pred_csi);

                        pred_n = list_next(&csi->inherit, pred_n);
                }
        }

        /* Following entires can only be interfaces. */
        while (pred_n != NULL) {
                pred = list_node_data(pred_n, stree_texpr_t *);
                pred_csi = ancr_csi_get_pred(prog, csi, pred);
                assert(pred_csi != NULL);

                /* Process implemented or accumulated interface. */
                ancr_csi_process(prog, pred_csi);

                switch (pred_csi->cc) {
                case csi_class:
                        switch (csi->cc) {
                        case csi_class:
                                cspan_print(csi->name->cspan);
                                printf(" Error: Only the first predecessor "
                                    "can be a class. ('");
                                symbol_print_fqn(csi_to_symbol(csi));
                                printf("' deriving from '");
                                symbol_print_fqn(csi_to_symbol(pred_csi));
                                printf("').\n");
                                exit(1);
                                break;
                        case csi_struct:
                                assert(b_false); /* XXX */
                                break;
                        case csi_interface:
                                cspan_print(csi->name->cspan);
                                printf(" Error: Interface predecessor must be "
                                    "an interface ('");
                                symbol_print_fqn(csi_to_symbol(csi));
                                printf("' deriving from '");
                                symbol_print_fqn(csi_to_symbol(pred_csi));
                                printf("').\n");
                                exit(1);
                                break;
                        }
                        break;
                case csi_struct:
                        assert(b_false); /* XXX */
                        break;
                case csi_interface:
                        break;
                }

                pred_n = list_next(&csi->inherit, pred_n);
        }

        /* Store base CSI and update node state. */
        csi->ancr_state = ws_visited;
        csi->base_csi = base_csi;
}

/** Resolve CSI predecessor reference.
 *
 * Returns the CSI predecessor referenced by @a pred_ref.
 * If the referenced CSI does not exist, an error is generated.
 *
 * @param prog          Program being processed.
 * @param csi           CSI node to process.
 * @param pred_ref      Type expression referencing the predecessor.
 * @return              Predecessor CSI.
 */
static stree_csi_t *ancr_csi_get_pred(stree_program_t *prog, stree_csi_t *csi,
    stree_texpr_t *pred_ref)
{
        stree_csi_t *outer_csi;
        stree_symbol_t *pred_sym;
        stree_csi_t *pred_csi;

        outer_csi = csi_to_symbol(csi)->outer_csi;
        pred_sym = symbol_xlookup_in_csi(prog, outer_csi, pred_ref);
        pred_csi = symbol_to_csi(pred_sym);
        assert(pred_csi != NULL); /* XXX */

        return pred_csi;
}

/** Print loop in CSI ancestry.
 *
 * We have detected a loop in CSI ancestry. Traverse it (by following the
 * nodes in ws_active state and print it.
 *
 * @param prog          Program.
 * @param node          CSI node participating in an ancestry cycle.
 */
static void ancr_csi_print_cycle(stree_program_t *prog, stree_csi_t *node)
{
        stree_csi_t *n;
        stree_symbol_t *pred_sym, *node_sym;
        stree_csi_t *pred_csi, *outer_csi;
        stree_texpr_t *pred;
        list_node_t *pred_n;

        n = node;
        do {
                node_sym = csi_to_symbol(node);
                symbol_print_fqn(node_sym);
                printf(", ");

                outer_csi = node_sym->outer_csi;

                if (outer_csi != NULL && outer_csi->ancr_state == ws_active) {
                        node = outer_csi;
                } else {
                        node = NULL;

                        pred_n = list_first(&node->inherit);
                        while (pred_n != NULL) {
                                pred = list_node_data(pred_n, stree_texpr_t *);
                                pred_sym = symbol_xlookup_in_csi(prog,
                                    outer_csi, pred);
                                pred_csi = symbol_to_csi(pred_sym);
                                assert(pred_csi != NULL);

                                if (pred_csi->ancr_state == ws_active) {
                                        node = pred_csi;
                                        break;
                                }
                        }

                        assert(node != NULL);
                }
        } while (n != node);

        node_sym = csi_to_symbol(node);
        symbol_print_fqn(node_sym);
}

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub