LSLiteral.h

// LSLiteral.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2006

#pragma once
#ifndef Ceda_cxMacroExpander_LSLiteral_H
#define Ceda_cxMacroExpander_LSLiteral_H

#include "cxMacroExpander.h"
#include "Ceda/cxUtils/xstring.h"
#include "Ceda/cxUtils/CedaAssert.h"
#include "Ceda/cxUtils/IException.h"
#include <type_traits>

namespace ceda
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// LSLiteral

struct cxMacroExpander_API LiteralException : public IException
{
    LiteralException(ConstStringZ description) :
        m_description(description)
    {
    }
	virtual void Write(xostream& os) const { os << m_description; }
    
    xstring m_description;    
};

enum ELiteral
{
    LITERAL_UNDEF,
    LITERAL_VOID,
    LITERAL_BOOL,
    LITERAL_INT64,
    LITERAL_FLOAT64,
    LITERAL_CHAR,
    LITERAL_STRING
};

struct cxMacroExpander_API LSLiteral
{
    LSLiteral() : m_type(LITERAL_UNDEF) {}
    LSLiteral(bool x) : m_type(LITERAL_BOOL), m_bool(x) {}

    template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
    LSLiteral(T x) : m_type(LITERAL_INT64), m_int64( static_cast<int64>(x) ) {}

    template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
    LSLiteral(T x) : m_type(LITERAL_FLOAT64), m_float64( static_cast<float64>(x) ) {}

    LSLiteral(const xstring& x) : m_type(LITERAL_STRING), m_string(x) {}
    
    // Needed since xchar might match int8
    static LSLiteral MakeChar(xchar x)
    {
        LSLiteral l;
        l.m_type = LITERAL_CHAR;
        l.m_char = x;
        return l;
    }

    LSLiteral operator[](const LSLiteral& index) const;
    
    // Used for strings or vector types
    ssize_t GetLength() const;

    bool operator==(ELiteral type) const { return m_type == type; }
    bool operator!=(ELiteral type) const { return m_type != type; }

    // The lhs must be an L-value.  Currently no l-value literals are supported
    void Assign(const LSLiteral& rhs);

