// SPDX-License-Identifier: MIT
#pragma once
#include <cmath>
#include <limits>
#include <memory>
#include <string>
#include "Value.hh"
namespace fedem
{
namespace fson
{
// ─────────────────────────────────────────────────────────────────────
// Number
//
// Stores BOTH the parsed numeric value and the original lexeme exactly
// as written in the source (docs/notation/00050-number.rrd and
// 00046-hex_integer.rrd / 00047-decimal_number.rrd), so that an
// unmodified number is written back verbatim ("1.50" stays "1.50",
// "0xDECAF" stays "0xDECAF").
//
// Kind records the lexical FORM the value came from (JSON5 superset,
// v0.9.0.0): a plain decimal literal, a hex integer literal, or one of
// the two non-finite literals JSON5 accepts as numbers (Infinity /
// -Infinity / NaN — the writer's Json dialect cannot represent these
// and throws; the Fson and Json5 dialects emit the lexeme verbatim,
// so hex stays hex and Infinity stays Infinity).
//
// setValue() regenerates the lexeme. For a finite value it uses the
// shortest round-trip decimal representation (std::to_chars) and sets
// Kind::Decimal; for a non-finite value (+inf / -inf / NaN) it sets the
// matching Kind and lexeme ("Infinity" / "-Infinity" / "NaN") instead
// of throwing — non-finite values are representable since v0.9.0.0.
// ─────────────────────────────────────────────────────────────────────
class Number final : public Value
{
public:
enum class Kind
{
Decimal,
Hex,
Infinity,
NegativeInfinity,
NaN
};
// Accepts non-finite doubles since v0.9.0.0: the Kind is derived
// from the value (finite → Decimal, +inf → Infinity, -inf →
// NegativeInfinity, NaN → NaN) and the lexeme is generated to
// match.
explicit Number( double value );
explicit Number( long long value );
// Parser entry: lexeme must already satisfy the number grammar.
// Kind defaults to Decimal; the parser passes the matching Kind
// explicitly for hex / Infinity / NaN literals.
Number( double value, std::string lexeme, Kind kind = Kind::Decimal );
Type getType( ) const noexcept override;
std::unique_ptr< Value > clone( ) const override;
Number* asNumber( ) noexcept override;
Number const* asNumber( ) const noexcept override;
double asDouble ( ) const noexcept;
long long asInteger( ) const noexcept;
// True for Decimal literals with no fraction and no exponent, and
// for Hex literals (always integral); false for Infinity/NaN.
bool isIntegral( ) const noexcept;
Kind getKind( ) const noexcept;
std::string const& getLexeme( ) const noexcept;
// Regenerates the lexeme from value; see class comment for the
// non-finite handling. Kind is derived from the new value.
void setValue( double value );
void setValue( long long value );
private:
double value;
std::string lexeme;
Kind kind;
};
inline Value::Type Number::getType( ) const noexcept
{
return Type::Number;
}
inline Number::Kind Number::getKind( ) const noexcept
{
return kind;
}
inline Number* Number::asNumber( ) noexcept
{
return this;
}
inline Number const* Number::asNumber( ) const noexcept
{
return this;
}
inline double Number::asDouble( ) const noexcept
{
return value;
}
inline long long Number::asInteger( ) const noexcept
{
if( !std::isfinite( value ) )
return value > 0.0 ? std::numeric_limits< long long >::max( )
: value < 0.0 ? std::numeric_limits< long long >::min( )
: 0LL; // NaN
return static_cast< long long >( value );
}
inline std::string const& Number::getLexeme( ) const noexcept
{
return lexeme;
}
} // end namespace fson
} // end namespace fedem