Code View

fson / source / fson-1.1.0.0 / libs / internal / sdk / fson / Number.hh
// 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