jasonwyatt/KWasm

View on GitHub
library/src/main/java/kwasm/format/text/token/util/Frac.kt

Summary

Maintainability
A
40 mins
Test Coverage
/*
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kwasm.format.text.token.util

import kwasm.format.ParseContext
import kwasm.format.ParseException
import kwasm.format.shiftColumnBy

/**
 * From [the docs](https://webassembly.github.io/spec/core/text/values.html#floating-point).
 *
 * ```
 *   frac       ::= empty                               => 0
 *                  d:digit q:frac                      => (d + q) / 10
 *                  d:digit '_' p:digit q:frac          => (d + (p + q) / 10) / 10
 *   hexfrac    ::= empty                               => 0
 *                  h:hexdigit q:hexfrac                => (h + q) / 16
 *                  h:hexdigit '_' p:hexdigit q:hexfrac => (h + (p + q) / 16) / 16
 * ```
 */
@OptIn(ExperimentalUnsignedTypes::class)
class Frac(private val sequence: CharSequence, private val context: ParseContext? = null) {
    var forceHex: Boolean = false

    val foundHexChars: Boolean by lazy { digits.any { it >= 10 } }

    val value: Double by lazy {
        if (sequence.isEmpty()) return@lazy 0.0

        val divisor = if (foundHexChars || forceHex) 16.0 else 10.0

        if (digits.size == 1) return@lazy digits[0] / divisor

        if (digits[1] != NumberConstants.UNDERSCORE) {
            // d:digit q:frac
            val q = Frac(
                sequence.subSequence(1, sequence.length),
                context.shiftColumnBy(1)
            )
            q.forceHex = forceHex
            return@lazy (digits[0] + q.value) / divisor
        }

        if (digits.size < 3) throw ParseException("Illegal format for fraction", context)

        val d = digits[0].toDouble()
        val p = digits[2].toDouble()
        val q = Frac(
            if (sequence.length > 3) sequence.subSequence(3, sequence.length) else "",
            context.shiftColumnBy(3)
        )
        q.forceHex = forceHex

        (d + (p + q.value) / divisor) / divisor
    }

    private val digits: ByteArray by lazy {
        val value = ByteArray(sequence.length)
        repeat(sequence.length) { index -> value[index] = sequence.parseDigit(index, context) }
        value
    }

    companion object {
        const val DECIMAL_PATTERN = "(${Num.DECIMAL_PATTERN}|^$)"
        const val HEX_PATTERN = "(${Num.HEX_PATTERN}|^$)"
    }
}