/*
* 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 Symbols. */
#include <stdlib.h>
#include <assert.h>
#include "list.h"
#include "mytypes.h"
#include "strtab.h"
#include "stree.h"
#include "symbol.h"
static stree_symbol_t *symbol_search_global(stree_program_t *prog,
stree_ident_t *name);
static stree_symbol_t *symbol_find_epoint_rec(stree_program_t *prog,
stree_ident_t *name, stree_csi_t *csi);
static stree_ident_t *symbol_get_ident(stree_symbol_t *symbol);
/** Lookup symbol in CSI using a type expression.
*
* XXX This should be removed in favor of full type expression evaluation
* (run_texpr). This cannot work properly with generics.
*
* @param prog Program
* @param scope CSI used as base for relative references
* @param texpr Type expression
*
* @return Symbol referenced by type expression or @c NULL
* if not found
*/
stree_symbol_t *symbol_xlookup_in_csi(stree_program_t *prog,
stree_csi_t *scope, stree_texpr_t *texpr)
{
stree_symbol_t *a, *b;
stree_csi_t *a_csi;
switch (texpr->tc) {
case tc_tnameref:
return symbol_lookup_in_csi(prog, scope, texpr->u.tnameref->name);
case tc_taccess:
a = symbol_xlookup_in_csi(prog, scope, texpr->u.taccess->arg);
a_csi = symbol_to_csi(a);
if (a_csi == NULL) {
printf("Error: Symbol is not CSI.\n");
exit(1);
}
b = symbol_search_csi(prog, a_csi, texpr->u.taccess->member_name);
if (b == NULL) {
printf("Error: CSI '%s' not found\n", strtab_get_str(texpr->u.taccess->member_name->sid));
exit(1);
}
return b;
case tc_tapply:
return symbol_xlookup_in_csi(prog, scope,
texpr->u.tapply->gtype);
default:
assert(b_false);
}
}
/** Lookup symbol reference in CSI.
*
* XXX These functions should take just an sid, not a full identifier.
* Sometimes we search for a name which has no associated cspan.
*
* @param prog Program to look in
* @param scope CSI in @a prog which is the base for references
* @param name Identifier of the symbol
*
* @return Symbol or @c NULL if symbol not found
*/
stree_symbol_t *symbol_lookup_in_csi(stree_program_t *prog, stree_csi_t *scope,
stree_ident_t *name)
{
stree_symbol_t *symbol;
/* This CSI node should have been processed. */
assert(scope == NULL || scope->ancr_state == ws_visited);
symbol = NULL;
while (scope != NULL && symbol == NULL) {
symbol = symbol_search_csi(prog, scope, name);
scope = csi_to_symbol(scope)->outer_csi;
}
if (symbol == NULL)
symbol = symbol_search_global(prog, name);
return symbol;
}
/** Look for symbol strictly in CSI.
*
* Look for symbol in definition of a CSI and its ancestors. (But not
* in lexically enclosing CSI.)
*
* @param prog Program to look in
* @param scope CSI in which to look
* @param name Identifier of the symbol
*
* @return Symbol or @c NULL if symbol not found.
*/
stree_symbol_t *symbol_search_csi(stree_program_t *prog,
stree_csi_t *scope, stree_ident_t *name)
{
stree_csi_t *base_csi;
stree_symbol_t *symbol;
/* Look in new members in this class. */
symbol = symbol_search_csi_no_base(prog, scope, name);
if (symbol != NULL)
return symbol;
/* Try inherited members. */
base_csi = symbol_get_base_class(prog, scope);
if (base_csi != NULL)
return symbol_search_csi(prog, base_csi, name);
/* No match */
return NULL;
}
/** Look for symbol strictly in CSI.
*
* Look for symbol in definition of a CSI and its ancestors. (But not
* in lexically enclosing CSI or in base CSI.)
*
* @param prog Program to look in
* @param scope CSI in which to look
* @param name Identifier of the symbol
*
* @return Symbol or @c NULL if symbol not found.
*/
stree_symbol_t *symbol_search_csi_no_base(stree_program_t *prog,
stree_csi_t *scope, stree_ident_t *name)
{
list_node_t *node;
stree_csimbr_t *csimbr;
stree_ident_t *mbr_name;
(void) prog;
/* Look in new members in this class. */
node = list_first(&scope->members);
while (node != NULL) {
csimbr = list_node_data(node, stree_csimbr_t *);
mbr_name = stree_csimbr_get_name(csimbr);
if (name->sid == mbr_name->sid) {
/* Match */
return csimbr_to_symbol(csimbr);
}
node = list_next(&scope->members, node);
}
/* No match */
return NULL;
}
/** Look for symbol in global scope.
*
* @param prog Program to look in
* @param name Identifier of the symbol
*
* @return Symbol or @c NULL if symbol not found.
*/
static stree_symbol_t *symbol_search_global(stree_program_t *prog,
stree_ident_t *name)
{
list_node_t *node;
stree_modm_t *modm;
stree_symbol_t *symbol;
stree_ident_t *mbr_name;
node = list_first(&prog->module->members);
while (node != NULL) {
modm = list_node_data(node, stree_modm_t *);
/* Make compiler happy. */
mbr_name = NULL;
switch (modm->mc) {
case mc_csi:
mbr_name = modm->u.csi->name;
break;
case mc_enum:
mbr_name = modm->u.enum_d->name;
break;
}
/* The Clang static analyzer is just too picky. */
assert(mbr_name != NULL);
if (name->sid == mbr_name->sid) {
/* Make compiler happy. */
symbol = NULL;
/* Match */
switch (modm->mc) {
case mc_csi:
symbol = csi_to_symbol(modm->u.csi);
break;
case mc_enum:
symbol = enum_to_symbol(modm->u.enum_d);
break;
}
return symbol;
}
node = list_next(&prog->module->members, node);
}
return NULL;
}
/** Get explicit base class for a CSI.
*
* Note that if there is no explicit base class (class is derived implicitly
* from @c object, then @c NULL is returned.
*
* @param prog Program to look in
* @param csi CSI
*
* @return Base class (CSI) or @c NULL if no explicit base class.
*/
stree_csi_t *symbol_get_base_class(stree_program_t *prog, stree_csi_t *csi)
{
list_node_t *pred_n;
stree_texpr_t *pred;
stree_symbol_t *pred_sym;
stree_csi_t *pred_csi;
stree_csi_t *outer_csi;
outer_csi = csi_to_symbol(csi)->outer_csi;
pred_n = list_first(&csi->inherit);
if (pred_n == NULL)
return 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); /* XXX! */
if (pred_csi->cc == csi_class)
return pred_csi;
return NULL;
}
/** Get type expression referencing base class for a CSI.
*
* Note that if there is no explicit base class (class is derived implicitly
* from @c object, then @c NULL is returned.
*
* @param prog Program to look in
* @param csi CSI
*
* @return Type expression or @c NULL if no explicit base class.
*/
stree_texpr_t *symbol_get_base_class_ref(stree_program_t *prog,
stree_csi_t *csi)
{
list_node_t *pred_n;
stree_texpr_t *pred;
stree_symbol_t *pred_sym;
stree_csi_t *pred_csi;
stree_csi_t *outer_csi;
outer_csi = csi_to_symbol(csi)->outer_csi;
pred_n = list_first(&csi->inherit);
if (pred_n == NULL)
return 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); /* XXX! */
if (pred_csi->cc == csi_class)
return pred;
return NULL;
}
/** Find entry point.
*
* Perform a walk of all CSIs and look for a function with the name @a name.
*
* @param prog Program to look in
* @param name Name of entry point
*
* @return Symbol or @c NULL if symbol not found.
*/
stree_symbol_t *symbol_find_epoint(stree_program_t *prog, stree_ident_t *name)
{
list_node_t *node;
stree_modm_t *modm;
stree_symbol_t *entry, *etmp;
entry = NULL;
node = list_first(&prog->module->members);
while (node != NULL) {
modm = list_node_data(node, stree_modm_t *);
if (modm->mc == mc_csi) {
etmp = symbol_find_epoint_rec(prog, name, modm->u.csi);
if (etmp != NULL) {
if (entry != NULL) {
printf("Error: Duplicate entry point.\n");
exit(1);
}
entry = etmp;
}
}
node = list_next(&prog->module->members, node);
}
return entry;
}
/** Find entry point under CSI.
*
* Internal part of symbol_find_epoint() that recursively walks CSIs.
*
* @param prog Program to look in
* @param name Name of entry point
*
* @return Symbol or @c NULL if symbol not found.
*/
static stree_symbol_t *symbol_find_epoint_rec(stree_program_t *prog,
stree_ident_t *name, stree_csi_t *csi)
{
list_node_t *node;
stree_csimbr_t *csimbr;
stree_symbol_t *entry, *etmp;
stree_symbol_t *fun_sym;
entry = NULL;
node = list_first(&csi->members);
while (node != NULL) {
csimbr = list_node_data(node, stree_csimbr_t *);
switch (csimbr->cc) {
case csimbr_csi:
etmp = symbol_find_epoint_rec(prog, name, csimbr->u.csi);
if (etmp != NULL) {
if (entry != NULL) {
printf("Error: Duplicate entry point.\n");
exit(1);
}
entry = etmp;
}
break;
case csimbr_fun:
fun_sym = fun_to_symbol(csimbr->u.fun);
if (csimbr->u.fun->name->sid == name->sid &&
stree_symbol_has_attr(fun_sym, sac_static)) {
if (entry != NULL) {
printf("Error: Duplicate entry point.\n");
exit(1);
}
entry = fun_sym;
}
default:
break;
}
node = list_next(&csi->members, node);
}
return entry;
}
/*
* The notion of symbol is designed as a common base class for several
* types of declarations with global and CSI scope. Here we simulate
* conversion from this base class (symbol) to derived classes (CSI,
* fun, ..) and vice versa.
*/
/** Convert symbol to delegate (base to derived).
*
* @param symbol Symbol
* @return Delegate or @c NULL if symbol is not a delegate
*/
stree_deleg_t *symbol_to_deleg(stree_symbol_t *symbol)
{
if (symbol->sc != sc_deleg)
return NULL;
return symbol->u.deleg;
}
/** Convert delegate to symbol (derived to base).
*
* @param deleg Delegate
* @return Symbol
*/
stree_symbol_t *deleg_to_symbol(stree_deleg_t *deleg)
{
assert(deleg->symbol);
return deleg->symbol;
}
/** Convert symbol to enum (base to derived).
*
* @param symbol Symbol
* @return Enum or @c NULL if symbol is not a enum
*/
stree_enum_t *symbol_to_enum(stree_symbol_t *symbol)
{
if (symbol->sc != sc_enum)
return NULL;
return symbol->u.enum_d;
}
/** Convert enum to symbol (derived to base).
*
* @param deleg Enum
* @return Symbol
*/
stree_symbol_t *enum_to_symbol(stree_enum_t *enum_d)
{
assert(enum_d->symbol);
return enum_d->symbol;
}
/** Convert symbol to CSI (base to derived).
*
* @param symbol Symbol
* @return CSI or @c NULL if symbol is not a CSI
*/
stree_csi_t *symbol_to_csi(stree_symbol_t *symbol)
{
if (symbol->sc != sc_csi)
return NULL;
return symbol->u.csi;
}
/** Convert CSI to symbol (derived to base).
*
* @param csi CSI
* @return Symbol
*/
stree_symbol_t *csi_to_symbol(stree_csi_t *csi)
{
assert(csi->symbol);
return csi->symbol;
}
/** Convert symbol to constructor (base to derived).
*
* @param symbol Symbol
* @return Constructor or @c NULL if symbol is not a constructor
*/
stree_ctor_t *symbol_to_ctor(stree_symbol_t *symbol)
{
if (symbol->sc != sc_ctor)
return NULL;
return symbol->u.ctor;
}
/** Convert constructor to symbol (derived to base).
*
* @param ctor Constructor
* @return Symbol
*/
stree_symbol_t *ctor_to_symbol(stree_ctor_t *ctor)
{
assert(ctor->symbol);
return ctor->symbol;
}
/** Convert symbol to function (base to derived).
*
* @param symbol Symbol
* @return Function or @c NULL if symbol is not a function
*/
stree_fun_t *symbol_to_fun(stree_symbol_t *symbol)
{
if (symbol->sc != sc_fun)
return NULL;
return symbol->u.fun;
}
/** Convert function to symbol (derived to base).
*
* @param fun Function
* @return Symbol
*/
stree_symbol_t *fun_to_symbol(stree_fun_t *fun)
{
assert(fun->symbol);
return fun->symbol;
}
/** Convert symbol to member variable (base to derived).
*
* @param symbol Symbol
* @return Variable or @c NULL if symbol is not a member variable
*/
stree_var_t *symbol_to_var(stree_symbol_t *symbol)
{
if (symbol->sc != sc_var)
return NULL;
return symbol->u.var;
}
/** Convert variable to symbol (derived to base).
*
* @param fun Variable
* @return Symbol
*/
stree_symbol_t *var_to_symbol(stree_var_t *var)
{
assert(var->symbol);
return var->symbol;
}
/** Convert symbol to property (base to derived).
*
* @param symbol Symbol
* @return Property or @c NULL if symbol is not a property
*/
stree_prop_t *symbol_to_prop(stree_symbol_t *symbol)
{
if (symbol->sc != sc_prop)
return NULL;
return symbol->u.prop;
}
/** Get symbol from CSI member.
*
* A symbol corresponds to any CSI member. Return it.
*
* @param csimbr CSI member
* @return Symbol
*/
stree_symbol_t *csimbr_to_symbol(stree_csimbr_t *csimbr)
{
stree_symbol_t *symbol;
/* Keep compiler happy. */
symbol = NULL;
/* Match */
switch (csimbr->cc) {
case csimbr_csi:
symbol = csi_to_symbol(csimbr->u.csi);
break;
case csimbr_ctor:
symbol = ctor_to_symbol(csimbr->u.ctor);
break;
case csimbr_deleg:
symbol = deleg_to_symbol(csimbr->u.deleg);
break;
case csimbr_enum:
symbol = enum_to_symbol(csimbr->u.enum_d);
break;
case csimbr_fun:
symbol = fun_to_symbol(csimbr->u.fun);
break;
case csimbr_var:
symbol = var_to_symbol(csimbr->u.var);
break;
case csimbr_prop:
symbol = prop_to_symbol(csimbr->u.prop);
break;
}
return symbol;
}
/** Convert property to symbol (derived to base).
*
* @param fun Property
* @return Symbol
*/
stree_symbol_t *prop_to_symbol(stree_prop_t *prop)
{
assert(prop->symbol);
return prop->symbol;
}
/** Print fully qualified name of symbol.
*
* @param symbol Symbol
*/
void symbol_print_fqn(stree_symbol_t *symbol)
{
stree_ident_t *name;
stree_symbol_t *outer_sym;
if (symbol->outer_csi != NULL) {
outer_sym = csi_to_symbol(symbol->outer_csi);
symbol_print_fqn(outer_sym);
printf(".");
}
name = symbol_get_ident(symbol);
printf("%s", strtab_get_str(name->sid));
}
/** Return symbol identifier.
*
* @param symbol Symbol
* @return Symbol identifier
*/
static stree_ident_t *symbol_get_ident(stree_symbol_t *symbol)
{
stree_ident_t *ident;
/* Make compiler happy. */
ident = NULL;
switch (symbol->sc) {
case sc_csi:
ident = symbol->u.csi->name;
break;
case sc_ctor:
ident = symbol->u.ctor->name;
break;
case sc_deleg:
ident = symbol->u.deleg->name;
break;
case sc_enum:
ident = symbol->u.enum_d->name;
break;
case sc_fun:
ident = symbol->u.fun->name;
break;
case sc_var:
ident = symbol->u.var->name;
break;
case sc_prop:
ident = symbol->u.prop->name;
break;
}
return ident;
}