jasonwyatt/KWasm

View on GitHub
library/src/main/java/kwasm/format/text/Identifier.kt

Summary

Maintainability
A
0 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

import kwasm.ast.Identifier
import kwasm.format.ParseException
import kwasm.format.parseCheckNotNull
import kwasm.format.text.token.IntegerLiteral
import kwasm.format.text.token.Token

/**
 * Parses an [Identifier] of type [T] from the receiving [List] of [Token]s. If no explicit
 * [Identifier] is found, an implicit one with [Identifier.stringRepr] and [Identifier.unique] both
 * set to `null` is returned.
 *
 * **Note:** Does not support [Identifier.TypeDef].
 */
@Suppress("RemoveExplicitTypeArguments")
inline fun <reified T : Identifier> List<Token>.parseIdentifier(
    fromIndex: Int,
    intAllowed: Boolean = false
): ParseResult<T>? {
    var currentIndex = fromIndex
    val maybeId = getOrNull(currentIndex) as? kwasm.format.text.token.Identifier
    val maybeInt = getOrNull(currentIndex) as? IntegerLiteral<*>

    val id = if (maybeId != null) {
        currentIndex++
        createIdentifier<T>(maybeId.value)
    } else if (maybeInt != null && intAllowed) {
        currentIndex++
        parseCheckNotNull(
            contextAt(currentIndex - 1),
            createIdentifier<T>(intValue = maybeInt.toSigned().value.toInt())
                ?.takeIf { it.unique != null && (it.unique as Int) >= 0 },
            "Identifier must not be negative"
        )
    } else null
    return id?.let { ParseResult(it, currentIndex - fromIndex) }
}

inline fun <reified T : Identifier> List<Token>.parseOrCreateIdentifier(
    fromIndex: Int,
    counts: TextModuleCounts
): Pair<ParseResult<T>, TextModuleCounts> {
    val parsed = parseIdentifier<T>(fromIndex, false)

    val value = when (T::class) {
        Identifier.Global::class -> counts.globals
        Identifier.Memory::class -> counts.memories
        Identifier.Table::class -> counts.tables
        Identifier.Function::class -> counts.functions
        Identifier.Type::class -> counts.types
        else -> null
    }
    val identifier = createIdentifier<T>(stringValue = null, intValue = value)
    return if (parsed == null && identifier != null) {
        ParseResult(identifier, 0) to counts.incrementFor(identifier)
    } else if (parsed != null) {
        parsed to counts.incrementFor(parsed.astNode)
    } else throw ParseException("Unsupported identifier found", contextAt(fromIndex))
}

/**
 * Creates an [Identifier] of type [T].
 */
inline fun <reified T : Identifier> createIdentifier(
    stringValue: String? = null,
    intValue: Int? = "$stringValue".hashCode()
): T? {
    return when (T::class) {
        Identifier.Global::class -> Identifier.Global(stringValue, intValue)
        Identifier.Memory::class -> Identifier.Memory(stringValue, intValue)
        Identifier.Table::class -> Identifier.Table(stringValue, intValue)
        Identifier.Function::class -> Identifier.Function(stringValue, intValue)
        Identifier.Type::class -> Identifier.Type(stringValue, intValue)
        Identifier.Local::class -> Identifier.Local(stringValue, intValue)
        Identifier.Label::class -> Identifier.Label(stringValue, intValue)
        else -> null // TypeDef not supported here.
    } as T?
}