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

commonMain.org.antlr.v4.kotlinruntime.atn.LexerActionExecutor.kt Maven / Gradle / Ivy

/*
 * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

package org.antlr.v4.kotlinruntime.atn

import org.antlr.v4.kotlinruntime.CharStream
import org.antlr.v4.kotlinruntime.IntStream
import org.antlr.v4.kotlinruntime.Lexer
import org.antlr.v4.kotlinruntime.dfa.DFA
import org.antlr.v4.kotlinruntime.misc.MurmurHash

/**
 * Represents an executor for a sequence of lexer actions which traversed during
 * the matching operation of a lexer rule (token).
 *
 *
 * The executor tracks position information for position-dependent lexer actions
 * efficiently, ensuring that actions appearing only at the end of the rule do
 * not cause bloating of the [DFA] created for the lexer.
 *
 * @author Sam Harwell
 * @since 4.2
 */
class LexerActionExecutor
/**
 * Constructs an executor for a sequence of [LexerAction] actions.
 * @param lexerActions The lexer actions to execute.
 */
(
        /**
         * Gets the lexer actions to be executed by this executor.
         * @return The lexer actions to be executed by this executor.
         */
        val lexerActions: Array) {
    /**
     * Caches the result of [.hashCode] since the hash code is an element
     * of the performance-critical [LexerATNConfig.hashCode] operation.
     */
    private val hashCode: Int

    init {

        var hash = MurmurHash.initialize()
        for (lexerAction in lexerActions) {
            hash = MurmurHash.update(hash, lexerAction)
        }

        this.hashCode = MurmurHash.finish(hash, lexerActions.size)
    }

    /**
     * Creates a [LexerActionExecutor] which encodes the current offset
     * for position-dependent lexer actions.
     *
     *
     * Normally, when the executor encounters lexer actions where
     * [LexerAction.isPositionDependent] returns `true`, it calls
     * [IntStream.seek] on the input [CharStream] to set the input
     * position to the *end* of the current token. This behavior provides
     * for efficient DFA representation of lexer actions which appear at the end
     * of a lexer rule, even when the lexer rule matches a variable number of
     * characters.
     *
     *
     * Prior to traversing a match transition in the ATN, the current offset
     * from the token start index is assigned to all position-dependent lexer
     * actions which have not already been assigned a fixed offset. By storing
     * the offsets relative to the token start index, the DFA representation of
     * lexer actions which appear in the middle of tokens remains efficient due
     * to sharing among tokens of the same length, regardless of their absolute
     * position in the input stream.
     *
     *
     * If the current executor already has offsets assigned to all
     * position-dependent lexer actions, the method returns `this`.
     *
     * @param offset The current offset to assign to all position-dependent
     * lexer actions which do not already have offsets assigned.
     *
     * @return A [LexerActionExecutor] which stores input stream offsets
     * for all position-dependent lexer actions.
     */
    fun fixOffsetBeforeMatch(offset: Int): LexerActionExecutor {
        var updatedLexerActions: Array? = null
        for (i in lexerActions.indices) {
            if (lexerActions[i].isPositionDependent && lexerActions[i] !is LexerIndexedCustomAction) {
                if (updatedLexerActions == null) {
                    updatedLexerActions = lexerActions.copyOf()
                }

                updatedLexerActions[i] = LexerIndexedCustomAction(offset, lexerActions[i])
            }
        }

        return if (updatedLexerActions == null) {
            this
        } else LexerActionExecutor(updatedLexerActions)

    }

    /**
     * Execute the actions encapsulated by this executor within the context of a
     * particular [Lexer].
     *
     *
     * This method calls [IntStream.seek] to set the position of the
     * `input` [CharStream] prior to calling
     * [LexerAction.execute] on a position-dependent action. Before the
     * method returns, the input position will be restored to the same position
     * it was in when the method was invoked.
     *
     * @param lexer The lexer instance.
     * @param input The input stream which is the source for the current token.
     * When this method is called, the current [IntStream.index] for
     * `input` should be the start of the following token, i.e. 1
     * character past the end of the current token.
     * @param startIndex The token start index. This value may be passed to
     * [IntStream.seek] to set the `input` position to the beginning
     * of the token.
     */
    fun execute(lexer: Lexer, input: CharStream, startIndex: Int) {
        var requiresSeek = false
        val stopIndex = input.index()
        try {
            for (lexerAction in lexerActions) {
                var mutableLexerAction = lexerAction
                if (mutableLexerAction is LexerIndexedCustomAction) {
                    val offset = mutableLexerAction.offset
                    input.seek(startIndex + offset)
                    mutableLexerAction = mutableLexerAction.action
                    requiresSeek = startIndex + offset != stopIndex
                } else if (mutableLexerAction.isPositionDependent) {
                    input.seek(stopIndex)
                    requiresSeek = false
                }

                mutableLexerAction.execute(lexer)
            }
        } finally {
            if (requiresSeek) {
                input.seek(stopIndex)
            }
        }
    }

    override fun hashCode(): Int {
        return this.hashCode
    }

    override fun equals(other: Any?): Boolean {
        if (other === this) {
            return true
        } else if (other !is LexerActionExecutor) {
            return false
        }

        val other1 = other as LexerActionExecutor?
        return hashCode == other1!!.hashCode && lexerActions.contentEquals(other1.lexerActions)
    }

    companion object {

        /**
         * Creates a [LexerActionExecutor] which executes the actions for
         * the input `lexerActionExecutor` followed by a specified
         * `lexerAction`.
         *
         * @param lexerActionExecutor The executor for actions already traversed by
         * the lexer while matching a token within a particular
         * [LexerATNConfig]. If this is `null`, the method behaves as
         * though it were an empty executor.
         * @param lexerAction The lexer action to execute after the actions
         * specified in `lexerActionExecutor`.
         *
         * @return A [LexerActionExecutor] for executing the combine actions
         * of `lexerActionExecutor` and `lexerAction`.
         */
        fun append(lexerActionExecutor: LexerActionExecutor?, lexerAction: LexerAction): LexerActionExecutor {
            if (lexerActionExecutor == null) {
                return LexerActionExecutor(arrayOf(lexerAction))
            }

            val lexerActions = lexerActionExecutor.lexerActions
                    .copyOf(lexerActionExecutor.lexerActions.size + 1)
            lexerActions[lexerActions.lastIndex] = lexerAction
            return LexerActionExecutor(lexerActions.filterNotNull().toTypedArray())
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy