// (c) 2000-2007 Electronic Arts Inc.
#include <string>
#include "macro.h"

void Macro::clear()
{
    macros.clear();
}

void Macro::set(const string& symbol, const string& value)
{
    StringMacroinfoMap::iterator i=macros.find(symbol);
    macroinfo temp;
    temp.expansion=value;
    macros[symbol]=temp;
}


bool isfirstIdentchar(const char& temp)
{
    return ('a'<=temp&&temp<='z'||'A'<=temp&&temp<='Z'||temp=='_'||temp=='$');
}

bool isnumber(const char& temp)
{
    return '0'<=temp&&temp<='9';
}


bool isIdentchar(const char& temp)
{
    return isfirstIdentchar(temp)||isnumber(temp);
}

string getident(string& temp, const bool& toggle)
{
    string retval;
    if (temp.length()&&(toggle^isfirstIdentchar(temp[0])))
        do
        {
            retval+=temp[0];
            temp.erase(0,1);
        } while (temp.length()&&(toggle^isIdentchar(temp[0])));
    return retval;
}

void eatwhite(string& temp)
{
    while (temp[0]==' '||temp[0]=='\t')
        temp.erase(0,1);
}


// returns false on infinite recursion
bool Macro::ParseExpansion(string inputbuffer, string& outputbuffer)
{
    string nextident;
    while (inputbuffer.length())                        // while there are characters left
    {
        nextident=getident(inputbuffer,false);
        if (nextident.length())     // if there is an identifier next
        {
            StringMacroinfoMap::iterator i=macros.find(nextident);  // find it in the map
            if (i!=macros.end())                                    // if it exists...
            {
                macroinfo &mi=(*i).second;
                if (mi.expanded)                                    // infinite recursion if already expanded
                    return false;
                mi.expanded=true;
                string tempoutput;
                if (!ParseExpansion(mi.expansion,tempoutput))
                    return false;                                   // down-propagate error
                outputbuffer.append(tempoutput);
                mi.expanded=false;
            } else
                outputbuffer.append(nextident);                     // copy unknown identifier
        } else {
            nextident=getident(inputbuffer,true);                   // get non-identifier characters
            if (nextident.compare("##"))
                outputbuffer.append(nextident);                     // and append them if they're not "##"
        }
    }
    return true;
}

// not const because it clears all of the expanded flags

string Macro::get(const string& symbol)
{
    for (StringMacroinfoMap::iterator i=macros.begin(); i!=macros.end(); i++)
        (*i).second.expanded=false;                         // all macros have not been expanded
    string outputbuffer;
    if (!ParseExpansion(symbol,outputbuffer))
        outputbuffer.erase();                               // on error, clear output buffer
    return outputbuffer;
}
