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

commonMain.piacenti.dslmaker.structures.derivationgraph.LeftRecursionRemover.kt Maven / Gradle / Ivy

Go to download

Kotlin multiplatform library to facilitate creation of DSLs with ANTLR or a simple built in parser

There is a newer version: 1.1.55
Show newest version
package piacenti.dslmaker.structures.derivationgraph

import piacenti.dslmaker.abstraction.ProductionStep
import piacenti.dslmaker.errors.BadGrammar
import piacenti.dslmaker.interfaces.GrammarInventory
import piacenti.dslmaker.structures.Expression
import piacenti.dslmaker.structures.Grammar
import piacenti.dslmaker.structures.Production

/**
 * Created by Piacenti on 3/23/2017.
 */
internal class LeftRecursionRemover {
    private val erroCausers = ArrayList()


    fun  removeLeftRecursion(grammar: Grammar): Grammar {
        erroCausers.clear()
        //first we remove direct left recursion so that when we remove indirect recursion we don't introduce more indirect recursions and the we finally remove direct left recursion again
        removeSameProductionLeftRecursion(grammar)
        removeIndirectLeftRecursion(grammar)
        removeSameProductionLeftRecursion(grammar)
        if (!erroCausers.isEmpty()) {
            for (error in erroCausers) {
                throw error
            }
        }
        return grammar
    }

    //technique to remove left side recursion on same production is the following
    //E-> EOE|(E)|N
    //becomes
    //E->(E)E`|NE`
    //E`->OEE`|empty

    private fun removeSameProductionLeftRecursion(grammar: Grammar<*>) {
        //get actual class that will be generated
        val newAndReplacementProductions = HashMap()
        val emptyValueStep = ProductionStep("", "")
        grammar.productions.entries.forEach { entry ->
            //make sure that for each production there is no expression that starts with same Symbol as the rule
            if (entry.value.expressions.filter { expression ->
                        expression.steps[0] === entry.key
                    }.count() > 0) {
                //create a new production using class
                try {
                    val newProductionExpressions = ArrayList()
                    val leftRecursionRemovingProductionType = ProductionStep(entry.key.toString() + "'")
                    //get recursive expressions and move them to the new production removing the original left recursion and adding a
                    // right recursion to the new expression relative to the new production
                    entry.value.expressions.filter { expression ->
                        expression.steps[0] === entry.key
                    }.forEach { expression ->
                        val newEx = Expression(expression.steps)
                        //remove left recursive component
                        newEx.steps.remove(entry.key)
                        //add right recursion to new production
                        newEx.steps.add(leftRecursionRemovingProductionType)
                        newProductionExpressions.add(newEx)
                    }
                    //add empty value
                    newProductionExpressions.add(Expression(emptyValueStep))
                    //add new production to maps of productions to be added
                    newAndReplacementProductions[leftRecursionRemovingProductionType] = Production(null,
                            newProductionExpressions)
                    //change the source production by removing left recursive expressions
                    entry.value.expressions.removeAll(entry.value.expressions.filter { expression ->
                        expression.steps[0] === entry.key
                    })
                    //add new production to end of remaining source productions
                    entry.value.expressions.forEach { expression ->
                        expression.steps.add(leftRecursionRemovingProductionType)
                    }

                } catch (e: Exception) {
                    println(e.message)
                }

            }
        }
        //replace and add productions to grammar
        grammar.productions.putAll(newAndReplacementProductions)
    }

    /*
    technique to remove indirect left side recursion  is the following
    S	->	Aa  |  b
    A	->	Ac  |  Sd  |  empty
    becomes
    S	->	Aa  |  b
    A	->	Ac  |  Aad  |  bd  |  empty
    so we basically identify that there is a indirect left recursion for S and wherever we find S as first item in Expression that is not part of production S
    we replace it by S contents
    Once this is done for all productions we should only have same production left recursion which is handled by the same production left recursion removal method
    * */
    private fun findNextIndirectRecursion(grammar: Grammar<*>): Pair>? {
        /*the algorithm has a chance depending on ordering of processing to make certain productions unreachable even though
        the grammar will match in the end. For the example above if the A was the starting production then S would become unreachable
        although in the end there would still be a match, the ast will be messed up

        To handle that situation we process the recursions that have highest number of usages first
        */
        val indirectRecursionProductions = mutableListOf>>()
        grammar.productions.forEach {
            val key = it.key
            val productionsWithKeyAsStartingExp = grammar.productions.filter { entry ->
                entry.key != key && entry.value.expressions.filter { expression -> expression.steps[0] === key }.count() > 0
            }
            //only keep the ones pointed to from the current
            val recursiveForKey = grammar.productions[key]!!.expressions.map { expression -> expression.steps[0] }.intersect(
                    productionsWithKeyAsStartingExp.map { entry -> entry.key })
            if (recursiveForKey.isNotEmpty()) {
                indirectRecursionProductions.add(key to recursiveForKey)
            }
        }
        return indirectRecursionProductions.maxBy { pair ->
            countNumberOfExpressionsUsingIt(grammar, pair.first)
        }
    }

    private fun countNumberOfExpressionsUsingIt(grammar: Grammar<*>, step: ProductionStep): Int {
        return grammar.productions.map {
            it.value.expressions.filter { exp ->
                exp.steps.contains(step)
            }.count()
        }.sum()
    }

    private fun removeIndirectLeftRecursion(grammar: Grammar<*>) {
        var indirectRecursions = findNextIndirectRecursion(grammar)
        while (indirectRecursions != null) {
            val pair = indirectRecursions
            //find its production expressions
            val sourceProduction = pair.first
            val replacementExpressions = grammar.productions[sourceProduction]!!.expressions
            //replace it in each production using it as first step in an expression
            pair.second.forEach { productionStep ->
                val newExpressions = mutableListOf()
                val production = grammar.productions[productionStep]!!
                production.expressions.forEach { expression ->
                    if (expression.steps.first() === sourceProduction) {
                        if (expression.steps.size == 1) {
                            erroCausers.add(BadGrammar(
                                    "Grammar contains indirect cycles that cannot be removed, causing rules: ${sourceProduction.name} and ${productionStep.name}"))
                        } else {
                            val remainingOriginal = expression.steps.subList(1, expression.steps.size)
                            //combine remaining original (without the starting recursion) with each of the replacements
                            replacementExpressions.forEach { replExpression ->
                                newExpressions.add(Expression(replExpression.steps + remainingOriginal))
                            }
                        }
                    } else {
                        //just add it as it is
                        newExpressions.add(expression)
                    }
                }
                production.expressions.clear()
                production.expressions.addAll(newExpressions)
            }
            indirectRecursions = findNextIndirectRecursion(grammar)

        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy