All Downloads are FREE. Search and download functionalities are using the official Maven repository.

parsley.token.numeric.Real.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Parsley Contributors 
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley.token.numeric

import parsley.Parsley
import parsley.token.errors.{ErrorConfig, LabelWithExplainConfig}

/** This class defines a uniform interface for defining parsers for floating
  * literals, independent of how whitespace should be handled after the literal.
  *
  * @since 4.0.0
  * @note implementations of this class found within `Lexer` may employ sharing
  *       and refine the non-final `def`s in this class into `val` or `lazy val` when overriding.
  *
  * @define disclaimer
  *   the exact behaviour of this parser is decided by the implementations given in
  *   `Lexer`, which will depend on user-defined configuration. Please see the
  *   relevant documentation of these specific objects.
  *
  * @define bounded1 This parser will behave the same as
  * @define bounded2Rounded except it will round the result to the nearest valid
  * @define bounded2Exact except it will ensure that the resulting `BigDecimal` is an ''exactly'' represented
  * @define bounded2Plausible except it will ensure that the resulting `BigDecimal` is within the maximum bounds of a
  * @define bounded3 IEEE 754 floating point value. The result is then converted to a
  * @define exact the number is considered exact when it can be losslessly represented in binary.
  * @define plausible
  *   the validation is performed when the value is within the
  *   precision range, and rounding to the nearest exact value
  *   will still occur.
  * @define rounded
  *   if the values are too big or too negatively big, they will
  *   be rounded to the corresponding infinity.
  */
abstract class RealParsers private[numeric](err: ErrorConfig) {
    /** This parser will parse a single real number literal, which is in decimal form (base 10).
      *
      * @since 4.0.0
      * @note $disclaimer
      * @todo examples
      */
    def decimal: Parsley[BigDecimal]
    /** This parser will parse a single real number literal, which is in hexadecimal form (base 16).
      *
      * @since 4.0.0
      * @note $disclaimer
      * @todo examples
      */
    def hexadecimal: Parsley[BigDecimal]
    /** This parser will parse a single real number literal, which is in octal form (base 8).
      *
      * @since 4.0.0
      * @note $disclaimer
      * @todo examples
      */
    def octal: Parsley[BigDecimal]
    /** This parser will parse a single real number literal, which is in binary form (base 2).
      *
      * @since 4.0.0
      * @note $disclaimer
      * @todo examples
      */
    def binary: Parsley[BigDecimal]
    /** This parser will parse a single number literal, which may be in many different forms/bases
      * depending on the configuration provided.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @todo examples
      */
    def number: Parsley[BigDecimal]

    // $COVERAGE-OFF$
    // It's not so important these are tested, they are just wrappers around the bottom ones
    /** $bounded1 [[decimal `decimal`]] $bounded2Rounded `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def decimalFloatRounded: Parsley[Float] = decimal.map(_.toFloat)
    /** $bounded1 [[hexadecimal `hexadecimal`]] $bounded2Rounded `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def hexadecimalFloatRounded: Parsley[Float] = hexadecimal.map(_.toFloat)
    /** $bounded1 [[octal `octal`]] $bounded2Rounded `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def octalFloatRounded: Parsley[Float] = octal.map(_.toFloat)
    /** $bounded1 [[binary `binary`]] $bounded2Rounded `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def binaryFloatRounded: Parsley[Float] = binary.map(_.toFloat)
    /** $bounded1 [[number `number`]] $bounded2Rounded `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def floatRounded: Parsley[Float] = number.map(_.toFloat)

    /** $bounded1 [[decimal `decimal`]] $bounded2Rounded `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def decimalDoubleRounded: Parsley[Double] = decimal.map(_.toDouble)
    /** $bounded1 [[hexadecimal `hexadecimal`]] $bounded2Rounded `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def hexadecimalDoubleRounded: Parsley[Double] = hexadecimal.map(_.toDouble)
    /** $bounded1 [[octal `octal`]] $bounded2Rounded `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def octalDoubleRounded: Parsley[Double] = octal.map(_.toDouble)
    /** $bounded1 [[binary `binary`]] $bounded2Rounded `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def binaryDoubleRounded: Parsley[Double] = binary.map(_.toDouble)
    /** $bounded1 [[number `number`]] $bounded2Rounded `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $rounded
      */
    @inline final def doubleRounded: Parsley[Double] = number.map(_.toDouble)

