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

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

There is a newer version: 5.0.0-M6
Show newest version
/* SPDX-FileCopyrightText: © 2021 Parsley Contributors 
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley.token.numeric

import parsley.Parsley, Parsley.{attempt, empty, pure, unit}
import parsley.character.{bit, digit, hexDigit, octDigit, oneOf}
import parsley.combinator, combinator.optional
import parsley.errors.combinator.{amendThenDislodge, entrench}
import parsley.implicits.character.charLift
import parsley.lift.lift2
import parsley.registers.Reg
import parsley.token.descriptions.numeric.{BreakCharDesc, ExponentDesc, NumericDesc}
import parsley.token.errors.{ErrorConfig, LabelConfig}

private [token] final class UnsignedReal(desc: NumericDesc, natural: UnsignedInteger, err: ErrorConfig, generic: Generic) extends Real(err) {
    override lazy val _decimal: Parsley[BigDecimal] = attempt(ofRadix(10, digit, err.labelRealDecimalEnd))
    override lazy val _hexadecimal: Parsley[BigDecimal] = attempt('0' *> noZeroHexadecimal)
    override lazy val _octal: Parsley[BigDecimal] = attempt('0' *> noZeroOctal)
    override lazy val _binary: Parsley[BigDecimal] = attempt('0' *> noZeroBinary)
    override lazy val _number: Parsley[BigDecimal] = {
        if (desc.decimalRealsOnly) decimal
        else {
            def addHex(p: Parsley[BigDecimal]) = {
                if (desc.realNumbersCanBeHexadecimal) noZeroHexadecimal <|> p
                else p
            }
            def addOct(p: Parsley[BigDecimal]) = {
                if (desc.realNumbersCanBeOctal) noZeroOctal <|> p
                else p
            }
            def addBin(p: Parsley[BigDecimal]) = {
                if (desc.realNumbersCanBeBinary) noZeroBinary <|> p
                else p
            }
            // this promotes sharing when the definitions would be otherwise equal
            val leadingDotAllowedDecimal = if (desc.leadingDotAllowed) decimal else ofRadix(10, digit, leadingDotAllowed = true, err.labelRealNumberEnd)
            // not even accounting for the leading and trailing dot being allowed!
            val zeroLead = '0' *> (addHex(addOct(addBin(leadingDotAllowedDecimal <|> pure(BigDecimal(0))))))
            attempt(zeroLead <|> decimal)
        }
    }

    override def decimal: Parsley[BigDecimal] = err.labelRealDecimal(_decimal)
    override def hexadecimal: Parsley[BigDecimal] = err.labelRealHexadecimal(_hexadecimal)
    override def octal: Parsley[BigDecimal] = err.labelRealOctal(_octal)
    override def binary: Parsley[BigDecimal] = err.labelRealBinary(_binary)
    override def number: Parsley[BigDecimal] = err.labelRealNumber(_number)

    private def when(b: Boolean, p: Parsley[_]): Parsley[_] = if (b) p else unit

    def leadingBreakChar(label: LabelConfig): Parsley[_] = desc.literalBreakChar match {
        case BreakCharDesc.NoBreakChar => unit
        case BreakCharDesc.Supported(breakChar, allowedAfterNonDecimalPrefix) =>
            when(allowedAfterNonDecimalPrefix, err.labelNumericBreakChar.orElse(label)(optional(breakChar)))
    }

    private val noZeroHexadecimal =
        when(desc.hexadecimalLeads.nonEmpty, oneOf(desc.hexadecimalLeads)) *>
        leadingBreakChar(err.labelRealHexadecimalEnd) *>
        ofRadix(16, hexDigit, err.labelRealHexadecimalEnd)
    private val noZeroOctal =
        when(desc.octalLeads.nonEmpty, oneOf(desc.octalLeads)) *>
        leadingBreakChar(err.labelRealOctalEnd) *>
        ofRadix(8, octDigit, err.labelRealOctalEnd)
    private val noZeroBinary =
        when(desc.binaryLeads.nonEmpty, oneOf(desc.binaryLeads)) *>
        leadingBreakChar(err.labelRealBinaryEnd) *>
        ofRadix(2, bit, err.labelRealBinaryEnd)

    // could allow integers to be parsed here according to configuration, the intOrFloat captures that case anyway
    private def ofRadix(radix: Int, digit: Parsley[Char], endLabel: LabelConfig): Parsley[BigDecimal] = {
        ofRadix(radix, digit, desc.leadingDotAllowed, endLabel)
    }
    private def ofRadix(radix: Int, digit: Parsley[Char], leadingDotAllowed: Boolean, endLabel: LabelConfig): Parsley[BigDecimal] = {
        lazy val leadingHappened = Reg.make[Boolean]
        lazy val _noDoubleDroppedZero = err.preventRealDoubleDroppedZero(leadingHappened.get)
        val expDesc = desc.exponentDescForRadix(radix)
        val whole = radix match {
            case 10 => generic.plainDecimal(desc, endLabel)
            case 16 => generic.plainHexadecimal(desc, endLabel)
            case 8 => generic.plainOctal(desc, endLabel)
            case 2 => generic.plainBinary(desc, endLabel)
        }
        val f = (d: Char, x: BigDecimal) => x/radix + d.asDigit
        def broken(c: Char) =
            lift2(f,
                endLabel(digit),
                (err.labelNumericBreakChar.orElse(endLabel)(optional(c)) *>
                 endLabel(digit)).foldRight[BigDecimal](0)(f))
        val fractional = amendThenDislodge {
            err.labelRealDot.orElse(endLabel)('.') *> {
                desc.literalBreakChar match {
                    case BreakCharDesc.NoBreakChar if desc.trailingDotAllowed     =>
                        if (!leadingDotAllowed) entrench(digit.foldRight[BigDecimal](0)(f))
                        else entrench(digit.foldRight1[BigDecimal](0)(f)) <|> _noDoubleDroppedZero *> pure[BigDecimal](0)
                    case BreakCharDesc.NoBreakChar                                => entrench(digit.foldRight1[BigDecimal](0)(f))
                    case BreakCharDesc.Supported(c, _) if desc.trailingDotAllowed =>
                        entrench(broken(c)) <|> when(leadingDotAllowed, _noDoubleDroppedZero) *> pure[BigDecimal](0)
                    case BreakCharDesc.Supported(c, _)                            => entrench(broken(c))
                }
            }
        }
        val (requiredExponent, exponent, base) = expDesc match {
            case ExponentDesc.Supported(compulsory, exp, base, sign) =>
                val expErr = new ErrorConfig {
                    override def labelIntegerSignedDecimal(bits: Int) = err.labelRealExponentEnd.orElse(endLabel)
                    override def labelIntegerDecimalEnd = err.labelRealExponentEnd.orElse(endLabel)
                }
                val integer = new SignedInteger(desc.copy(positiveSign = sign), natural, expErr)
                val exponent = err.labelRealExponent.orElse(endLabel)(oneOf(exp)) *> integer.decimal32
                if (compulsory) (exponent, exponent, base)
                else (exponent, exponent <|> pure(0), base)
            // this can't fail for non-required, it has to be the identity exponent
            case ExponentDesc.NoExponents => (empty, pure(0), 1)
        }
        val fractExponent =
                (lift2((f: BigDecimal, e: Int) => (w: BigInt) => (BigDecimal(w) + f / radix) * BigDecimal(base).pow(e),
                       fractional,
                       exponent)
            <|> requiredExponent.map(e => (w: BigInt) => BigDecimal(w) * BigDecimal(base).pow(e)))
        val configuredWhole =
            if (leadingDotAllowed) (
                    when(desc.trailingDotAllowed, leadingHappened.put(false)) *> whole
                <|> when(desc.trailingDotAllowed, leadingHappened.put(true)) *> pure(BigInt(0))
            )
            else whole
        configuredWhole <**> fractExponent
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy