/*  $Id: RealConstantExpr.cpp,v 1.7 2017/08/11 23:43:56 sarrazip Exp $

    CMOC - A C-like cross-compiler
    Copyright (C) 2003-2015 Pierre Sarrazin <http://sarrazip.com/>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "RealConstantExpr.h"

#include "TranslationUnit.h"

#include <algorithm>
#include <iomanip>

using namespace std;


#if 0  /* Would be used if double were supported. */
static bool
hasFloatSuffix(const char *floatNumber)
{
    if (!floatNumber)
        return false;
    size_t len = strlen(floatNumber);
    return len > 0 && tolower(floatNumber[len - 1]) == 'f';
}
#endif


RealConstantExpr::RealConstantExpr(double value, const char * /*tokenText*/)
  : Tree(TranslationUnit::getTypeManager().getRealType(false)),  // false would be !hasFloatSuffix(tokenText) if double were supported
    realValue(value),
    asmLabel()
{
}


/*virtual*/
RealConstantExpr::~RealConstantExpr()
{
}


void
RealConstantExpr::setLabel(const string &newLabel)
{
    assert(newLabel.length() > 0);
    asmLabel = newLabel;
}


bool
RealConstantExpr::isDoublePrecision() const
{
    return getTypeDesc() == TranslationUnit::getTypeManager().getRealType(true);
}


vector<uint8_t>
RealConstantExpr::getRepresentation() const
{
    if (isDoublePrecision())
        return vector<uint8_t>();

    // Extract info from IEEE 754 of 'realValue'.
    // CAUTION: This assumes a little endian platform.

    vector<uint8_t> rep;

    if (realValue == 0.0)
    {
        fill_n(back_inserter(rep), 5, 0);
    }
    else
    {
        // Source: https://en.wikipedia.org/wiki/Double-precision_floating-point_format

        const uint8_t *bytes = (uint8_t *) &realValue;
        bool isNegative = (bytes[7] & 0x80) != 0;

        uint16_t tmp = (uint16_t(bytes[7] & 0x7F) << 8) | bytes[6];
        tmp >>= 4;
        int16_t exponent = int16_t(tmp) - 0x3FF;

        uint64_t mantissa = uint64_t(bytes[6] & 0x0F) << 48;
        for (int i = 6; i--; )  // 5..0
            mantissa |= uint64_t(bytes[i]) << (i * 8);

        //cout << "# RealConstantExpr::getRepresentation: " << realValue
        //        << " -> " << (isNegative ? "-" : "+") << ", " << exponent << ", $" << hex << mantissa << dec << endl;

        if (exponent < -257 || exponent > 126)
            return vector<uint8_t>();  // cannot represent on target platform

        rep.push_back(uint8_t(exponent + 1) + 0x80);
        for (int i = 0; i < 4; ++i)
            rep.push_back(uint8_t(mantissa >> (52 - 7 - i * 8)));
        if (isNegative)
            rep[1] |= 0x80;

        /*cout << "#   Result: " << hex
                 << setw(2) << (unsigned) rep[0] << " "
                 << setw(2) << (unsigned) rep[1] << " "
                 << setw(2) << (unsigned) rep[2] << " "
                 << setw(2) << (unsigned) rep[3] << " "
                 << setw(2) << (unsigned) rep[4] << dec << endl;*/
    }
    return rep;
}


void
RealConstantExpr::emitRealConstantDefinition(ASMText &out, const std::vector<uint8_t> &representation)
{
    stringstream arg;
    for (size_t len = representation.size(), i = 0; i < len; ++i)
    {
        if (i > 0)
            arg << ',';
        arg << wordToString(representation[i], true);
    }
    out.ins("FCB", arg.str());
}


/*virtual*/
void
RealConstantExpr::checkSemantics(Functor &)
{
}


/*virtual*/
CodeStatus
RealConstantExpr::emitCode(ASMText &out, bool lValue) const
{
    if (!lValue)
    {
        errormsg("cannot emit a real number as an r-value");  // doesn't fit in D
        return false;
    }

    out.ins("LEAX", asmLabel + ",PCR", "real constant: " + doubleToString(realValue));
    return true;
}