    // Coercion functions.
    // allowDataLoss indicates whether a conversion that results in data loss is permitted.
    // throwExceptions indicates whether exceptions should be thrown if a conversion can't be achieved
    bool AsBool(bool& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsInt8(int8& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsInt16(int16& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsInt32(int32& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsInt64(int64& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsUInt8(uint8& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsUInt16(uint16& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsUInt32(uint32& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsUInt64(uint64& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsFloat32(float32& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsFloat64(float64& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsChar(xchar& v, bool allowDataLoss, bool throwExceptions) const;
    bool AsString(xstring& v, bool allowDataLoss, bool throwExceptions) const;

    bool CoercibleToBool() const { bool v; return AsBool(v,false,false); }
    bool AsBool(bool allowDataLoss = false) const { bool v; cxVerify(AsBool(v,allowDataLoss,true)); return v; }

    bool CoercibleToInt8() const { int8 v; return AsInt8(v,false,false); }
    int8 AsInt8(bool allowDataLoss = false) const { int8 v; cxVerify(AsInt8(v,allowDataLoss,true)); return v; }

    bool CoercibleToInt16() const { int16 v; return AsInt16(v,false,false); }
    int16 AsInt16(bool allowDataLoss = false) const { int16 v; cxVerify(AsInt16(v,allowDataLoss,true)); return v; }

    bool CoercibleToInt32() const { int32 v; return AsInt32(v,false,false); }
    int32 AsInt32(bool allowDataLoss = false) const { int32 v; cxVerify(AsInt32(v,allowDataLoss,true)); return v; }

    bool CoercibleToInt64() const { int64 v; return AsInt64(v,false,false); }
    int64 AsInt64(bool allowDataLoss = false) const { int64 v; cxVerify(AsInt64(v,allowDataLoss,true)); return v; }

    bool CoercibleToUInt8() const { uint8 v; return AsUInt8(v,false,false); }
    uint8 AsUInt8(bool allowDataLoss = false) const { uint8 v; cxVerify(AsUInt8(v,allowDataLoss,true)); return v; }

    bool CoercibleToUInt16() const { uint16 v; return AsUInt16(v,false,false); }
    uint16 AsUInt16(bool allowDataLoss = false) const { uint16 v; cxVerify(AsUInt16(v,allowDataLoss,true)); return v; }

    bool CoercibleToUInt32() const { uint32 v; return AsUInt32(v,false,false); }
    uint32 AsUInt32(bool allowDataLoss = false) const { uint32 v; cxVerify(AsUInt32(v,allowDataLoss,true)); return v; }

    bool CoercibleToUInt64() const { uint64 v; return AsUInt64(v,false,false); }
    uint64 AsUInt64(bool allowDataLoss = false) const { uint64 v; cxVerify(AsUInt64(v,allowDataLoss,true)); return v; }

    bool CoercibleToFloat32() const { float32 v; return AsFloat32(v,false,false); }
    float32 AsFloat32(bool allowDataLoss = false) const { float32 v; cxVerify(AsFloat32(v,allowDataLoss,true)); return v; }

    bool CoercibleToFloat64() const { float64 v; return AsFloat64(v,false,false); }
    float64 AsFloat64(bool allowDataLoss = false) const { float64 v; cxVerify(AsFloat64(v,allowDataLoss,true)); return v; }

    bool CoercibleToChar() const { xchar v; return AsChar(v,false,false); }
    xchar AsChar(bool allowDataLoss = false) const { xchar v; cxVerify(AsChar(v,allowDataLoss,true)); return v; }

    bool CoercibleToString() const { xstring v; return AsString(v,false,false); }
    void AsString(xstring& v, bool allowDataLoss = false) const { cxVerify(AsString(v,allowDataLoss,true)); }
    xstring AsString(bool allowDataLoss = false) const { xstring v; AsString(v,allowDataLoss); return v; }

    ELiteral m_type;
    union
    {
        bool m_bool;
        int64 m_int64;
        float64 m_float64;
        xchar m_char;
    };
    xstring m_string;
};

// Binary arithmetic operations
cxMacroExpander_API const LSLiteral operator+(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator-(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator*(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator/(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator%(const LSLiteral& x, const LSLiteral& y);

// Binary shift operations
cxMacroExpander_API const LSLiteral operator<<(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator>>(const LSLiteral& x, const LSLiteral& y);

// Binary logical operations
cxMacroExpander_API const LSLiteral operator||(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator&&(const LSLiteral& x, const LSLiteral& y);

// Binary bitwise operations
cxMacroExpander_API const LSLiteral operator|(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator&(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API const LSLiteral operator^(const LSLiteral& x, const LSLiteral& y);

// Unary operations
cxMacroExpander_API bool operator!(const LSLiteral& x);
cxMacroExpander_API LSLiteral operator+(const LSLiteral& x);
cxMacroExpander_API LSLiteral operator-(const LSLiteral& x);
cxMacroExpander_API LSLiteral operator~(const LSLiteral& x);

// Comparision assuming value semantics (ie equivalence of value only)
cxMacroExpander_API bool IsEqual(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API bool IsLess(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API bool IsLessThanOrEqual(const LSLiteral& x, const LSLiteral& y);

// Comparision assuming object semantics
cxMacroExpander_API bool operator==(const LSLiteral& x, const LSLiteral& y);
cxMacroExpander_API inline bool operator!=(const LSLiteral& x, const LSLiteral& y) { return !(x == y); }

// Uses a total ordering as a C++ type
//cxMacroExpander_API bool operator<(const LSLiteral& x, const LSLiteral& y);
//cxMacroExpander_API bool operator<=(const LSLiteral& x, const LSLiteral& y);

//inline bool operator>(const LSLiteral& x, const LSLiteral& y) { return y < x; }
//inline bool operator>=(const LSLiteral& x, const LSLiteral& y) { return y <= x; }

cxMacroExpander_API LSLiteral Power(const LSLiteral& x, const LSLiteral& y);

} // namespace ceda

#endif // include guard