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

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

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

import parsley.Parsley, Parsley.pure
import parsley.character, character.{isHexDigit, isOctDigit, satisfy}
import parsley.combinator.optional
import parsley.errors.combinator.ErrorMethods
import parsley.syntax.character.charLift
import parsley.token.descriptions.numeric.{BreakCharDesc, NumericDesc}
import parsley.token.errors.{ErrorConfig, LabelConfig}

private [token] class Generic(err: ErrorConfig) {
    private def ofRadix(radix: Int, digit: Parsley[Char], label: LabelConfig): Parsley[BigInt] = ofRadix(radix, digit, digit, label)
    private def ofRadix(radix: Int, startDigit: Parsley[Char], digit: Parsley[Char], label: LabelConfig): Parsley[BigInt] = {
        val pf = pure((x: BigInt, d: Char) => x*radix + d.asDigit)
        val trailingDigit = label(digit)
        parsley.expr.infix.secretLeft1(startDigit.map(d => BigInt(d.asDigit)), trailingDigit, pf)
    }

    private def ofRadix(radix: Int, digit: Parsley[Char], breakChar: Char, label: LabelConfig): Parsley[BigInt] = {
        ofRadix(radix, digit, digit, breakChar, label)
    }
    private def ofRadix(radix: Int, startDigit: Parsley[Char], digit: Parsley[Char], breakChar: Char, label: LabelConfig): Parsley[BigInt] = {
        val pf = pure((x: BigInt, d: Char) => x*radix + d.asDigit)
        val trailingDigit =
            optional(err.labelNumericBreakChar.orElse(label)(breakChar)) *>
            label(digit)
        parsley.expr.infix.secretLeft1(startDigit.map(d => BigInt(d.asDigit)), trailingDigit, pf)
    }

    // Digits
    private def nonZeroDigit = satisfy(c => c.isDigit && c != '0').label("digit")
    private def nonZeroHexDigit = satisfy(c => isHexDigit(c) && c != '0').label("hexadecimal digit")
    private def nonZeroOctDigit = satisfy(c => isOctDigit(c) && c != '0').label("octal digit")
    private def nonZeroBit = '1'.label("bit")
    // why secret? so that the above digits can be marked as digits without "non-zero or zero digit" nonsense
    private def secretZero = '0'.hide.as(BigInt(0))

    private def digit = character.digit
    private def hexDigit = character.hexDigit
    private def octDigit = character.octDigit
    private def bit = character.bit

    def zeroAllowedDecimal(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(10, digit, endLabel)
    def zeroAllowedHexadecimal(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(16, hexDigit, endLabel)
    def zeroAllowedOctal(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(8, octDigit, endLabel)
    def zeroAllowedBinary(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(2, bit, endLabel)

    def zeroNotAllowedDecimal(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(10, nonZeroDigit, digit, endLabel) <|> secretZero
    def zeroNotAllowedHexadecimal(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(16, nonZeroHexDigit, hexDigit, endLabel) <|> secretZero
    def zeroNotAllowedOctal(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(8, nonZeroOctDigit, octDigit, endLabel) <|> secretZero
    def zeroNotAllowedBinary(endLabel: LabelConfig): Parsley[BigInt] = ofRadix(2, nonZeroBit, bit, endLabel) <|> secretZero

    def plainDecimal(desc: NumericDesc, endLabel: LabelConfig): Parsley[BigInt] = plainDecimal(desc.leadingZerosAllowed, desc.literalBreakChar, endLabel)
    private def plainDecimal(leadingZerosAllowed: Boolean, literalBreakChar: BreakCharDesc, endLabel: LabelConfig): Parsley[BigInt] = literalBreakChar match {
        case BreakCharDesc.NoBreakChar if leadingZerosAllowed     => zeroAllowedDecimal(endLabel)
        case BreakCharDesc.NoBreakChar                            => zeroNotAllowedDecimal(endLabel)
        case BreakCharDesc.Supported(c, _) if leadingZerosAllowed => ofRadix(10, digit, c, endLabel)
        case BreakCharDesc.Supported(c, _)                        => ofRadix(10, nonZeroDigit, digit, c, endLabel) <|> secretZero
    }

    def plainHexadecimal(desc: NumericDesc, endLabel: LabelConfig): Parsley[BigInt] = {
        plainHexadecimal(desc.leadingZerosAllowed, desc.literalBreakChar, endLabel)
    }
    private def plainHexadecimal(leadingZerosAllowed: Boolean, literalBreakChar: BreakCharDesc, endLabel: LabelConfig): Parsley[BigInt] = {
        literalBreakChar match {
            case BreakCharDesc.NoBreakChar if leadingZerosAllowed     => zeroAllowedHexadecimal(endLabel)
            case BreakCharDesc.NoBreakChar                            => zeroNotAllowedHexadecimal(endLabel)
            case BreakCharDesc.Supported(c, _) if leadingZerosAllowed => ofRadix(16, hexDigit, c, endLabel)
            case BreakCharDesc.Supported(c, _)                        => ofRadix(16, nonZeroHexDigit, hexDigit, c, endLabel) <|> secretZero
        }
    }

    def plainOctal(desc: NumericDesc, endLabel: LabelConfig): Parsley[BigInt] = plainOctal(desc.leadingZerosAllowed, desc.literalBreakChar, endLabel)
    private def plainOctal(leadingZerosAllowed: Boolean, literalBreakChar: BreakCharDesc, endLabel: LabelConfig): Parsley[BigInt] = literalBreakChar match {
        case BreakCharDesc.NoBreakChar if leadingZerosAllowed     => zeroAllowedOctal(endLabel)
        case BreakCharDesc.NoBreakChar                            => zeroNotAllowedOctal(endLabel)
        case BreakCharDesc.Supported(c, _) if leadingZerosAllowed => ofRadix(8, octDigit, c, endLabel)
        case BreakCharDesc.Supported(c, _)                        => ofRadix(8, nonZeroOctDigit, octDigit, c, endLabel) <|> secretZero
    }

    def plainBinary(desc: NumericDesc, endLabel: LabelConfig): Parsley[BigInt] = plainBinary(desc.leadingZerosAllowed, desc.literalBreakChar, endLabel)
    private def plainBinary(leadingZerosAllowed: Boolean, literalBreakChar: BreakCharDesc, endLabel: LabelConfig): Parsley[BigInt] = literalBreakChar match {
        case BreakCharDesc.NoBreakChar if leadingZerosAllowed     => zeroAllowedBinary(endLabel)
        case BreakCharDesc.NoBreakChar                            => zeroNotAllowedBinary(endLabel)
        case BreakCharDesc.Supported(c, _) if leadingZerosAllowed => ofRadix(2, bit, c, endLabel)
        case BreakCharDesc.Supported(c, _)                        => ofRadix(2, nonZeroBit, bit, c, endLabel) <|> secretZero
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy