Skip to content

Fix recursive case + more tests #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions solver/src/main/kotlin/org/ucfs/descriptors/DescriptorsStorage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,16 @@ import org.ucfs.parser.ParsingException
* Collection of default descriptors
* @param VertexType - type of vertex in input graph
*/
open class DescriptorsStorage<VertexType>{
open class DescriptorsStorage<VertexType> {
/**
* Collection of already handled descriptors, accessible via descriptor's hashcode
*/
private val handledDescriptors = ArrayList<Descriptor<VertexType>>()
private val handledDescriptors = HashSet<Descriptor<VertexType>>()

private val descriptorsToHandle = ArrayDeque<Descriptor<VertexType>>()

private fun isEmpty() = descriptorsToHandle.isEmpty()

private fun addToHandling(descriptor: Descriptor<VertexType>) {
if (!isAlreadyHandled(descriptor)) {
descriptorsToHandle.addLast(descriptor)
}
}

/**
* @return next default descriptor to handle
*/
Expand All @@ -32,23 +26,13 @@ open class DescriptorsStorage<VertexType>{
return descriptorsToHandle.removeLast()
}

/**
* @param descriptor - descriptor to check
* @return true if the descriptor was already processed, false otherwise
*/
private fun isAlreadyHandled(descriptor: Descriptor<VertexType>): Boolean {
val handledDescriptor = handledDescriptors.find { descriptor.hashCode() == it.hashCode() }

return handledDescriptor != null
}

fun addToHandled(descriptor: Descriptor<VertexType>) {
handledDescriptors.add(descriptor)
}

fun add(descriptor: Descriptor<VertexType>) {
if(!isAlreadyHandled(descriptor)){
addToHandling(descriptor)
if(!handledDescriptors.contains(descriptor)){
descriptorsToHandle.addLast(descriptor)
}
}
/**
Expand Down
10 changes: 3 additions & 7 deletions solver/src/main/kotlin/org/ucfs/gss/GraphStructuredStack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@ import org.ucfs.rsm.RsmState
import org.ucfs.sppf.node.RangeSppfNode

class GraphStructuredStack<InputNode> {
val nodes = ArrayList<GssNode<InputNode>>()
val nodes = HashMap<GssNode<InputNode>, GssNode<InputNode>>()

fun getOrCreateNode(input: InputNode, rsm: RsmState): GssNode<InputNode> {
val node = findNode(input, rsm)
return if (node == null) GssNode(rsm, input) else node
}

fun findNode(input: InputNode, rsm: RsmState): GssNode<InputNode>? {
return nodes.find { node -> node.inputPosition == input && node.rsm == rsm }
val node = GssNode(rsm, input)
return nodes.getOrPut(node, {node})
}

fun addEdge(
Expand Down
5 changes: 2 additions & 3 deletions solver/src/main/kotlin/org/ucfs/gss/GssNode.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.ucfs.gss

import org.ucfs.descriptors.Descriptor
import org.ucfs.rsm.RsmState
import org.ucfs.sppf.node.RangeSppfNode
import java.util.*
Expand All @@ -20,9 +19,10 @@ data class GssNode<InputNodeType>(
/**
* Pointer to vertex in input graph
*/
val inputPosition: InputNodeType, val id: Int = lastId++
val inputPosition: InputNodeType,

) {
val id: Int = lastId++
val popped = ArrayList<RangeSppfNode<InputNodeType>>()

val outgoingEdges = ArrayList<GssEdge<InputNodeType>>()
Expand All @@ -32,7 +32,6 @@ data class GssNode<InputNodeType>(
*/
fun addEdge(edge: GssEdge<InputNodeType>): ArrayList<RangeSppfNode<InputNodeType>> {
outgoingEdges.add(edge)
//TODO
return popped
}

Expand Down
24 changes: 12 additions & 12 deletions solver/src/main/kotlin/org/ucfs/parser/Gll.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,24 @@ class Gll<VertexType, LabelType : ILabel> private constructor(
descriptor.rsmState,
descriptor.rsmState,
)
val range = ctx.sppfStorage.addNode(RangeSppfNode(input, rsm, Range))
val epsilon = ctx.sppfStorage.addNode(
RangeSppfNode(input, rsm, EpsilonNonterminalType(descriptor.gssNode.rsm))
)
range.children.add(epsilon)
return range
return ctx.sppfStorage.addEpsilonNode(input, rsm, descriptor.gssNode.rsm)
}

private fun handlePoppedGssEdge(
poppedGssEdge: GssEdge<VertexType>, descriptor: Descriptor<VertexType>, childSppf: RangeSppfNode<VertexType>
) {
val leftRange = poppedGssEdge.matchedRange
val startRsmState = if (poppedGssEdge.matchedRange.type == EmptyType) poppedGssEdge.gssNode.rsm
val startRsmState = if (poppedGssEdge.matchedRange.type is EmptyType) poppedGssEdge.gssNode.rsm
else poppedGssEdge.matchedRange.rsmRange!!.to
val rightRange = ctx.sppfStorage.addNode(
val rightRange = ctx.sppfStorage.addNonterminalNode(
InputRange(
descriptor.gssNode.inputPosition, descriptor.inputPosition
), RsmRange(
startRsmState,
poppedGssEdge.state,
), descriptor.gssNode.rsm, childSppf
)
ctx.sppfStorage.addNode(rightRange)
val newRange = ctx.sppfStorage.addNode(leftRange, rightRange)
val newRange = ctx.sppfStorage.addIntermediateNode(leftRange, rightRange)
val newDescriptor = Descriptor(
descriptor.inputPosition, poppedGssEdge.gssNode, poppedGssEdge.state, newRange
)
Expand All @@ -79,8 +73,14 @@ class Gll<VertexType, LabelType : ILabel> private constructor(
override fun handleDescriptor(descriptor: Descriptor<VertexType>) {
ctx.descriptors.addToHandled(descriptor)
if (descriptor.rsmState.isFinal) {
val matchedRange = if (descriptor.sppfNode.type == EmptyType) {
getEpsilonRange(descriptor)
val matchedRange = if (descriptor.sppfNode.type is EmptyType) {
val node = getEpsilonRange(descriptor)
//TODO fix
// dirty hack: in fact it's equivavelnt descriptors
// but only initial was added in handlet set
ctx.descriptors.addToHandled(Descriptor(descriptor.inputPosition,
descriptor.gssNode, descriptor.rsmState, node))
node
} else {
descriptor.sppfNode
}
Expand Down
13 changes: 6 additions & 7 deletions solver/src/main/kotlin/org/ucfs/parser/IGll.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,13 @@ interface IGll<InputNodeType, LabelType : ILabel> {

for (rangeToPop in positionToPops) {
val leftSubRange = descriptor.sppfNode
val rightSubRange = ctx.sppfStorage.addNode(
RangeSppfNode(
rangeToPop.inputRange, RsmRange(
val rightSubRange = ctx.sppfStorage.addNonterminalNode(
rangeToPop.inputRange!!, RsmRange(
descriptor.rsmState, destinationRsmState
), NonterminalType(rsmStartState)
), rsmStartState
)
)
val newSppfNode = ctx.sppfStorage.addNode(leftSubRange, rightSubRange)

val newSppfNode = ctx.sppfStorage.addIntermediateNode(leftSubRange, rightSubRange)

//TODO why these parameters???
newDescriptor = Descriptor(
Expand All @@ -108,7 +107,7 @@ interface IGll<InputNodeType, LabelType : ILabel> {
destinationRsmState,
), terminal
)
val intermediateOrTerminalSppf = ctx.sppfStorage.addNode(descriptor.sppfNode, terminalSppfNode)
val intermediateOrTerminalSppf = ctx.sppfStorage.addIntermediateNode(descriptor.sppfNode, terminalSppfNode)
val descriptorForTerminal = Descriptor(
inputEdge.targetVertex, descriptor.gssNode, destinationRsmState, intermediateOrTerminalSppf
)
Expand Down
31 changes: 24 additions & 7 deletions solver/src/main/kotlin/org/ucfs/sppf/SppfStorage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,30 @@ open class SppfStorage<InputEdgeType> {
private val createdSppfNodes: HashMap<RangeSppfNode<InputEdgeType>, RangeSppfNode<InputEdgeType>> = HashMap()


fun addNode(node: RangeSppfNode<InputEdgeType>): RangeSppfNode<InputEdgeType> {
private fun addNode(node: RangeSppfNode<InputEdgeType>): RangeSppfNode<InputEdgeType> {
return createdSppfNodes.getOrPut(node, { node })
}

/**
* Add nonterminal node after pop
*/
fun addNode(
input: InputRange<InputEdgeType>, rsm: RsmRange, startState: RsmState, childSppf: RangeSppfNode<InputEdgeType>
fun addNonterminalNode(
input: InputRange<InputEdgeType>,
rsm: RsmRange,
startState: RsmState,
childSppf: RangeSppfNode<InputEdgeType>? = null
): RangeSppfNode<InputEdgeType> {
return if (childSppf == null) addNode(input, rsm, NonterminalType(startState))
else addNode(input, rsm, NonterminalType(startState), listOf(childSppf))
}

fun addEpsilonNode(
input: InputRange<InputEdgeType>,
rsmRange: RsmRange,
rsmState: RsmState
): RangeSppfNode<InputEdgeType> {
return addNode(input, rsm, NonterminalType(startState), listOf(childSppf))
return addNode(
input, rsmRange, EpsilonNonterminalType(rsmState))
}

/**
Expand All @@ -36,11 +49,11 @@ open class SppfStorage<InputEdgeType> {
return addNode(input, rsm, TerminalType(terminal))
}

fun addNode(
fun addIntermediateNode(
leftSubtree: RangeSppfNode<InputEdgeType>,
rightSubtree: RangeSppfNode<InputEdgeType>
): RangeSppfNode<InputEdgeType> {
if (leftSubtree.type == EmptyType) {
if (leftSubtree.type is EmptyType) {
return rightSubtree
}
return addNode(
Expand All @@ -66,7 +79,11 @@ open class SppfStorage<InputEdgeType> {
if (!rangeNode.children.contains(valueNode)) {
rangeNode.children.add(valueNode)
}
valueNode.children.addAll(children)
for(child in children){
if (!valueNode.children.contains(child)){
valueNode.children.add(child)
}
}
return rangeNode
}
}
6 changes: 3 additions & 3 deletions solver/src/main/kotlin/org/ucfs/sppf/node/RangeSppfNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data class RangeSppfNode<VertexType>(
val children = ArrayList<RangeSppfNode<VertexType>>()
}

fun <VertexType> getEmptyRange(): RangeSppfNode<VertexType> = RangeSppfNode(null, null, EmptyType)
fun <VertexType> getEmptyRange(): RangeSppfNode<VertexType> = RangeSppfNode(null, null, EmptyType())

data class InputRange<VertexType>(
val from: VertexType,
Expand All @@ -39,10 +39,10 @@ data class RsmRange(
)

interface RangeType

var lastEmptyId = 0
object Range : RangeType
data class TerminalType<T : ITerminal>(val terminal: T) : RangeType
data class NonterminalType(val startState: RsmState) : RangeType
data class EpsilonNonterminalType(val startState: RsmState) : RangeType
data class IntermediateType<VertexType>(val grammarSlot: RsmState, val inputPosition: VertexType) : RangeType
object EmptyType : RangeType
data class EmptyType(private val id: Int = lastEmptyId++) : RangeType
47 changes: 26 additions & 21 deletions solver/src/main/kotlin/org/ucfs/sppf/writeSppfToDot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,101 +16,106 @@ fun <InputNode> writeSppfToDot(sppfNode: RangeSppfNode<InputNode>, filePath: Str

fun <InputNode> getSppfDot(sppfNode: RangeSppfNode<InputNode>, label: String = ""): String {
val queue: ArrayDeque<RangeSppfNode<InputNode>> = ArrayDeque(listOf(sppfNode))
val edges: HashMap<RangeSppfNode<InputNode>, HashSet<RangeSppfNode<InputNode>>> = HashMap()
val visited: HashSet<Int> = HashSet()
var node: RangeSppfNode<InputNode>
val sb = StringBuilder()
sb.appendLine("digraph g {")
sb.appendLine("labelloc=\"t\"")
sb.appendLine("label=\"$label\"")
var nextNodeId = 0
val nodeViews = HashMap<RangeSppfNode<InputNode>, String>()
while (queue.isNotEmpty()) {
node = queue.removeFirst()
if (!visited.add(node.hashCode())) continue

nodeViews[node] = getNodeView(node)
nodeViews[node] = getNodeView(node, )

node.children.forEach {
queue.addLast(it)
edges.getOrPut(node, { HashSet() }).add(it)
}
}
val sortedViews = nodeViews.values.sorted()
val nodeIds = HashMap<RangeSppfNode<InputNode>, Int>()
val nodeById = HashMap<Int, RangeSppfNode<InputNode>>()
for ((node, view) in nodeViews) {
nodeIds[node] = sortedViews.indexOf(view)
val id = sortedViews.indexOf(view)
nodeIds[node] = id
nodeById[id] = node
}

for (i in sortedViews.indices) {
sb.appendLine("$i ${sortedViews[i]}")
}

val sortedEdges = ArrayList<String>()
for ((head, tails) in edges) {
for (tail in tails) {
sortedEdges.add("${nodeIds[head]}->${nodeIds[tail]}")
for (i in nodeById.keys) {
val node = nodeById[i]
for(child in node!!.children) {
sb.appendLine("${nodeIds[node]}->${nodeIds[child]}")
}
// if(node.children.size < 2){
// continue
// }
// val cs = node.children.map({nodeIds[it]}).joinToString("->")
// sb.appendLine("{ rank = same; $cs [style=invis]}")
}
for (edge in sortedEdges.sorted()) {
sb.appendLine(edge)
}

sb.appendLine("}")
return sb.toString()

}


enum class NodeShape(val view: String) {
Terminal("rectangle"), Nonterminal("invtrapezium"), Intermediate("plain"), Empty("ellipse"), Range("ellipse"), Epsilon(
"invhouse"
)
}

fun fillNodeTemplate(
nodeInfo: String, inputRange: InputRange<*>?, shape: NodeShape, rsmRange: RsmRange? = null
id: String? = null, nodeInfo: String, inputRange: InputRange<*>?, shape: NodeShape, rsmRange: RsmRange? = null
): String {
val inputRangeView = if (inputRange != null) "input: [${inputRange.from}, ${inputRange.to}]" else null
val rsmRangeView = if (rsmRange != null) "rsm: [${rsmRange.from.id}, ${rsmRange.to.id}]" else null
val view = listOfNotNull(nodeInfo, inputRangeView, rsmRangeView).joinToString(", ")
return "[label = \"${shape.name} $view\", shape = ${shape.view}]"
return "[label = \"${id?: ""}${shape.name} $view\", shape = ${shape.view}]"
}


fun <InputNode> getNodeView(node: RangeSppfNode<InputNode>): String {
fun <InputNode> getNodeView(node: RangeSppfNode<InputNode>, id: String? = null): String {
val type = node.type
return when (type) {
is TerminalType<*> -> {
fillNodeTemplate(
"'${type.terminal}'", node.inputRange, NodeShape.Terminal
id, "'${type.terminal}'", node.inputRange, NodeShape.Terminal
)
}

is NonterminalType -> {
fillNodeTemplate(
"${type.startState.nonterminal.name}", node.inputRange, NodeShape.Nonterminal
id, "${type.startState.nonterminal.name}", node.inputRange, NodeShape.Nonterminal
)
}

is IntermediateType<*> -> {
fillNodeTemplate(
"input: ${type.inputPosition}, rsm: ${type.grammarSlot.id}", null, NodeShape.Intermediate
id, "input: ${type.inputPosition}, rsm: ${type.grammarSlot.id}", node.inputRange, NodeShape.Intermediate
)
}

is EmptyType -> {
fillNodeTemplate(
"", null, NodeShape.Empty
id, "", null, NodeShape.Empty
)
}

is EpsilonNonterminalType -> {
fillNodeTemplate(
"RSM: ${type.startState.id}", node.inputRange, NodeShape.Epsilon
id, "RSM: ${type.startState.id}", node.inputRange, NodeShape.Epsilon
)
}

is RangeType -> {
fillNodeTemplate(
"", node.inputRange, NodeShape.Range, node.rsmRange
id, "", node.inputRange, NodeShape.Range, node.rsmRange
)
}

Expand Down
2 changes: 1 addition & 1 deletion test-shared/src/test/kotlin/grammars/SimpleDyck.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class LoopDyck : Grammar() {
val S by Nt().asStart()

init {
S /= Option("(" * S * ")")
S /= Many( "(" * S * ")")
}
}

Expand Down
Loading
Loading