    /** $bounded1 [[decimal `decimal`]] $bounded2Plausible single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val decimalFloat: Parsley[Float] = ensureFloat(_decimal, err.labelRealFloatDecimal)
    /** $bounded1 [[hexadecimal `hexadecimal`]] $bounded2Plausible single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val hexadecimalFloat: Parsley[Float] = ensureFloat(_hexadecimal, err.labelRealFloatHexadecimal)
    /** $bounded1 [[octal `octal`]] $bounded2Plausible single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val octalFloat: Parsley[Float] = ensureFloat(_octal, err.labelRealFloatOctal)
    /** $bounded1 [[binary `binary`]] $bounded2Plausible single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val binaryFloat: Parsley[Float] = ensureFloat(_binary, err.labelRealFloatBinary)
    /** $bounded1 [[number `number`]] $bounded2Plausible single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val float: Parsley[Float] = ensureFloat(_number, err.labelRealFloatNumber)

    /** $bounded1 [[decimal `decimal`]] $bounded2Plausible double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val decimalDouble: Parsley[Double] = ensureDouble(_decimal, err.labelRealDoubleDecimal)
    /** $bounded1 [[hexadecimal `hexadecimal`]] $bounded2Plausible double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val hexadecimalDouble: Parsley[Double] = ensureDouble(_hexadecimal, err.labelRealDoubleHexadecimal)
    /** $bounded1 [[octal `octal`]] $bounded2Plausible double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val octalDouble: Parsley[Double] = ensureDouble(_octal, err.labelRealDoubleOctal)
    /** $bounded1 [[binary `binary`]] $bounded2Plausible double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val binaryDouble: Parsley[Double] = ensureDouble(_binary, err.labelRealDoubleBinary)
    /** $bounded1 [[number `number`]] $bounded2Plausible double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $plausible
      */
    lazy val double: Parsley[Double] = ensureDouble(_number, err.labelRealDoubleNumber)
    /** $bounded1 [[decimal `decimal`]] $bounded2Plausible single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val decimalExactFloat: Parsley[Float] = ensureExactFloat(_decimal, err.labelRealFloatDecimal)
    /** $bounded1 [[hexadecimal `hexadecimal`]] $bounded2Exact single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val hexadecimalExactFloat: Parsley[Float] = ensureExactFloat(_hexadecimal, err.labelRealFloatHexadecimal)
    /** $bounded1 [[octal `octal`]] $bounded2Exact single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val octalExactFloat: Parsley[Float] = ensureExactFloat(_octal, err.labelRealFloatOctal)
    /** $bounded1 [[binary `binary`]] $bounded2Exact single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val binaryExactFloat: Parsley[Float] = ensureExactFloat(_binary, err.labelRealFloatBinary)
    /** $bounded1 [[number `number`]] $bounded2Exact single-precision $bounded3 `Float`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val exactFloat: Parsley[Float] = ensureExactFloat(_number, err.labelRealFloatNumber)

    /** $bounded1 [[decimal `decimal`]] $bounded2Exact double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val decimalExactDouble: Parsley[Double] = ensureExactDouble(_decimal, err.labelRealDoubleDecimal)
    /** $bounded1 [[hexadecimal `hexadecimal`]] $bounded2Exact double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val hexadecimalExactDouble: Parsley[Double] = ensureExactDouble(_hexadecimal, err.labelRealDoubleHexadecimal)
    /** $bounded1 [[octal `octal`]] $bounded2Exact double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val octalExactDouble: Parsley[Double] = ensureExactDouble(_octal, err.labelRealDoubleOctal)
    /** $bounded1 [[binary `binary`]] $bounded2Exact double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val binaryExactDouble: Parsley[Double] = ensureExactDouble(_binary, err.labelRealDoubleBinary)
    /** $bounded1 [[number `number`]] $bounded2Exact double-precision $bounded3 `Double`.
      *
      * @since 4.0.0
      * @note $disclaimer
      * @note $exact
      */
    lazy val exactDouble: Parsley[Double] = ensureExactDouble(_number, err.labelRealDoubleNumber)
    // $COVERAGE-ON$

    protected [numeric] def ensureFloat(number: Parsley[BigDecimal], label: LabelWithExplainConfig): Parsley[Float] = {
        err.filterRealOutOfBounds(err.floatName, BigDecimal(Float.MinValue.toDouble), BigDecimal(Float.MaxValue.toDouble)).collect(label(number)) {
            case n if RealParsers.isFloat(n) => n.toFloat
        }
    }

    protected [numeric] def ensureDouble(number: Parsley[BigDecimal], label: LabelWithExplainConfig): Parsley[Double] = {
        err.filterRealOutOfBounds(err.doubleName, BigDecimal(Double.MinValue), BigDecimal(Double.MaxValue)).collect(label(number)) {
            case n if RealParsers.isDouble(n) => n.toDouble
        }
    }

    protected [numeric] def ensureExactFloat(number: Parsley[BigDecimal], label: LabelWithExplainConfig): Parsley[Float] = {
        err.filterRealNotExact(err.floatName).collect(label(number)) {
            case n if n.isExactFloat => n.toFloat
        }
    }

    protected [numeric] def ensureExactDouble(number: Parsley[BigDecimal], label: LabelWithExplainConfig): Parsley[Double] = {
        err.filterRealNotExact(err.doubleName).collect(label(number)) {
            case n if n.isExactDouble => n.toDouble
        }
    }

    // $COVERAGE-OFF$
    protected [numeric] def _decimal: Parsley[BigDecimal] = decimal
    protected [numeric] def _hexadecimal: Parsley[BigDecimal] = hexadecimal
    protected [numeric] def _octal: Parsley[BigDecimal] = octal
    protected [numeric] def _binary: Parsley[BigDecimal] = binary
    protected [numeric] def _number: Parsley[BigDecimal] = number
    // $COVERAGE-ON$
}

private [numeric] object RealParsers {
    def isDouble(n: BigDecimal): Boolean = {
        n == 0.0 || n == -0.0 || {
            val x = n.toDouble
            !x.isInfinity && x != 0.0 && x != -0.0
        }
    }

    def isFloat(n: BigDecimal): Boolean = {
        n == 0.0 || n == -0.0 || {
            val x = n.toFloat
            !x.isInfinity && x != 0.0 && x != -0.0
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy