
// Compiler implementation of the D programming language
// Copyright: Copyright (C) 2014-2019 by The D Language Foundation, All Rights Reserved
// Authors: Walter Bright, http://www.digitalmars.com
// License: http://boost.org/LICENSE_1_0.txt
// Source: https://github.com/D-Programming-Language/dmd/blob/master/src/nspace.c


#include "root/dsystem.h"

#include "mars.h"
#include "dsymbol.h"
#include "nspace.h"
#include "identifier.h"
#include "scope.h"

/* This implements namespaces.
 */

Nspace::Nspace(Loc loc, Identifier *ident, Dsymbols *members, bool mangleOnly)
    : ScopeDsymbol(ident)
{
    //printf("Nspace::Nspace(ident = %s)\n", ident->toChars());
    this->loc = loc;
    this->members = members;
    // Determines whether the symbol for this namespace should be included in
    // the symbol table.
    this->mangleOnly = mangleOnly;
}

Dsymbol *Nspace::syntaxCopy(Dsymbol *)
{
    Nspace *ns = new Nspace(loc, ident, NULL, mangleOnly);
    return ScopeDsymbol::syntaxCopy(ns);
}

void Nspace::addMember(Scope *sc, ScopeDsymbol *sds)
{
    if (mangleOnly)
        parent = sds;
    else
        ScopeDsymbol::addMember(sc, sds);
    if (members)
    {
        if (!symtab)
            symtab = new DsymbolTable();
        // The namespace becomes 'imported' into the enclosing scope
        for (Scope *sce = sc; 1; sce = sce->enclosing)
        {
            ScopeDsymbol *sds2 = sce->scopesym;
            if (sds2)
            {
                sds2->importScope(this, Prot(PROTpublic));
                break;
            }
        }
        assert(sc);
        sc = sc->push(this);
        sc->linkage = LINKcpp; // namespaces default to C++ linkage
        sc->parent = this;
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            //printf("add %s to scope %s\n", s->toChars(), toChars());
            s->addMember(sc, this);
        }
        sc->pop();
    }
}

void Nspace::setScope(Scope *sc)
{
    ScopeDsymbol::setScope(sc);
    if (members)
    {
        assert(sc);
        sc = sc->push(this);
        sc->linkage = LINKcpp; // namespaces default to C++ linkage
        sc->parent = this;
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            s->setScope(sc);
        }
        sc->pop();
    }
}

void Nspace::semantic(Scope *sc)
{
    if (semanticRun != PASSinit)
        return;
    if (_scope)
    {
        sc = _scope;
        _scope = NULL;
    }
    if (!sc)
        return;

    semanticRun = PASSsemantic;
    parent = sc->parent;
    if (members)
    {
        assert(sc);
        sc = sc->push(this);
        sc->linkage = LINKcpp;          // note that namespaces imply C++ linkage
        sc->parent = this;

        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            s->importAll(sc);
        }

        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            s->semantic(sc);
        }
        sc->pop();
    }
    semanticRun = PASSsemanticdone;
}

void Nspace::semantic2(Scope *sc)
{
    if (semanticRun >= PASSsemantic2)
        return;
    semanticRun = PASSsemantic2;
    if (members)
    {
        assert(sc);
        sc = sc->push(this);
        sc->linkage = LINKcpp;
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            s->semantic2(sc);
        }
        sc->pop();
    }
}

void Nspace::semantic3(Scope *sc)
{
    if (semanticRun >= PASSsemantic3)
        return;
    semanticRun = PASSsemantic3;
    if (members)
    {
        sc = sc->push(this);
        sc->linkage = LINKcpp;
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            s->semantic3(sc);
        }
        sc->pop();
    }
}

const char *Nspace::kind() const
{
    return "namespace";
}

bool Nspace::oneMember(Dsymbol **ps, Identifier *ident)
{
    return Dsymbol::oneMember(ps, ident);
}

Dsymbol *Nspace::search(const Loc &loc, Identifier *ident, int flags)
{
    //printf("%s::Nspace::search('%s')\n", toChars(), ident->toChars());
    if (_scope && !symtab)
        semantic(_scope);

    if (!members || !symtab) // opaque or semantic() is not yet called
    {
        error("is forward referenced when looking for '%s'", ident->toChars());
        return NULL;
    }

    return ScopeDsymbol::search(loc, ident, flags);
}

int Nspace::apply(Dsymbol_apply_ft_t fp, void *param)
{
    if (members)
    {
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            if (s)
            {
                if (s->apply(fp, param))
                    return 1;
            }
        }
    }
    return 0;
}

bool Nspace::hasPointers()
{
    //printf("Nspace::hasPointers() %s\n", toChars());

    if (members)
    {
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            //printf(" s = %s %s\n", s->kind(), s->toChars());
            if (s->hasPointers())
            {
                return true;
            }
        }
    }
    return false;
}

void Nspace::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
{
    //printf("Nspace::setFieldOffset() %s\n", toChars());
    if (_scope)                  // if fwd reference
        semantic(NULL);         // try to resolve it
    if (members)
    {
        for (size_t i = 0; i < members->dim; i++)
        {
            Dsymbol *s = (*members)[i];
            //printf("\t%s\n", s->toChars());
            s->setFieldOffset(ad, poffset, isunion);
        }
    }
}
