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

commonMain.agl.runtime.graph.GrowingChildren.kt Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2020 Dr. David H. Akehurst (http://dr.david.h.akehurst.net)
 *
 * 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 net.akehurst.language.agl.runtime.graph

import net.akehurst.language.agl.runtime.structure.RuleOptionId
import net.akehurst.language.api.sppt.SPPTNode

//TODO: there is a lot of code here very specific to handling
// initial skip...maybe there is a better way!
internal class GrowingChildren {

    var length: Int = 0; private set
    var numberNonSkip: Int = 0; private set
    var nextInputPosition: Int = -1
    var startPosition: Int = -1

    var containedFor = mutableSetOf>(); private set

    private var _firstChild: GrowingChildNode? = null
    private var _firstChildAlternatives: MutableMap, MutableList>? = null

    //private var _lastChild: GrowingChildNode? = null
    private var _lastChildAlternatives: MutableMap, GrowingChildNode> = mutableMapOf()
    private var _nextChildAlts: MutableMap, Int>>? = null

    val isEmpty: Boolean get() = null == _firstChild && null == _firstChildAlternatives
    val hasSkipAtStart: Boolean get() = _firstChild!!.isSkip

    val lastInitialSkipChild: GrowingChildNode?
        get() = when {
            isEmpty -> null
            null != this._firstChildAlternatives -> null //skips never have alternatives
            else -> {
                var n = this._firstChild
                var last: GrowingChildNode? = null
                while (null != n && n.isSkip) {
                    last = n
                    n = n.nextChild
                }
                last
            }
        }

    val matchedTextLength get() = this.nextInputPosition - this.startPosition

    private fun clone(): GrowingChildren {
        val cl = GrowingChildren()
        cl.containedFor.addAll(this.containedFor)
        cl.length = this.length
        cl.numberNonSkip = this.numberNonSkip
        cl.nextInputPosition = this.nextInputPosition
        cl.startPosition = this.startPosition
        cl._firstChild = this._firstChild
        cl._lastChildAlternatives.putAll(this._lastChildAlternatives)
        if (null != this._firstChildAlternatives) {
            cl._firstChildAlternatives = mutableMapOf()
            this._firstChildAlternatives!!.entries.forEach {
                val l = it.value.toMutableList() // copy the list of nodes, it can get modified in the original
                cl._firstChildAlternatives!![it.key] = l
            }
        }
        if (null != this._nextChildAlts) {
            cl._nextChildAlts = mutableMapOf()
            this._nextChildAlts!!.entries.forEach {
                val mapCl = it.value.toMutableMap()
                cl._nextChildAlts!![it.key] = mapCl
            }
        }
        return cl
    }

    private fun nextChildAlt(childIndex: Int, ruleOption: RuleOptionId): Int {
        return when {
            null == _nextChildAlts -> 0
            else -> {
                val m = _nextChildAlts!![childIndex]
                when {
                    null == m -> 0
                    else -> m.entries.firstOrNull { it.key.contains(ruleOption) }?.value ?: 0
                }
            }
        }
    }

    private fun nextChildAlt(childIndex: Int, ruleOptionList: Set): Int {
        return when {
            null == _nextChildAlts -> 0
            else -> {
                val m = _nextChildAlts!![childIndex]
                when {
                    null == m -> 0
                    else -> m[ruleOptionList] ?: 0
                }
            }
        }
    }

    internal fun incNextChildAlt(childIndex: Int, value: Set) {
        if (null == _nextChildAlts) _nextChildAlts = mutableMapOf()
        var m = _nextChildAlts!![childIndex]
        if (null == m) {
            m = mutableMapOf()
            _nextChildAlts!![childIndex] = m
        }
        val v = m[value]
        if (null == v) {
            m[value] = 1
        } else {
            m[value] = v + 1
        }
    }

    internal fun setNextChildAlt(childIndex: Int, value: Set, altNum: Int) {
        if (null == _nextChildAlts) _nextChildAlts = mutableMapOf()
        var m = _nextChildAlts!![childIndex]
        if (null == m) {
            m = mutableMapOf()
            _nextChildAlts!![childIndex] = m
        }
        m[value] = altNum
    }

    // only used in RuntimeParser.useGoal && internally in this class
    fun firstChild(ruleOptionSet: Set): GrowingChildNode? = when {
        null == this._firstChildAlternatives -> this._firstChild
        //null == ruleOption -> null //skip will not have alternatives
        else -> this._firstChildAlternatives!!.entries.firstOrNull {
            it.key.isEmpty() || // initial skip is first child for anything, emptyList is used to mark initialSkip
            it.key==ruleOptionSet
        }?.value?.get(this.nextChildAlt(0, ruleOptionSet))
    }

    private fun firstChildWithIndexContaining(ruleOption: RuleOptionId): GrowingChildNode? = when {
        null == this._firstChildAlternatives -> this._firstChild
        //null == ruleOption -> null //skip will not have alternatives
        else -> this._firstChildAlternatives!!.entries.firstOrNull {
            it.key.isEmpty() || // initial skip is first child for anything, emptyList is used to mark initialSkip
                    it.key.contains(ruleOption)
        }?.value?.get(this.nextChildAlt(0, ruleOption))
    }

    fun lastChild(ruleOption: Set): GrowingChildNode? =  this._lastChildAlternatives[ruleOption]

    fun lastChild2(ruleOption: Set): GrowingChildNode? =  this._lastChildAlternatives.entries.firstOrNull {
        it.key.containsAll(ruleOption)
    }?.value

    // only used in RuntimeParser.useGoal
    fun firstNonSkipChild(ruleOption: Set): GrowingChildNode? {
        var r = this.firstChild(ruleOption)
        var index = 1
        while (null != r && r.isSkip) {
            r = r.next(this.nextChildAlt(index, ruleOption), ruleOption)
            index++
        }
        return r
    }

    private fun setFirstChild(ruleOptionList: Set, node: GrowingChildNode) {
        when {
            this.isEmpty -> this._firstChild = node
            else -> {
                _firstChildAlternatives = mutableMapOf()
                val alts = mutableListOf(this._firstChild!!)
                this._firstChildAlternatives!![ruleOptionList] = alts
                this._firstChild = null
                alts.add(node)
            }
        }
        this.setLastChild(ruleOptionList, node)
        if (node.isSkip.not()) this.numberNonSkip=1
        this.length = 1
        this.containedFor.add(ruleOptionList)
    }

    private fun setLastChild(ruleOption: Set, node: GrowingChildNode) {
        this._lastChildAlternatives[ruleOption] = node
    }

    /**
     * create new GrowingChildren unless empty
     */
    //TODO: do we need to create new object or can we reuse existing because it may not be used anymore ?
    fun appendChild(ruleOptionList: Set, nextChildAlts: List): GrowingChildren {
        val nextChild = GrowingChildNode(ruleOptionList, nextChildAlts, false)
        return when {
            isEmpty -> {
                this.setFirstChild(ruleOptionList, nextChild)
                startPosition = nextChildAlts[0].startPosition
                this.nextInputPosition = nextChildAlts[0].nextInputPosition //FIXME: not really correct, are all children same length?
                this
            }
            ruleOptionList.isEmpty() -> {
                TODO("I think this never happens")
                val lisc = lastInitialSkipChild
                when {
                    null == lisc -> { // must be duplicate of firstNode which is goal
                        //TODO: if nextInputPosition of duplicate ??
                        val res = this.clone()
                        res._firstChildAlternatives = mutableMapOf()
                        val alts = mutableListOf(res._firstChild!!)
                        res._firstChildAlternatives!![ruleOptionList] = alts
                        res._firstChild = null
                        alts.add(nextChild)
                        res.incNextChildAlt(0, ruleOptionList)
                        res.setLastChild(ruleOptionList, nextChild)
                        // because its a duplicate of existing goal
                        // will not changed the length or numberNonSkip
                        res.nextInputPosition = nextChildAlts[0].nextInputPosition //FIXME: not really correct, are all children same length?
                        res.containedFor.add(ruleOptionList)
                        res
                    }
                    else -> { // must be initial skip and duplicate of existing goal
                        val changeLength = lisc.isLast
                        val appended = lisc.appendLast(this, this.length - 1, nextChild)
                        appended?.let {
                            this.setLastChild(ruleOptionList, it)
                            if (changeLength) {
                                this.length++
                                this.numberNonSkip++
                            } else {
                                // because its a duplicate of existing goal
                                // will not change the length or numberNonSkip
                            }
                            this.nextInputPosition = nextChildAlts[0].nextInputPosition //FIXME: not really correct, are all children same length?
                            this.containedFor.add(ruleOptionList)
                            this
                        }
                    }
                }
            }
            // only contains initialSkip
            this.containedFor.all { it.isEmpty() } -> {
                var res = GrowingChildren()
                var nc = this._firstChild
                while(null!=nc) {
                    //TODO: find a more perf way to do this if needed..but maybe this is ok as only doen for initial skip
                    res = res.appendSkipIfNotEmpty(ruleOptionList, nc.children)
                    nc = nc.nextChild
                }
                val lastChild = res.lastInitialSkipChild ?: TODO()
                val appended = lastChild.appendLast(res, this.length, nextChild)
                res.setLastChild(ruleOptionList, appended)
                res.length++
                res.numberNonSkip++
                res.nextInputPosition = nextChildAlts[0].nextInputPosition //FIXME: not really correct, are all children same length?
                res.containedFor.add(ruleOptionList)
                res
            }
            else -> {
                val res = this.clone()
                val lastChild = res.lastChild(ruleOptionList)?: res.lastChild2(ruleOptionList)?: error("should never be null!")
                val appended = lastChild.appendLast(res, res.length, nextChild)
                res.setLastChild(ruleOptionList, appended)
                res.length++
                res.numberNonSkip++
                //check(1 == nextChildAlts.map { it.nextInputPosition }.toSet().size)
                res.nextInputPosition = nextChildAlts[0].nextInputPosition //FIXME: not really correct, are all children same length?
                res.containedFor.add(ruleOptionList)
                res
            }
        }
    }

    /**
     * create new GrowingChildren unless empty
     */
    //TODO: do we need to create new object or can we reuse existing because it may not be used anymore ?
    fun appendSkipIfNotEmpty(ruleOptionList: Set, skipChildren: List): GrowingChildren {
        val nextChild = GrowingChildNode(ruleOptionList, skipChildren,true)
        return if (skipChildren.isEmpty()) {
            //do nothing
            this
        } else {
            if (isEmpty) {
                this.setFirstChild(ruleOptionList, nextChild)
                this.startPosition = skipChildren[0].startPosition
                this.nextInputPosition = skipChildren.last().nextInputPosition
                this
            } else {
                val res = this.clone()
                res.containedFor.add(ruleOptionList)
                val lastChild = res.lastChild(ruleOptionList)?: TODO("?")
                val appended = lastChild.appendLast(res, res.length, nextChild)
                res.setLastChild(ruleOptionList, appended)
                res.length++
                //check(1 == nextChildAlts.map { it.nextInputPosition }.toSet().size)
                res.nextInputPosition = skipChildren[0].nextInputPosition //FIXME: not really correct, are all children same length?
                res
            }
        }
    }

    operator fun get(ruleOption: RuleOptionId): List {
        return when {
            this.isEmpty -> emptyList()
            else -> {
                val res = mutableListOf()
                var n: GrowingChildNode? = this.firstChildWithIndexContaining(ruleOption)
                var index = 1
                while (null != n && this._lastChildAlternatives.values.contains(n).not()) {
                    res.addAll(n[ruleOption])
                    n = n.next(this.nextChildAlt(index, ruleOption), ruleOption)
                    index++
                }
                if (null == n) {
                    //this list of children died
                    error("TODO")
                } else {
                    res.addAll(
                        n[ruleOption]
                    )
                }
                res
            }
        }
    }

    // only used in context of rewriting initial skip !
    fun concatenate(other: GrowingChildren) {
        for (ruleOptionList in other.containedFor) {
            val lastChild = this.lastChild(ruleOptionList)!!
            lastChild.nextChild = other._firstChild
            if (null != other._firstChildAlternatives) {
                lastChild.nextChildAlternatives = mutableMapOf()
                other._firstChildAlternatives!!.entries.forEach {
                    val l = it.value.toMutableList()
                    lastChild.nextChildAlternatives!![it.key] = l
                }
            }
            if (null != other._nextChildAlts) {
                other._nextChildAlts!!.forEach {
                    this._nextChildAlts!![it.key + this.length] = it.value.toMutableMap()
                }
            }
            this._lastChildAlternatives = other._lastChildAlternatives
            this.length += other.length
            this.numberNonSkip += other.numberNonSkip
            this.nextInputPosition = other.nextInputPosition
        }
    }

    /**
     * merge other into this
     */
    //TODO: should it return a new object?
    fun mergeOrDropWithPriority(other: GrowingChildren) {
        when {
            other.isEmpty -> Unit
            this.isEmpty -> {
                this._firstChild = other._firstChild
                this._firstChildAlternatives = other._firstChildAlternatives
                this._nextChildAlts = other._nextChildAlts
                this._lastChildAlternatives = other._lastChildAlternatives
                this.length = other.length
                this.containedFor = other.containedFor
                this.startPosition = other.startPosition
                this.numberNonSkip = other.numberNonSkip
                this.nextInputPosition = other.nextInputPosition //FIXME: not really correct, are all children same length?
            }
            this.startPosition != other.startPosition -> error("Cannot merge children starting at a different position")
            else -> {
                for (ruleOption in other.containedFor) {
                    TODO()
                }
            }
        }
    }

    override fun toString(): String = when {
        this.isEmpty -> "{}"
        else -> {
            val res = mutableMapOf>()
            val initialSkip = mutableListOf()

            for (ruleOptionList in this.containedFor) {
                var n = firstChild(ruleOptionList)
                var skip = ""
                var index = 1
                while (null != n && n!=this._lastChildAlternatives[ruleOptionList]) {
                    for(ro in ruleOptionList) {
                        when {
                            n!!.isSkip ->res[ro] = (res[ro] ?: emptyList()) + n[ro].joinToString() { it.name }
                            else -> res[ro] = (res[ro] ?: emptyList()) + n[ro].joinToString() { "${it.name}|${it.option}" }
                        }
                        n = n.next(this.nextChildAlt(index, ro), ro)
                        index++
                    }
                }
                when {
                    ruleOptionList.isEmpty() -> when { //only contains initialskip
                        null==n -> (res[null] ?: emptyList())+"-X"
                        else -> res[null] = (res[null] ?: emptyList())+ n[null].joinToString() { it.name }
                    }
                    else -> {
                        for (ro in ruleOptionList) {
                            if (null == n) {
                                //this list of children died
                                res[ro] = (res[ro] ?: emptyList()) + "-X"
                            } else {
                                when {
                                    n.isSkip -> res[ro] = (res[ro] ?: emptyList()) + n[ro].joinToString() { it.name }
                                    else -> res[ro] = (res[ro] ?: emptyList()) + n[ro].joinToString() { "${it.name}|${it.option}" }
                                }
                            }
                        }
                    }
                }
            }

            res.entries.map { "(${this.startPosition},${this.nextInputPosition},${it.key?.runtimeRule?.tag}|${it.key?.option}) -> ${it.value.joinToString()}" }
                .joinToString(separator = "\n")
        }
    }
/*
     fun toString1(): String = when {
        isEmpty -> "{}"
        else -> {
            val res = mutableMapOf>()
            val initialSkip = mutableListOf()

            var sn = _firstChild
            var lastSkip = sn
            while (sn != null && sn.isSkip) {
                initialSkip.add(sn.children.joinToString() { it.name })
                lastSkip = sn
                sn = sn.nextChild
            }
            val rpIds = when {
                // there are no skips
                null != _firstChildAlternatives -> _firstChildAlternatives!!.entries.mapNotNull { it.key }.flatten()
                null == sn -> when {
                    //lastSkip.next has alts
                    null != lastSkip!!.nextChildAlternatives -> lastSkip.nextChildAlternatives!!.entries.mapNotNull { it.key }.flatten()
                    //nothing after skips
                    else -> emptyList()
                }
                //sn (lastSkip.next) is the first nonSkip node
                else -> sn.ruleOptionList ?: emptyList()
            }
            when {
                rpIds.isEmpty() -> res[null] = initialSkip
                else -> {
                    for (rpId in rpIds) {
                        res[rpId] = initialSkip
                        var n = firstNonSkipChild(rpId)
                        var skip = ""
                        var index = 1
                        while (null != n && _lastChildAlternatives.values.contains(n).not()) {
                            when {
                                n.isSkip -> skip += n.children.joinToString { it.name } //skipNodes
                                else -> {
                                    if (skip.isNotBlank()) {
                                        res[rpId] = res[rpId]!! + skip
                                        skip = ""
                                    }
                                    res[rpId] = res[rpId]!! + n[rpId].joinToString() { "${it.name}|${it.option}" }
                                }
                            }
                            n = n.next(this.nextChildAlt(index, rpId), rpId)
                            index++
                        }
                        if (null == n) {
                            //this list of children died
                            res[rpId] = res[rpId]!! + "-X"
                        } else {
                            when {
                                n.isSkip -> {
                                    skip += n.children.joinToString { it.name } //skipNodes
                                    if (skip.isNotBlank()) {
                                        res[rpId] = res[rpId]!! + skip
                                        skip = ""
                                    }
                                }
                                else -> {
                                    if (skip.isNotBlank()) {
                                        res[rpId] = res[rpId]!! + skip
                                        skip = ""
                                    }
                                    res[rpId] = res[rpId]!! + n[rpId].joinToString() { "${it.name}|${it.option}" }
                                }
                            }
                        }
                    }
                }
            }

            res.entries.map { "(${this.startPosition},${this.nextInputPosition},${it.key?.runtimeRule?.tag}|${it.key?.option}) -> ${it.value.joinToString()}" }
                .joinToString(separator = "\n")
        }
    }
*/
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy