diff --git a/benchmarks/src/main/kotlin/Benchmarks.kt b/benchmarks/src/main/kotlin/Benchmarks.kt index 28857f898..bf567873d 100644 --- a/benchmarks/src/main/kotlin/Benchmarks.kt +++ b/benchmarks/src/main/kotlin/Benchmarks.kt @@ -15,15 +15,15 @@ fun getResultPath( } -fun getTokenStream(input: String): LinearInput { - val graph = LinearInput() +fun getTokenStream(input: String): LinearInput { + val graph = LinearInput() getTokenStream(input, graph) return graph } -fun > getTokenStream(input: String, inputGraph: G): G { +fun > getTokenStream(input: String, inputGraph: G): G { val lexer = Scanner(StringReader(input)) var token: JavaToken var vertexId = 1 @@ -34,21 +34,21 @@ fun > getTokenStream(input: String, input while (true) { token = lexer.yylex() as JavaToken if (token == JavaToken.EOF) break - inputGraph.addEdge(vertexId, LinearInputLabel(token), ++vertexId) + inputGraph.addEdge(vertexId, TerminalInputLabel(token), ++vertexId) } return inputGraph } -fun getCharStream(input: String): LinearInput { - val inputGraph = LinearInput() +fun getCharStream(input: String): LinearInput { + val inputGraph = LinearInput() var vertexId = 1 inputGraph.addVertex(vertexId) inputGraph.addStartVertex(vertexId) for (ch in input) { - inputGraph.addEdge(vertexId, LinearInputLabel(Term(ch.toString())), ++vertexId) + inputGraph.addEdge(vertexId, TerminalInputLabel(Term(ch.toString())), ++vertexId) inputGraph.addVertex(vertexId) } diff --git a/benchmarks/src/main/kotlin/org/ucfs/Java8ParserRecovery.kt b/benchmarks/src/main/kotlin/org/ucfs/Java8ParserRecovery.kt index 002fc2c9e..15a29e389 100644 --- a/benchmarks/src/main/kotlin/org/ucfs/Java8ParserRecovery.kt +++ b/benchmarks/src/main/kotlin/org/ucfs/Java8ParserRecovery.kt @@ -2,7 +2,6 @@ package org.ucfs -import org.ucfs.JavaToken import org.ucfs.descriptors.Descriptor import org.ucfs.input.IInputGraph import org.ucfs.input.ILabel @@ -8649,8 +8648,8 @@ public class Java8ParserRecovery : } } - override fun parse(descriptor: Descriptor) { - super.parse(descriptor) + override fun handleDescriptor(descriptor: Descriptor) { + super.handleDescriptor(descriptor) org.ucfs.intersection.RecoveryIntersection.handleRecoveryEdges(this, descriptor) } diff --git a/benchmarks/src/test/kotlin/OfflineUcfsBenchmark.kt b/benchmarks/src/test/kotlin/OfflineUcfsBenchmark.kt index 4d1e232fd..298f0ec08 100644 --- a/benchmarks/src/test/kotlin/OfflineUcfsBenchmark.kt +++ b/benchmarks/src/test/kotlin/OfflineUcfsBenchmark.kt @@ -1,5 +1,4 @@ -import org.junit.jupiter.api.Disabled -import org.ucfs.input.LinearInputLabel +import org.ucfs.input.TerminalInputLabel import java.io.File fun main(args: Array) { @@ -19,7 +18,7 @@ class OfflineUcfsBenchmark : ParsingBenchmarks() { } override fun parse(text: String) { - val parser = org.ucfs.Java8Parser() + val parser = org.ucfs.Java8Parser() parser.setInput(getTokenStream(text)) parser.parse() assert(parser.parse().first != null) diff --git a/benchmarks/src/test/kotlin/RecoveryOfflineUcfsBenchmark.kt b/benchmarks/src/test/kotlin/RecoveryOfflineUcfsBenchmark.kt index ac48e0390..66399eec2 100644 --- a/benchmarks/src/test/kotlin/RecoveryOfflineUcfsBenchmark.kt +++ b/benchmarks/src/test/kotlin/RecoveryOfflineUcfsBenchmark.kt @@ -1,11 +1,11 @@ -import org.ucfs.input.LinearInputLabel +import org.ucfs.input.TerminalInputLabel import kotlin.test.Ignore @Ignore class RecoveryOfflineUcfsBenchmark : ParsingBenchmarks() { override fun getShortName(): String = "RecUcfsOff" override fun parse(text: String) { - val parser = org.ucfs.Java8ParserRecovery() + val parser = org.ucfs.Java8ParserRecovery() parser.setInput(getTokenStream(text)) assert(parser.parse().first!= null){"can't build sppf"} } diff --git a/benchmarks/src/test/kotlin/SimpleUcfsCorrect.kt b/benchmarks/src/test/kotlin/SimpleUcfsCorrect.kt index 7631c46d8..601b0cc01 100644 --- a/benchmarks/src/test/kotlin/SimpleUcfsCorrect.kt +++ b/benchmarks/src/test/kotlin/SimpleUcfsCorrect.kt @@ -1,7 +1,7 @@ import org.junit.jupiter.api.Test import org.ucfs.input.IInputGraph import org.ucfs.input.LinearInput -import org.ucfs.input.LinearInputLabel +import org.ucfs.input.TerminalInputLabel import org.ucfs.parser.Gll import org.ucfs.rsm.symbol.Term import org.ucfs.rsm.writeRsmToDot @@ -25,14 +25,14 @@ class SimpleUcfsCorrect { } - fun getTokenStream(input: List>): IInputGraph { - val inputGraph = LinearInput() + fun getTokenStream(input: List>): IInputGraph { + val inputGraph = LinearInput() var vertexId = 1 inputGraph.addVertex(vertexId) inputGraph.addStartVertex(vertexId) for (term in input) { - inputGraph.addEdge(vertexId, LinearInputLabel(term), ++vertexId) + inputGraph.addEdge(vertexId, TerminalInputLabel(term), ++vertexId) } return inputGraph diff --git a/generator/build.gradle.kts b/generator/build.gradle.kts index c32f710d7..133781def 100644 --- a/generator/build.gradle.kts +++ b/generator/build.gradle.kts @@ -9,6 +9,7 @@ repositories { dependencies { implementation(project(":solver")) implementation("com.squareup:kotlinpoet:1.16.0") + testImplementation(kotlin("test")) } tasks.test { diff --git a/generator/src/main/kotlin/org/ucfs/ast/AstExtractor.kt b/generator/src/main/kotlin/org/ucfs/ast/AstExtractor.kt index eb0e2c9f8..e69de29bb 100644 --- a/generator/src/main/kotlin/org/ucfs/ast/AstExtractor.kt +++ b/generator/src/main/kotlin/org/ucfs/ast/AstExtractor.kt @@ -1,76 +0,0 @@ -package org.ucfs.ast - -import org.ucfs.GeneratorException -import org.ucfs.rsm.symbol.Nonterminal -import org.ucfs.sppf.node.* - -class AstExtractor(val pkg: String) { - val nonterminalToClass = HashMap>() - - /** - * need to handle "many" in rules (many can make cycles in sppf) - */ - val used = HashSet>() - fun extract(sppf: ISppfNode?): Node { - val root = Node(null, 0) - extract(sppf, root, null) - return root.children.firstOrNull() ?: root - } - - private fun getOffset(left: Node?, parent: Node): Int { - return if (left == null) { - parent.offset - } else { - left.offset + left.length - } - } - - /** - * return rightest node of subtree - */ - private fun extract(sppf: ISppfNode?, parent: Node, left: Node?): Node? { - when (sppf) { - is PackedSppfNode<*> -> { - val newLeft = extract(sppf.leftSppfNode, parent, left) - return extract(sppf.rightSppfNode, parent, newLeft) - } - - is IntermediateSppfNode<*> -> { - return extract(sppf.children.firstOrNull(), parent, left) - } - - is SymbolSppfNode<*> -> { - val nodeClass = getNodeClass(sppf.symbol) - val ctor = nodeClass.getConstructor(Node::class.java, Int::class.java) - - val node: Node = ctor.newInstance(parent, getOffset(left, parent)) as Node - node.isRecovered = sppf.weight > 0 - node.left = left - parent.children.add(node) - - val packedNode: PackedSppfNode<*> = sppf.children.first { pn -> !used.contains(pn) } - used.add(packedNode) - - extract(packedNode, node, null) - parent.length += node.length - return node - } - - is TerminalSppfNode<*> -> { - val node = TerminalNode(parent, getOffset(left, parent), sppf.terminal, left) - node.isRecovered = sppf.weight > 0 - parent.children.add(node) - parent.length += sppf.terminal.toString().length - return node - } - - null -> return null - else -> throw GeneratorException("Unknown sppf node type : $sppf") - } - } - - private fun getNodeClass(nt: Nonterminal): Class<*> { - return nonterminalToClass.getOrPut(nt) - { Class.forName("$pkg.${NodeClassesGenerator.getClassName(nt)}") } - } -} \ No newline at end of file diff --git a/generator/src/main/kotlin/org/ucfs/examples/Examples.kt b/generator/src/main/kotlin/org/ucfs/examples/Examples.kt index 1ee1fc85a..e69de29bb 100644 --- a/generator/src/main/kotlin/org/ucfs/examples/Examples.kt +++ b/generator/src/main/kotlin/org/ucfs/examples/Examples.kt @@ -1,51 +0,0 @@ -package org.ucfs.examples - -import org.ucfs.ast.AstExtractor -import org.ucfs.ast.DotWriter -import org.ucfs.ast.NodeClassesGenerator -import org.ucfs.examples.dyck.DyckGrammar -import org.ucfs.examples.golang.SimpleGolang -import org.ucfs.grammar.combinator.Grammar -import org.ucfs.input.LinearInput -import org.ucfs.parser.Gll -import org.ucfs.rsm.writeRsmToDot -import org.ucfs.sppf.writeSppfToDot -import java.nio.file.Path - - -object Examples { - fun generateAst(grammar: Grammar, pkg: String, input: String, name: String) { - val grammarClass = grammar::class.java - NodeClassesGenerator(grammarClass).generate(Path.of("generator", "src", "main", "kotlin"), pkg) - val gll = Gll.gll(grammar.rsm, LinearInput.buildFromString(input)) - val sppf = gll.parse().first - writeSppfToDot(sppf!!, Path.of("${name}.dot").toString(), "${grammarClass.simpleName} SPPF for $input") - val ast = AstExtractor(pkg).extract(sppf) - val label = "${grammarClass.simpleName} AST for $input" - DotWriter().writeToFile( - ast, - name, - label, - false - ) - DotWriter().writeToFile( - ast, - "$name with siblings", - label, - true - ) - - } -} - - -fun main() { - writeRsmToDot(DyckGrammar().rsm, "rsm.dot") - Examples.generateAst(SimpleGolang(), "org.ucfs.examples.golang", "r 1 + 1 ;", "simple golang") - Examples.generateAst(SimpleGolang(), "org.ucfs.examples.golang", "r 1 + 1 ; 1 ; r 1 ;", "simple golang") - Examples.generateAst(DyckGrammar(), "org.ucfs.examples.dyck", "[ ( ) ] ", "1_dyck") - Examples.generateAst(DyckGrammar(), "org.ucfs.examples.dyck", "[ ( ) ] { }", "2_dyck") - Examples.generateAst(DyckGrammar(), "org.ucfs.examples.dyck", "[ ] { } [ ( ) ]", "3_dyck") - Examples.generateAst(DyckGrammar(), "org.ucfs.examples.dyck", " [ { } ( ) ] ", "3_dyck") - Examples.generateAst(DyckGrammar(), "org.ucfs.examples.dyck", "[ ] { { } ( ) } [ ( ) ]", "3_dyck") -} \ No newline at end of file diff --git a/generator/src/main/kotlin/org/ucfs/parser/AbstractParserGenerator.kt b/generator/src/main/kotlin/org/ucfs/parser/AbstractParserGenerator.kt index 8089e4d29..e69de29bb 100644 --- a/generator/src/main/kotlin/org/ucfs/parser/AbstractParserGenerator.kt +++ b/generator/src/main/kotlin/org/ucfs/parser/AbstractParserGenerator.kt @@ -1,300 +0,0 @@ -package org.ucfs.parser - -import com.squareup.kotlinpoet.* -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import org.ucfs.GeneratorException -import org.ucfs.IGeneratorFromGrammar -import org.ucfs.descriptors.Descriptor -import org.ucfs.grammar.combinator.Grammar -import org.ucfs.grammar.combinator.regexp.Nt -import org.ucfs.input.IInputGraph -import org.ucfs.input.ILabel -import org.ucfs.nullable -import org.ucfs.parser.context.Context -import org.ucfs.parser.context.IContext -import org.ucfs.rsm.RsmState -import org.ucfs.rsm.symbol.ITerminal -import org.ucfs.rsm.symbol.Nonterminal -import org.ucfs.sppf.node.SppfNode -import org.ucfs.suppressWarningTypes -import java.nio.file.Path -import java.util.stream.Collectors.toList - - -abstract class AbstractParserGenerator(final override val grammarClazz: Class<*>) : IGeneratorFromGrammar { - val grammar: Grammar = buildGrammar(grammarClazz) - - companion object { - /** - * Types - */ - val vertexType = TypeVariableName("VertexType") - val labelType = TypeVariableName("LabelType", ILabel::class.java) - val superClass = GeneratedParser::class.asTypeName().parameterizedBy(vertexType, labelType) - val descriptorType = Descriptor::class.asTypeName().parameterizedBy(vertexType) - val sppfType = SppfNode::class.asTypeName().parameterizedBy(vertexType).nullable() - - /** - * Variable identifiers - */ - const val PARSER = "Parser" - const val RECOVERY = "Recovery" - const val VALUE_NAME = "value" - const val CTX_NAME = "ctx" - const val GRAMMAR_NAME = "grammar" - const val DESCRIPTOR = "descriptor" - const val SPPF_NODE = "curSppfNode" - const val RSM_FIELD = "rsmState" - const val RSM_GRAMMAR_FIELD = "rsm" - const val POS_FIELD = "inputPosition" - const val INPUT_NAME = "input" - const val NONTERMINAL = "nonterm" - const val GET_TERMINALS = "getTerminals" - const val TERMINALS = "terminals" - const val HANDLE_TERMINAL = "handleTerminal" - const val STATE_NAME = "state" - const val ID_FIELD_NAME = "id" - const val POS_VAR_NAME = "pos" - const val INPUT_EDGE_NAME = "inputEdge" - const val MAIN_PARSE_FUNC = "parse" - - /** - * Common methods - */ - - fun getParseFunName(nonterminalName: String): String = "parse${nonterminalName}" - } - - /** - * Generate all parser properties and methods - */ - fun generate(location: Path, pkg: String) { - val file = getFileBuilder(pkg).build() - file.writeTo(location) - } - - open fun getParserClassName(): String { - return grammarClazz.simpleName + PARSER - } - - /** - * Build file builder - */ - protected open fun getFileBuilder(pkg: String): FileSpec.Builder { - val fileName = getParserClassName() - val parserClass = ClassName(pkg, fileName).parameterizedBy(vertexType, labelType) - - val parserClassBuilder = TypeSpec.classBuilder(parserClass.rawType.simpleName) - .addTypeVariable(vertexType) - .addTypeVariable(labelType) - .superclass(superClass) - .addProperties(generateProperties()) - .addNtMapping() - .addFunctions(generateMethods()) - - val fileBuilder = FileSpec - .builder(pkg, parserClass.rawType.simpleName) - .addType(parserClassBuilder.build()) - - // KotlinPoet set `public` modifier to class by default (wontFix) - // https://github.com/square/kotlinpoet/issues/1098 - fileBuilder.suppressWarningTypes("RedundantVisibilityModifier") - return fileBuilder - } - - fun TypeSpec.Builder.addNtMapping(): TypeSpec.Builder { - addFunction(generateCallNtFuncs()) - return this - } - - /** - * Add properties in Parser class - */ - open fun generateProperties(): Iterable { - return listOf( - // generateCtxProperty(), - generateGrammarProperty(grammarClazz), - ) + generateNonterminalsSpec() - } - - /** - * Generate overriding of ctx property - */ - private fun generateCtxProperty(): PropertySpec { - val ctxType = IContext::class.asTypeName().parameterizedBy(vertexType, labelType) - return PropertySpec.builder(CTX_NAME, ctxType, KModifier.LATEINIT, KModifier.OVERRIDE) - .mutable() - .build() - } - - /** - * Generate overriding of grammar property - * Anr it's initialization of corresponding @Grammar class - */ - private fun generateGrammarProperty(grammarClazz: Class<*>): PropertySpec { - return PropertySpec - .builder(GRAMMAR_NAME, grammarClazz) - .initializer( - CodeBlock.of("${grammarClazz.simpleName}()") - ) - .build() - } - - private fun generateCallNtFuncs(): FunSpec { - val funSpec = FunSpec.builder("callNtFuncs") - funSpec.addModifiers(KModifier.OVERRIDE) - .addParameter("nt", Nonterminal::class.asTypeName()) - .addParameter(DESCRIPTOR, descriptorType) - .addParameter(SPPF_NODE, sppfType) - .beginControlFlow("when(nt.name)", STATE_NAME, ID_FIELD_NAME) - for (nt in grammar.nonTerms) { - val ntName = nt.nonterm.name - ?: throw GeneratorException("Unnamed nonterminal in grammar ${grammarClazz.simpleName}") - funSpec.addStatement("%S -> %L($DESCRIPTOR, $SPPF_NODE)", ntName, getParseFunName(ntName)) - } - funSpec.endControlFlow() - return funSpec.build() - } - - private fun generateMethods(): Iterable { - return generateParseFunctions() + generateInputSetter() - } - - /** - * Generate Parse methods for all nonterminals - */ - protected open fun generateParseFunctions(): Iterable { - return grammar.nonTerms.map { generateParseFunction(it) } - } - - /** - * Generate Parse method for concrete nonterminal - */ - private fun generateParseFunction(nt: Nt): FunSpec { - val states = nt.nonterm.getStates() - val funSpec = FunSpec.builder(getParseFunName(nt.nonterm.name!!)) - funSpec.addModifiers(KModifier.PRIVATE) - .addParameter(DESCRIPTOR, descriptorType) - .addParameter(SPPF_NODE, sppfType) - .addStatement("val %L = %L.%L", STATE_NAME, DESCRIPTOR, RSM_FIELD) - - if (states.any { state -> state.terminalEdges.isNotEmpty() }) { - funSpec.addStatement("val %L = %L.%L", POS_VAR_NAME, DESCRIPTOR, POS_FIELD) - } - - funSpec.beginControlFlow("when(%L.%L)", STATE_NAME, "numId") - - for (state in states.sortedBy { it.numId }) { - generateParseForState(state, funSpec) - } - - funSpec.endControlFlow() - return funSpec.build() - } - - /** - * Generate code for concrete switch block by nonterminal RSM states - * (handle parsing for concrete state) - */ - fun generateParseForState(state: RsmState, funSpec: FunSpec.Builder) { - funSpec.addStatement("%L -> ", state.numId) - funSpec.beginControlFlow("") - generateTerminalParsing(state, funSpec) - generateNonterminalParsing(state, funSpec) - funSpec.endControlFlow() - } - - /** - * Generate and add to funSpec method that parse all terminals edge from current state - */ - private fun generateTerminalParsing(state: RsmState, funSpec: FunSpec.Builder) { - if (state.terminalEdges.isNotEmpty()) { - funSpec.addComment("handle terminal edges") - funSpec.beginControlFlow( - "for (%L in %L.%L.getEdges(%L))", - INPUT_EDGE_NAME, - CTX_NAME, - INPUT_NAME, - POS_VAR_NAME - ) - - funSpec.beginControlFlow("when(%L.label.terminal)", INPUT_EDGE_NAME) - for (term in state.terminalEdges.keys) { - val terminalName = getTerminalName(term) - funSpec.addStatement("%L -> ", terminalName) - funSpec.addStatement("%L(%L, %L, %L, %L, %L)", HANDLE_TERMINAL, terminalName, - STATE_NAME, INPUT_EDGE_NAME, DESCRIPTOR, SPPF_NODE) - } - funSpec.addStatement("else -> {}") - funSpec.endControlFlow() - - funSpec.endControlFlow() - } - } - - abstract fun getTerminalName(terminal: ITerminal): String - - /** - * Generate code for parsing all edges with Nonterminal label - * from given @RsmState state - */ - private fun generateNonterminalParsing(state: RsmState, funSpec: FunSpec.Builder) { - if (state.nonterminalEdges.isNotEmpty()) { - funSpec.addComment("handle nonterminal edges") - for (edge in state.nonterminalEdges) { - val ntName = edge.key.name!! - funSpec.addStatement( - "handleNonterminalEdge(%L, %L, state.nonterminalEdges[%L]!!, %L)", - DESCRIPTOR, - ntName, - ntName, - SPPF_NODE - ) - } - } - - } - - /** - * Generate definition and initialization for all Nonterminals - * as parser fields (with correspond nonterminal names) - */ - private fun generateNonterminalsSpec(): Iterable { - return grammar.nonTerms.stream().map { generateNonterminalSpec(it) }.collect(toList()) - } - - /** - * Generate definition and initialization for concrete Nonterminal - * as parser field (with correspond nonterminal name) - */ - private fun generateNonterminalSpec(nt: Nt): PropertySpec { - val ntName = nt.nonterm.name!! - val propertyBuilder = - PropertySpec.builder(ntName, Nonterminal::class.asTypeName()) - .addModifiers(KModifier.PRIVATE) - .initializer("%L.%L.%L", GRAMMAR_NAME, ntName, NONTERMINAL) - return propertyBuilder.build() - } - - protected open fun getContextType(): ParameterizedTypeName { - return Context::class.asTypeName().parameterizedBy(vertexType, labelType) - } - - private fun generateInputSetter(): FunSpec { - val ctxType = getContextType() - val inputType = IInputGraph::class.asTypeName().parameterizedBy(vertexType, labelType) - return FunSpec.builder("setInput") - .addModifiers(KModifier.OVERRIDE) - .addParameter(VALUE_NAME, inputType) - .addStatement( - "%L = %L(%L.%L, %L)", - CTX_NAME, - ctxType.rawType, - GRAMMAR_NAME, - RSM_GRAMMAR_FIELD, - VALUE_NAME - ) - .build() - } -} - diff --git a/generator/src/main/kotlin/org/ucfs/parser/GeneratedParser.kt b/generator/src/main/kotlin/org/ucfs/parser/GeneratedParser.kt index 8fd160aba..e69de29bb 100644 --- a/generator/src/main/kotlin/org/ucfs/parser/GeneratedParser.kt +++ b/generator/src/main/kotlin/org/ucfs/parser/GeneratedParser.kt @@ -1,78 +0,0 @@ -package org.ucfs.parser - -import org.ucfs.descriptors.Descriptor -import org.ucfs.input.Edge -import org.ucfs.input.IInputGraph -import org.ucfs.input.ILabel -import org.ucfs.parser.context.IContext -import org.ucfs.rsm.RsmState -import org.ucfs.rsm.symbol.ITerminal -import org.ucfs.rsm.symbol.Nonterminal -import org.ucfs.sppf.node.SppfNode - -/** - * If overriding field uses -- 1.2 % longer parser operation (due to hashset initialisation) - */ -abstract class GeneratedParser : - IGll { - override lateinit var ctx: IContext - - /** - * Processes faster than map from nonterminal to method (proved experimentally) - */ - protected abstract fun callNtFuncs( - nt: Nonterminal, - descriptor: Descriptor, - curSppfNode: SppfNode? - ): Unit - - override fun parse(descriptor: Descriptor) { - val state = descriptor.rsmState - val nt = state.nonterminal - - val pos = descriptor.inputPosition - val curSppfNode = descriptor.sppfNode - val epsilonSppfNode = ctx.sppf.getEpsilonSppfNode(descriptor) - val leftExtent = curSppfNode?.leftExtent - val rightExtent = curSppfNode?.rightExtent - - if (state.isFinal) { - pop(descriptor.gssNode, curSppfNode ?: epsilonSppfNode, pos) - } - - ctx.descriptors.addToHandled(descriptor) - - if (state.isStart && state.isFinal) { - checkAcceptance( - epsilonSppfNode, - epsilonSppfNode!!.leftExtent, - epsilonSppfNode!!.rightExtent, - nt - ) - } - checkAcceptance(curSppfNode, leftExtent, rightExtent, nt) - - callNtFuncs(nt, descriptor, curSppfNode) - } - - protected fun handleTerminal( - terminal: ITerminal, - state: RsmState, - inputEdge: Edge, - descriptor: Descriptor, - curSppfNode: SppfNode? - ) { - for (target in state.terminalEdges[terminal] ?: emptyList()) { - handleTerminalOrEpsilonEdge( - descriptor, - curSppfNode, - terminal, - target, - inputEdge.head, - 0 - ) - } - } - - abstract fun setInput(input: IInputGraph) -} \ No newline at end of file diff --git a/generator/src/main/kotlin/org/ucfs/parser/ParserGenerator.kt b/generator/src/main/kotlin/org/ucfs/parser/ParserGenerator.kt index adda63a87..3086c390b 100644 --- a/generator/src/main/kotlin/org/ucfs/parser/ParserGenerator.kt +++ b/generator/src/main/kotlin/org/ucfs/parser/ParserGenerator.kt @@ -7,18 +7,4 @@ import org.ucfs.rsm.symbol.ITerminal * Generator for a parser that uses a third-party lexer. * Unlike the scannerless parser , it uses scanner enumeration objects as terminals. */ -open class ParserGenerator(grammarClazz: Class<*>, private val terminalsEnum: Class<*>) : - AbstractParserGenerator(grammarClazz) { - - - override fun getTerminalName(terminal: ITerminal): String { - return "${terminalsEnum.simpleName}.$terminal" - } - - override fun getFileBuilder(pkg: String): FileSpec.Builder { - val builder = super.getFileBuilder(pkg) - builder.addImport(terminalsEnum.packageName, terminalsEnum.simpleName) - return builder - } - -} +open class ParserGenerator(grammarClazz: Class<*>, private val terminalsEnum: Class<*>) diff --git a/generator/src/main/kotlin/org/ucfs/parser/RecoveryParserGenerator.kt b/generator/src/main/kotlin/org/ucfs/parser/RecoveryParserGenerator.kt index 7c1bb5ac7..e69de29bb 100644 --- a/generator/src/main/kotlin/org/ucfs/parser/RecoveryParserGenerator.kt +++ b/generator/src/main/kotlin/org/ucfs/parser/RecoveryParserGenerator.kt @@ -1,36 +0,0 @@ -package org.ucfs.parser - -import com.squareup.kotlinpoet.* -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import org.ucfs.intersection.RecoveryIntersection -import org.ucfs.parser.context.RecoveryContext - -/** - * Generator for a parser with error-recovery that uses a third-party lexer. - */ -class RecoveryParserGenerator(grammarClazz: Class<*>, terminalsEnum: Class<*>) : - ParserGenerator(grammarClazz, terminalsEnum) { - companion object { - val recoveryEngineType = RecoveryIntersection::class.java.asTypeName() - const val RECOVERY_METHOD_NAME = "handleRecoveryEdges" - } - - override fun getContextType(): ParameterizedTypeName { - return RecoveryContext::class.asTypeName().parameterizedBy(vertexType, labelType) - } - - override fun generateParseFunctions(): Iterable { - return super.generateParseFunctions() + generateMainLoopFunction() - } - - private fun generateMainLoopFunction(): FunSpec { - return FunSpec.builder(MAIN_PARSE_FUNC).addModifiers(KModifier.OVERRIDE).addParameter( - DESCRIPTOR, descriptorType - ).addStatement("super.%L(%L)", MAIN_PARSE_FUNC, DESCRIPTOR) - .addStatement("%L.%L(this, %L)", recoveryEngineType, RECOVERY_METHOD_NAME, DESCRIPTOR).build() - } - override fun getParserClassName(): String { - return super.getParserClassName() + RECOVERY - } - -} diff --git a/generator/src/main/kotlin/org/ucfs/parser/ScanerlessParserGenerator.kt b/generator/src/main/kotlin/org/ucfs/parser/ScanerlessParserGenerator.kt index 7db03b47d..e69de29bb 100644 --- a/generator/src/main/kotlin/org/ucfs/parser/ScanerlessParserGenerator.kt +++ b/generator/src/main/kotlin/org/ucfs/parser/ScanerlessParserGenerator.kt @@ -1,44 +0,0 @@ -package org.ucfs.parser - -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import com.squareup.kotlinpoet.PropertySpec -import com.squareup.kotlinpoet.asTypeName -import org.ucfs.rsm.symbol.ITerminal - -/** - * Scanerless parser generator - * Store @Grammar terminals as list of @Terminal<*> type - */ -class ScanerlessParserGenerator(grammarClazz: Class<*>) : AbstractParserGenerator(grammarClazz) { - private val terminals: List = grammar.getTerminals().toList() - - override fun generateProperties(): Iterable { - return super.generateProperties() + generateTerminalsSpec() - } - - override fun getTerminalName(terminal: ITerminal): String { - return "$TERMINALS[${terminals.indexOf(terminal)}]" - } - - - /** - * Generate definition and initialization for Terminals as - * filed in parser - */ - private fun generateTerminalsSpec(): PropertySpec { - val termListType = List::class.asTypeName().parameterizedBy( - ITerminal::class.asTypeName() - ) - val propertyBuilder = - PropertySpec.builder(TERMINALS, termListType).addModifiers(KModifier.PRIVATE) - .initializer( - "%L.%L().%L()", - GRAMMAR_NAME, - GET_TERMINALS, - "toList" - ) - return propertyBuilder.build() - } - -} \ No newline at end of file diff --git a/solver/build.gradle.kts b/solver/build.gradle.kts index 99d7b0952..ef4c2afc6 100644 --- a/solver/build.gradle.kts +++ b/solver/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { testImplementation(kotlin("test")) testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + implementation("org.antlr:antlr4:4.13.1") } kotlin { jvmToolchain(11) } diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/Dot.g4 b/solver/src/main/java/org/ucfs/input/utils/dot/Dot.g4 new file mode 100644 index 000000000..a80f1901d --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/Dot.g4 @@ -0,0 +1,180 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/** Derived from http://www.graphviz.org/doc/info/lang.html. + Comments pulled from spec. + */ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +grammar Dot; + +graph + : STRICT? (GRAPH | DIGRAPH) id_? '{' stmt_list '}' EOF + ; + +stmt_list + : (stmt ';'?)* + ; + +stmt + : node_stmt + | edge_stmt + | attr_stmt + | id_ '=' id_ + | subgraph + ; + +attr_stmt + : (GRAPH | NODE | EDGE) attr_list + ; + +attr_list + : ('[' attr* ']')+ + ; + +attr + : label_name=id_ ( '=' label_value=id_)? (';' | ',')? + ; + +edge_stmt + : (node_id | subgraph) edgeRHS attr_list? + ; + +edgeRHS + : (edgeop ( node_id | subgraph))+ + ; + +edgeop + : '->' + | '--' + ; + +node_stmt + : node_id attr_list? + ; + +node_id + : id_ port? + ; + +port + : ':' id_ (':' id_)? + ; + +subgraph + : (SUBGRAPH id_?)? '{' stmt_list '}' + ; + +id_ + : ID + | STRING + | HTML_STRING + | NUMBER + ; + +// "The keywords node, edge, graph, digraph, subgraph, and strict are +// case-independent" +STRICT + : [Ss] [Tt] [Rr] [Ii] [Cc] [Tt] + ; + +GRAPH + : [Gg] [Rr] [Aa] [Pp] [Hh] + ; + +DIGRAPH + : [Dd] [Ii] [Gg] [Rr] [Aa] [Pp] [Hh] + ; + +NODE + : [Nn] [Oo] [Dd] [Ee] + ; + +EDGE + : [Ee] [Dd] [Gg] [Ee] + ; + +SUBGRAPH + : [Ss] [Uu] [Bb] [Gg] [Rr] [Aa] [Pp] [Hh] + ; + +/** "a numeral [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? )" */ +NUMBER + : '-'? ('.' DIGIT+ | DIGIT+ ( '.' DIGIT*)?) + ; + +fragment DIGIT + : [0-9] + ; + +/** "any double-quoted string ("...") possibly containing escaped quotes" */ +STRING + : '"' ('\\"' | .)*? '"' + ; + +/** "Any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores + * ('_') or digits ([0-9]), not beginning with a digit" + */ +ID + : LETTER (LETTER | DIGIT)* + ; + +fragment LETTER + : [a-zA-Z\u0080-\u00FF_] + ; + +/** "HTML strings, angle brackets must occur in matched pairs, and + * unescaped newlines are allowed." + */ +HTML_STRING + : '<' (TAG | ~ [<>])* '>' + ; + +fragment TAG + : '<' .*? '>' + ; + +COMMENT + : '/*' .*? '*/' -> skip + ; + +LINE_COMMENT + : '//' .*? '\r'? '\n' -> skip + ; + +/** "a '#' character is considered a line output from a C preprocessor (e.g., + * # 34 to indicate line 34 ) and discarded" + */ +PREPROC + : '#' ~[\r\n]* -> skip + ; + +WS + : [ \t\n\r]+ -> skip + ; \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/Dot.interp b/solver/src/main/java/org/ucfs/input/utils/dot/Dot.interp new file mode 100644 index 000000000..8b74aeb02 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/Dot.interp @@ -0,0 +1,73 @@ +token literal names: +null +'{' +'}' +';' +'=' +'[' +']' +',' +'->' +'--' +':' +null +null +null +null +null +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +STRICT +GRAPH +DIGRAPH +NODE +EDGE +SUBGRAPH +NUMBER +STRING +ID +HTML_STRING +COMMENT +LINE_COMMENT +PREPROC +WS + +rule names: +graph +stmt_list +stmt +attr_stmt +attr_list +attr +edge_stmt +edgeRHS +edgeop +node_stmt +node_id +port +subgraph +id_ + + +atn: +[4, 1, 24, 128, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 1, 0, 3, 0, 30, 8, 0, 1, 0, 1, 0, 3, 0, 34, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 43, 8, 1, 5, 1, 45, 8, 1, 10, 1, 12, 1, 48, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 58, 8, 2, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 5, 4, 65, 8, 4, 10, 4, 12, 4, 68, 9, 4, 1, 4, 4, 4, 71, 8, 4, 11, 4, 12, 4, 72, 1, 5, 1, 5, 1, 5, 3, 5, 78, 8, 5, 1, 5, 3, 5, 81, 8, 5, 1, 6, 1, 6, 3, 6, 85, 8, 6, 1, 6, 1, 6, 3, 6, 89, 8, 6, 1, 7, 1, 7, 1, 7, 3, 7, 94, 8, 7, 4, 7, 96, 8, 7, 11, 7, 12, 7, 97, 1, 8, 1, 8, 1, 9, 1, 9, 3, 9, 104, 8, 9, 1, 10, 1, 10, 3, 10, 108, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 114, 8, 11, 1, 12, 1, 12, 3, 12, 118, 8, 12, 3, 12, 120, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 0, 0, 14, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 0, 5, 1, 0, 12, 13, 2, 0, 12, 12, 14, 15, 2, 0, 3, 3, 7, 7, 1, 0, 8, 9, 1, 0, 17, 20, 134, 0, 29, 1, 0, 0, 0, 2, 46, 1, 0, 0, 0, 4, 57, 1, 0, 0, 0, 6, 59, 1, 0, 0, 0, 8, 70, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 84, 1, 0, 0, 0, 14, 95, 1, 0, 0, 0, 16, 99, 1, 0, 0, 0, 18, 101, 1, 0, 0, 0, 20, 105, 1, 0, 0, 0, 22, 109, 1, 0, 0, 0, 24, 119, 1, 0, 0, 0, 26, 125, 1, 0, 0, 0, 28, 30, 5, 11, 0, 0, 29, 28, 1, 0, 0, 0, 29, 30, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 33, 7, 0, 0, 0, 32, 34, 3, 26, 13, 0, 33, 32, 1, 0, 0, 0, 33, 34, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 36, 5, 1, 0, 0, 36, 37, 3, 2, 1, 0, 37, 38, 5, 2, 0, 0, 38, 39, 5, 0, 0, 1, 39, 1, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, 41, 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 45, 1, 0, 0, 0, 44, 40, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 3, 1, 0, 0, 0, 48, 46, 1, 0, 0, 0, 49, 58, 3, 18, 9, 0, 50, 58, 3, 12, 6, 0, 51, 58, 3, 6, 3, 0, 52, 53, 3, 26, 13, 0, 53, 54, 5, 4, 0, 0, 54, 55, 3, 26, 13, 0, 55, 58, 1, 0, 0, 0, 56, 58, 3, 24, 12, 0, 57, 49, 1, 0, 0, 0, 57, 50, 1, 0, 0, 0, 57, 51, 1, 0, 0, 0, 57, 52, 1, 0, 0, 0, 57, 56, 1, 0, 0, 0, 58, 5, 1, 0, 0, 0, 59, 60, 7, 1, 0, 0, 60, 61, 3, 8, 4, 0, 61, 7, 1, 0, 0, 0, 62, 66, 5, 5, 0, 0, 63, 65, 3, 10, 5, 0, 64, 63, 1, 0, 0, 0, 65, 68, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 66, 67, 1, 0, 0, 0, 67, 69, 1, 0, 0, 0, 68, 66, 1, 0, 0, 0, 69, 71, 5, 6, 0, 0, 70, 62, 1, 0, 0, 0, 71, 72, 1, 0, 0, 0, 72, 70, 1, 0, 0, 0, 72, 73, 1, 0, 0, 0, 73, 9, 1, 0, 0, 0, 74, 77, 3, 26, 13, 0, 75, 76, 5, 4, 0, 0, 76, 78, 3, 26, 13, 0, 77, 75, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 80, 1, 0, 0, 0, 79, 81, 7, 2, 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 11, 1, 0, 0, 0, 82, 85, 3, 20, 10, 0, 83, 85, 3, 24, 12, 0, 84, 82, 1, 0, 0, 0, 84, 83, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 88, 3, 14, 7, 0, 87, 89, 3, 8, 4, 0, 88, 87, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 13, 1, 0, 0, 0, 90, 93, 3, 16, 8, 0, 91, 94, 3, 20, 10, 0, 92, 94, 3, 24, 12, 0, 93, 91, 1, 0, 0, 0, 93, 92, 1, 0, 0, 0, 94, 96, 1, 0, 0, 0, 95, 90, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 15, 1, 0, 0, 0, 99, 100, 7, 3, 0, 0, 100, 17, 1, 0, 0, 0, 101, 103, 3, 20, 10, 0, 102, 104, 3, 8, 4, 0, 103, 102, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 19, 1, 0, 0, 0, 105, 107, 3, 26, 13, 0, 106, 108, 3, 22, 11, 0, 107, 106, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 21, 1, 0, 0, 0, 109, 110, 5, 10, 0, 0, 110, 113, 3, 26, 13, 0, 111, 112, 5, 10, 0, 0, 112, 114, 3, 26, 13, 0, 113, 111, 1, 0, 0, 0, 113, 114, 1, 0, 0, 0, 114, 23, 1, 0, 0, 0, 115, 117, 5, 16, 0, 0, 116, 118, 3, 26, 13, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 120, 1, 0, 0, 0, 119, 115, 1, 0, 0, 0, 119, 120, 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 122, 5, 1, 0, 0, 122, 123, 3, 2, 1, 0, 123, 124, 5, 2, 0, 0, 124, 25, 1, 0, 0, 0, 125, 126, 7, 4, 0, 0, 126, 27, 1, 0, 0, 0, 18, 29, 33, 42, 46, 57, 66, 72, 77, 80, 84, 88, 93, 97, 103, 107, 113, 117, 119] \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/Dot.tokens b/solver/src/main/java/org/ucfs/input/utils/dot/Dot.tokens new file mode 100644 index 000000000..9c46a081b --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/Dot.tokens @@ -0,0 +1,34 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +STRICT=11 +GRAPH=12 +DIGRAPH=13 +NODE=14 +EDGE=15 +SUBGRAPH=16 +NUMBER=17 +STRING=18 +ID=19 +HTML_STRING=20 +COMMENT=21 +LINE_COMMENT=22 +PREPROC=23 +WS=24 +'{'=1 +'}'=2 +';'=3 +'='=4 +'['=5 +']'=6 +','=7 +'->'=8 +'--'=9 +':'=10 diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotBaseListener.java b/solver/src/main/java/org/ucfs/input/utils/dot/DotBaseListener.java new file mode 100644 index 000000000..e6449bf91 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotBaseListener.java @@ -0,0 +1,208 @@ +// Generated from Dot.g4 by ANTLR 4.13.2 +package org.ucfs.input.utils.dot; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link DotListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +@SuppressWarnings("CheckReturnValue") +public class DotBaseListener implements DotListener { + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterGraph(DotParser.GraphContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitGraph(DotParser.GraphContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmt_list(DotParser.Stmt_listContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmt_list(DotParser.Stmt_listContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterStmt(DotParser.StmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitStmt(DotParser.StmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterAttr_stmt(DotParser.Attr_stmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitAttr_stmt(DotParser.Attr_stmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterAttr_list(DotParser.Attr_listContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitAttr_list(DotParser.Attr_listContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterAttr(DotParser.AttrContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitAttr(DotParser.AttrContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEdge_stmt(DotParser.Edge_stmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEdge_stmt(DotParser.Edge_stmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEdgeRHS(DotParser.EdgeRHSContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEdgeRHS(DotParser.EdgeRHSContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEdgeop(DotParser.EdgeopContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEdgeop(DotParser.EdgeopContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNode_stmt(DotParser.Node_stmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNode_stmt(DotParser.Node_stmtContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterNode_id(DotParser.Node_idContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitNode_id(DotParser.Node_idContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterPort(DotParser.PortContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitPort(DotParser.PortContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSubgraph(DotParser.SubgraphContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSubgraph(DotParser.SubgraphContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterId_(DotParser.Id_Context ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitId_(DotParser.Id_Context ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitTerminal(TerminalNode node) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitErrorNode(ErrorNode node) { } +} \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotBaseVisitor.java b/solver/src/main/java/org/ucfs/input/utils/dot/DotBaseVisitor.java new file mode 100644 index 000000000..ec7d74789 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotBaseVisitor.java @@ -0,0 +1,113 @@ +// Generated from Dot.g4 by ANTLR 4.13.2 +package org.ucfs.input.utils.dot; +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link DotVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +@SuppressWarnings("CheckReturnValue") +public class DotBaseVisitor extends AbstractParseTreeVisitor implements DotVisitor { + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitGraph(DotParser.GraphContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmt_list(DotParser.Stmt_listContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitStmt(DotParser.StmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitAttr_stmt(DotParser.Attr_stmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitAttr_list(DotParser.Attr_listContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitAttr(DotParser.AttrContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitEdge_stmt(DotParser.Edge_stmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitEdgeRHS(DotParser.EdgeRHSContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitEdgeop(DotParser.EdgeopContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNode_stmt(DotParser.Node_stmtContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitNode_id(DotParser.Node_idContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitPort(DotParser.PortContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitSubgraph(DotParser.SubgraphContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitId_(DotParser.Id_Context ctx) { return visitChildren(ctx); } +} \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.interp b/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.interp new file mode 100644 index 000000000..3cde96987 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.interp @@ -0,0 +1,92 @@ +token literal names: +null +'{' +'}' +';' +'=' +'[' +']' +',' +'->' +'--' +':' +null +null +null +null +null +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +STRICT +GRAPH +DIGRAPH +NODE +EDGE +SUBGRAPH +NUMBER +STRING +ID +HTML_STRING +COMMENT +LINE_COMMENT +PREPROC +WS + +rule names: +T__0 +T__1 +T__2 +T__3 +T__4 +T__5 +T__6 +T__7 +T__8 +T__9 +STRICT +GRAPH +DIGRAPH +NODE +EDGE +SUBGRAPH +NUMBER +DIGIT +STRING +ID +LETTER +HTML_STRING +TAG +COMMENT +LINE_COMMENT +PREPROC +WS + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 24, 230, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 3, 16, 119, 8, 16, 1, 16, 1, 16, 4, 16, 123, 8, 16, 11, 16, 12, 16, 124, 1, 16, 4, 16, 128, 8, 16, 11, 16, 12, 16, 129, 1, 16, 1, 16, 5, 16, 134, 8, 16, 10, 16, 12, 16, 137, 9, 16, 3, 16, 139, 8, 16, 3, 16, 141, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 5, 18, 149, 8, 18, 10, 18, 12, 18, 152, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 5, 19, 159, 8, 19, 10, 19, 12, 19, 162, 9, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 5, 21, 169, 8, 21, 10, 21, 12, 21, 172, 9, 21, 1, 21, 1, 21, 1, 22, 1, 22, 5, 22, 178, 8, 22, 10, 22, 12, 22, 181, 9, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 189, 8, 23, 10, 23, 12, 23, 192, 9, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 203, 8, 24, 10, 24, 12, 24, 206, 9, 24, 1, 24, 3, 24, 209, 8, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 5, 25, 217, 8, 25, 10, 25, 12, 25, 220, 9, 25, 1, 25, 1, 25, 1, 26, 4, 26, 225, 8, 26, 11, 26, 12, 26, 226, 1, 26, 1, 26, 4, 150, 179, 190, 204, 0, 27, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 0, 37, 18, 39, 19, 41, 0, 43, 20, 45, 0, 47, 21, 49, 22, 51, 23, 53, 24, 1, 0, 20, 2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 73, 73, 105, 105, 2, 0, 67, 67, 99, 99, 2, 0, 71, 71, 103, 103, 2, 0, 65, 65, 97, 97, 2, 0, 80, 80, 112, 112, 2, 0, 72, 72, 104, 104, 2, 0, 68, 68, 100, 100, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 69, 69, 101, 101, 2, 0, 85, 85, 117, 117, 2, 0, 66, 66, 98, 98, 1, 0, 48, 57, 4, 0, 65, 90, 95, 95, 97, 122, 128, 255, 2, 0, 60, 60, 62, 62, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 244, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 1, 55, 1, 0, 0, 0, 3, 57, 1, 0, 0, 0, 5, 59, 1, 0, 0, 0, 7, 61, 1, 0, 0, 0, 9, 63, 1, 0, 0, 0, 11, 65, 1, 0, 0, 0, 13, 67, 1, 0, 0, 0, 15, 69, 1, 0, 0, 0, 17, 72, 1, 0, 0, 0, 19, 75, 1, 0, 0, 0, 21, 77, 1, 0, 0, 0, 23, 84, 1, 0, 0, 0, 25, 90, 1, 0, 0, 0, 27, 98, 1, 0, 0, 0, 29, 103, 1, 0, 0, 0, 31, 108, 1, 0, 0, 0, 33, 118, 1, 0, 0, 0, 35, 142, 1, 0, 0, 0, 37, 144, 1, 0, 0, 0, 39, 155, 1, 0, 0, 0, 41, 163, 1, 0, 0, 0, 43, 165, 1, 0, 0, 0, 45, 175, 1, 0, 0, 0, 47, 184, 1, 0, 0, 0, 49, 198, 1, 0, 0, 0, 51, 214, 1, 0, 0, 0, 53, 224, 1, 0, 0, 0, 55, 56, 5, 123, 0, 0, 56, 2, 1, 0, 0, 0, 57, 58, 5, 125, 0, 0, 58, 4, 1, 0, 0, 0, 59, 60, 5, 59, 0, 0, 60, 6, 1, 0, 0, 0, 61, 62, 5, 61, 0, 0, 62, 8, 1, 0, 0, 0, 63, 64, 5, 91, 0, 0, 64, 10, 1, 0, 0, 0, 65, 66, 5, 93, 0, 0, 66, 12, 1, 0, 0, 0, 67, 68, 5, 44, 0, 0, 68, 14, 1, 0, 0, 0, 69, 70, 5, 45, 0, 0, 70, 71, 5, 62, 0, 0, 71, 16, 1, 0, 0, 0, 72, 73, 5, 45, 0, 0, 73, 74, 5, 45, 0, 0, 74, 18, 1, 0, 0, 0, 75, 76, 5, 58, 0, 0, 76, 20, 1, 0, 0, 0, 77, 78, 7, 0, 0, 0, 78, 79, 7, 1, 0, 0, 79, 80, 7, 2, 0, 0, 80, 81, 7, 3, 0, 0, 81, 82, 7, 4, 0, 0, 82, 83, 7, 1, 0, 0, 83, 22, 1, 0, 0, 0, 84, 85, 7, 5, 0, 0, 85, 86, 7, 2, 0, 0, 86, 87, 7, 6, 0, 0, 87, 88, 7, 7, 0, 0, 88, 89, 7, 8, 0, 0, 89, 24, 1, 0, 0, 0, 90, 91, 7, 9, 0, 0, 91, 92, 7, 3, 0, 0, 92, 93, 7, 5, 0, 0, 93, 94, 7, 2, 0, 0, 94, 95, 7, 6, 0, 0, 95, 96, 7, 7, 0, 0, 96, 97, 7, 8, 0, 0, 97, 26, 1, 0, 0, 0, 98, 99, 7, 10, 0, 0, 99, 100, 7, 11, 0, 0, 100, 101, 7, 9, 0, 0, 101, 102, 7, 12, 0, 0, 102, 28, 1, 0, 0, 0, 103, 104, 7, 12, 0, 0, 104, 105, 7, 9, 0, 0, 105, 106, 7, 5, 0, 0, 106, 107, 7, 12, 0, 0, 107, 30, 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110, 7, 13, 0, 0, 110, 111, 7, 14, 0, 0, 111, 112, 7, 5, 0, 0, 112, 113, 7, 2, 0, 0, 113, 114, 7, 6, 0, 0, 114, 115, 7, 7, 0, 0, 115, 116, 7, 8, 0, 0, 116, 32, 1, 0, 0, 0, 117, 119, 5, 45, 0, 0, 118, 117, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 140, 1, 0, 0, 0, 120, 122, 5, 46, 0, 0, 121, 123, 3, 35, 17, 0, 122, 121, 1, 0, 0, 0, 123, 124, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 124, 125, 1, 0, 0, 0, 125, 141, 1, 0, 0, 0, 126, 128, 3, 35, 17, 0, 127, 126, 1, 0, 0, 0, 128, 129, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 138, 1, 0, 0, 0, 131, 135, 5, 46, 0, 0, 132, 134, 3, 35, 17, 0, 133, 132, 1, 0, 0, 0, 134, 137, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 139, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 138, 131, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 141, 1, 0, 0, 0, 140, 120, 1, 0, 0, 0, 140, 127, 1, 0, 0, 0, 141, 34, 1, 0, 0, 0, 142, 143, 7, 15, 0, 0, 143, 36, 1, 0, 0, 0, 144, 150, 5, 34, 0, 0, 145, 146, 5, 92, 0, 0, 146, 149, 5, 34, 0, 0, 147, 149, 9, 0, 0, 0, 148, 145, 1, 0, 0, 0, 148, 147, 1, 0, 0, 0, 149, 152, 1, 0, 0, 0, 150, 151, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 151, 153, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 153, 154, 5, 34, 0, 0, 154, 38, 1, 0, 0, 0, 155, 160, 3, 41, 20, 0, 156, 159, 3, 41, 20, 0, 157, 159, 3, 35, 17, 0, 158, 156, 1, 0, 0, 0, 158, 157, 1, 0, 0, 0, 159, 162, 1, 0, 0, 0, 160, 158, 1, 0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 40, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 163, 164, 7, 16, 0, 0, 164, 42, 1, 0, 0, 0, 165, 170, 5, 60, 0, 0, 166, 169, 3, 45, 22, 0, 167, 169, 8, 17, 0, 0, 168, 166, 1, 0, 0, 0, 168, 167, 1, 0, 0, 0, 169, 172, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 170, 171, 1, 0, 0, 0, 171, 173, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 174, 5, 62, 0, 0, 174, 44, 1, 0, 0, 0, 175, 179, 5, 60, 0, 0, 176, 178, 9, 0, 0, 0, 177, 176, 1, 0, 0, 0, 178, 181, 1, 0, 0, 0, 179, 180, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 180, 182, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 182, 183, 5, 62, 0, 0, 183, 46, 1, 0, 0, 0, 184, 185, 5, 47, 0, 0, 185, 186, 5, 42, 0, 0, 186, 190, 1, 0, 0, 0, 187, 189, 9, 0, 0, 0, 188, 187, 1, 0, 0, 0, 189, 192, 1, 0, 0, 0, 190, 191, 1, 0, 0, 0, 190, 188, 1, 0, 0, 0, 191, 193, 1, 0, 0, 0, 192, 190, 1, 0, 0, 0, 193, 194, 5, 42, 0, 0, 194, 195, 5, 47, 0, 0, 195, 196, 1, 0, 0, 0, 196, 197, 6, 23, 0, 0, 197, 48, 1, 0, 0, 0, 198, 199, 5, 47, 0, 0, 199, 200, 5, 47, 0, 0, 200, 204, 1, 0, 0, 0, 201, 203, 9, 0, 0, 0, 202, 201, 1, 0, 0, 0, 203, 206, 1, 0, 0, 0, 204, 205, 1, 0, 0, 0, 204, 202, 1, 0, 0, 0, 205, 208, 1, 0, 0, 0, 206, 204, 1, 0, 0, 0, 207, 209, 5, 13, 0, 0, 208, 207, 1, 0, 0, 0, 208, 209, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 211, 5, 10, 0, 0, 211, 212, 1, 0, 0, 0, 212, 213, 6, 24, 0, 0, 213, 50, 1, 0, 0, 0, 214, 218, 5, 35, 0, 0, 215, 217, 8, 18, 0, 0, 216, 215, 1, 0, 0, 0, 217, 220, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 218, 219, 1, 0, 0, 0, 219, 221, 1, 0, 0, 0, 220, 218, 1, 0, 0, 0, 221, 222, 6, 25, 0, 0, 222, 52, 1, 0, 0, 0, 223, 225, 7, 19, 0, 0, 224, 223, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 224, 1, 0, 0, 0, 226, 227, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228, 229, 6, 26, 0, 0, 229, 54, 1, 0, 0, 0, 19, 0, 118, 124, 129, 135, 138, 140, 148, 150, 158, 160, 168, 170, 179, 190, 204, 208, 218, 226, 1, 6, 0, 0] \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.java b/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.java new file mode 100644 index 000000000..ba3b9808f --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.java @@ -0,0 +1,276 @@ +// Generated from Dot.g4 by ANTLR 4.13.2 +package org.ucfs.input.utils.dot; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class DotLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9, + T__9=10, STRICT=11, GRAPH=12, DIGRAPH=13, NODE=14, EDGE=15, SUBGRAPH=16, + NUMBER=17, STRING=18, ID=19, HTML_STRING=20, COMMENT=21, LINE_COMMENT=22, + PREPROC=23, WS=24; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[] { + "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", + "T__9", "STRICT", "GRAPH", "DIGRAPH", "NODE", "EDGE", "SUBGRAPH", "NUMBER", + "DIGIT", "STRING", "ID", "LETTER", "HTML_STRING", "TAG", "COMMENT", "LINE_COMMENT", + "PREPROC", "WS" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, "'{'", "'}'", "';'", "'='", "'['", "']'", "','", "'->'", "'--'", + "':'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, "STRICT", + "GRAPH", "DIGRAPH", "NODE", "EDGE", "SUBGRAPH", "NUMBER", "STRING", "ID", + "HTML_STRING", "COMMENT", "LINE_COMMENT", "PREPROC", "WS" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public DotLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "Dot.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getChannelNames() { return channelNames; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\u0004\u0000\u0018\u00e6\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002"+ + "\u0001\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002"+ + "\u0004\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002"+ + "\u0007\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002"+ + "\u000b\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e"+ + "\u0002\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011"+ + "\u0002\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014"+ + "\u0002\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017"+ + "\u0002\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a"+ + "\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002"+ + "\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005"+ + "\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001\b\u0001"+ + "\b\u0001\b\u0001\t\u0001\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0001"+ + "\n\u0001\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b"+ + "\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\f\u0001\f\u0001\f\u0001\f\u0001"+ + "\f\u0001\r\u0001\r\u0001\r\u0001\r\u0001\r\u0001\u000e\u0001\u000e\u0001"+ + "\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001\u000f\u0001\u000f\u0001"+ + "\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001"+ + "\u0010\u0003\u0010w\b\u0010\u0001\u0010\u0001\u0010\u0004\u0010{\b\u0010"+ + "\u000b\u0010\f\u0010|\u0001\u0010\u0004\u0010\u0080\b\u0010\u000b\u0010"+ + "\f\u0010\u0081\u0001\u0010\u0001\u0010\u0005\u0010\u0086\b\u0010\n\u0010"+ + "\f\u0010\u0089\t\u0010\u0003\u0010\u008b\b\u0010\u0003\u0010\u008d\b\u0010"+ + "\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012"+ + "\u0005\u0012\u0095\b\u0012\n\u0012\f\u0012\u0098\t\u0012\u0001\u0012\u0001"+ + "\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0005\u0013\u009f\b\u0013\n"+ + "\u0013\f\u0013\u00a2\t\u0013\u0001\u0014\u0001\u0014\u0001\u0015\u0001"+ + "\u0015\u0001\u0015\u0005\u0015\u00a9\b\u0015\n\u0015\f\u0015\u00ac\t\u0015"+ + "\u0001\u0015\u0001\u0015\u0001\u0016\u0001\u0016\u0005\u0016\u00b2\b\u0016"+ + "\n\u0016\f\u0016\u00b5\t\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+ + "\u0017\u0001\u0017\u0001\u0017\u0005\u0017\u00bd\b\u0017\n\u0017\f\u0017"+ + "\u00c0\t\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017"+ + "\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0005\u0018\u00cb\b\u0018"+ + "\n\u0018\f\u0018\u00ce\t\u0018\u0001\u0018\u0003\u0018\u00d1\b\u0018\u0001"+ + "\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0005"+ + "\u0019\u00d9\b\u0019\n\u0019\f\u0019\u00dc\t\u0019\u0001\u0019\u0001\u0019"+ + "\u0001\u001a\u0004\u001a\u00e1\b\u001a\u000b\u001a\f\u001a\u00e2\u0001"+ + "\u001a\u0001\u001a\u0004\u0096\u00b3\u00be\u00cc\u0000\u001b\u0001\u0001"+ + "\u0003\u0002\u0005\u0003\u0007\u0004\t\u0005\u000b\u0006\r\u0007\u000f"+ + "\b\u0011\t\u0013\n\u0015\u000b\u0017\f\u0019\r\u001b\u000e\u001d\u000f"+ + "\u001f\u0010!\u0011#\u0000%\u0012\'\u0013)\u0000+\u0014-\u0000/\u0015"+ + "1\u00163\u00175\u0018\u0001\u0000\u0014\u0002\u0000SSss\u0002\u0000TT"+ + "tt\u0002\u0000RRrr\u0002\u0000IIii\u0002\u0000CCcc\u0002\u0000GGgg\u0002"+ + "\u0000AAaa\u0002\u0000PPpp\u0002\u0000HHhh\u0002\u0000DDdd\u0002\u0000"+ + "NNnn\u0002\u0000OOoo\u0002\u0000EEee\u0002\u0000UUuu\u0002\u0000BBbb\u0001"+ + "\u000009\u0004\u0000AZ__az\u0080\u00ff\u0002\u0000<<>>\u0002\u0000\n\n"+ + "\r\r\u0003\u0000\t\n\r\r \u00f4\u0000\u0001\u0001\u0000\u0000\u0000\u0000"+ + "\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000"+ + "\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b"+ + "\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001"+ + "\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001"+ + "\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001"+ + "\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001"+ + "\u0000\u0000\u0000\u0000\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001"+ + "\u0000\u0000\u0000\u0000!\u0001\u0000\u0000\u0000\u0000%\u0001\u0000\u0000"+ + "\u0000\u0000\'\u0001\u0000\u0000\u0000\u0000+\u0001\u0000\u0000\u0000"+ + "\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000\u0000\u0000\u00003"+ + "\u0001\u0000\u0000\u0000\u00005\u0001\u0000\u0000\u0000\u00017\u0001\u0000"+ + "\u0000\u0000\u00039\u0001\u0000\u0000\u0000\u0005;\u0001\u0000\u0000\u0000"+ + "\u0007=\u0001\u0000\u0000\u0000\t?\u0001\u0000\u0000\u0000\u000bA\u0001"+ + "\u0000\u0000\u0000\rC\u0001\u0000\u0000\u0000\u000fE\u0001\u0000\u0000"+ + "\u0000\u0011H\u0001\u0000\u0000\u0000\u0013K\u0001\u0000\u0000\u0000\u0015"+ + "M\u0001\u0000\u0000\u0000\u0017T\u0001\u0000\u0000\u0000\u0019Z\u0001"+ + "\u0000\u0000\u0000\u001bb\u0001\u0000\u0000\u0000\u001dg\u0001\u0000\u0000"+ + "\u0000\u001fl\u0001\u0000\u0000\u0000!v\u0001\u0000\u0000\u0000#\u008e"+ + "\u0001\u0000\u0000\u0000%\u0090\u0001\u0000\u0000\u0000\'\u009b\u0001"+ + "\u0000\u0000\u0000)\u00a3\u0001\u0000\u0000\u0000+\u00a5\u0001\u0000\u0000"+ + "\u0000-\u00af\u0001\u0000\u0000\u0000/\u00b8\u0001\u0000\u0000\u00001"+ + "\u00c6\u0001\u0000\u0000\u00003\u00d6\u0001\u0000\u0000\u00005\u00e0\u0001"+ + "\u0000\u0000\u000078\u0005{\u0000\u00008\u0002\u0001\u0000\u0000\u0000"+ + "9:\u0005}\u0000\u0000:\u0004\u0001\u0000\u0000\u0000;<\u0005;\u0000\u0000"+ + "<\u0006\u0001\u0000\u0000\u0000=>\u0005=\u0000\u0000>\b\u0001\u0000\u0000"+ + "\u0000?@\u0005[\u0000\u0000@\n\u0001\u0000\u0000\u0000AB\u0005]\u0000"+ + "\u0000B\f\u0001\u0000\u0000\u0000CD\u0005,\u0000\u0000D\u000e\u0001\u0000"+ + "\u0000\u0000EF\u0005-\u0000\u0000FG\u0005>\u0000\u0000G\u0010\u0001\u0000"+ + "\u0000\u0000HI\u0005-\u0000\u0000IJ\u0005-\u0000\u0000J\u0012\u0001\u0000"+ + "\u0000\u0000KL\u0005:\u0000\u0000L\u0014\u0001\u0000\u0000\u0000MN\u0007"+ + "\u0000\u0000\u0000NO\u0007\u0001\u0000\u0000OP\u0007\u0002\u0000\u0000"+ + "PQ\u0007\u0003\u0000\u0000QR\u0007\u0004\u0000\u0000RS\u0007\u0001\u0000"+ + "\u0000S\u0016\u0001\u0000\u0000\u0000TU\u0007\u0005\u0000\u0000UV\u0007"+ + "\u0002\u0000\u0000VW\u0007\u0006\u0000\u0000WX\u0007\u0007\u0000\u0000"+ + "XY\u0007\b\u0000\u0000Y\u0018\u0001\u0000\u0000\u0000Z[\u0007\t\u0000"+ + "\u0000[\\\u0007\u0003\u0000\u0000\\]\u0007\u0005\u0000\u0000]^\u0007\u0002"+ + "\u0000\u0000^_\u0007\u0006\u0000\u0000_`\u0007\u0007\u0000\u0000`a\u0007"+ + "\b\u0000\u0000a\u001a\u0001\u0000\u0000\u0000bc\u0007\n\u0000\u0000cd"+ + "\u0007\u000b\u0000\u0000de\u0007\t\u0000\u0000ef\u0007\f\u0000\u0000f"+ + "\u001c\u0001\u0000\u0000\u0000gh\u0007\f\u0000\u0000hi\u0007\t\u0000\u0000"+ + "ij\u0007\u0005\u0000\u0000jk\u0007\f\u0000\u0000k\u001e\u0001\u0000\u0000"+ + "\u0000lm\u0007\u0000\u0000\u0000mn\u0007\r\u0000\u0000no\u0007\u000e\u0000"+ + "\u0000op\u0007\u0005\u0000\u0000pq\u0007\u0002\u0000\u0000qr\u0007\u0006"+ + "\u0000\u0000rs\u0007\u0007\u0000\u0000st\u0007\b\u0000\u0000t \u0001\u0000"+ + "\u0000\u0000uw\u0005-\u0000\u0000vu\u0001\u0000\u0000\u0000vw\u0001\u0000"+ + "\u0000\u0000w\u008c\u0001\u0000\u0000\u0000xz\u0005.\u0000\u0000y{\u0003"+ + "#\u0011\u0000zy\u0001\u0000\u0000\u0000{|\u0001\u0000\u0000\u0000|z\u0001"+ + "\u0000\u0000\u0000|}\u0001\u0000\u0000\u0000}\u008d\u0001\u0000\u0000"+ + "\u0000~\u0080\u0003#\u0011\u0000\u007f~\u0001\u0000\u0000\u0000\u0080"+ + "\u0081\u0001\u0000\u0000\u0000\u0081\u007f\u0001\u0000\u0000\u0000\u0081"+ + "\u0082\u0001\u0000\u0000\u0000\u0082\u008a\u0001\u0000\u0000\u0000\u0083"+ + "\u0087\u0005.\u0000\u0000\u0084\u0086\u0003#\u0011\u0000\u0085\u0084\u0001"+ + "\u0000\u0000\u0000\u0086\u0089\u0001\u0000\u0000\u0000\u0087\u0085\u0001"+ + "\u0000\u0000\u0000\u0087\u0088\u0001\u0000\u0000\u0000\u0088\u008b\u0001"+ + "\u0000\u0000\u0000\u0089\u0087\u0001\u0000\u0000\u0000\u008a\u0083\u0001"+ + "\u0000\u0000\u0000\u008a\u008b\u0001\u0000\u0000\u0000\u008b\u008d\u0001"+ + "\u0000\u0000\u0000\u008cx\u0001\u0000\u0000\u0000\u008c\u007f\u0001\u0000"+ + "\u0000\u0000\u008d\"\u0001\u0000\u0000\u0000\u008e\u008f\u0007\u000f\u0000"+ + "\u0000\u008f$\u0001\u0000\u0000\u0000\u0090\u0096\u0005\"\u0000\u0000"+ + "\u0091\u0092\u0005\\\u0000\u0000\u0092\u0095\u0005\"\u0000\u0000\u0093"+ + "\u0095\t\u0000\u0000\u0000\u0094\u0091\u0001\u0000\u0000\u0000\u0094\u0093"+ + "\u0001\u0000\u0000\u0000\u0095\u0098\u0001\u0000\u0000\u0000\u0096\u0097"+ + "\u0001\u0000\u0000\u0000\u0096\u0094\u0001\u0000\u0000\u0000\u0097\u0099"+ + "\u0001\u0000\u0000\u0000\u0098\u0096\u0001\u0000\u0000\u0000\u0099\u009a"+ + "\u0005\"\u0000\u0000\u009a&\u0001\u0000\u0000\u0000\u009b\u00a0\u0003"+ + ")\u0014\u0000\u009c\u009f\u0003)\u0014\u0000\u009d\u009f\u0003#\u0011"+ + "\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009e\u009d\u0001\u0000\u0000"+ + "\u0000\u009f\u00a2\u0001\u0000\u0000\u0000\u00a0\u009e\u0001\u0000\u0000"+ + "\u0000\u00a0\u00a1\u0001\u0000\u0000\u0000\u00a1(\u0001\u0000\u0000\u0000"+ + "\u00a2\u00a0\u0001\u0000\u0000\u0000\u00a3\u00a4\u0007\u0010\u0000\u0000"+ + "\u00a4*\u0001\u0000\u0000\u0000\u00a5\u00aa\u0005<\u0000\u0000\u00a6\u00a9"+ + "\u0003-\u0016\u0000\u00a7\u00a9\b\u0011\u0000\u0000\u00a8\u00a6\u0001"+ + "\u0000\u0000\u0000\u00a8\u00a7\u0001\u0000\u0000\u0000\u00a9\u00ac\u0001"+ + "\u0000\u0000\u0000\u00aa\u00a8\u0001\u0000\u0000\u0000\u00aa\u00ab\u0001"+ + "\u0000\u0000\u0000\u00ab\u00ad\u0001\u0000\u0000\u0000\u00ac\u00aa\u0001"+ + "\u0000\u0000\u0000\u00ad\u00ae\u0005>\u0000\u0000\u00ae,\u0001\u0000\u0000"+ + "\u0000\u00af\u00b3\u0005<\u0000\u0000\u00b0\u00b2\t\u0000\u0000\u0000"+ + "\u00b1\u00b0\u0001\u0000\u0000\u0000\u00b2\u00b5\u0001\u0000\u0000\u0000"+ + "\u00b3\u00b4\u0001\u0000\u0000\u0000\u00b3\u00b1\u0001\u0000\u0000\u0000"+ + "\u00b4\u00b6\u0001\u0000\u0000\u0000\u00b5\u00b3\u0001\u0000\u0000\u0000"+ + "\u00b6\u00b7\u0005>\u0000\u0000\u00b7.\u0001\u0000\u0000\u0000\u00b8\u00b9"+ + "\u0005/\u0000\u0000\u00b9\u00ba\u0005*\u0000\u0000\u00ba\u00be\u0001\u0000"+ + "\u0000\u0000\u00bb\u00bd\t\u0000\u0000\u0000\u00bc\u00bb\u0001\u0000\u0000"+ + "\u0000\u00bd\u00c0\u0001\u0000\u0000\u0000\u00be\u00bf\u0001\u0000\u0000"+ + "\u0000\u00be\u00bc\u0001\u0000\u0000\u0000\u00bf\u00c1\u0001\u0000\u0000"+ + "\u0000\u00c0\u00be\u0001\u0000\u0000\u0000\u00c1\u00c2\u0005*\u0000\u0000"+ + "\u00c2\u00c3\u0005/\u0000\u0000\u00c3\u00c4\u0001\u0000\u0000\u0000\u00c4"+ + "\u00c5\u0006\u0017\u0000\u0000\u00c50\u0001\u0000\u0000\u0000\u00c6\u00c7"+ + "\u0005/\u0000\u0000\u00c7\u00c8\u0005/\u0000\u0000\u00c8\u00cc\u0001\u0000"+ + "\u0000\u0000\u00c9\u00cb\t\u0000\u0000\u0000\u00ca\u00c9\u0001\u0000\u0000"+ + "\u0000\u00cb\u00ce\u0001\u0000\u0000\u0000\u00cc\u00cd\u0001\u0000\u0000"+ + "\u0000\u00cc\u00ca\u0001\u0000\u0000\u0000\u00cd\u00d0\u0001\u0000\u0000"+ + "\u0000\u00ce\u00cc\u0001\u0000\u0000\u0000\u00cf\u00d1\u0005\r\u0000\u0000"+ + "\u00d0\u00cf\u0001\u0000\u0000\u0000\u00d0\u00d1\u0001\u0000\u0000\u0000"+ + "\u00d1\u00d2\u0001\u0000\u0000\u0000\u00d2\u00d3\u0005\n\u0000\u0000\u00d3"+ + "\u00d4\u0001\u0000\u0000\u0000\u00d4\u00d5\u0006\u0018\u0000\u0000\u00d5"+ + "2\u0001\u0000\u0000\u0000\u00d6\u00da\u0005#\u0000\u0000\u00d7\u00d9\b"+ + "\u0012\u0000\u0000\u00d8\u00d7\u0001\u0000\u0000\u0000\u00d9\u00dc\u0001"+ + "\u0000\u0000\u0000\u00da\u00d8\u0001\u0000\u0000\u0000\u00da\u00db\u0001"+ + "\u0000\u0000\u0000\u00db\u00dd\u0001\u0000\u0000\u0000\u00dc\u00da\u0001"+ + "\u0000\u0000\u0000\u00dd\u00de\u0006\u0019\u0000\u0000\u00de4\u0001\u0000"+ + "\u0000\u0000\u00df\u00e1\u0007\u0013\u0000\u0000\u00e0\u00df\u0001\u0000"+ + "\u0000\u0000\u00e1\u00e2\u0001\u0000\u0000\u0000\u00e2\u00e0\u0001\u0000"+ + "\u0000\u0000\u00e2\u00e3\u0001\u0000\u0000\u0000\u00e3\u00e4\u0001\u0000"+ + "\u0000\u0000\u00e4\u00e5\u0006\u001a\u0000\u0000\u00e56\u0001\u0000\u0000"+ + "\u0000\u0013\u0000v|\u0081\u0087\u008a\u008c\u0094\u0096\u009e\u00a0\u00a8"+ + "\u00aa\u00b3\u00be\u00cc\u00d0\u00da\u00e2\u0001\u0006\u0000\u0000"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.tokens b/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.tokens new file mode 100644 index 000000000..9c46a081b --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotLexer.tokens @@ -0,0 +1,34 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +STRICT=11 +GRAPH=12 +DIGRAPH=13 +NODE=14 +EDGE=15 +SUBGRAPH=16 +NUMBER=17 +STRING=18 +ID=19 +HTML_STRING=20 +COMMENT=21 +LINE_COMMENT=22 +PREPROC=23 +WS=24 +'{'=1 +'}'=2 +';'=3 +'='=4 +'['=5 +']'=6 +','=7 +'->'=8 +'--'=9 +':'=10 diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotListener.java b/solver/src/main/java/org/ucfs/input/utils/dot/DotListener.java new file mode 100644 index 000000000..51ad2f205 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotListener.java @@ -0,0 +1,150 @@ +// Generated from Dot.g4 by ANTLR 4.13.2 +package org.ucfs.input.utils.dot; +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link DotParser}. + */ +public interface DotListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link DotParser#graph}. + * @param ctx the parse tree + */ + void enterGraph(DotParser.GraphContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#graph}. + * @param ctx the parse tree + */ + void exitGraph(DotParser.GraphContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#stmt_list}. + * @param ctx the parse tree + */ + void enterStmt_list(DotParser.Stmt_listContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#stmt_list}. + * @param ctx the parse tree + */ + void exitStmt_list(DotParser.Stmt_listContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#stmt}. + * @param ctx the parse tree + */ + void enterStmt(DotParser.StmtContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#stmt}. + * @param ctx the parse tree + */ + void exitStmt(DotParser.StmtContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#attr_stmt}. + * @param ctx the parse tree + */ + void enterAttr_stmt(DotParser.Attr_stmtContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#attr_stmt}. + * @param ctx the parse tree + */ + void exitAttr_stmt(DotParser.Attr_stmtContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#attr_list}. + * @param ctx the parse tree + */ + void enterAttr_list(DotParser.Attr_listContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#attr_list}. + * @param ctx the parse tree + */ + void exitAttr_list(DotParser.Attr_listContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#attr}. + * @param ctx the parse tree + */ + void enterAttr(DotParser.AttrContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#attr}. + * @param ctx the parse tree + */ + void exitAttr(DotParser.AttrContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#edge_stmt}. + * @param ctx the parse tree + */ + void enterEdge_stmt(DotParser.Edge_stmtContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#edge_stmt}. + * @param ctx the parse tree + */ + void exitEdge_stmt(DotParser.Edge_stmtContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#edgeRHS}. + * @param ctx the parse tree + */ + void enterEdgeRHS(DotParser.EdgeRHSContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#edgeRHS}. + * @param ctx the parse tree + */ + void exitEdgeRHS(DotParser.EdgeRHSContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#edgeop}. + * @param ctx the parse tree + */ + void enterEdgeop(DotParser.EdgeopContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#edgeop}. + * @param ctx the parse tree + */ + void exitEdgeop(DotParser.EdgeopContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#node_stmt}. + * @param ctx the parse tree + */ + void enterNode_stmt(DotParser.Node_stmtContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#node_stmt}. + * @param ctx the parse tree + */ + void exitNode_stmt(DotParser.Node_stmtContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#node_id}. + * @param ctx the parse tree + */ + void enterNode_id(DotParser.Node_idContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#node_id}. + * @param ctx the parse tree + */ + void exitNode_id(DotParser.Node_idContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#port}. + * @param ctx the parse tree + */ + void enterPort(DotParser.PortContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#port}. + * @param ctx the parse tree + */ + void exitPort(DotParser.PortContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#subgraph}. + * @param ctx the parse tree + */ + void enterSubgraph(DotParser.SubgraphContext ctx); + /** + * Exit a parse tree produced by {@link DotParser#subgraph}. + * @param ctx the parse tree + */ + void exitSubgraph(DotParser.SubgraphContext ctx); + /** + * Enter a parse tree produced by {@link DotParser#id_}. + * @param ctx the parse tree + */ + void enterId_(DotParser.Id_Context ctx); + /** + * Exit a parse tree produced by {@link DotParser#id_}. + * @param ctx the parse tree + */ + void exitId_(DotParser.Id_Context ctx); +} \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotParser.java b/solver/src/main/java/org/ucfs/input/utils/dot/DotParser.java new file mode 100644 index 000000000..0f28170e7 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotParser.java @@ -0,0 +1,1202 @@ +// Generated from Dot.g4 by ANTLR 4.13.2 +package org.ucfs.input.utils.dot; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class DotParser extends Parser { + static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9, + T__9=10, STRICT=11, GRAPH=12, DIGRAPH=13, NODE=14, EDGE=15, SUBGRAPH=16, + NUMBER=17, STRING=18, ID=19, HTML_STRING=20, COMMENT=21, LINE_COMMENT=22, + PREPROC=23, WS=24; + public static final int + RULE_graph = 0, RULE_stmt_list = 1, RULE_stmt = 2, RULE_attr_stmt = 3, + RULE_attr_list = 4, RULE_attr = 5, RULE_edge_stmt = 6, RULE_edgeRHS = 7, + RULE_edgeop = 8, RULE_node_stmt = 9, RULE_node_id = 10, RULE_port = 11, + RULE_subgraph = 12, RULE_id_ = 13; + private static String[] makeRuleNames() { + return new String[] { + "graph", "stmt_list", "stmt", "attr_stmt", "attr_list", "attr", "edge_stmt", + "edgeRHS", "edgeop", "node_stmt", "node_id", "port", "subgraph", "id_" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, "'{'", "'}'", "';'", "'='", "'['", "']'", "','", "'->'", "'--'", + "':'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, null, null, null, null, null, null, null, null, null, null, "STRICT", + "GRAPH", "DIGRAPH", "NODE", "EDGE", "SUBGRAPH", "NUMBER", "STRING", "ID", + "HTML_STRING", "COMMENT", "LINE_COMMENT", "PREPROC", "WS" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "Dot.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public DotParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @SuppressWarnings("CheckReturnValue") + public static class GraphContext extends ParserRuleContext { + public Stmt_listContext stmt_list() { + return getRuleContext(Stmt_listContext.class,0); + } + public TerminalNode EOF() { return getToken(DotParser.EOF, 0); } + public TerminalNode GRAPH() { return getToken(DotParser.GRAPH, 0); } + public TerminalNode DIGRAPH() { return getToken(DotParser.DIGRAPH, 0); } + public TerminalNode STRICT() { return getToken(DotParser.STRICT, 0); } + public Id_Context id_() { + return getRuleContext(Id_Context.class,0); + } + public GraphContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_graph; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterGraph(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitGraph(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitGraph(this); + else return visitor.visitChildren(this); + } + } + + public final GraphContext graph() throws RecognitionException { + GraphContext _localctx = new GraphContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_graph); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(29); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==STRICT) { + { + setState(28); + match(STRICT); + } + } + + setState(31); + _la = _input.LA(1); + if ( !(_la==GRAPH || _la==DIGRAPH) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(33); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 1966080L) != 0)) { + { + setState(32); + id_(); + } + } + + setState(35); + match(T__0); + setState(36); + stmt_list(); + setState(37); + match(T__1); + setState(38); + match(EOF); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Stmt_listContext extends ParserRuleContext { + public List stmt() { + return getRuleContexts(StmtContext.class); + } + public StmtContext stmt(int i) { + return getRuleContext(StmtContext.class,i); + } + public Stmt_listContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_stmt_list; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterStmt_list(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitStmt_list(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitStmt_list(this); + else return visitor.visitChildren(this); + } + } + + public final Stmt_listContext stmt_list() throws RecognitionException { + Stmt_listContext _localctx = new Stmt_listContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_stmt_list); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(46); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 2084866L) != 0)) { + { + { + setState(40); + stmt(); + setState(42); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__2) { + { + setState(41); + match(T__2); + } + } + + } + } + setState(48); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class StmtContext extends ParserRuleContext { + public Node_stmtContext node_stmt() { + return getRuleContext(Node_stmtContext.class,0); + } + public Edge_stmtContext edge_stmt() { + return getRuleContext(Edge_stmtContext.class,0); + } + public Attr_stmtContext attr_stmt() { + return getRuleContext(Attr_stmtContext.class,0); + } + public List id_() { + return getRuleContexts(Id_Context.class); + } + public Id_Context id_(int i) { + return getRuleContext(Id_Context.class,i); + } + public SubgraphContext subgraph() { + return getRuleContext(SubgraphContext.class,0); + } + public StmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_stmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterStmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitStmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitStmt(this); + else return visitor.visitChildren(this); + } + } + + public final StmtContext stmt() throws RecognitionException { + StmtContext _localctx = new StmtContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_stmt); + try { + setState(57); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(49); + node_stmt(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(50); + edge_stmt(); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(51); + attr_stmt(); + } + break; + case 4: + enterOuterAlt(_localctx, 4); + { + setState(52); + id_(); + setState(53); + match(T__3); + setState(54); + id_(); + } + break; + case 5: + enterOuterAlt(_localctx, 5); + { + setState(56); + subgraph(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Attr_stmtContext extends ParserRuleContext { + public Attr_listContext attr_list() { + return getRuleContext(Attr_listContext.class,0); + } + public TerminalNode GRAPH() { return getToken(DotParser.GRAPH, 0); } + public TerminalNode NODE() { return getToken(DotParser.NODE, 0); } + public TerminalNode EDGE() { return getToken(DotParser.EDGE, 0); } + public Attr_stmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_attr_stmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterAttr_stmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitAttr_stmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitAttr_stmt(this); + else return visitor.visitChildren(this); + } + } + + public final Attr_stmtContext attr_stmt() throws RecognitionException { + Attr_stmtContext _localctx = new Attr_stmtContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_attr_stmt); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(59); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 53248L) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(60); + attr_list(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Attr_listContext extends ParserRuleContext { + public List attr() { + return getRuleContexts(AttrContext.class); + } + public AttrContext attr(int i) { + return getRuleContext(AttrContext.class,i); + } + public Attr_listContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_attr_list; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterAttr_list(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitAttr_list(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitAttr_list(this); + else return visitor.visitChildren(this); + } + } + + public final Attr_listContext attr_list() throws RecognitionException { + Attr_listContext _localctx = new Attr_listContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_attr_list); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(70); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(62); + match(T__4); + setState(66); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 1966080L) != 0)) { + { + { + setState(63); + attr(); + } + } + setState(68); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(69); + match(T__5); + } + } + setState(72); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==T__4 ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class AttrContext extends ParserRuleContext { + public Id_Context label_name; + public Id_Context label_value; + public List id_() { + return getRuleContexts(Id_Context.class); + } + public Id_Context id_(int i) { + return getRuleContext(Id_Context.class,i); + } + public AttrContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_attr; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterAttr(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitAttr(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitAttr(this); + else return visitor.visitChildren(this); + } + } + + public final AttrContext attr() throws RecognitionException { + AttrContext _localctx = new AttrContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_attr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(74); + ((AttrContext)_localctx).label_name = id_(); + setState(77); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__3) { + { + setState(75); + match(T__3); + setState(76); + ((AttrContext)_localctx).label_value = id_(); + } + } + + setState(80); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__2 || _la==T__6) { + { + setState(79); + _la = _input.LA(1); + if ( !(_la==T__2 || _la==T__6) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Edge_stmtContext extends ParserRuleContext { + public EdgeRHSContext edgeRHS() { + return getRuleContext(EdgeRHSContext.class,0); + } + public Node_idContext node_id() { + return getRuleContext(Node_idContext.class,0); + } + public SubgraphContext subgraph() { + return getRuleContext(SubgraphContext.class,0); + } + public Attr_listContext attr_list() { + return getRuleContext(Attr_listContext.class,0); + } + public Edge_stmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_edge_stmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterEdge_stmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitEdge_stmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitEdge_stmt(this); + else return visitor.visitChildren(this); + } + } + + public final Edge_stmtContext edge_stmt() throws RecognitionException { + Edge_stmtContext _localctx = new Edge_stmtContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_edge_stmt); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(84); + _errHandler.sync(this); + switch (_input.LA(1)) { + case NUMBER: + case STRING: + case ID: + case HTML_STRING: + { + setState(82); + node_id(); + } + break; + case T__0: + case SUBGRAPH: + { + setState(83); + subgraph(); + } + break; + default: + throw new NoViableAltException(this); + } + setState(86); + edgeRHS(); + setState(88); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__4) { + { + setState(87); + attr_list(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class EdgeRHSContext extends ParserRuleContext { + public List edgeop() { + return getRuleContexts(EdgeopContext.class); + } + public EdgeopContext edgeop(int i) { + return getRuleContext(EdgeopContext.class,i); + } + public List node_id() { + return getRuleContexts(Node_idContext.class); + } + public Node_idContext node_id(int i) { + return getRuleContext(Node_idContext.class,i); + } + public List subgraph() { + return getRuleContexts(SubgraphContext.class); + } + public SubgraphContext subgraph(int i) { + return getRuleContext(SubgraphContext.class,i); + } + public EdgeRHSContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_edgeRHS; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterEdgeRHS(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitEdgeRHS(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitEdgeRHS(this); + else return visitor.visitChildren(this); + } + } + + public final EdgeRHSContext edgeRHS() throws RecognitionException { + EdgeRHSContext _localctx = new EdgeRHSContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_edgeRHS); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(95); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(90); + edgeop(); + setState(93); + _errHandler.sync(this); + switch (_input.LA(1)) { + case NUMBER: + case STRING: + case ID: + case HTML_STRING: + { + setState(91); + node_id(); + } + break; + case T__0: + case SUBGRAPH: + { + setState(92); + subgraph(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + setState(97); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==T__7 || _la==T__8 ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class EdgeopContext extends ParserRuleContext { + public EdgeopContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_edgeop; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterEdgeop(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitEdgeop(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitEdgeop(this); + else return visitor.visitChildren(this); + } + } + + public final EdgeopContext edgeop() throws RecognitionException { + EdgeopContext _localctx = new EdgeopContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_edgeop); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(99); + _la = _input.LA(1); + if ( !(_la==T__7 || _la==T__8) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Node_stmtContext extends ParserRuleContext { + public Node_idContext node_id() { + return getRuleContext(Node_idContext.class,0); + } + public Attr_listContext attr_list() { + return getRuleContext(Attr_listContext.class,0); + } + public Node_stmtContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_node_stmt; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterNode_stmt(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitNode_stmt(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitNode_stmt(this); + else return visitor.visitChildren(this); + } + } + + public final Node_stmtContext node_stmt() throws RecognitionException { + Node_stmtContext _localctx = new Node_stmtContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_node_stmt); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(101); + node_id(); + setState(103); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__4) { + { + setState(102); + attr_list(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Node_idContext extends ParserRuleContext { + public Id_Context id_() { + return getRuleContext(Id_Context.class,0); + } + public PortContext port() { + return getRuleContext(PortContext.class,0); + } + public Node_idContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_node_id; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterNode_id(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitNode_id(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitNode_id(this); + else return visitor.visitChildren(this); + } + } + + public final Node_idContext node_id() throws RecognitionException { + Node_idContext _localctx = new Node_idContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_node_id); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(105); + id_(); + setState(107); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__9) { + { + setState(106); + port(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class PortContext extends ParserRuleContext { + public List id_() { + return getRuleContexts(Id_Context.class); + } + public Id_Context id_(int i) { + return getRuleContext(Id_Context.class,i); + } + public PortContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_port; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterPort(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitPort(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitPort(this); + else return visitor.visitChildren(this); + } + } + + public final PortContext port() throws RecognitionException { + PortContext _localctx = new PortContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_port); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(109); + match(T__9); + setState(110); + id_(); + setState(113); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==T__9) { + { + setState(111); + match(T__9); + setState(112); + id_(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SubgraphContext extends ParserRuleContext { + public Stmt_listContext stmt_list() { + return getRuleContext(Stmt_listContext.class,0); + } + public TerminalNode SUBGRAPH() { return getToken(DotParser.SUBGRAPH, 0); } + public Id_Context id_() { + return getRuleContext(Id_Context.class,0); + } + public SubgraphContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_subgraph; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterSubgraph(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitSubgraph(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitSubgraph(this); + else return visitor.visitChildren(this); + } + } + + public final SubgraphContext subgraph() throws RecognitionException { + SubgraphContext _localctx = new SubgraphContext(_ctx, getState()); + enterRule(_localctx, 24, RULE_subgraph); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(119); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==SUBGRAPH) { + { + setState(115); + match(SUBGRAPH); + setState(117); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 1966080L) != 0)) { + { + setState(116); + id_(); + } + } + + } + } + + setState(121); + match(T__0); + setState(122); + stmt_list(); + setState(123); + match(T__1); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class Id_Context extends ParserRuleContext { + public TerminalNode ID() { return getToken(DotParser.ID, 0); } + public TerminalNode STRING() { return getToken(DotParser.STRING, 0); } + public TerminalNode HTML_STRING() { return getToken(DotParser.HTML_STRING, 0); } + public TerminalNode NUMBER() { return getToken(DotParser.NUMBER, 0); } + public Id_Context(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_id_; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).enterId_(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof DotListener ) ((DotListener)listener).exitId_(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof DotVisitor ) return ((DotVisitor)visitor).visitId_(this); + else return visitor.visitChildren(this); + } + } + + public final Id_Context id_() throws RecognitionException { + Id_Context _localctx = new Id_Context(_ctx, getState()); + enterRule(_localctx, 26, RULE_id_); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(125); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 1966080L) != 0)) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static final String _serializedATN = + "\u0004\u0001\u0018\u0080\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001"+ + "\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004"+ + "\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007"+ + "\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b"+ + "\u0002\f\u0007\f\u0002\r\u0007\r\u0001\u0000\u0003\u0000\u001e\b\u0000"+ + "\u0001\u0000\u0001\u0000\u0003\u0000\"\b\u0000\u0001\u0000\u0001\u0000"+ + "\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0003\u0001"+ + "+\b\u0001\u0005\u0001-\b\u0001\n\u0001\f\u00010\t\u0001\u0001\u0002\u0001"+ + "\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001"+ + "\u0002\u0003\u0002:\b\u0002\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+ + "\u0004\u0001\u0004\u0005\u0004A\b\u0004\n\u0004\f\u0004D\t\u0004\u0001"+ + "\u0004\u0004\u0004G\b\u0004\u000b\u0004\f\u0004H\u0001\u0005\u0001\u0005"+ + "\u0001\u0005\u0003\u0005N\b\u0005\u0001\u0005\u0003\u0005Q\b\u0005\u0001"+ + "\u0006\u0001\u0006\u0003\u0006U\b\u0006\u0001\u0006\u0001\u0006\u0003"+ + "\u0006Y\b\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0003\u0007^\b\u0007"+ + "\u0004\u0007`\b\u0007\u000b\u0007\f\u0007a\u0001\b\u0001\b\u0001\t\u0001"+ + "\t\u0003\th\b\t\u0001\n\u0001\n\u0003\nl\b\n\u0001\u000b\u0001\u000b\u0001"+ + "\u000b\u0001\u000b\u0003\u000br\b\u000b\u0001\f\u0001\f\u0003\fv\b\f\u0003"+ + "\fx\b\f\u0001\f\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0000"+ + "\u0000\u000e\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012\u0014\u0016"+ + "\u0018\u001a\u0000\u0005\u0001\u0000\f\r\u0002\u0000\f\f\u000e\u000f\u0002"+ + "\u0000\u0003\u0003\u0007\u0007\u0001\u0000\b\t\u0001\u0000\u0011\u0014"+ + "\u0086\u0000\u001d\u0001\u0000\u0000\u0000\u0002.\u0001\u0000\u0000\u0000"+ + "\u00049\u0001\u0000\u0000\u0000\u0006;\u0001\u0000\u0000\u0000\bF\u0001"+ + "\u0000\u0000\u0000\nJ\u0001\u0000\u0000\u0000\fT\u0001\u0000\u0000\u0000"+ + "\u000e_\u0001\u0000\u0000\u0000\u0010c\u0001\u0000\u0000\u0000\u0012e"+ + "\u0001\u0000\u0000\u0000\u0014i\u0001\u0000\u0000\u0000\u0016m\u0001\u0000"+ + "\u0000\u0000\u0018w\u0001\u0000\u0000\u0000\u001a}\u0001\u0000\u0000\u0000"+ + "\u001c\u001e\u0005\u000b\u0000\u0000\u001d\u001c\u0001\u0000\u0000\u0000"+ + "\u001d\u001e\u0001\u0000\u0000\u0000\u001e\u001f\u0001\u0000\u0000\u0000"+ + "\u001f!\u0007\u0000\u0000\u0000 \"\u0003\u001a\r\u0000! \u0001\u0000\u0000"+ + "\u0000!\"\u0001\u0000\u0000\u0000\"#\u0001\u0000\u0000\u0000#$\u0005\u0001"+ + "\u0000\u0000$%\u0003\u0002\u0001\u0000%&\u0005\u0002\u0000\u0000&\'\u0005"+ + "\u0000\u0000\u0001\'\u0001\u0001\u0000\u0000\u0000(*\u0003\u0004\u0002"+ + "\u0000)+\u0005\u0003\u0000\u0000*)\u0001\u0000\u0000\u0000*+\u0001\u0000"+ + "\u0000\u0000+-\u0001\u0000\u0000\u0000,(\u0001\u0000\u0000\u0000-0\u0001"+ + "\u0000\u0000\u0000.,\u0001\u0000\u0000\u0000./\u0001\u0000\u0000\u0000"+ + "/\u0003\u0001\u0000\u0000\u00000.\u0001\u0000\u0000\u00001:\u0003\u0012"+ + "\t\u00002:\u0003\f\u0006\u00003:\u0003\u0006\u0003\u000045\u0003\u001a"+ + "\r\u000056\u0005\u0004\u0000\u000067\u0003\u001a\r\u00007:\u0001\u0000"+ + "\u0000\u00008:\u0003\u0018\f\u000091\u0001\u0000\u0000\u000092\u0001\u0000"+ + "\u0000\u000093\u0001\u0000\u0000\u000094\u0001\u0000\u0000\u000098\u0001"+ + "\u0000\u0000\u0000:\u0005\u0001\u0000\u0000\u0000;<\u0007\u0001\u0000"+ + "\u0000<=\u0003\b\u0004\u0000=\u0007\u0001\u0000\u0000\u0000>B\u0005\u0005"+ + "\u0000\u0000?A\u0003\n\u0005\u0000@?\u0001\u0000\u0000\u0000AD\u0001\u0000"+ + "\u0000\u0000B@\u0001\u0000\u0000\u0000BC\u0001\u0000\u0000\u0000CE\u0001"+ + "\u0000\u0000\u0000DB\u0001\u0000\u0000\u0000EG\u0005\u0006\u0000\u0000"+ + "F>\u0001\u0000\u0000\u0000GH\u0001\u0000\u0000\u0000HF\u0001\u0000\u0000"+ + "\u0000HI\u0001\u0000\u0000\u0000I\t\u0001\u0000\u0000\u0000JM\u0003\u001a"+ + "\r\u0000KL\u0005\u0004\u0000\u0000LN\u0003\u001a\r\u0000MK\u0001\u0000"+ + "\u0000\u0000MN\u0001\u0000\u0000\u0000NP\u0001\u0000\u0000\u0000OQ\u0007"+ + "\u0002\u0000\u0000PO\u0001\u0000\u0000\u0000PQ\u0001\u0000\u0000\u0000"+ + "Q\u000b\u0001\u0000\u0000\u0000RU\u0003\u0014\n\u0000SU\u0003\u0018\f"+ + "\u0000TR\u0001\u0000\u0000\u0000TS\u0001\u0000\u0000\u0000UV\u0001\u0000"+ + "\u0000\u0000VX\u0003\u000e\u0007\u0000WY\u0003\b\u0004\u0000XW\u0001\u0000"+ + "\u0000\u0000XY\u0001\u0000\u0000\u0000Y\r\u0001\u0000\u0000\u0000Z]\u0003"+ + "\u0010\b\u0000[^\u0003\u0014\n\u0000\\^\u0003\u0018\f\u0000][\u0001\u0000"+ + "\u0000\u0000]\\\u0001\u0000\u0000\u0000^`\u0001\u0000\u0000\u0000_Z\u0001"+ + "\u0000\u0000\u0000`a\u0001\u0000\u0000\u0000a_\u0001\u0000\u0000\u0000"+ + "ab\u0001\u0000\u0000\u0000b\u000f\u0001\u0000\u0000\u0000cd\u0007\u0003"+ + "\u0000\u0000d\u0011\u0001\u0000\u0000\u0000eg\u0003\u0014\n\u0000fh\u0003"+ + "\b\u0004\u0000gf\u0001\u0000\u0000\u0000gh\u0001\u0000\u0000\u0000h\u0013"+ + "\u0001\u0000\u0000\u0000ik\u0003\u001a\r\u0000jl\u0003\u0016\u000b\u0000"+ + "kj\u0001\u0000\u0000\u0000kl\u0001\u0000\u0000\u0000l\u0015\u0001\u0000"+ + "\u0000\u0000mn\u0005\n\u0000\u0000nq\u0003\u001a\r\u0000op\u0005\n\u0000"+ + "\u0000pr\u0003\u001a\r\u0000qo\u0001\u0000\u0000\u0000qr\u0001\u0000\u0000"+ + "\u0000r\u0017\u0001\u0000\u0000\u0000su\u0005\u0010\u0000\u0000tv\u0003"+ + "\u001a\r\u0000ut\u0001\u0000\u0000\u0000uv\u0001\u0000\u0000\u0000vx\u0001"+ + "\u0000\u0000\u0000ws\u0001\u0000\u0000\u0000wx\u0001\u0000\u0000\u0000"+ + "xy\u0001\u0000\u0000\u0000yz\u0005\u0001\u0000\u0000z{\u0003\u0002\u0001"+ + "\u0000{|\u0005\u0002\u0000\u0000|\u0019\u0001\u0000\u0000\u0000}~\u0007"+ + "\u0004\u0000\u0000~\u001b\u0001\u0000\u0000\u0000\u0012\u001d!*.9BHMP"+ + "TX]agkquw"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/solver/src/main/java/org/ucfs/input/utils/dot/DotVisitor.java b/solver/src/main/java/org/ucfs/input/utils/dot/DotVisitor.java new file mode 100644 index 000000000..ce4178d74 --- /dev/null +++ b/solver/src/main/java/org/ucfs/input/utils/dot/DotVisitor.java @@ -0,0 +1,97 @@ +// Generated from Dot.g4 by ANTLR 4.13.2 +package org.ucfs.input.utils.dot; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link DotParser}. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface DotVisitor extends ParseTreeVisitor { + /** + * Visit a parse tree produced by {@link DotParser#graph}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitGraph(DotParser.GraphContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#stmt_list}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmt_list(DotParser.Stmt_listContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#stmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitStmt(DotParser.StmtContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#attr_stmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitAttr_stmt(DotParser.Attr_stmtContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#attr_list}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitAttr_list(DotParser.Attr_listContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#attr}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitAttr(DotParser.AttrContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#edge_stmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitEdge_stmt(DotParser.Edge_stmtContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#edgeRHS}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitEdgeRHS(DotParser.EdgeRHSContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#edgeop}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitEdgeop(DotParser.EdgeopContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#node_stmt}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNode_stmt(DotParser.Node_stmtContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#node_id}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitNode_id(DotParser.Node_idContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#port}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitPort(DotParser.PortContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#subgraph}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitSubgraph(DotParser.SubgraphContext ctx); + /** + * Visit a parse tree produced by {@link DotParser#id_}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitId_(DotParser.Id_Context ctx); +} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/descriptors/Descriptor.kt b/solver/src/main/kotlin/org/ucfs/descriptors/Descriptor.kt index 90bc3c8dd..b31854a05 100644 --- a/solver/src/main/kotlin/org/ucfs/descriptors/Descriptor.kt +++ b/solver/src/main/kotlin/org/ucfs/descriptors/Descriptor.kt @@ -1,55 +1,31 @@ package org.ucfs.descriptors import org.ucfs.gss.GssNode -import org.ucfs.input.ILabel -import org.ucfs.parser.context.IContext import org.ucfs.rsm.RsmState -import org.ucfs.sppf.node.SppfNode +import org.ucfs.sppf.node.RangeSppfNode /** * Descriptor represents current parsing stage - * @param VertexType - type of vertex in input graph + * @param InputNodeType - type of vertex in input graph */ -open class Descriptor( +data class Descriptor( /** - * State in RSM, corresponds to slot in CF grammar + * Pointer to vertex in input graph */ - val rsmState: RsmState, + val inputPosition: InputNodeType, /** * Pointer to node in top layer of graph structured stack */ - val gssNode: GssNode, + val gssNode: GssNode, /** - * Pointer to already parsed portion of input, represented as derivation tree, which shall be connected afterwards - * to derivation trees, stored on edges of GSS, it corresponds to return from recursive function + * State in RSM, corresponds to slot in CF grammar */ - val sppfNode: SppfNode?, + val rsmState: RsmState, /** - * Pointer to vertex in input graph + * Pointer to already parsed portion of input, represented as derivation tree, which shall be connected afterwards + * to derivation trees, stored on edges of GSS, it corresponds to return from recursive function */ - val inputPosition: VertexType, -) { - val hashCode = 23 * (23 * (23 * 17 + rsmState.hashCode()) + inputPosition.hashCode()) + gssNode.hashCode() - - val weight: Int - get() = (sppfNode?.weight ?: 0) + gssNode.minWeightOfLeftPart - - override fun hashCode() = hashCode - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Descriptor<*>) return false - if (other.rsmState != rsmState) return false - if (other.gssNode != gssNode) return false - if (other.sppfNode != sppfNode) return false - if (other.inputPosition != inputPosition) return false - - return true - } - - override fun toString(): String { - return "Descriptor(${rsmState.nonterminal}, inputPosition=$inputPosition)" - } -} + val sppfNode: RangeSppfNode, +) diff --git a/solver/src/main/kotlin/org/ucfs/descriptors/DescriptorsStorage.kt b/solver/src/main/kotlin/org/ucfs/descriptors/DescriptorsStorage.kt index 23dec7f4e..a4ad244a1 100644 --- a/solver/src/main/kotlin/org/ucfs/descriptors/DescriptorsStorage.kt +++ b/solver/src/main/kotlin/org/ucfs/descriptors/DescriptorsStorage.kt @@ -10,21 +10,15 @@ open class DescriptorsStorage{ /** * Collection of already handled descriptors, accessible via descriptor's hashcode */ - protected val handledDescriptors = HashMap>>() + private val handledDescriptors = ArrayList>() - /** - * Collection of descriptors with zero weight - */ - protected val defaultDescriptorsStorage = ArrayDeque>() + private val descriptorsToHandle = ArrayDeque>() - /** - * @return true if we have default descriptors to handle, false otherwise - */ - fun defaultDescriptorsStorageIsEmpty() = defaultDescriptorsStorage.isEmpty() + private fun isEmpty() = descriptorsToHandle.isEmpty() - open fun addToHandling(descriptor: Descriptor) { + private fun addToHandling(descriptor: Descriptor) { if (!isAlreadyHandled(descriptor)) { - defaultDescriptorsStorage.addLast(descriptor) + descriptorsToHandle.addLast(descriptor) } } @@ -32,57 +26,40 @@ open class DescriptorsStorage{ * @return next default descriptor to handle */ open fun next(): Descriptor { - if (defaultDescriptorsStorageIsEmpty()) { + if (isEmpty()) { throw ParsingException("Descriptor storage is empty") } - return defaultDescriptorsStorage.removeLast() + return descriptorsToHandle.removeLast() } /** * @param descriptor - descriptor to check * @return true if the descriptor was already processed, false otherwise */ - fun isAlreadyHandled(descriptor: Descriptor): Boolean { - val handledDescriptor = descriptor.gssNode.handledDescriptors.find { descriptor.hashCode() == it.hashCode() } + private fun isAlreadyHandled(descriptor: Descriptor): Boolean { + val handledDescriptor = handledDescriptors.find { descriptor.hashCode() == it.hashCode() } - return handledDescriptor != null && handledDescriptor.weight <= descriptor.weight + return handledDescriptor != null } fun addToHandled(descriptor: Descriptor) { - descriptor.gssNode.handledDescriptors.add(descriptor) - - if (!handledDescriptors.containsKey(descriptor.inputPosition)) { - handledDescriptors[descriptor.inputPosition] = HashSet() - } - - handledDescriptors.getValue(descriptor.inputPosition).add(descriptor) + handledDescriptors.add(descriptor) } - /** - * Part of incrementality mechanism. - * Remove descriptor from already handled to process them again - * @param descriptor - descriptor to remove from handled - */ - fun removeFromHandled(descriptor: Descriptor) { - descriptor.gssNode.handledDescriptors.remove(descriptor) - - if (handledDescriptors.containsKey(descriptor.inputPosition)) { - handledDescriptors.getValue(descriptor.inputPosition).remove(descriptor) + fun add(descriptor: Descriptor) { + if(!isAlreadyHandled(descriptor)){ + addToHandling(descriptor) } } - /** - * Part of incrementality mechanism. - * Restore all descriptors which contain passed vertex as value of the corresponding field. - * Add all such descriptors to process again - * @param vertex - vertex in input graph + * Gets next descriptor to handle + * @return default descriptor if there is available one, null otherwise */ - fun restoreDescriptors(vertex: VertexType) { - handledDescriptors.getOrDefault(vertex, HashSet()).forEach { descriptor -> - descriptor.gssNode.handledDescriptors.remove(descriptor) - addToHandling(descriptor) + fun nextToHandle(): Descriptor? { + if (!isEmpty()) { + return next() } - handledDescriptors.remove(vertex) + return null } } diff --git a/solver/src/main/kotlin/org/ucfs/descriptors/RecoveringDescriptorsStorage.kt b/solver/src/main/kotlin/org/ucfs/descriptors/RecoveringDescriptorsStorage.kt deleted file mode 100644 index 3cd4a65c1..000000000 --- a/solver/src/main/kotlin/org/ucfs/descriptors/RecoveringDescriptorsStorage.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.ucfs.descriptors - -/** - * Collection of error recovery descriptors - * @param VertexType - type of vertex in input graph - */ -class RecoveringDescriptorsStorage : DescriptorsStorage() { - /** - * Collection of descriptors with nonzero weight - */ - private val errorRecoveringDescriptorsStorage = LinkedHashMap>>() - - /** - * Part of error recovery mechanism. - * Calculate weight of descriptor. If weight is 0 => add descriptor to default descriptors storage, otherwise - * add descriptor to error recovery descriptors storage - * @param descriptor - descriptor to add - */ - override fun addToHandling(descriptor: Descriptor) { - if (!isAlreadyHandled(descriptor)) { - val pathWeight = descriptor.weight - - if (pathWeight == 0) { - defaultDescriptorsStorage.addLast(descriptor) - } else { - if (!errorRecoveringDescriptorsStorage.containsKey(pathWeight)) { - errorRecoveringDescriptorsStorage[pathWeight] = ArrayDeque() - } - errorRecoveringDescriptorsStorage.getValue(pathWeight).addLast(descriptor) - } - } - } - - - /** - * If default descriptor storage is not empty - retrieve descriptors from it, otherwise retrieve - * error recovery descriptor - * @return next descriptor to handle - */ - override fun next(): Descriptor { - if (defaultDescriptorsStorageIsEmpty()) { - val iterator = errorRecoveringDescriptorsStorage.keys.iterator() - val currentMin = iterator.next() - val result = errorRecoveringDescriptorsStorage.getValue(currentMin).removeLast() - - if (errorRecoveringDescriptorsStorage.getValue(currentMin).isEmpty()) { - errorRecoveringDescriptorsStorage.remove(currentMin) - } - - return result - } - return defaultDescriptorsStorage.removeLast() - } -} - diff --git a/solver/src/main/kotlin/org/ucfs/grammar/combinator/Grammar.kt b/solver/src/main/kotlin/org/ucfs/grammar/combinator/Grammar.kt index dc9b58020..abc205a51 100644 --- a/solver/src/main/kotlin/org/ucfs/grammar/combinator/Grammar.kt +++ b/solver/src/main/kotlin/org/ucfs/grammar/combinator/Grammar.kt @@ -5,6 +5,7 @@ import org.ucfs.grammar.combinator.regexp.Regexp import org.ucfs.incrementalDfs import org.ucfs.rsm.RsmState import org.ucfs.rsm.symbol.ITerminal +import org.ucfs.rsm.symbol.Nonterminal open class Grammar { @@ -44,22 +45,4 @@ open class Grammar { //if nonterminal not initialized -- it will be checked in buildRsmBox() return startNt.nonterm.startState } - - /** - * Get all terminals used in RSM from current state (recursive) - */ - fun getTerminals(): Iterable { - val terms : HashSet = incrementalDfs( - rsm, - { state: RsmState -> - state.outgoingEdges.values.flatten() + - state.nonterminalEdges.keys.map { it.startState } - }, - hashSetOf(), - { state, set -> set.addAll(state.terminalEdges.keys) } - ) - val comparator = terms.firstOrNull()?.getComparator() ?: return emptyList() - return terms.toSortedSet(comparator).toList() - } - } diff --git a/solver/src/main/kotlin/org/ucfs/gss/GraphStructuredStack.kt b/solver/src/main/kotlin/org/ucfs/gss/GraphStructuredStack.kt new file mode 100644 index 000000000..14d06b529 --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/gss/GraphStructuredStack.kt @@ -0,0 +1,56 @@ +package org.ucfs.gss + +import org.ucfs.descriptors.Descriptor +import org.ucfs.rsm.RsmState +import org.ucfs.sppf.node.RangeSppfNode + +class GraphStructuredStack { + val nodes = ArrayList>() + + fun getOrCreateNode(input: InputNode, rsm: RsmState): GssNode { + val node = findNode(input, rsm) + return if (node == null) GssNode(rsm, input) else node + } + + fun findNode(input: InputNode, rsm: RsmState): GssNode? { + return nodes.find { node -> node.inputPosition == input && node.rsm == rsm } + } + + fun addEdge( + gssNode: GssNode, + rsmStateToReturn: RsmState, + inputToContinue: InputNode, + rsmStateToContinue: RsmState, + matcherRange: RangeSppfNode + ): GssResult { + val addedNode = getOrCreateNode(inputToContinue, rsmStateToContinue) + val edge = GssEdge(gssNode, rsmStateToReturn, matcherRange) + + + // There is no need to check GSS edges duplication. + // "Faster, Practical GLL Parsing", Ali Afroozeh and Anastasia Izmaylova + // p.13: "There is at most one call to the create function with the same arguments. + // Thus no check for duplicate GSS edges is needed." + val popped = addedNode.addEdge(edge) + return GssResult(addedNode, popped) + } + + + /** + * return outgoing edges + */ + fun pop( + descriptor: Descriptor, range: RangeSppfNode + ): ArrayList> { + val gssNode = descriptor.gssNode + gssNode.popped.add(range) + return gssNode.outgoingEdges + } + +} + +data class GssResult( + val gssNode: GssNode, val popped: ArrayList> +) + + diff --git a/solver/src/main/kotlin/org/ucfs/gss/GssEdge.kt b/solver/src/main/kotlin/org/ucfs/gss/GssEdge.kt new file mode 100644 index 000000000..018baff78 --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/gss/GssEdge.kt @@ -0,0 +1,8 @@ +package org.ucfs.gss + +import org.ucfs.rsm.RsmState +import org.ucfs.sppf.node.RangeSppfNode + +data class GssEdge (val gssNode: GssNode, val state: RsmState, val matchedRange: RangeSppfNode){ + +} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/gss/GssNode.kt b/solver/src/main/kotlin/org/ucfs/gss/GssNode.kt index 86d74c6f3..ed6da5e5e 100644 --- a/solver/src/main/kotlin/org/ucfs/gss/GssNode.kt +++ b/solver/src/main/kotlin/org/ucfs/gss/GssNode.kt @@ -2,63 +2,38 @@ package org.ucfs.gss import org.ucfs.descriptors.Descriptor import org.ucfs.rsm.RsmState -import org.ucfs.rsm.symbol.Nonterminal -import org.ucfs.sppf.node.SppfNode +import org.ucfs.sppf.node.RangeSppfNode import java.util.* +import kotlin.collections.ArrayList /** * Node in Graph Structured Stack - * @param VertexType - type of vertex in input graph + * @param InputNodeType - type of vertex in input graph */ -class GssNode( +var lastId = 0 + +data class GssNode( /** - * Nonterminal A, corresponding to grammar slot of the form aA·b + * RSM corresponding to grammar slot */ - val nonterminal: Nonterminal, + val rsm: RsmState, /** * Pointer to vertex in input graph */ - val inputPosition: VertexType, - /** - * Part of error recovery mechanism. - * Stores minimally possible weight of already parsed portion of input - */ - var minWeightOfLeftPart: Int, + val inputPosition: InputNodeType, val id: Int = lastId++ + ) { - /** - * Maps edge label (rsmState, sppfNode) to destination gssNode - */ - val edges: HashMap?>, HashSet>> = HashMap() + val popped = ArrayList>() - /** - * Stores handled descriptors, which contained current gssNode as value of corresponding field - */ - val handledDescriptors: HashSet> = HashSet() + val outgoingEdges = ArrayList>() /** - * Add new edge from current gssNode - * @param rsmState - rsmState to store on the edge - * @param sppfNode - sppfNode to store on the edge - * @param gssNode - destination gssNode - * @return true if creation was successful, false otherwise + * Add edge and return popped */ - fun addEdge(rsmState: RsmState, sppfNode: SppfNode?, gssNode: GssNode): Boolean { - val label = Pair(rsmState, sppfNode) - - return edges.computeIfAbsent(label) { HashSet() }.add(gssNode) - } - - override fun toString() = "GSSNode(nonterminal=$nonterminal, inputPosition=$inputPosition)" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is GssNode<*>) return false - if (nonterminal != other.nonterminal) return false - if (inputPosition != other.inputPosition) return false - - return true + fun addEdge(edge: GssEdge): ArrayList> { + outgoingEdges.add(edge) + //TODO + return popped } - val hashCode = Objects.hash(nonterminal, inputPosition) - override fun hashCode() = hashCode } diff --git a/solver/src/main/kotlin/org/ucfs/input/DotParser.kt b/solver/src/main/kotlin/org/ucfs/input/DotParser.kt new file mode 100644 index 000000000..818f61000 --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/input/DotParser.kt @@ -0,0 +1,33 @@ +package org.ucfs.input + +import org.antlr.v4.runtime.CharStreams +import org.antlr.v4.runtime.CommonTokenStream +import org.ucfs.input.utils.dot.DotLexer +import org.ucfs.input.utils.dot.DotParser +import org.ucfs.input.utils.dot.GraphFromDotVisitor +import java.io.File +import java.io.IOException + +class DotParser { + + fun parseDotFile(filePath: String): InputGraph { + val file = File(filePath) + + if (!file.exists()) { + throw IOException("File not found: $filePath") + } + return parseDot(file.readText()) + } + + + fun parseDot(dotView: String): InputGraph { + val realParser = DotParser( + CommonTokenStream( + DotLexer( + CharStreams.fromString(dotView) + ) + ) + ) + return GraphFromDotVisitor().visitGraph(realParser.graph()) + } +} diff --git a/solver/src/main/kotlin/org/ucfs/input/Edge.kt b/solver/src/main/kotlin/org/ucfs/input/Edge.kt index b25828915..3124e70ad 100644 --- a/solver/src/main/kotlin/org/ucfs/input/Edge.kt +++ b/solver/src/main/kotlin/org/ucfs/input/Edge.kt @@ -2,5 +2,5 @@ package org.ucfs.input data class Edge( val label: LabelType, - val head: VertexType, + val targetVertex: VertexType, ) \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/input/IInputGraph.kt b/solver/src/main/kotlin/org/ucfs/input/IInputGraph.kt index fcf0f00ad..4fe5788d7 100644 --- a/solver/src/main/kotlin/org/ucfs/input/IInputGraph.kt +++ b/solver/src/main/kotlin/org/ucfs/input/IInputGraph.kt @@ -6,32 +6,11 @@ package org.ucfs.input * @param LabelType - type of label on edges in input graph */ interface IInputGraph { - - /** - * Collection of all vertices in graph - */ - val vertices: MutableSet - - /** - * Maps vertex to edges, outgoing from it - */ - val edges: MutableMap>> - - /** - * Collection of all starting vertices, used to create initial descriptors to begin parsing - */ - val startVertices: MutableSet - /** * @return collection of all starting vertices */ fun getInputStartVertices(): MutableSet - /** - * @return Collection of all vertices - */ - fun getAllVertices(): MutableSet - /** * Adds passed vertex both to collection of starting vertices and collection of all vertices * @param vertex - vertex to add @@ -44,11 +23,6 @@ interface IInputGraph { */ fun addVertex(vertex: VertexType) - /** - * Removes vertex both from collection of starting vertices and collection of all vertices - * @param vertex - vertex to remove - */ - fun removeVertex(vertex: VertexType) /** * Returns all outgoing edges from given vertex @@ -85,4 +59,5 @@ interface IInputGraph { */ fun isFinal(vertex: VertexType): Boolean + fun removeVertex(vertex: VertexType) } \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/input/InputGraph.kt b/solver/src/main/kotlin/org/ucfs/input/InputGraph.kt new file mode 100644 index 000000000..a8a4e009a --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/input/InputGraph.kt @@ -0,0 +1,51 @@ +package org.ucfs.input + +open class InputGraph : IInputGraph { + + var name = "G" + + val vertices: MutableSet = HashSet() + + val edges: MutableMap>> = HashMap() + + val startVertices: MutableSet = HashSet() + + override fun getInputStartVertices(): MutableSet { + return startVertices + } + + override fun addStartVertex(vertex: VertexType) { + startVertices.add(vertex) + vertices.add(vertex) + } + + override fun addVertex(vertex: VertexType) { + vertices.add(vertex) + } + + override fun removeVertex(vertex: VertexType) { + startVertices.remove(vertex) + vertices.remove(vertex) + } + + override fun getEdges(from: VertexType): MutableList> { + return edges.getOrDefault(from, ArrayList()) + } + + override fun addEdge(from: VertexType, label: LabelType, to: VertexType) { + val edge = Edge(label, to) + + if (!edges.containsKey(from)) edges[from] = ArrayList() + + edges.getValue(from).add(edge) + } + + + override fun removeEdge(from: VertexType, label: LabelType, to: VertexType) { + val edge = Edge(label, to) + edges.getValue(from).remove(edge) + } + + override fun isStart(vertex: VertexType) = startVertices.contains(vertex) + override fun isFinal(vertex: VertexType) = getEdges(vertex).isEmpty() +} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/input/LinearInput.kt b/solver/src/main/kotlin/org/ucfs/input/LinearInput.kt index 58bf29a52..6ddf1465d 100644 --- a/solver/src/main/kotlin/org/ucfs/input/LinearInput.kt +++ b/solver/src/main/kotlin/org/ucfs/input/LinearInput.kt @@ -2,52 +2,7 @@ package org.ucfs.input import org.ucfs.rsm.symbol.Term -open class LinearInput : IInputGraph { - override val vertices: MutableSet = HashSet() - override val edges: MutableMap>> = HashMap() - - override val startVertices: MutableSet = HashSet() - - override fun getInputStartVertices(): MutableSet { - return startVertices - } - - override fun getAllVertices(): MutableSet = vertices - - override fun addStartVertex(vertex: VertexType) { - startVertices.add(vertex) - vertices.add(vertex) - } - - override fun addVertex(vertex: VertexType) { - vertices.add(vertex) - } - - override fun removeVertex(vertex: VertexType) { - startVertices.remove(vertex) - vertices.remove(vertex) - } - - override fun getEdges(from: VertexType): MutableList> { - return edges.getOrDefault(from, ArrayList()) - } - - override fun addEdge(from: VertexType, label: LabelType, to: VertexType) { - val edge = Edge(label, to) - - if (!edges.containsKey(from)) edges[from] = ArrayList() - - edges.getValue(from).add(edge) - } - - - override fun removeEdge(from: VertexType, label: LabelType, to: VertexType) { - val edge = Edge(label, to) - edges.getValue(from).remove(edge) - } - - override fun isStart(vertex: VertexType) = startVertices.contains(vertex) - override fun isFinal(vertex: VertexType) = getEdges(vertex).isEmpty() +open class LinearInput : InputGraph() { override fun toString(): String { if(startVertices.isEmpty()){ @@ -59,7 +14,7 @@ open class LinearInput : IInputGraph : IInputGraph { - val inputGraph = LinearInput() + fun buildFromString(input: String): IInputGraph { + val inputGraph = LinearInput() var curVertexId = 0 inputGraph.addStartVertex(curVertexId) @@ -77,7 +32,7 @@ open class LinearInput : IInputGraph, isDirected: Boolean = true): String { + val builder = StringBuilder() + val graphType = if (isDirected) "digraph" else "graph" + val connector = if (isDirected) "->" else "--" + + builder.append("$graphType ${graph.name} {\n") + + for (vertex in graph.startVertices) { + builder.append(" start $connector $vertex;\n") + } + + for ((from, edges) in graph.edges) { + for (edge in edges) { + val to = edge.targetVertex + val label = edge.label.terminal.toString() + builder.append(" $from $connector $to [label = $label];\n") + } + } + + builder.append("}") + return builder.toString() + } +} diff --git a/solver/src/main/kotlin/org/ucfs/input/utils/dot/GraphFromDotVisitor.kt b/solver/src/main/kotlin/org/ucfs/input/utils/dot/GraphFromDotVisitor.kt new file mode 100644 index 000000000..bc668bab7 --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/input/utils/dot/GraphFromDotVisitor.kt @@ -0,0 +1,61 @@ +package org.ucfs.input.utils.dot + +import org.ucfs.input.InputGraph +import org.ucfs.input.TerminalInputLabel +import org.ucfs.rsm.symbol.Term + +class GraphFromDotVisitor : DotBaseVisitor>() { + lateinit var graph: InputGraph + + override fun visitGraph(ctx: DotParser.GraphContext?): InputGraph { + graph = InputGraph() + super.visitGraph(ctx) + ctx?.id_()?.let { graph.name = it.text } + return graph + } + + + private fun getNodeId(vertexView: String): Int { + return vertexView.toInt() + } + + private fun parseSimpleEdge(edgeView: String): TerminalInputLabel { + val viewWithoutQuotes = edgeView.substring(1, edgeView.length - 1) + return TerminalInputLabel(Term(viewWithoutQuotes)) + } + + override fun visitEdge_stmt(ctx: DotParser.Edge_stmtContext?): InputGraph { + val tos = ctx?.edgeRHS()?.node_id() + //we don't handle subgraph here + ?: return super.visitEdge_stmt(ctx) + if (tos.size > 1) { + throw Exception("we can't handle transitives in dot yet!") + } + val to = getNodeId(tos[0].text) + if (ctx.node_id()?.text == "start") { + graph.addVertex(to) + graph.addStartVertex(to) + } else { + val from = getNodeId(ctx.node_id().text) + val attrs = ctx.attr_list().attr() ?: throw Exception("we can't handle edges without labels yet!") + + val labelNode = attrs.find { it.label_name.text == "label" } + ?: throw Exception("we can't handle edges without labels yet!") + graph.addVertex(from) + graph.addVertex(to) + graph.addEdge(from, parseSimpleEdge(labelNode.label_value.text), to) + } + super.visitEdge_stmt(ctx) + return graph + } + + override fun visitNode_stmt(ctx: DotParser.Node_stmtContext?): InputGraph { + if (ctx?.node_id()?.text == "start") { + return super.visitNode_stmt(ctx) + + } + //add node info + super.visitNode_stmt(ctx) + return graph + } +} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/intersection/IIntersectionEngine.kt b/solver/src/main/kotlin/org/ucfs/intersection/IIntersectionEngine.kt index 44d5b0583..7747d850f 100644 --- a/solver/src/main/kotlin/org/ucfs/intersection/IIntersectionEngine.kt +++ b/solver/src/main/kotlin/org/ucfs/intersection/IIntersectionEngine.kt @@ -3,12 +3,10 @@ package org.ucfs.intersection import org.ucfs.descriptors.Descriptor import org.ucfs.input.ILabel import org.ucfs.parser.IGll -import org.ucfs.sppf.node.SppfNode interface IIntersectionEngine { fun handleEdges( gll: IGll, descriptor: Descriptor, - sppfNode: SppfNode? ) } \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/intersection/IntersectionEngine.kt b/solver/src/main/kotlin/org/ucfs/intersection/IntersectionEngine.kt index 6c46e1b5e..ce9338808 100644 --- a/solver/src/main/kotlin/org/ucfs/intersection/IntersectionEngine.kt +++ b/solver/src/main/kotlin/org/ucfs/intersection/IntersectionEngine.kt @@ -3,45 +3,40 @@ package org.ucfs.intersection import org.ucfs.descriptors.Descriptor import org.ucfs.input.ILabel import org.ucfs.parser.IGll -import org.ucfs.sppf.node.SppfNode +import org.ucfs.rsm.symbol.ITerminal +import org.ucfs.rsm.symbol.Nonterminal object IntersectionEngine : IIntersectionEngine { + /** * Process outgoing edges from input position in given descriptor, according to processing logic, represented as * separate functions for both outgoing terminal and nonterminal edges from rsmState in descriptor * @param gll - Gll parser instance * @param descriptor - descriptor, represents current parsing stage - * @param sppfNode - root node of derivation tree, corresponds to already parsed portion of input */ override fun handleEdges( gll: IGll, descriptor: Descriptor, - sppfNode: SppfNode? ) { - val rsmState = descriptor.rsmState - val inputPosition = descriptor.inputPosition - val terminalEdges = rsmState.terminalEdges - val nonterminalEdges = rsmState.nonterminalEdges - //input contains only terminal edges - for (inputEdge in gll.ctx.input.getEdges(inputPosition)) { - val terminal = inputEdge.label.terminal - if (terminal == null) { - gll.handleTerminalOrEpsilonEdge(descriptor, sppfNode, null, descriptor.rsmState, inputEdge.head, 0) + for (inputEdge in gll.ctx.input.getEdges(descriptor.inputPosition)) { + val inputTerminal = inputEdge.label.terminal + val rsmEdge = descriptor.rsmState.terminalEdgesStorage.find { + it.symbol == inputTerminal } - else{ - val targetStates = terminalEdges[inputEdge.label.terminal] - if (targetStates != null) { - for (targetState in targetStates) { - gll.handleTerminalOrEpsilonEdge(descriptor, sppfNode, terminal, targetState, inputEdge.head, 0) - } - } + if (rsmEdge != null) { + gll.handleTerminalEdge( + descriptor, inputEdge, rsmEdge.destinationState, rsmEdge.symbol as ITerminal + ) } } - for ((edgeNonterminal, targetStates) in nonterminalEdges) { - gll.handleNonterminalEdge(descriptor, edgeNonterminal, targetStates, sppfNode) + for (nonterminalEdge in descriptor.rsmState.nonterminalEdgesStorage) { + gll.handleNonterminalEdge( + descriptor, nonterminalEdge.destinationState, nonterminalEdge.symbol as Nonterminal + ) } } -} \ No newline at end of file +} + diff --git a/solver/src/main/kotlin/org/ucfs/intersection/RecoveryIntersection.kt b/solver/src/main/kotlin/org/ucfs/intersection/RecoveryIntersection.kt deleted file mode 100644 index 4741bb0af..000000000 --- a/solver/src/main/kotlin/org/ucfs/intersection/RecoveryIntersection.kt +++ /dev/null @@ -1,139 +0,0 @@ -package org.ucfs.intersection - -import org.ucfs.descriptors.Descriptor -import org.ucfs.input.Edge -import org.ucfs.input.ILabel -import org.ucfs.input.RecoveryEdge -import org.ucfs.parser.IGll -import org.ucfs.rsm.RsmState -import org.ucfs.rsm.symbol.ITerminal -import org.ucfs.sppf.node.SppfNode - -object RecoveryIntersection : IIntersectionEngine{ - /** - * Process outgoing edges from input position in given descriptor, according to processing logic, represented as - * separate functions for processing both outgoing terminal and nonterminal edges from rsmState in descriptor. - * Additionally, considers error recovering edges in input graph. Those are the edges that were not present in - * initial graph, but could be useful later to successfully parse input - * @param gll - Gll parser instance - * @param descriptor - descriptor, represents current parsing stage - * @param sppfNode - root node of derivation tree, corresponds to already parsed portion of input - */ - override fun handleEdges( - gll: IGll, descriptor: Descriptor, sppfNode: SppfNode? - ) { - IntersectionEngine.handleEdges(gll, descriptor, sppfNode) - handleRecoveryEdges(gll, descriptor) - } - - /** - * Collects all possible edges, via which we can traverse in RSM - * @param descriptor - current parsing stage - * @return Map terminal -> (destination, weight) - */ - private fun createRecoveryEdges( - gll: IGll, descriptor: Descriptor - ): HashSet> { - val inputPosition = descriptor.inputPosition - val rsmState = descriptor.rsmState - val terminalEdges = rsmState.terminalEdges - - val errorRecoveryEdges = HashSet>() - val currentEdges = gll.ctx.input.getEdges(inputPosition) - - if (currentEdges.isNotEmpty()) { - addTerminalRecoveryEdges(terminalEdges, errorRecoveryEdges, inputPosition, rsmState, currentEdges) - } else { - addEpsilonRecoveryEdges(terminalEdges, errorRecoveryEdges, inputPosition, rsmState) - } - - return errorRecoveryEdges - } - - /** - * Collects edges with epsilon on label. This corresponds to deletion in input - * @param terminalEdges - outgoing terminal edges from current rsmState - * @param errorRecoveryEdges - reference to map of all error recovery edges - * @param inputPosition - current vertex in input graph - * @param rsmState - current rsmState - */ - private fun addEpsilonRecoveryEdges( - terminalEdges: HashMap>, - errorRecoveryEdges: HashSet>, - inputPosition: VertexType, - rsmState: RsmState - ) { - for (terminal in rsmState.errorRecoveryLabels) { - if (!terminalEdges[terminal].isNullOrEmpty()) { - errorRecoveryEdges.add(RecoveryEdge(terminal, inputPosition, weight = 1)) - } - } - } - - /** - * Collects edges with terminal on label. This corresponds to insertion in input - * @param terminalEdges - outgoing terminal edges from current rsmState - * @param errorRecoveryEdges - reference to map of all error recovery edges - * @param inputPosition - current vertex in input graph - * @param rsmState - current rsmState - * @param currentEdges - outgoing edges from current vertex in input graph - */ - private fun addTerminalRecoveryEdges( - terminalEdges: HashMap>, - errorRecoveryEdges: HashSet>, - inputPosition: VertexType, - rsmState: RsmState, - currentEdges: MutableList> - ) { - for (currentEdge in currentEdges) { - if (currentEdge.label.terminal == null) continue - val currentTerminal = currentEdge.label.terminal!! - - val coveredByCurrentTerminal: HashSet = terminalEdges[currentTerminal] ?: HashSet() - - for (terminal in rsmState.errorRecoveryLabels) { - //accessible states - val coveredByTerminal = HashSet(terminalEdges[terminal] as HashSet) - - coveredByCurrentTerminal.forEach { coveredByTerminal.remove(it) } - - if (terminal != currentTerminal && coveredByTerminal.isNotEmpty()) { - errorRecoveryEdges.add(RecoveryEdge(terminal, inputPosition, weight = 1)) - } - } - errorRecoveryEdges.add(RecoveryEdge(null, currentEdge.head, weight = 1)) - } - } - - /** - * Creates and processes error recovery edges and adds corresponding error recovery descriptors to handling - * @param gll - GLL parser instance - * @param descriptor - current parsing stage - */ - fun handleRecoveryEdges( - gll: IGll, descriptor: Descriptor - ) { - val errorRecoveryEdges: HashSet> = createRecoveryEdges(gll, descriptor) - val terminalEdges = descriptor.rsmState.terminalEdges - - for (errorRecoveryEdge in errorRecoveryEdges) { - val terminal = errorRecoveryEdge.label - val head = errorRecoveryEdge.head - val weight = errorRecoveryEdge.weight - - if (terminal == null) { - gll.handleTerminalOrEpsilonEdge( - descriptor, descriptor.sppfNode, null, descriptor.rsmState, head, weight - ) - } else if (terminalEdges.containsKey(errorRecoveryEdge.label)) { - for (targetState in terminalEdges.getValue(terminal)) { - gll.handleTerminalOrEpsilonEdge( - descriptor, descriptor.sppfNode, terminal, targetState, head, weight - ) - } - } - } - - } - -} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/parser/Gll.kt b/solver/src/main/kotlin/org/ucfs/parser/Gll.kt index 410a8ecb6..722e3d9ac 100644 --- a/solver/src/main/kotlin/org/ucfs/parser/Gll.kt +++ b/solver/src/main/kotlin/org/ucfs/parser/Gll.kt @@ -1,17 +1,14 @@ package org.ucfs.parser import org.ucfs.descriptors.Descriptor +import org.ucfs.gss.GssEdge import org.ucfs.input.IInputGraph import org.ucfs.input.ILabel import org.ucfs.intersection.IIntersectionEngine import org.ucfs.intersection.IntersectionEngine -import org.ucfs.intersection.RecoveryIntersection import org.ucfs.parser.context.Context -import org.ucfs.parser.context.IContext -import org.ucfs.parser.context.RecoveryContext import org.ucfs.rsm.RsmState -import org.ucfs.sppf.node.ISppfNode -import org.ucfs.sppf.node.SppfNode +import org.ucfs.sppf.node.* /** * Gll Factory @@ -19,7 +16,7 @@ import org.ucfs.sppf.node.SppfNode * @param LabelType - type of label on edges in input graph */ class Gll private constructor( - override var ctx: IContext, val engine: IIntersectionEngine + override var ctx: Context, private val engine: IIntersectionEngine ) : IGll { companion object { @@ -34,60 +31,68 @@ class Gll private constructor( ): Gll { return Gll(Context(startState, inputGraph), IntersectionEngine) } + } - /** - * Part of error recovery mechanism - * Creates instance of incremental Gll with error recovery - * @param startState - starting state of accepting nonterminal in RSM - * @param inputGraph - input graph - * @return recovery instance of gll parser - */ - fun recoveryGll( - startState: RsmState, inputGraph: IInputGraph - ): Gll { - return Gll(RecoveryContext(startState, inputGraph), RecoveryIntersection) - } + private fun getEpsilonRange(descriptor: Descriptor): RangeSppfNode { + val input = InputRange( + descriptor.inputPosition, + descriptor.inputPosition, + ) + val rsm = RsmRange( + 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 } - /** - * Part of incrementality mechanism. - * Perform incremental parsing, via restoring and adding to handling already processed descriptors, - * which contain given vertex as their field - * @param vertex - vertex in input graph, which outgoing edges were modified - */ - fun parse(vertex: VertexType): Pair?, HashMap, Int>> { - ctx.descriptors.restoreDescriptors(vertex) - ctx.sppf.invalidate(vertex, ctx.parseResult as ISppfNode) - ctx.parseResult = null - return parse() + private fun handlePoppedGssEdge( + poppedGssEdge: GssEdge, descriptor: Descriptor, childSppf: RangeSppfNode + ) { + val leftRange = poppedGssEdge.matchedRange + val startRsmState = if (poppedGssEdge.matchedRange.type == EmptyType) poppedGssEdge.gssNode.rsm + else poppedGssEdge.matchedRange.rsmRange!!.to + val rightRange = ctx.sppfStorage.addNode( + 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 newDescriptor = Descriptor( + descriptor.inputPosition, poppedGssEdge.gssNode, poppedGssEdge.state, newRange + ) + ctx.descriptors.add(newDescriptor) } /** * Processes descriptor * @param descriptor - descriptor to process */ - override fun parse(descriptor: Descriptor) { - val state = descriptor.rsmState - val pos = descriptor.inputPosition - val sppfNode = descriptor.sppfNode - val epsilonSppfNode = ctx.sppf.getEpsilonSppfNode(descriptor) - val leftExtent = sppfNode?.leftExtent - val rightExtent = sppfNode?.rightExtent - - if (state.isFinal) { - pop(descriptor.gssNode, sppfNode ?: epsilonSppfNode, pos) - } - + override fun handleDescriptor(descriptor: Descriptor) { ctx.descriptors.addToHandled(descriptor) - - if (state.isStart && state.isFinal) { - checkAcceptance( - epsilonSppfNode, epsilonSppfNode!!.leftExtent, epsilonSppfNode!!.rightExtent, state.nonterminal - ) + if (descriptor.rsmState.isFinal) { + val matchedRange = if (descriptor.sppfNode.type == EmptyType) { + getEpsilonRange(descriptor) + } else { + descriptor.sppfNode + } + for (poppedEdge in ctx.gss.pop(descriptor, matchedRange)) { + handlePoppedGssEdge(poppedEdge, descriptor, matchedRange) + } + if (descriptor.gssNode.outgoingEdges.isEmpty() && descriptor.gssNode.rsm.isStart) { + ctx.parseResult = matchedRange + } } - checkAcceptance(sppfNode, leftExtent, rightExtent, state.nonterminal) - engine.handleEdges(this, descriptor, sppfNode) + engine.handleEdges(this, descriptor) } } diff --git a/solver/src/main/kotlin/org/ucfs/parser/IGll.kt b/solver/src/main/kotlin/org/ucfs/parser/IGll.kt index 9b4a760d1..1b156b726 100644 --- a/solver/src/main/kotlin/org/ucfs/parser/IGll.kt +++ b/solver/src/main/kotlin/org/ucfs/parser/IGll.kt @@ -1,224 +1,117 @@ package org.ucfs.parser import org.ucfs.descriptors.Descriptor -import org.ucfs.gss.GssNode +import org.ucfs.input.Edge import org.ucfs.input.IInputGraph import org.ucfs.input.ILabel -import org.ucfs.parser.context.IContext +import org.ucfs.parser.context.Context import org.ucfs.rsm.RsmState import org.ucfs.rsm.symbol.ITerminal import org.ucfs.rsm.symbol.Nonterminal -import org.ucfs.sppf.node.SppfNode -import org.ucfs.sppf.node.SymbolSppfNode +import org.ucfs.sppf.node.* /** * Interface for Gll parser - * @param VertexType - type of vertex in input graph + * @param InputNodeType - type of vertex in input graph * @param LabelType - type of label on edges in input graph */ -interface IGll { +interface IGll { /** * Parser configuration */ - var ctx: IContext + var ctx: Context /** * Main parsing loop. Iterates over available descriptors and processes them * @return Pair of derivation tree root and collection of reachability pairs */ - fun parse(): Pair?, HashMap, Int>> { + fun parse(): RangeSppfNode? { + ctx.parseResult = null initDescriptors(ctx.input) - // Continue parsing until all default descriptors processed - var curDescriptor = ctx.nextDescriptorToHandle() + var curDescriptor = ctx.descriptors.nextToHandle() + while (curDescriptor != null) { - parse(curDescriptor) - curDescriptor = ctx.nextDescriptorToHandle() + handleDescriptor(curDescriptor) + curDescriptor = ctx.descriptors.nextToHandle() } - return Pair(ctx.parseResult, ctx.reachabilityPairs) + return ctx.parseResult } /** * Processes descriptor * @param descriptor - descriptor to process */ - fun parse(descriptor: Descriptor) + fun handleDescriptor(descriptor: Descriptor) /** * Creates descriptors for all starting vertices in input graph * @param input - input graph */ - fun initDescriptors(input: IInputGraph) { + fun initDescriptors(input: IInputGraph) { for (startVertex in input.getInputStartVertices()) { - val descriptor = Descriptor( - ctx.startState, - getOrCreateGssNode(ctx.startState.nonterminal, startVertex, weight = 0), - sppfNode = null, - startVertex - ) - addDescriptor(descriptor) - } - } - /** - * Creates or retrieves gssNode with given parameters - * @param nonterminal - nonterminal, corresponding to grammar slot aA·b - * @param inputPosition - vertex in input graph - * @param weight - weight of minimal left part, i.e. derivation tree with minimum weight - * @return gssNode - */ - fun getOrCreateGssNode( - nonterminal: Nonterminal, - inputPosition: VertexType, - weight: Int, - ): GssNode { - val gssNode = GssNode(nonterminal, inputPosition, weight) - val storedNode = ctx.createdGssNodes.computeIfAbsent(gssNode) { gssNode } - if (storedNode.minWeightOfLeftPart > weight) { - storedNode.minWeightOfLeftPart = weight + val gssNode = ctx.gss.getOrCreateNode(startVertex, ctx.startState) + val startDescriptor = Descriptor( + startVertex, gssNode, ctx.startState, getEmptyRange() + ) + ctx.descriptors.add(startDescriptor) } - return storedNode } - /** - * Creates gssNode with given parameters, along with corresponding edge - * @param nonterminal - nonterminal, corresponding to grammar slot aA·b - * @param rsmState - rsmState - * @param gssNode - tail of the edge in Gss - * @param sppfNode - derivation tree to store on edge in Gss - * @param inputPosition - vertex in input graph - */ - fun createGssNode( - nonterminal: Nonterminal, - rsmState: RsmState, - gssNode: GssNode, - sppfNode: SppfNode?, - inputPosition: VertexType, - ): GssNode { - val newNode = getOrCreateGssNode( - nonterminal, inputPosition, weight = gssNode.minWeightOfLeftPart + (sppfNode?.weight ?: 0) + fun handleNonterminalEdge( + descriptor: Descriptor, destinationRsmState: RsmState, edgeNonterminal: Nonterminal + ) { + val rsmStartState = edgeNonterminal.startState + val (newGssNode, positionToPops) = ctx.gss.addEdge( + descriptor.gssNode, destinationRsmState, descriptor.inputPosition, rsmStartState, descriptor.sppfNode ) - if (newNode.addEdge(rsmState, sppfNode, gssNode)) { - if (ctx.poppedGssNodes.containsKey(newNode)) { - for (popped in ctx.poppedGssNodes[newNode]!!) { - val descriptor = Descriptor( - rsmState, gssNode, ctx.sppf.getParentNodeAfterTerminal(rsmState, sppfNode, popped!!), popped.rightExtent - ) - addDescriptor(descriptor) - } - } - } - - return newNode - } - - /** - * Adds descriptor to processing - * @param descriptor - descriptor to add - */ - fun addDescriptor(descriptor: Descriptor) { - ctx.addDescriptor(descriptor) - } - - /** - * Iterates over all outgoing edges from current gssNode, collects derivation trees, stored on edges and - * combines them with current derivation tree, given as sppfNode - * @param gssNode - current node in top layer of Graph Structured Stack - * @param sppfNode - derivation tree, corresponding to parsed portion of input - * @param inputPosition - vertex in input graph - */ - fun pop(gssNode: GssNode, sppfNode: SppfNode?, inputPosition: VertexType) { - if (!ctx.poppedGssNodes.containsKey(gssNode)) ctx.poppedGssNodes[gssNode] = HashSet() - ctx.poppedGssNodes.getValue(gssNode).add(sppfNode) - - for ((label, target) in gssNode.edges) { - for (node in target) { - val descriptor = Descriptor( - label.first, node, ctx.sppf.getParentNodeAfterTerminal(label.first, label.second, sppfNode!!), inputPosition + var newDescriptor = Descriptor( + descriptor.inputPosition, newGssNode, rsmStartState, getEmptyRange() + ) + ctx.descriptors.add(newDescriptor) + + for (rangeToPop in positionToPops) { + val leftSubRange = descriptor.sppfNode + val rightSubRange = ctx.sppfStorage.addNode( + RangeSppfNode( + rangeToPop.inputRange, RsmRange( + descriptor.rsmState, destinationRsmState + ), NonterminalType(rsmStartState) ) - addDescriptor(descriptor) - } - } - } - - /** - * Check whether range leftExtent - rightExtent belongs to the language, defined by given nonterminal. And if so - - * updates parseResult field in context with given sppfNode, i.e. derivation tree of corresponding range - * @param sppfNode - derivation tree of the range leftExtent - rightExtent - * @param leftExtent - left limit of the range - * @param rightExtent - right limit of the range - * @param nonterminal - nonterminal, which defines language we check belonging to - */ - fun checkAcceptance( - sppfNode: SppfNode?, leftExtent: VertexType?, rightExtent: VertexType?, nonterminal: Nonterminal - ) { - if (sppfNode is SymbolSppfNode && nonterminal == ctx.startState.nonterminal && ctx.input.isStart( - leftExtent!! - ) && ctx.input.isFinal(rightExtent!!) - ) { - if (ctx.parseResult == null || ctx.parseResult!!.weight > sppfNode.weight) { - ctx.parseResult = sppfNode - } - - //update reachability - val pair = Pair(leftExtent, rightExtent) - val distance = ctx.sppf.minDistance(sppfNode) - - ctx.reachabilityPairs[pair] = if (ctx.reachabilityPairs.containsKey(pair)) { - minOf(distance, ctx.reachabilityPairs[pair]!!) - } else { - distance - } - } - } + ) + val newSppfNode = ctx.sppfStorage.addNode(leftSubRange, rightSubRange) - /** - * Function for processing nonterminal edges in RSM - * @param descriptor - current parsing stage - * @param nonterminal - nonterminal, defining box in RSM - * @param targetStates - set of all adjacent nodes in RSM - * @param sppfNode - derivation tree of already parsed portion of input - */ - fun handleNonterminalEdge( - descriptor: Descriptor, - nonterminal: Nonterminal, - targetStates: HashSet, - sppfNode: SppfNode? - ) { - for (target in targetStates) { - val newDescriptor = Descriptor( - nonterminal.startState, - createGssNode(nonterminal, target, descriptor.gssNode, sppfNode, descriptor.inputPosition), - sppfNode = null, - descriptor.inputPosition + //TODO why these parameters??? + newDescriptor = Descriptor( + rangeToPop.inputRange!!.to, descriptor.gssNode, destinationRsmState, newSppfNode ) - ctx.addDescriptor(newDescriptor) + ctx.descriptors.add(newDescriptor) } } - /** - * Function for processing terminal edges in RSM, with respect to terminal or epsilon value on label - * in input graph edges - * @param descriptor - current parsing stage - * @param sppfNode - derivation tree of already parsed portion of input - * @param terminal - value on label in input graph - * @param targetState - head of edge in RSM - * @param targetVertex - head of edge in input graph - * @param targetWeight - weight that should be assigned to newly created nodes in sppf - */ - fun handleTerminalOrEpsilonEdge( - descriptor: Descriptor, - sppfNode: SppfNode?, - terminal: ITerminal?, - targetState: RsmState, - targetVertex: VertexType, - targetWeight: Int, + + fun handleTerminalEdge( + descriptor: Descriptor, + inputEdge: Edge, + destinationRsmState: RsmState, + terminal: ITerminal ) { - val terminalNode = ctx.sppf.getOrCreateTerminalSppfNode(terminal, descriptor.inputPosition, targetVertex, targetWeight) - val parentNode = ctx.sppf.getParentNodeAfterTerminal(targetState, sppfNode, terminalNode) - val newDescriptor = Descriptor(targetState, descriptor.gssNode, parentNode, targetVertex) - addDescriptor(newDescriptor) + var terminalSppfNode = ctx.sppfStorage.addNode( + InputRange( + descriptor.inputPosition, + inputEdge.targetVertex, + ), RsmRange( + descriptor.rsmState, + destinationRsmState, + ), terminal + ) + val intermediateOrTerminalSppf = ctx.sppfStorage.addNode(descriptor.sppfNode, terminalSppfNode) + val descriptorForTerminal = Descriptor( + inputEdge.targetVertex, descriptor.gssNode, destinationRsmState, intermediateOrTerminalSppf + ) + ctx.descriptors.add(descriptorForTerminal) } } diff --git a/solver/src/main/kotlin/org/ucfs/parser/context/Context.kt b/solver/src/main/kotlin/org/ucfs/parser/context/Context.kt index 6498b5539..a18eefef4 100644 --- a/solver/src/main/kotlin/org/ucfs/parser/context/Context.kt +++ b/solver/src/main/kotlin/org/ucfs/parser/context/Context.kt @@ -1,25 +1,40 @@ package org.ucfs.parser.context +import org.ucfs.descriptors.Descriptor import org.ucfs.descriptors.DescriptorsStorage -import org.ucfs.gss.GssNode +import org.ucfs.gss.GraphStructuredStack import org.ucfs.input.IInputGraph import org.ucfs.input.ILabel import org.ucfs.rsm.RsmState -import org.ucfs.sppf.Sppf -import org.ucfs.sppf.node.SppfNode +import org.ucfs.sppf.SppfStorage +import org.ucfs.sppf.node.RangeSppfNode + /** - * Default context for parsing without error recovery - * @param VertexType - type of vertex in input graph + * @param InputNodeType - type of vertex in input graph * @param LabelType - type of label on edges in input graph */ -class Context( - override val startState: RsmState, override val input: IInputGraph -) : IContext { - override val descriptors: DescriptorsStorage = DescriptorsStorage() - override val sppf: Sppf = Sppf() - override val poppedGssNodes: HashMap, HashSet?>> = HashMap() - override val createdGssNodes: HashMap, GssNode> = HashMap() - override val reachabilityPairs: HashMap, Int> = HashMap() - override var parseResult: SppfNode? = null -} +class Context ( + /** + * Starting state of accepting Nonterminal in RSM + */ + val startState: RsmState, + val input: IInputGraph + + +) { + + /** + * Collection of descriptors + */ + val descriptors: DescriptorsStorage = DescriptorsStorage() + + /** + * Derivation trees storage + */ + val sppfStorage: SppfStorage = SppfStorage() + + val gss: GraphStructuredStack = GraphStructuredStack() + + var parseResult: RangeSppfNode? = null +} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/parser/context/IContext.kt b/solver/src/main/kotlin/org/ucfs/parser/context/IContext.kt deleted file mode 100644 index 821a047f3..000000000 --- a/solver/src/main/kotlin/org/ucfs/parser/context/IContext.kt +++ /dev/null @@ -1,95 +0,0 @@ -package org.ucfs.parser.context - -import org.ucfs.descriptors.Descriptor -import org.ucfs.descriptors.DescriptorsStorage -import org.ucfs.gss.GssNode -import org.ucfs.input.IInputGraph -import org.ucfs.input.ILabel -import org.ucfs.rsm.RsmState -import org.ucfs.sppf.Sppf -import org.ucfs.sppf.node.SppfNode -import org.ucfs.sppf.node.SymbolSppfNode - -/** - * Context interface. Represents configuration of Gll parser instance - * @param VertexType - type of vertex in input graph - * @param LabelType - type of label on edges in input graph - */ -interface IContext { - /** - * Starting state of accepting Nonterminal in RSM - */ - val startState: RsmState - - /** - * Input graph - */ - val input: IInputGraph - - /** - * Collection of descriptors - */ - val descriptors: DescriptorsStorage - - /** - * Derivation tree - */ - val sppf: Sppf - - /** - * Map gssNode -> Set of derivation trees that were obtained in gssNode, i.e. when processing descriptors, - * which contained gssNode as value in their field. - */ - val poppedGssNodes: HashMap, HashSet?>> - - /** - * Collection of created gssNodes, with O(1)-time access and search - */ - val createdGssNodes: HashMap, GssNode> - - /** - * Part of incrementality mechanism. - * Map (vertex, vertex) -> weight. Essentially, for every pair (first, second) stores weight of - * minimal path between first and second, such that corresponding path is accepted - */ - val reachabilityPairs: HashMap, Int> - - /** - * Parsing result. Either root of derivation tree of null - */ - var parseResult: SppfNode? - - /** - * Part of incrementality mechanism - * Adds descriptors to process. If the descriptor is "final", i.e. can be used to make a derivation, then - * remove from already handled and process again - * TODO get only the descriptors you need at the right time. - * TODO remove unnecessary descriptor processing. It's broke GLL invariant - * @param descriptor - descriptor to add - */ - fun addDescriptor(descriptor: Descriptor) { - val sppfNode = descriptor.sppfNode - val state = descriptor.rsmState - val leftExtent = sppfNode?.leftExtent - val rightExtent = sppfNode?.rightExtent - - if (parseResult == null && sppfNode is SymbolSppfNode<*> && state.nonterminal == startState.nonterminal && input.isStart( - leftExtent!! - ) && input.isFinal(rightExtent!!) - ) { - descriptors.removeFromHandled(descriptor) - } - descriptors.addToHandling(descriptor) - } - - /** - * Gets next descriptor to handle - * @return default descriptor if there is available one, null otherwise - */ - fun nextDescriptorToHandle(): Descriptor? { - if (!descriptors.defaultDescriptorsStorageIsEmpty()) { - return descriptors.next() - } - return null - } -} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/parser/context/RecoveryContext.kt b/solver/src/main/kotlin/org/ucfs/parser/context/RecoveryContext.kt deleted file mode 100644 index d4b77bd36..000000000 --- a/solver/src/main/kotlin/org/ucfs/parser/context/RecoveryContext.kt +++ /dev/null @@ -1,48 +0,0 @@ -package org.ucfs.parser.context - -import org.ucfs.descriptors.Descriptor -import org.ucfs.descriptors.RecoveringDescriptorsStorage -import org.ucfs.gss.GssNode -import org.ucfs.input.IInputGraph -import org.ucfs.input.ILabel -import org.ucfs.rsm.RsmState -import org.ucfs.sppf.RecoverySppf -import org.ucfs.sppf.node.SppfNode - -/** - * Recovery context for parsing with enabled error recovery - * @param VertexType - type of vertex in input graph - * @param LabelType - type of label on edges in input graph - */ -class RecoveryContext( - override val startState: RsmState, override val input: IInputGraph -) : IContext { - override val descriptors: RecoveringDescriptorsStorage = RecoveringDescriptorsStorage() - override val sppf: RecoverySppf = RecoverySppf() - override val poppedGssNodes: HashMap, HashSet?>> = HashMap() - override val createdGssNodes: HashMap, GssNode> = HashMap() - override val reachabilityPairs: HashMap, Int> = HashMap() - override var parseResult: SppfNode? = null - - /** - * Part of error recovery mechanism. - * Gets next descriptor to handle. First tries to get default descriptor. If there is non and derivation tree - * was not obtained, then tries to get recovery descriptor - * @return descriptor if available one, null otherwise - */ - override fun nextDescriptorToHandle(): Descriptor? { - if (!descriptors.defaultDescriptorsStorageIsEmpty()) { - return descriptors.next() - } - - // If string was not parsed - process recovery descriptors until first valid parse tree is found - // Due to the Error Recovery algorithm used it will be parse tree of the string with min editing cost - if (parseResult == null) { - //return recovery descriptor - return descriptors.next() - } - - return null - } -} - diff --git a/solver/src/main/kotlin/org/ucfs/rsm/RsmState.kt b/solver/src/main/kotlin/org/ucfs/rsm/RsmState.kt index a3e219e17..dfd042d19 100644 --- a/solver/src/main/kotlin/org/ucfs/rsm/RsmState.kt +++ b/solver/src/main/kotlin/org/ucfs/rsm/RsmState.kt @@ -7,80 +7,42 @@ import org.ucfs.rsm.symbol.ITerminal import org.ucfs.rsm.symbol.Nonterminal import org.ucfs.rsm.symbol.Symbol import java.util.* +import kotlin.collections.ArrayList -open class RsmState( +data class RsmEdge(val symbol: Symbol, val destinationState: RsmState) +//data class TerminalRsmEdge(val terminal: Term, val destinationState: RsmState) +//data class NonterminalRsmEdge(val nonterminal: Nonterminal, val destinationState: RsmState) + +data class RsmState( val nonterminal: Nonterminal, val isStart: Boolean = false, val isFinal: Boolean = false, + var numId: Int = nonterminal.getNextRsmStateId() ) { - val numId: Int = nonterminal.getNextRsmStateId() val id: String = "${nonterminal.name}_${(numId)}" val outgoingEdges - get() = terminalEdges.plus(nonterminalEdges) - - /** - * Map from terminal to edges set - */ - val terminalEdges = HashMap>() - - /** - * Map from nonterminal to edges set - */ - val nonterminalEdges = HashMap>() + get() = terminalEdgesStorage.plus(nonterminalEdgesStorage) - /** - * Keep a list of all available RsmStates - */ - val targetStates: HashSet = HashSet() + val terminalEdgesStorage = ArrayList() - /** - * Part of error recovery mechanism. - * A set of terminals that can be used to move from a given state to other states. - * Moreover, if there are several different edges that can be used to move to one state, - * then only 1 is chosen non-deterministically. - * TODO Maybe you can get rid of it or find a better optimization (?) - */ - val errorRecoveryLabels: HashSet = HashSet() + val nonterminalEdgesStorage = ArrayList() - override fun toString() = "RsmState(nonterminal=$nonterminal, isStart=$isStart, isFinal=$isFinal)" /** * Adds edge from current rsmState to given destinationState via given symbol, terminal or nonterminal * @param symbol - symbol to store on edge - * @param destinationState - head of edge + * @param destinationState */ - open fun addEdge(symbol: Symbol, destinationState: RsmState) { - val destinationStates: HashSet + fun addEdge(symbol: Symbol, destinationState: RsmState) { when (symbol) { - is ITerminal -> { - destinationStates = terminalEdges.getOrPut(symbol) { hashSetOf() } - addRecoveryInfo(symbol, destinationState) - } - - is Nonterminal -> { - destinationStates = nonterminalEdges.getOrPut(symbol) { hashSetOf() } - } - + is ITerminal -> terminalEdgesStorage.add(RsmEdge(symbol, destinationState)) + is Nonterminal -> nonterminalEdgesStorage.add(RsmEdge(symbol, destinationState)) else -> throw RsmException("Unsupported type of symbol") } - destinationStates.add(destinationState) - } - - /** - * Part of error recovery mechanism. - * Adds given rsmState to set of available rsmStates from current one, via given terminal - * @param terminal - terminal on edge - * @param head - destination state - */ - private fun addRecoveryInfo(terminal: ITerminal, head: RsmState) { - if (!targetStates.contains(head)) { - errorRecoveryLabels.add(terminal) - targetStates.add(head) - } } - protected open fun getNewState(regex: Regexp): RsmState { + protected fun getNewState(regex: Regexp): RsmState { return RsmState(this.nonterminal, isStart = false, regex.acceptEpsilon()) } diff --git a/solver/src/main/kotlin/org/ucfs/rsm/RsmWrite.kt b/solver/src/main/kotlin/org/ucfs/rsm/RsmWrite.kt index a53147564..0e36732e6 100644 --- a/solver/src/main/kotlin/org/ucfs/rsm/RsmWrite.kt +++ b/solver/src/main/kotlin/org/ucfs/rsm/RsmWrite.kt @@ -1,6 +1,5 @@ package org.ucfs.rsm -import org.ucfs.rsm.symbol.ITerminal import org.ucfs.rsm.symbol.Nonterminal import org.ucfs.rsm.symbol.Symbol import org.ucfs.rsm.symbol.Term @@ -17,14 +16,12 @@ private fun getAllStates(startState: RsmState): HashSet { if (!states.contains(state)) { states.add(state) - for ((symbol, destStates) in state.outgoingEdges) { + for ((symbol, destState) in state.outgoingEdges) { if (symbol is Nonterminal) { queue.addLast(symbol.startState) } - for (destState in destStates) { - queue.addLast(destState) - queue.addLast(destState.nonterminal.startState) - } + queue.addLast(destState) + queue.addLast(destState.nonterminal.startState) } } } @@ -75,9 +72,8 @@ fun writeRsmToTxt(startState: RsmState, pathToTXT: String) { } for (state in states) { - for ((symbol, destStates) in state.outgoingEdges) { + for ((symbol, destState) in state.outgoingEdges) { val (typeView, symbolView, typeLabel) = getSymbolView(symbol) - for (destState in destStates) { out.println( """${typeView}Edge( |tail=${state.id}, @@ -85,7 +81,6 @@ fun writeRsmToTxt(startState: RsmState, pathToTXT: String) { |$typeLabel=$typeView("$symbolView") |)""".trimMargin().replace("\n", "") ) - } } } } @@ -118,10 +113,8 @@ fun writeRsmToDot(startState: RsmState, filePath: String) { } states.forEach { state -> - state.outgoingEdges.forEach { (symbol, destStates) -> - destStates.forEach { destState -> + state.outgoingEdges.forEach { (symbol, destState) -> out.println("${state.id} -> ${destState.id} [label = \"${getView(symbol)}\"]") - } } } diff --git a/solver/src/main/kotlin/org/ucfs/rsm/symbol/Nonterminal.kt b/solver/src/main/kotlin/org/ucfs/rsm/symbol/Nonterminal.kt index c68da6e16..cdf4af191 100644 --- a/solver/src/main/kotlin/org/ucfs/rsm/symbol/Nonterminal.kt +++ b/solver/src/main/kotlin/org/ucfs/rsm/symbol/Nonterminal.kt @@ -24,9 +24,9 @@ data class Nonterminal(val name: String?) : Symbol { while (queue.isNotEmpty()) { val state = queue.remove() used.add(state) - for (nextState in state.outgoingEdges.values.flatten()) { - if (!used.contains(nextState)) { - queue.add(nextState) + for ((_, destinationState) in state.outgoingEdges) { + if (!used.contains(destinationState)) { + queue.add(destinationState) } } } diff --git a/solver/src/main/kotlin/org/ucfs/rsm/symbol/Term.kt b/solver/src/main/kotlin/org/ucfs/rsm/symbol/Term.kt index 5f1f0ab63..a72eb6ad0 100644 --- a/solver/src/main/kotlin/org/ucfs/rsm/symbol/Term.kt +++ b/solver/src/main/kotlin/org/ucfs/rsm/symbol/Term.kt @@ -3,7 +3,7 @@ package org.ucfs.rsm.symbol import org.ucfs.grammar.combinator.regexp.DerivedSymbol import org.ucfs.parser.ParsingException -class Term(val value: TerminalType) : ITerminal, DerivedSymbol { +data class Term(val value: TerminalType) : ITerminal, DerivedSymbol { override fun toString() = value.toString() override fun getComparator(): Comparator { //TODO improve comparable interfaces @@ -12,21 +12,11 @@ class Term(val value: TerminalType) : ITerminal, DerivedSymbol { override fun compare(a: ITerminal, b: ITerminal): Int { if (a !is Term<*> || b !is Term<*>) { throw ParsingException( - "used comparator for $javaClass, " + - "but got elements of ${a.javaClass}$ and ${b.javaClass}\$" + "used comparator for $javaClass, " + "but got elements of ${a.javaClass}$ and ${b.javaClass}\$" ) } return a.value.toString().compareTo(b.value.toString()) } } } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Term<*>) return false - return value == other.value - } - - val hashCode: Int = value.hashCode() - override fun hashCode() = hashCode } \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/sppf/RecoverySppf.kt b/solver/src/main/kotlin/org/ucfs/sppf/RecoverySppf.kt deleted file mode 100644 index 771acfba3..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/RecoverySppf.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.ucfs.sppf - -import org.ucfs.rsm.RsmState -import org.ucfs.sppf.node.* - -/** - * Part of error recovery mechanism. - * Sppf with additional support for updating weights, when necessary - * @param VertexType - type of vertex in input graph - */ -class RecoverySppf : Sppf() { - /** - * Part of error recovery mechanism. - * Receives two subtrees of SPPF and connects them via PackedNode. Additionally, for newly created/retrieved - * parent sppfNode traverses upwards, updating weights on the path, when necessary - * @param rsmState - current rsmState - * @param sppfNode - left subtree - * @param nextSppfNode - right subtree - * @return ParentNode, which has combined subtree as alternative derivation - */ - override fun getParentNodeAfterTerminal( - rsmState: RsmState, - sppfNode: SppfNode?, - nextSppfNode: SppfNode, - ): SppfNode { - val parent = super.getParentNodeAfterTerminal(rsmState, sppfNode, nextSppfNode) - updateWeights(parent) - return parent - } - - /** - * Part of error recovery mechanism. - * Traverses from given node all the way up to the root, updating weights on the path when necessary - * @param sppfNode - given sppfNode to start traverse from - */ - fun updateWeights(sppfNode: ISppfNode) { - val added = HashSet(listOf(sppfNode)) - val queue = ArrayDeque(listOf(sppfNode)) - var curSPPFNode: ISppfNode - - while (queue.isNotEmpty()) { - curSPPFNode = queue.removeFirst() - val oldWeight = curSPPFNode.weight - - when (curSPPFNode) { - is NonterminalSppfNode<*> -> { - var newWeight = Int.MAX_VALUE - - curSPPFNode.children.forEach { newWeight = minOf(newWeight, it.weight) } - - if (oldWeight > newWeight) { - curSPPFNode.weight = newWeight - - curSPPFNode.children.forEach { if (it.weight > newWeight) it.parents.remove(curSPPFNode) } - curSPPFNode.children.removeIf { it.weight > newWeight } - - curSPPFNode.parents.forEach { - queue.addLast(it) - added.add(it) - } - } - } - - is PackedSppfNode<*> -> { - val newWeight = (curSPPFNode.leftSppfNode?.weight ?: 0) + (curSPPFNode.rightSppfNode?.weight ?: 0) - - if (oldWeight > newWeight) { - curSPPFNode.weight = newWeight - - curSPPFNode.parents.forEach { - queue.addLast(it) - added.add(it) - } - } - } - - else -> { - throw Error("Terminal node can not be parent") - } - } - } - } - -} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/sppf/Sppf.kt b/solver/src/main/kotlin/org/ucfs/sppf/Sppf.kt deleted file mode 100644 index 1fc440c3c..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/Sppf.kt +++ /dev/null @@ -1,334 +0,0 @@ -package org.ucfs.sppf - -import org.ucfs.descriptors.Descriptor -import org.ucfs.rsm.RsmState -import org.ucfs.rsm.symbol.ITerminal -import org.ucfs.rsm.symbol.Nonterminal -import org.ucfs.sppf.node.* - -/** - * Default sppf - * @param VertexType - type of vertex in input graph - */ -open class Sppf { - /** - * Collection of created sppfNodes with access and search in O(1) time - */ - private val createdSppfNodes: HashMap, SppfNode> = HashMap() - - /** - * Part of incrementality mechanism. - * Map vertex in input graph -> set of all created terminal nodes that have vertex as their leftExtent - */ - private val createdTerminalNodes: HashMap>> = HashMap() - - /** - * Used when rsmState in descriptor is both starting and final. In such case this function creates special - * epsilon node which is used to produce correct derivation tree - * @param descriptor - current parsing stage - * @return created epsilonNode - */ - fun getEpsilonSppfNode(descriptor: Descriptor): SppfNode? { - val rsmState = descriptor.rsmState - val sppfNode = descriptor.sppfNode - val inputPosition = descriptor.inputPosition - - return if (rsmState.isStart && rsmState.isFinal) { - // if nonterminal accepts epsilon - getParentNodeAfterTerminal( - rsmState, - sppfNode, - getOrCreateIntermediateSppfNode(rsmState, inputPosition, inputPosition, weight = 0) - ) - } else { - sppfNode - } - } - - /** - * Part of incrementality mechanism. - * Removes given node from collection of created. If the sppfNode is terminal, additionally removes it - * from set in createdTerminalNodes, corresponding to it's leftExtent - * @param sppfNode - sppfNode to remove - */ - fun removeNode(sppfNode: SppfNode) { - createdSppfNodes.remove(sppfNode) - if (sppfNode is TerminalSppfNode<*>) { - createdTerminalNodes.remove(sppfNode.leftExtent) - } - } - - /** - * Receives two subtrees of SPPF and connects them via PackedNode. - * If given subtrees repesent derivation tree for nonterminal and state is final, then retrieves or creates - * Symbol sppfNode, otherwise retrieves or creates Intermediate sppfNode - * @param rsmState - current rsmState - * @param sppfNode - left subtree - * @param nextSppfNode - right subtree - * @return ParentNode, which has combined subtree as alternative derivation - */ - open fun getParentNodeAfterTerminal( - rsmState: RsmState, - sppfNode: SppfNode?, - nextSppfNode: SppfNode, - ): SppfNode { - //there can't be only right extent by definition? - val leftExtent = sppfNode?.leftExtent ?: nextSppfNode.leftExtent - val rightExtent = nextSppfNode.rightExtent - - val packedNode = PackedSppfNode(nextSppfNode.leftExtent, rsmState, sppfNode, nextSppfNode) - - val parent: NonterminalSppfNode = - if (rsmState.isFinal) getOrCreateSymbolSppfNode(rsmState.nonterminal, leftExtent, rightExtent, packedNode.weight) - else getOrCreateIntermediateSppfNode(rsmState, leftExtent, rightExtent, packedNode.weight) - - if (sppfNode != null || parent != nextSppfNode) { // Restrict SPPF from creating loops PARENT -> PACKED -> PARENT - sppfNode?.parents?.add(packedNode) - nextSppfNode.parents.add(packedNode) - packedNode.parents.add(parent) - - parent.children.add(packedNode) - } - - return parent - } - - - /** - *#TODO fix it for cases S = a | bc | abc - */ - private fun hasCommonSymbolInLeftExtend( - symbolSppfInLeft: SppfNode?, - rsmState: RsmState, - packedNode: PackedSppfNode - ): NonterminalSppfNode? { - if(symbolSppfInLeft is SymbolSppfNode && rsmState.isFinal){ - val commonNodes = symbolSppfInLeft.children.filter { packed -> packed.rsmState.targetStates.contains(rsmState) } - for(node in commonNodes){ - if(node.rightSppfNode == null){ - // node.rightSppfNode = - } - symbolSppfInLeft.children.remove(node) - // node. - - } - } - return null - } - - /** - * Creates or retrieves Terminal sppfNode with given parameters - * @param terminal - terminal value - * @param leftExtent - left limit subrange - * @param rightExtent - right limit subrange - * @param weight - weight of the node, default value is 0 - * @return Terminal sppfNode - */ - fun getOrCreateTerminalSppfNode( - terminal: ITerminal?, - leftExtent: VertexType, - rightExtent: VertexType, - weight: Int = 0, - ): SppfNode { - val node = TerminalSppfNode(terminal, leftExtent, rightExtent, weight) - - if (!createdSppfNodes.containsKey(node)) { - createdSppfNodes[node] = node - } - if (!createdTerminalNodes.containsKey(leftExtent)) { - createdTerminalNodes[leftExtent] = HashSet() - } - createdTerminalNodes[leftExtent]!!.add(createdSppfNodes[node] as TerminalSppfNode) - - return createdSppfNodes[node]!! - } - - /** - * Creates of retrieves Intermediate sppfNode with given parameters - * @param state - current rsmState - * @param leftExtent - left limit of subrange - * @param rightExtent - right limit of subrange - * @param weight - weight of the node, default value is Int.MAX_VALUE - */ - fun getOrCreateIntermediateSppfNode( - state: RsmState, - leftExtent: VertexType, - rightExtent: VertexType, - weight: Int = Int.MAX_VALUE, - ): NonterminalSppfNode { - val node = IntermediateSppfNode(state, leftExtent, rightExtent) - - node.weight = weight - - if (!createdSppfNodes.containsKey(node)) { - createdSppfNodes[node] = node - } - - return createdSppfNodes[node]!! as IntermediateSppfNode - } - - /** - * Creates or retrieves Symbol sppfNode with given parameters - * @param nonterminal - nonterminal - * @param leftExtent - left limit of subrange - * @param rightExtent - right limit of subrange - * @param weight - weight of the node, default value is Int.MAX_VALUE - */ - fun getOrCreateSymbolSppfNode( - nonterminal: Nonterminal, - leftExtent: VertexType, - rightExtent: VertexType, - weight: Int = Int.MAX_VALUE, - ): SymbolSppfNode { - val node = SymbolSppfNode(nonterminal, leftExtent, rightExtent) - - node.weight = weight - - if (!createdSppfNodes.containsKey(node)) { - createdSppfNodes[node] = node - } - - return createdSppfNodes[node]!! as SymbolSppfNode - } - - /** - * Part of incrementality mechanism. - * Traverses all the way up to the root from all Terminal sppfNodes, which have given vertex as their leftExtent, - * deconstructing the derivation trees on the path. - * parseResult is given to restrict algorithm from deleting parents of parseResult node - * @param vertex - position in input graph - * @param parseResult - current derivation tree - */ - fun invalidate(vertex: VertexType, parseResult: ISppfNode) { - val queue = ArrayDeque() - val added = HashSet() - var curSPPFNode: ISppfNode? - - createdTerminalNodes[vertex]!!.forEach { node -> - queue.add(node) - added.add(node) - } - - while (queue.isNotEmpty()) { - curSPPFNode = queue.removeFirst() - - when (curSPPFNode) { - is NonterminalSppfNode<*> -> { - if (curSPPFNode.children.isEmpty()) { - curSPPFNode.parents.forEach { packed -> - if (!added.contains(packed)) { - queue.addLast(packed) - added.add(packed) - } - if (packed is PackedSppfNode<*>) { - (packed as PackedSppfNode).rightSppfNode = null - (packed as PackedSppfNode).leftSppfNode = null - } - } - removeNode(curSPPFNode as SppfNode) - } - } - - is PackedSppfNode<*> -> { - curSPPFNode.parents.forEach { parent -> - if ((parent as NonterminalSppfNode<*>).children.contains(curSPPFNode)) { - if (!added.contains(parent)) { - queue.addLast(parent) - added.add(parent) - } - parent.children.remove(curSPPFNode) - } - } - } - - is TerminalSppfNode<*> -> { - curSPPFNode.parents.forEach { packed -> - if (!added.contains(packed)) { - queue.addLast(packed) - added.add(packed) - } - (packed as PackedSppfNode).rightSppfNode = null - (packed as PackedSppfNode).leftSppfNode = null - } - removeNode(curSPPFNode as SppfNode) - } - } - - if (curSPPFNode != parseResult) { - curSPPFNode.parents.clear() - } - } - } - - - /** - * Part of reachability mechanism. - * Calculates minimal distance between two vertices in input graph amongst all paths that are recognized by - * accepting nonterminal of RSM (intersection of language, defined by RSM, and input graph). - * @param root - root of the derivation tree - * @return minimal distance in number of edges in the path - */ - fun minDistance(root: ISppfNode): Int { - val minDistanceRecognisedBySymbol: HashMap, Int> = HashMap() - - val cycle = HashSet() - val visited = HashSet() - val stack = ArrayDeque(listOf(root)) - var curSPPFNode: ISppfNode - var minDistance = 0 - - while (stack.isNotEmpty()) { - curSPPFNode = stack.last() - visited.add(curSPPFNode) - - if (!cycle.contains(curSPPFNode)) { - cycle.add(curSPPFNode) - - when (curSPPFNode) { - is TerminalSppfNode<*> -> { - minDistance++ - } - - is PackedSppfNode<*> -> { - if (curSPPFNode.rightSppfNode != null) stack.add(curSPPFNode.rightSppfNode!!) - if (curSPPFNode.leftSppfNode != null) stack.add(curSPPFNode.leftSppfNode!!) - } - - is IntermediateSppfNode<*> -> { - if (curSPPFNode.children.isNotEmpty()) { - curSPPFNode.children.findLast { - it.rightSppfNode != curSPPFNode && it.leftSppfNode != curSPPFNode && !visited.contains( - it - ) - }?.let { stack.add(it) } - curSPPFNode.children.forEach { visited.add(it) } - } - } - - is SymbolSppfNode<*> -> { - if (minDistanceRecognisedBySymbol.containsKey(curSPPFNode)) { - minDistance += minDistanceRecognisedBySymbol[curSPPFNode]!! - } else { - if (curSPPFNode.children.isNotEmpty()) { - curSPPFNode.children.findLast { - it.rightSppfNode != curSPPFNode && it.leftSppfNode != curSPPFNode && !visited.contains( - it - ) - }?.let { stack.add(it) } - curSPPFNode.children.forEach { visited.add(it) } - } - } - } - } - } - if (curSPPFNode == stack.last()) { - stack.removeLast() - cycle.remove(curSPPFNode) - } - } - - minDistanceRecognisedBySymbol[root as SymbolSppfNode] = minDistance - - return minDistance - } -} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/sppf/SppfStorage.kt b/solver/src/main/kotlin/org/ucfs/sppf/SppfStorage.kt new file mode 100644 index 000000000..61b24598d --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/sppf/SppfStorage.kt @@ -0,0 +1,72 @@ +package org.ucfs.sppf + +import org.ucfs.rsm.RsmState +import org.ucfs.rsm.symbol.ITerminal +import org.ucfs.sppf.node.* + +/** + * @param InputEdgeType - type of vertex in input graph + */ +open class SppfStorage { + /** + * Collection of created sppfNodes with access and search in O(1) time + */ + private val createdSppfNodes: HashMap, RangeSppfNode> = HashMap() + + + fun addNode(node: RangeSppfNode): RangeSppfNode { + return createdSppfNodes.getOrPut(node, { node }) + } + + /** + * Add nonterminal node after pop + */ + fun addNode( + input: InputRange, rsm: RsmRange, startState: RsmState, childSppf: RangeSppfNode + ): RangeSppfNode { + return addNode(input, rsm, NonterminalType(startState), listOf(childSppf)) + } + + /** + * Add temrminal node + */ + fun addNode( + input: InputRange, rsm: RsmRange, terminal: ITerminal + ): RangeSppfNode { + return addNode(input, rsm, TerminalType(terminal)) + } + + fun addNode( + leftSubtree: RangeSppfNode, + rightSubtree: RangeSppfNode + ): RangeSppfNode { + if (leftSubtree.type == EmptyType) { + return rightSubtree + } + return addNode( + InputRange( + leftSubtree.inputRange!!.from, rightSubtree.inputRange!!.to + ), RsmRange( + leftSubtree.rsmRange!!.from, rightSubtree.rsmRange!!.to + ), IntermediateType( + leftSubtree.rsmRange.to, leftSubtree.inputRange.to + ), listOf(leftSubtree, rightSubtree) + ) + } + + private fun addNode( + input: InputRange, + rsm: RsmRange, + rangeType: RangeType, + children: List> = listOf() + ): RangeSppfNode { + val rangeNode = addNode(RangeSppfNode(input, rsm, Range)) + val valueRsm = if (rangeType is TerminalType<*>) null else rsm + val valueNode = addNode(RangeSppfNode(input, valueRsm, rangeType)) + if (!rangeNode.children.contains(valueNode)) { + rangeNode.children.add(valueNode) + } + valueNode.children.addAll(children) + return rangeNode + } +} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/sppf/buildStringFromSppf.kt b/solver/src/main/kotlin/org/ucfs/sppf/buildStringFromSppf.kt index 022911063..243923193 100644 --- a/solver/src/main/kotlin/org/ucfs/sppf/buildStringFromSppf.kt +++ b/solver/src/main/kotlin/org/ucfs/sppf/buildStringFromSppf.kt @@ -1,38 +1,37 @@ package org.ucfs.sppf -import org.ucfs.sppf.node.ISppfNode -import org.ucfs.sppf.node.PackedSppfNode -import org.ucfs.sppf.node.NonterminalSppfNode -import org.ucfs.sppf.node.TerminalSppfNode +import org.ucfs.sppf.node.* /** * Collects leaves of the derivation tree in order from left to right. * @return Ordered collection of terminals */ -fun buildTokenStreamFromSppf(sppfNode: ISppfNode): MutableList { - val visited: HashSet = HashSet() - val stack: ArrayDeque = ArrayDeque(listOf(sppfNode)) +fun buildTokenStreamFromSppf(sppfNode: RangeSppfNode): MutableList { + val visited: HashSet> = HashSet() + val stack: ArrayDeque> = ArrayDeque(listOf(sppfNode)) val result: MutableList = ArrayList() - var curNode: ISppfNode + var curNode: RangeSppfNode while (stack.isNotEmpty()) { curNode = stack.removeLast() visited.add(curNode) - when (curNode) { - is TerminalSppfNode<*> -> { - if (curNode.terminal != null) result.add(curNode.terminal!!.toString()) + val type = curNode.type + when (type) { + is TerminalType<*> -> { + result.add(type.terminal.toString()) } - is PackedSppfNode<*> -> { - if (curNode.rightSppfNode != null) stack.add(curNode.rightSppfNode!!) - if (curNode.leftSppfNode != null) stack.add(curNode.leftSppfNode!!) + is IntermediateType<*> -> { + for(child in curNode.children) { + stack.add(child) + } } - is NonterminalSppfNode<*> -> { + is NonterminalType -> { if (curNode.children.isNotEmpty()) { curNode.children.findLast { - it.rightSppfNode != curNode && it.leftSppfNode != curNode && !visited.contains( + !visited.contains( it ) }?.let { stack.add(it) } @@ -49,6 +48,6 @@ fun buildTokenStreamFromSppf(sppfNode: ISppfNode): MutableList { * Collects leaves of the derivation tree in order from left to right and joins them into one string * @return String value of recognized subrange */ -fun buildStringFromSppf(sppfNode: ISppfNode): String { +fun buildStringFromSppf(sppfNode: RangeSppfNode): String { return buildTokenStreamFromSppf(sppfNode).joinToString(separator = "") } \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/ISppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/ISppfNode.kt deleted file mode 100644 index 4608a9da3..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/ISppfNode.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.ucfs.sppf.node - -/** - * SppfNode interface - */ -interface ISppfNode { - var id: Int - /** - * Part of the error recovery mechanism - * weight of the node defines how many insertions/deletions are needed - * for the subrange to be recognised by corresponding Nonterminal - */ - var weight: Int - - /** - * Set of all nodes, that have current one as a child - */ - val parents: HashSet -} \ No newline at end of file diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/IntermediateSppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/IntermediateSppfNode.kt deleted file mode 100644 index d52c77bda..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/IntermediateSppfNode.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.ucfs.sppf.node - -import org.ucfs.rsm.RsmState -import java.util.* - -/** - * An Intermediate node which corresponds to the intermediate - * point used in the path index. This node has two children, - * both are range nodes. - *

- * Ensures that the resulting derivation tree has at most cubic complexity - * @param VertexType - type of vertex in input graph - */ -class IntermediateSppfNode( - /** - * rsmState, corresponding to grammar slot in CFG - */ - val rsmState: RsmState, - /** - * Left limit of the subrange - */ - leftExtent: VertexType, - /** - * Right limit of the subrange - */ - rightExtent: VertexType, -) : NonterminalSppfNode(leftExtent, rightExtent) { - override fun toString() = "IntermediateSppfNode(leftExtent=$leftExtent, rightExtent=$rightExtent, rsmState=$rsmState)" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is IntermediateSppfNode<*>) return false - if (!super.equals(other)) return false - if (rsmState != other.rsmState) return false - - return true - } - - override val hashCode: Int = Objects.hash(leftExtent, rightExtent, rsmState) - override fun hashCode() = hashCode -} diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/NonterminalSppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/NonterminalSppfNode.kt deleted file mode 100644 index 0f3a0cda4..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/NonterminalSppfNode.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.ucfs.sppf.node - -import java.util.* - -/** - * Abstract nonterminal sppfNode, generalization of both Symbol and Intermediate sppfNodes - */ -abstract class NonterminalSppfNode( - /** - * Left limit of the subrange - */ - leftExtent: VertexType, - /** - * Right limit of the subrange - */ - rightExtent: VertexType, -) : SppfNode(leftExtent, rightExtent, Int.MAX_VALUE) { - /** - * Set of all children nodes - */ - val children: HashSet> = HashSet() -} diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/PackedSppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/PackedSppfNode.kt deleted file mode 100644 index 11b04bc2a..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/PackedSppfNode.kt +++ /dev/null @@ -1,58 +0,0 @@ -package org.ucfs.sppf.node - -import org.ucfs.rsm.RsmState -import java.util.* - -/** - * Packed sppfNode. Every nonterminal sppfNode has packed nodes as children. Each packed node represents ambiguity in - * derivation. Furthermore, each packed node has at most two children - * @param VertexType - type of vertex in input graph - */ -open class PackedSppfNode( - /** - * Divides subrange leftExtent - rightExtent into two subranges leftExtent - pivot, pivot - rightExtent - */ - val pivot: VertexType, - /** - * rsmState, corresponding to grammar slot in CFS - */ - val rsmState: RsmState, - /** - * Left child - */ - var leftSppfNode: SppfNode? = null, - /** - * Right child - */ - var rightSppfNode: SppfNode? = null, - override var id: Int = SppfNodeId.getFirstFreeSppfNodeId(), -) : ISppfNode { - /** - * Set of all nodes that have current one as child - */ - override val parents: HashSet = HashSet() - - /** - * Part of error recovery mechanism. - * Represents minimum number of insertions/deletions that are needed for the subrange leftExtent - rightExtent - * to be recognized - */ - override var weight: Int = (leftSppfNode?.weight ?: 0) + (rightSppfNode?.weight ?: 0) - - override fun toString() = - "PackedSppfNode(pivot=$pivot, rsmState=$rsmState, leftSppfNode=$leftSppfNode, rightSppfNode=$rightSppfNode)" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is PackedSppfNode<*>) return false - if (pivot != other.pivot) return false - if (rsmState != other.rsmState) return false - if (leftSppfNode != other.leftSppfNode) return false - if (rightSppfNode != other.rightSppfNode) return false - - return true - } - - val hashCode: Int = Objects.hash(pivot, rsmState, leftSppfNode, rightSppfNode) - override fun hashCode() = hashCode -} diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/RangeSppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/RangeSppfNode.kt new file mode 100644 index 000000000..6d6294c34 --- /dev/null +++ b/solver/src/main/kotlin/org/ucfs/sppf/node/RangeSppfNode.kt @@ -0,0 +1,48 @@ +package org.ucfs.sppf.node + +import org.ucfs.rsm.RsmState +import org.ucfs.rsm.symbol.ITerminal + +/** + * + * A Range node which corresponds to a matched range. It + * represents all possible ways to get a specific range and can + * have arbitrary many children. A child node can be of any + * type, besides a Range node. Nodes of this type can be reused. + *

+ * Contains two range: in RSM and in Input graph + *

+ * May be used as extended packed sppfNode. + * @param VertexType - type of vertex in input graph + */ +var lastId = 0 + +data class RangeSppfNode( + val inputRange: InputRange?, + val rsmRange: RsmRange?, + val type: RangeType, +) { + val id: Int = lastId++ + val children = ArrayList>() +} + +fun getEmptyRange(): RangeSppfNode = RangeSppfNode(null, null, EmptyType) + +data class InputRange( + val from: VertexType, + val to: VertexType, +) + +data class RsmRange( + val from: RsmState, + val to: RsmState, +) + +interface RangeType + +object Range : RangeType +data class TerminalType(val terminal: T) : RangeType +data class NonterminalType(val startState: RsmState) : RangeType +data class EpsilonNonterminalType(val startState: RsmState) : RangeType +data class IntermediateType(val grammarSlot: RsmState, val inputPosition: VertexType) : RangeType +object EmptyType : RangeType diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/SppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/SppfNode.kt deleted file mode 100644 index aa4a43fa4..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/SppfNode.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.ucfs.sppf.node - -import java.util.* - -class SppfNodeId private constructor() { - companion object { - private var curSPPFNodeId: Int = 0 - - fun getFirstFreeSppfNodeId() = curSPPFNodeId++ - } -} - -/** - * Abstract class of sppfNode, generalizes all sppfNodes, except packed ones - * @param VertexType - type of vertex in input graph - */ -abstract class SppfNode( - /** - * Left limit of subrange - */ - val leftExtent: VertexType, - /** - * Right limit of subrange - */ - val rightExtent: VertexType, - /** - * Part of error recovery mechanism. - * Represents minimum number of insertions/deletions that are needed for the subrange leftExtent - rightExtent - * to be recognized - */ - override var weight: Int, - override var id: Int = SppfNodeId.getFirstFreeSppfNodeId(), -) : ISppfNode { - /** - * Set of all node that have current one as a child - */ - override val parents: HashSet = HashSet() - - override fun toString() = "SppfNode(leftExtent=$leftExtent, rightExtent=$rightExtent)" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is SppfNode<*>) return false - if (leftExtent != other.leftExtent) return false - if (rightExtent != other.rightExtent) return false - if (weight != other.weight) return false - - return true - } - - open val hashCode: Int = Objects.hash(leftExtent, rightExtent) - override fun hashCode() = hashCode -} diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/SymbolSppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/SymbolSppfNode.kt deleted file mode 100644 index 726f17910..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/SymbolSppfNode.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.ucfs.sppf.node - -import org.ucfs.rsm.symbol.Nonterminal -import java.util.* - -/** - * Symbol sppfNode to represent the root of the subtree which - * corresponds to paths can be derived from the respective - * nonterminal. - */ -class SymbolSppfNode( - /** - * Nonterminal, which defines language recognized by it - */ - val symbol: Nonterminal, - /** - * Left limit of the subrange - */ - leftExtent: VertexType, - /** - * Right limit of the subrange - */ - rightExtent: VertexType, -) : NonterminalSppfNode(leftExtent, rightExtent) { - override fun toString() = "SymbolSppfNode(leftExtent=$leftExtent, rightExtent=$rightExtent, symbol=$symbol)" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is SymbolSppfNode<*>) return false - if (!super.equals(other)) return false - if (symbol != other.symbol) return false - - return true - } - - override val hashCode: Int = Objects.hash(leftExtent, rightExtent, symbol) - override fun hashCode() = hashCode -} diff --git a/solver/src/main/kotlin/org/ucfs/sppf/node/TerminalSppfNode.kt b/solver/src/main/kotlin/org/ucfs/sppf/node/TerminalSppfNode.kt deleted file mode 100644 index e61260b28..000000000 --- a/solver/src/main/kotlin/org/ucfs/sppf/node/TerminalSppfNode.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.ucfs.sppf.node - -import org.ucfs.rsm.symbol.ITerminal -import java.util.* - -/** - * A Terminal node to represent a matched edge label. - * Only terminal sppfNodes can be leaves of the derivation tree - */ -class TerminalSppfNode( - /** - * Terminal, recognized by parser - */ - val terminal: ITerminal?, - /** - * Left limit of the subrange - */ - leftExtent: VertexType, - /** - * Right limit of the subrange - */ - rightExtent: VertexType, - /** - * Part of error recovery mechanism. - * Represents minimum number of insertions/deletions that are needed for the subrange leftExtent - rightExtent - * to be recognized - */ - weight: Int = 0, -) : SppfNode(leftExtent, rightExtent, weight) { - override fun toString() = "TerminalSppfNode(leftExtent=$leftExtent, rightExtent=$rightExtent, terminal=$terminal)" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is TerminalSppfNode<*>) return false - if (!super.equals(other)) return false - if (terminal != other.terminal) return false - - return true - } - - override val hashCode: Int = Objects.hash(leftExtent, rightExtent, terminal) - override fun hashCode() = hashCode -} diff --git a/solver/src/main/kotlin/org/ucfs/sppf/writeSppfToDot.kt b/solver/src/main/kotlin/org/ucfs/sppf/writeSppfToDot.kt index 410adae19..ab358363a 100644 --- a/solver/src/main/kotlin/org/ucfs/sppf/writeSppfToDot.kt +++ b/solver/src/main/kotlin/org/ucfs/sppf/writeSppfToDot.kt @@ -1,90 +1,127 @@ package org.ucfs.sppf import org.ucfs.sppf.node.* -import java.io.File import java.nio.file.Files import java.nio.file.Path -import java.nio.file.Paths -fun writeSppfToDot(sppfNode: ISppfNode, filePath: String, label: String = "") { - val queue: ArrayDeque = ArrayDeque(listOf(sppfNode)) - val edges: HashMap> = HashMap() - val visited: HashSet = HashSet() - var node: ISppfNode +fun writeSppfToDot(sppfNode: RangeSppfNode, filePath: String, label: String = "") { val genPath = Path.of("gen", "sppf") Files.createDirectories(genPath) val file = genPath.resolve(filePath).toFile() - file.printWriter().use { out -> - out.println("digraph g {") - out.println("labelloc=\"t\"") - out.println("label=\"$label\"") - - while (queue.isNotEmpty()) { - node = queue.removeFirst() - if (!visited.add(node.id)) continue - - out.println(printNode(node.id, node)) - - (node as? NonterminalSppfNode<*>)?.children?.forEach { - queue.addLast(it) - if (!edges.containsKey(node.id)) { - edges[node.id] = HashSet() - } - edges.getValue(node.id).add(it.id) - } - - val leftChild = (node as? PackedSppfNode<*>)?.leftSppfNode - val rightChild = (node as? PackedSppfNode<*>)?.rightSppfNode - - if (leftChild != null) { - queue.addLast(leftChild) - if (!edges.containsKey(node.id)) { - edges[node.id] = HashSet() - } - edges.getValue(node.id).add(leftChild.id) - } - if (rightChild != null) { - queue.addLast(rightChild) - if (!edges.containsKey(node.id)) { - edges[node.id] = HashSet() - } - edges.getValue(node.id).add(rightChild.id) - } + out.println(getSppfDot(sppfNode, label)) + } +} + +fun getSppfDot(sppfNode: RangeSppfNode, label: String = ""): String { + val queue: ArrayDeque> = ArrayDeque(listOf(sppfNode)) + val edges: HashMap, HashSet>> = HashMap() + val visited: HashSet = HashSet() + var node: RangeSppfNode + val sb = StringBuilder() + sb.appendLine("digraph g {") + sb.appendLine("labelloc=\"t\"") + sb.appendLine("label=\"$label\"") + var nextNodeId = 0 + val nodeViews = HashMap, String>() + while (queue.isNotEmpty()) { + node = queue.removeFirst() + if (!visited.add(node.hashCode())) continue + + nodeViews[node] = getNodeView(node) + + node.children.forEach { + queue.addLast(it) + edges.getOrPut(node, { HashSet() }).add(it) } - for (kvp in edges) { - val head = kvp.key - for (tail in kvp.value) out.println(printEdge(head, tail)) + } + val sortedViews = nodeViews.values.sorted() + val nodeIds = HashMap, Int>() + for ((node, view) in nodeViews) { + nodeIds[node] = sortedViews.indexOf(view) + } + for (i in sortedViews.indices) { + sb.appendLine("$i ${sortedViews[i]}") + } + + val sortedEdges = ArrayList() + for ((head, tails) in edges) { + for (tail in tails) { + sortedEdges.add("${nodeIds[head]}->${nodeIds[tail]}") } - out.println("}") } + for (edge in sortedEdges.sorted()) { + sb.appendLine(edge) + } + sb.appendLine("}") + return sb.toString() + } -fun getColor(weight: Int): String = if (weight == 0) "black" else "red" +enum class NodeShape(val view: String) { + Terminal("rectangle"), Nonterminal("invtrapezium"), Intermediate("plain"), Empty("ellipse"), Range("ellipse"), Epsilon( + "invhouse" + ) +} -fun printEdge(x: Int, y: Int): String { - return "${x}->${y}" +fun fillNodeTemplate( + 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}]" } -fun printNode(nodeId: Int, node: ISppfNode): String { - return when (node) { - is TerminalSppfNode<*> -> { - "${nodeId} [label = \"Terminal ${nodeId} ; ${node.terminal ?: "eps"}, ${node.leftExtent}, ${node.rightExtent}, Weight: ${node.weight}\", shape = ellipse, color = ${getColor(node.weight)}]" + +fun getNodeView(node: RangeSppfNode): String { + val type = node.type + return when (type) { + is TerminalType<*> -> { + fillNodeTemplate( + "'${type.terminal}'", node.inputRange, NodeShape.Terminal + ) + } + + is NonterminalType -> { + fillNodeTemplate( + "${type.startState.nonterminal.name}", node.inputRange, NodeShape.Nonterminal + ) + } + + is IntermediateType<*> -> { + fillNodeTemplate( + "input: ${type.inputPosition}, rsm: ${type.grammarSlot.id}", null, NodeShape.Intermediate + ) } - is SymbolSppfNode<*> -> { - "${nodeId} [label = \"Symbol ${nodeId} ; ${node.symbol.name}, ${node.leftExtent}, ${node.rightExtent}, Weight: ${node.weight}\", shape = octagon, color = ${getColor(node.weight)}]" + is EmptyType -> { + fillNodeTemplate( + "", null, NodeShape.Empty + ) } - is IntermediateSppfNode<*> -> { - "${nodeId} [label = \"Intermediate ${nodeId} ; RSM: ${node.rsmState.nonterminal.name}, ${node.leftExtent}, ${node.rightExtent}, Weight: ${node.weight}\", shape = rectangle, color = ${getColor(node.weight)}]" + is EpsilonNonterminalType -> { + fillNodeTemplate( + "RSM: ${type.startState.id}", node.inputRange, NodeShape.Epsilon + ) } - is PackedSppfNode<*> -> { - "${nodeId} [label = \"Packed ${nodeId} ; Weight: ${node.weight}\", shape = point, width = 0.5, color = ${getColor(node.weight)}]" + is RangeType -> { + fillNodeTemplate( + "", node.inputRange, NodeShape.Range, node.rsmRange + ) } - else -> "" + else -> throw IllegalStateException("Can't write node type $type to DOT") + } + + +} + +private fun getView(range: RsmRange?): String { + if (range == null) return "" + return "rsm: [(${range.from.nonterminal.name}:${range.from.numId}), " + "(${range.to.nonterminal.name}:${range.to.numId})]" } \ No newline at end of file diff --git a/solver/src/test/kotlin/TestDotParser.kt b/solver/src/test/kotlin/TestDotParser.kt new file mode 100644 index 000000000..d8b74ffe0 --- /dev/null +++ b/solver/src/test/kotlin/TestDotParser.kt @@ -0,0 +1,25 @@ +import org.junit.jupiter.api.Test +import org.ucfs.input.DotParser +import org.ucfs.input.InputGraph +import org.ucfs.input.TerminalInputLabel +import org.ucfs.input.utils.DotWriter +import java.io.File +import java.nio.file.Path +import kotlin.test.assertEquals + +class TestDotParser { + @Test + fun testParser(){ + val testCasesFolder = File(Path.of("src", "test", "resources", "dotParserTest").toUri()) + if (!testCasesFolder.exists()) { + println("Can't find test case for dotParserTest") + } + for (file in testCasesFolder.listFiles()) { + val originalDot = file.readText() + val graph: InputGraph = DotParser().parseDot(originalDot) + assertEquals(originalDot, DotWriter().getDotView(graph)) + } + } + + +} \ No newline at end of file diff --git a/solver/src/test/resources/dotParserTest/simpleLoop.dot b/solver/src/test/resources/dotParserTest/simpleLoop.dot new file mode 100644 index 000000000..c24d6260a --- /dev/null +++ b/solver/src/test/resources/dotParserTest/simpleLoop.dot @@ -0,0 +1,5 @@ +digraph Input { + start -> 0; + 0 -> 0 [label = "("]; + 0 -> 0 [label = ")"]; +} \ No newline at end of file diff --git a/test-shared/src/test/kotlin/grammars/SimpleDyck.kt b/test-shared/src/test/kotlin/grammars/SimpleDyck.kt new file mode 100644 index 000000000..51b6104e4 --- /dev/null +++ b/test-shared/src/test/kotlin/grammars/SimpleDyck.kt @@ -0,0 +1,39 @@ +package grammars + +import org.ucfs.grammar.combinator.Grammar +import org.ucfs.grammar.combinator.extension.StringExtension.times +import org.ucfs.grammar.combinator.regexp.* +import org.ucfs.grammar.combinator.regexp.Epsilon +import org.ucfs.rsm.symbol.Term + +class SimplifiedDyck : Grammar() { + val S by Nt().asStart() + + init { + S /= Option("(" * S * ")") + } +} + +class LoopDyck : Grammar() { + val S by Nt().asStart() + + init { + S /= Option("(" * S * ")") + } +} + +class ABGrammar : Grammar() { + val A by Nt(Term("a")) + val C by Nt(Term("a")) + val B by Nt(C) + val S by Nt(A or B).asStart() +} + +class SALang : Grammar() { + val A by Nt("a" * "b") + val S by Nt((A or ("a" * "b")) * "c").asStart() +} + +class Epsilon : Grammar() { + val S by Nt(Epsilon).asStart() +} \ No newline at end of file diff --git a/test-shared/src/test/kotlin/parser/generated/GllGeneratedTest.kt b/test-shared/src/test/kotlin/parser/generated/GllGeneratedTest.kt index b1e705d39..e69de29bb 100644 --- a/test-shared/src/test/kotlin/parser/generated/GllGeneratedTest.kt +++ b/test-shared/src/test/kotlin/parser/generated/GllGeneratedTest.kt @@ -1,87 +0,0 @@ -package parser.generated - -import org.junit.jupiter.api.DynamicNode -import org.ucfs.GeneratorException -import org.ucfs.IDynamicGllTest -import org.ucfs.IDynamicGllTest.Companion.getFiles -import org.ucfs.IDynamicGllTest.Companion.getLines -import org.ucfs.IDynamicGllTest.Companion.readFile -import org.ucfs.input.LinearInput -import org.ucfs.input.LinearInputLabel -import org.ucfs.parser.ParsingException -import org.ucfs.rsm.symbol.ITerminal -import java.io.File -import java.io.Reader - - -open class GllGeneratedTest : IOfflineGllTest { - companion object { - const val DSL_FILE_NAME = "GrammarDsl" - const val TOKENS = "Token" - const val LEXER_NAME = "Lexer" - } - - override val mainFileName: String - get() = "$DSL_FILE_NAME.kt" - - private fun tokenizeInput(input: String, lexerClass: Class<*>): LinearInput { - val inputGraph = LinearInput() - - //equals to `val lexer = Lexer(input.reader())` - val lexer = lexerClass.getConstructor(Reader::class.java).newInstance(input.reader()) - val yylex = lexerClass.methods.firstOrNull { it.name == "yylex" } - ?: throw GeneratorException("cant find jflex class $lexerClass") - - var token: ITerminal - var vertexId = 0 - inputGraph.addStartVertex(vertexId) - inputGraph.addVertex(vertexId) - while (true) { - try { - val tkn = yylex.invoke(lexer) - if (tkn !is ITerminal) { - throw ParsingException("Lexer must return ITerminal instance, but got ${tkn.javaClass}") - } - token = tkn - } catch (e: java.lang.Error) { - throw ParsingException("Lexer exception: $e") - } - if (token.toString() == "EOF") break - inputGraph.addEdge(vertexId, LinearInputLabel(token), ++vertexId) - inputGraph.addVertex(vertexId) - } - return inputGraph - } - - override fun getTestCases(concreteGrammarFolder: File): Iterable { - val classes = RuntimeCompiler.loadParser(concreteGrammarFolder) - val gll = RuntimeCompiler.instantiateParser(classes.parser) - - val correctOneLineInputs = getLines(IDynamicGllTest.ONE_LINE_INPUTS, concreteGrammarFolder) - .map { - getCorrectTestContainer(IDynamicGllTest.getTestName(it), gll, tokenizeInput(it, classes.lexer)) - } - - val incorrectOneLineInputs = getLines(IDynamicGllTest.ONE_LINE_ERRORS_INPUTS, concreteGrammarFolder) - .map { - getIncorrectTestContainer( - IDynamicGllTest.getTestName(it), - gll, - tokenizeInput(it, classes.lexer) - ) - } - - val correctInputs = - getFiles(IDynamicGllTest.INPUTS, concreteGrammarFolder)?.map { file -> - getCorrectTestContainer(file.name, gll, tokenizeInput(readFile(file), classes.lexer)) - } ?: emptyList() - - val incorrectInputs = - getFiles(IDynamicGllTest.INCORRECT_INPUTS, concreteGrammarFolder)?.map { file -> - getIncorrectTestContainer(file.name, gll, tokenizeInput(readFile(file), classes.lexer)) - } ?: emptyList() - - return correctOneLineInputs + incorrectOneLineInputs + correctInputs + incorrectInputs - } - -} diff --git a/test-shared/src/test/kotlin/parser/generated/IOfflineGllTest.kt b/test-shared/src/test/kotlin/parser/generated/IOfflineGllTest.kt index 026b6c256..e69de29bb 100644 --- a/test-shared/src/test/kotlin/parser/generated/IOfflineGllTest.kt +++ b/test-shared/src/test/kotlin/parser/generated/IOfflineGllTest.kt @@ -1,48 +0,0 @@ -package parser.generated - -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.DynamicNode -import org.junit.jupiter.api.DynamicTest -import org.ucfs.IDynamicGllTest -import org.ucfs.parser.GeneratedParser -import org.ucfs.input.IInputGraph -import org.ucfs.input.ILabel -import kotlin.test.assertNotNull - -interface IOfflineGllTest : IDynamicGllTest { - /** - * Test for any type of incorrect input - * Parametrize parser with it's input before parsing - */ - fun getIncorrectTestContainer( - caseName: String, - gll: GeneratedParser, - input: IInputGraph - ): DynamicNode { - return DynamicTest.dynamicTest("[fail] $caseName") { - gll.setInput(input) - val result = gll.parse().first - Assertions.assertNull(result) - } - } - - /** - * Test for any type of correct input - * Parametrize parser with it's input before parsing - */ - fun getCorrectTestContainer( - caseName: String, - gll: GeneratedParser, - input: IInputGraph - ): DynamicNode { - return DynamicTest.dynamicTest("[ok] $caseName") { - gll.setInput(input) - val result = gll.parse() - if (result.first == null) { - System.err.println("input: $input") - } - //TODO add check for parsing result quality - assertNotNull(result.first) - } - } -} \ No newline at end of file diff --git a/test-shared/src/test/kotlin/parser/generated/RuntimeCompiler.kt b/test-shared/src/test/kotlin/parser/generated/RuntimeCompiler.kt index 084dbd068..e69de29bb 100644 --- a/test-shared/src/test/kotlin/parser/generated/RuntimeCompiler.kt +++ b/test-shared/src/test/kotlin/parser/generated/RuntimeCompiler.kt @@ -1,148 +0,0 @@ -package parser.generated - -import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.SourceFile -import org.ucfs.GeneratorException -import org.ucfs.input.LinearInputLabel -import org.ucfs.parser.GeneratedParser -import org.ucfs.parser.ParserGenerator -import org.ucfs.parser.ScanerlessParserGenerator -import parser.generated.GllGeneratedTest.Companion.DSL_FILE_NAME -import parser.generated.GllGeneratedTest.Companion.LEXER_NAME -import parser.generated.GllGeneratedTest.Companion.TOKENS -import parser.generated.ScanerlessGllGeneratedTest.Companion.SCANERLESS_DSL_FILE_NAME -import java.io.File -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths - -object RuntimeCompiler { - - private fun resourceOf(name: String): Path { - return Path.of( - this.javaClass.getResource(name)?.path - ?: throw GeneratorException("Can't find $name file in test resources") - ) - } - - private fun getParserPkg(grammarName: String): String = "gen.parser.$grammarName" - private val parsersFolder = resourceOf("/") - private val generationPath: Path = Files - .createDirectories(Paths.get("${parsersFolder.toAbsolutePath()}/gen/parser")) - - /** - * Compile ScanerlessGrammarDsl and generate ScanerlessParser, - * compile it and return loaded class - */ - @Suppress("UNCHECKED_CAST") - fun loadScanerlessParser(grammarFolderFile: File): Class<*> { - val grammarName = grammarFolderFile.name - //val parserName = ScanerlessParserGenerator().getParserClassName(SCANERLESS_DSL_FILE_NAME) - - fun generateParserCode(): Pair { - val grammar = getKtSource(grammarFolderFile, SCANERLESS_DSL_FILE_NAME) - val compilationResult = compileClasses(listOf(grammar)) - val classLoader = compilationResult.classLoader - - val grammarClass = classLoader.loadFromGrammar(SCANERLESS_DSL_FILE_NAME, grammarName) - - val generator = ScanerlessParserGenerator(grammarClass) - generator.generate(parsersFolder, getParserPkg(grammarName)) - return Pair(compilationResult, generator.getParserClassName()) - } - - var (compilationResult, parserName) = generateParserCode() - val parser = getKtSource(generationPath.resolve(grammarName).toFile(), parserName) - - compilationResult = compileClasses( - listOf(parser), - listOf(compilationResult.outputDirectory) - ) - return compilationResult.classLoader.loadClass( - "${getParserPkg(grammarName)}.$parserName" - ) - } - - data class ParsingClasses( - val parser: Class<*>, val tokens: Class<*>, - val lexer: Class<*> - ) - - private fun ClassLoader.loadFromGrammar(fileName: String, grammarName: String): Class<*> { - return loadClass("grammars.$grammarName.$fileName") - } - - - @Suppress("UNCHECKED_CAST") - fun loadParser(grammarFolderFile: File): ParsingClasses { - val grammarName = grammarFolderFile.name - - fun generateParserCode(): Pair { - val token = getKtSource(grammarFolderFile, TOKENS) - val grammar = getKtSource(grammarFolderFile, DSL_FILE_NAME) - val compilationResult = compileClasses(listOf(token, grammar)) - val classLoader = compilationResult.classLoader - - val grammarClass = classLoader.loadFromGrammar(DSL_FILE_NAME, grammarName) - val tokenClass = classLoader.loadFromGrammar(TOKENS, grammarName) - - val generator = ParserGenerator(grammarClass, tokenClass) - generator.generate(parsersFolder, getParserPkg(grammarName)) - return Pair(compilationResult, generator.getParserClassName()) - } - - var (compilationResult, parserName) = generateParserCode() - val lexer = getKtSource(grammarFolderFile, LEXER_NAME) - val parser = getKtSource(generationPath.resolve(grammarName).toFile(), parserName) - - compilationResult = compileClasses( - listOf(parser, lexer), - listOf(compilationResult.outputDirectory) - ) - val loader = compilationResult.classLoader - return ParsingClasses( - loader.loadClass("${getParserPkg(grammarName)}.$parserName"), - loader.loadFromGrammar(TOKENS, grammarName), - loader.loadFromGrammar(LEXER_NAME, grammarName) - ) - } - - fun instantiateParser(parserClass: Class<*>): GeneratedParser { - val parser = parserClass.getConstructor().newInstance() - if (parser !is (GeneratedParser<*, *>)) { - throw Exception("Loader exception: the generated parser is not inherited from the ${GeneratedParser::class} ") - } - return parser as (GeneratedParser) - } - - - /** - * Get generation source from file - */ - private fun getSource(sourcePath: File): SourceFile { - assert(sourcePath.exists()) { "Source file ${sourcePath.path} doesn't exists" } - return SourceFile.fromPath(sourcePath) - } - - private fun getKtSource(grammarFolderFile: File, fileName: String): SourceFile { - return getSource(grammarFolderFile.resolve("$fileName.kt")) - } - - /** - * Compile all files for given sources - */ - private fun compileClasses(sourceFiles: List, classpath: List = emptyList()): KotlinCompilation.Result { - val compileResult = KotlinCompilation().apply { - sources = sourceFiles - //use application classpath - inheritClassPath = true - verbose = false - classpaths += classpath - }.compile() - if (compileResult.exitCode != KotlinCompilation.ExitCode.OK) { - throw Exception(compileResult.messages) - } - return compileResult - } - -} \ No newline at end of file diff --git a/test-shared/src/test/kotlin/parser/generated/ScanerlessGllGeneratedTest.kt b/test-shared/src/test/kotlin/parser/generated/ScanerlessGllGeneratedTest.kt index fc556a7fb..e69de29bb 100644 --- a/test-shared/src/test/kotlin/parser/generated/ScanerlessGllGeneratedTest.kt +++ b/test-shared/src/test/kotlin/parser/generated/ScanerlessGllGeneratedTest.kt @@ -1,62 +0,0 @@ -package parser.generated - -import org.junit.jupiter.api.DynamicNode -import org.junit.jupiter.api.DynamicTest -import org.ucfs.IDynamicGllTest -import org.ucfs.IDynamicGllTest.Companion.getLines -import org.ucfs.input.LinearInput -import org.ucfs.input.LinearInput.Companion.SPACE -import org.ucfs.input.LinearInputLabel -import org.ucfs.parser.GeneratedParser -import org.ucfs.sppf.buildStringFromSppf -import java.io.File -import kotlin.test.assertEquals -import kotlin.test.assertNotNull - - -class ScanerlessGllGeneratedTest : IOfflineGllTest { - companion object { - const val SCANERLESS_DSL_FILE_NAME = "ScanerlessGrammarDsl" - } - - override val mainFileName: String - get() = "$SCANERLESS_DSL_FILE_NAME.kt" - - - override fun getTestCases(concreteGrammarFolder: File): Iterable { - val gll: GeneratedParser = getGll(concreteGrammarFolder) - - val correctOneLineInputs = getLines(IDynamicGllTest.ONE_LINE_INPUTS, concreteGrammarFolder) - .map { input -> getCorrectTestContainer(input, IDynamicGllTest.getTestName(input), gll) } - - val incorrectOneLineInputs = getLines(IDynamicGllTest.ONE_LINE_ERRORS_INPUTS, concreteGrammarFolder) - .map { input -> - getIncorrectTestContainer(IDynamicGllTest.getTestName(input), gll, LinearInput.buildFromString(input)) - } - - return correctOneLineInputs + incorrectOneLineInputs - } - - private fun getGll(concreteGrammarFolder: File): GeneratedParser { - val parserClass = RuntimeCompiler.loadScanerlessParser(concreteGrammarFolder) - return RuntimeCompiler.instantiateParser(parserClass) - } - - /** - * Test case for String input without escape symbols - * Contains additional check for parsing result - */ - private fun getCorrectTestContainer( - input: String, - caseName: String, - gll: GeneratedParser - ): DynamicNode { - return DynamicTest.dynamicTest("[ok] $caseName") { - gll.setInput(LinearInput.buildFromString(input)) - val result = gll.parse().first - assertNotNull(result) - assertEquals(input.replace(SPACE, ""), buildStringFromSppf(result)) - } - } - -} diff --git a/test-shared/src/test/kotlin/solver/AbstractCorrectnessTest.kt b/test-shared/src/test/kotlin/solver/AbstractCorrectnessTest.kt new file mode 100644 index 000000000..43b728486 --- /dev/null +++ b/test-shared/src/test/kotlin/solver/AbstractCorrectnessTest.kt @@ -0,0 +1,42 @@ +package solver + +import grammars.* +import org.jetbrains.kotlin.incremental.createDirectory +import org.junit.jupiter.api.Test +import org.ucfs.grammar.combinator.Grammar +import org.ucfs.rsm.writeRsmToDot +import java.io.File +import java.nio.file.Path + +abstract class AbstractCorrectnessTest { + val rootPath: Path = Path.of("src", "test", "resources", "correctness") + + abstract fun getRootDataFolder(): Path + + val grammars = listOf(SimplifiedDyck(), ABGrammar(), SALang(), Epsilon()) + //val grammars = listOf(LoopDyck()) + + //@TestFactory + //TODO make it abstract by used grammar + @Test + fun testCorrectness() { + for (grammar in grammars) { + val grammarName = grammar.javaClass.simpleName + writeRsmToDot(grammar.rsm, "${grammarName}Rsm") + val path: Path = getRootDataFolder() + val testCasesFolder = File(path.resolve(grammarName).toUri()) + if (!testCasesFolder.exists()) { + println("Can't find test case for $grammarName") + } + testCasesFolder.createDirectory() + for (folder in testCasesFolder.listFiles()) { + if (folder.isDirectory) { + runGoldTest(folder, grammar) + } + } + } + + } + + abstract fun runGoldTest(testCasesFolder: File, grammar: Grammar) +} \ No newline at end of file diff --git a/test-shared/src/test/kotlin/solver/GllCorrectnessTest.kt b/test-shared/src/test/kotlin/solver/GllCorrectnessTest.kt new file mode 100644 index 000000000..e69de29bb diff --git a/test-shared/src/test/kotlin/solver/GllRsmTest.kt b/test-shared/src/test/kotlin/solver/GllRsmTest.kt deleted file mode 100644 index 42e85385b..000000000 --- a/test-shared/src/test/kotlin/solver/GllRsmTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package solver - -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.DynamicNode -import org.junit.jupiter.api.DynamicTest -import org.ucfs.IDynamicGllTest -import org.ucfs.IDynamicGllTest.Companion.ONE_LINE_ERRORS_INPUTS -import org.ucfs.IDynamicGllTest.Companion.ONE_LINE_INPUTS -import org.ucfs.IDynamicGllTest.Companion.getFile -import org.ucfs.IDynamicGllTest.Companion.getLines -import org.ucfs.IDynamicGllTest.Companion.getTestName -import org.ucfs.input.LinearInput -import org.ucfs.input.LinearInputLabel -import org.ucfs.parser.Gll -import org.ucfs.parser.IGll -import org.ucfs.rsm.RsmState -import org.ucfs.rsm.readRsmFromTxt -import java.io.File -import kotlin.test.assertNotNull - - -class GllRsmTest : IDynamicGllTest { - override val mainFileName: String - get() = "grammar.rsm" - - - private fun getGll(input: String, rsm: RsmState): Gll { - return Gll.gll(rsm, LinearInput.buildFromString(input)) - } - - private fun getRsm(concreteGrammarFolder: File): RsmState { - val rsmFile = getFile(mainFileName, concreteGrammarFolder) - ?: throw Exception("Folder $concreteGrammarFolder not contains $mainFileName") - return readRsmFromTxt(rsmFile.toPath()) - } - - override fun getTestCases(concreteGrammarFolder: File): Iterable { - val rsm = getRsm(concreteGrammarFolder) - - val inputs = getLines(ONE_LINE_INPUTS, concreteGrammarFolder) - .map { input -> getCorrectTestContainer(getTestName(input), getGll(input, rsm)) } - val errorInputs = getLines(ONE_LINE_ERRORS_INPUTS, concreteGrammarFolder) - .map { input -> getIncorrectTestContainer(getTestName(input), getGll(input, rsm)) } - - return inputs + errorInputs - } - - /** - * Test for any type of incorrect input - * Gll should be parametrized by it's input! - */ - private fun getIncorrectTestContainer(caseName: String, gll: IGll): DynamicNode { - return DynamicTest.dynamicTest(caseName) { - val result = gll.parse().first - Assertions.assertNull(result) - } - } - - /** - * Test for any type of correct input - * Gll should be parametrized by it's input! - */ - private fun getCorrectTestContainer(caseName: String, gll: IGll): DynamicNode { - return DynamicTest.dynamicTest(caseName) { - val result = gll.parse().first - //TODO add check for parsing result quality - assertNotNull(result) - } - } -} diff --git a/test-shared/src/test/kotlin/solver/TreeCorrectnessTest.kt b/test-shared/src/test/kotlin/solver/TreeCorrectnessTest.kt new file mode 100644 index 000000000..3fac03f48 --- /dev/null +++ b/test-shared/src/test/kotlin/solver/TreeCorrectnessTest.kt @@ -0,0 +1,40 @@ +package solver + +import java.io.File +import org.ucfs.grammar.combinator.Grammar +import org.ucfs.input.DotParser +import org.ucfs.parser.Gll +import org.ucfs.sppf.getSppfDot +import java.nio.file.Path +import kotlin.io.path.readText +import kotlin.io.path.writeText +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class TreeCorrectnessTest : AbstractCorrectnessTest() { + override fun getRootDataFolder(): Path { + return rootPath.resolve("tree") + } + + override fun runGoldTest(testCasesFolder: File, grammar: Grammar) { + val inputFile = testCasesFolder.toPath().resolve("input.dot") + val expectedFile = testCasesFolder.toPath().resolve("result.dot") + val input = inputFile.readText() + val expectedResult = expectedFile.readText() + val actualResult = runTest(input, grammar) + if (expectedResult.isEmpty()) { + expectedFile.writeText(actualResult) + } else { + assertEquals(expectedResult, actualResult) + } + } + + + fun runTest(input: String, grammar: Grammar): String { + val inputGraph = DotParser().parseDot(input) + val gll = Gll.gll(grammar.rsm, inputGraph) + val sppf = gll.parse() + assertNotNull(sppf) { "Can't parse input!" } + return getSppfDot(sppf) + } +} \ No newline at end of file diff --git a/test-shared/src/test/resources/correctness/tree/ABGrammar/ambig/input.dot b/test-shared/src/test/resources/correctness/tree/ABGrammar/ambig/input.dot new file mode 100644 index 000000000..f97b01474 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/ABGrammar/ambig/input.dot @@ -0,0 +1,4 @@ +digraph Input { + start -> 0; + 0 -> 1 [label = "a"]; +} \ No newline at end of file diff --git a/test-shared/src/test/resources/correctness/tree/ABGrammar/ambig/result.dot b/test-shared/src/test/resources/correctness/tree/ABGrammar/ambig/result.dot new file mode 100644 index 000000000..8f45278aa --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/ABGrammar/ambig/result.dot @@ -0,0 +1,20 @@ +digraph g { +labelloc="t" +label="" +0 [label = "Nonterminal A, input: [0, 1]", shape = invtrapezium] +1 [label = "Nonterminal B, input: [0, 1]", shape = invtrapezium] +2 [label = "Nonterminal C, input: [0, 1]", shape = invtrapezium] +3 [label = "Range , input: [0, 1], rsm: [A_0, A_1]", shape = ellipse] +4 [label = "Range , input: [0, 1], rsm: [B_0, B_1]", shape = ellipse] +5 [label = "Range , input: [0, 1], rsm: [C_0, C_1]", shape = ellipse] +6 [label = "Range , input: [0, 1], rsm: [S_0, S_1]", shape = ellipse] +7 [label = "Terminal 'a', input: [0, 1]", shape = rectangle] +0->3 +1->4 +2->5 +3->7 +4->2 +5->7 +6->0 +6->1 +} diff --git a/test-shared/src/test/resources/correctness/tree/Epsilon/epsilon/input.dot b/test-shared/src/test/resources/correctness/tree/Epsilon/epsilon/input.dot new file mode 100644 index 000000000..e858712e8 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/Epsilon/epsilon/input.dot @@ -0,0 +1,3 @@ +digraph Input { + start -> 0; +} \ No newline at end of file diff --git a/test-shared/src/test/resources/correctness/tree/Epsilon/epsilon/result.dot b/test-shared/src/test/resources/correctness/tree/Epsilon/epsilon/result.dot new file mode 100644 index 000000000..b7962fa25 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/Epsilon/epsilon/result.dot @@ -0,0 +1,7 @@ +digraph g { +labelloc="t" +label="" +0 [label = "Epsilon RSM: S_0, input: [0, 0]", shape = invhouse] +1 [label = "Range , input: [0, 0], rsm: [S_0, S_0]", shape = ellipse] +1->0 +} diff --git a/test-shared/src/test/resources/correctness/tree/LoopDyck/linear/input.dot b/test-shared/src/test/resources/correctness/tree/LoopDyck/linear/input.dot new file mode 100644 index 000000000..2ce933246 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/LoopDyck/linear/input.dot @@ -0,0 +1,5 @@ +digraph Input { + start -> 0; + 0 -> 0 [label = "("]; + 0 -> 0 [label = ")"]; +} \ No newline at end of file diff --git a/test-shared/src/test/resources/correctness/tree/LoopDyck/linear/result.dot b/test-shared/src/test/resources/correctness/tree/LoopDyck/linear/result.dot new file mode 100644 index 000000000..57fd89a37 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/LoopDyck/linear/result.dot @@ -0,0 +1,27 @@ +digraph g { +labelloc="t" +label="" +0 [label = "Epsilon RSM: S_0, input: [1, 1]", shape = invhouse] +1 [label = "Intermediate input: 1, rsm: S_1", shape = plain] +2 [label = "Intermediate input: 1, rsm: S_2", shape = plain] +3 [label = "Nonterminal S, input: [1, 1]", shape = invtrapezium] +4 [label = "Range , input: [0, 1], rsm: [S_0, S_1]", shape = ellipse] +5 [label = "Range , input: [0, 1], rsm: [S_0, S_2]", shape = ellipse] +6 [label = "Range , input: [0, 2], rsm: [S_0, S_3]", shape = ellipse] +7 [label = "Range , input: [1, 1], rsm: [S_0, S_0]", shape = ellipse] +8 [label = "Range , input: [1, 1], rsm: [S_1, S_2]", shape = ellipse] +9 [label = "Range , input: [1, 2], rsm: [S_2, S_3]", shape = ellipse] +10 [label = "Terminal '(', input: [0, 1]", shape = rectangle] +11 [label = "Terminal ')', input: [1, 2]", shape = rectangle] +1->4 +1->8 +2->5 +2->9 +3->7 +4->10 +5->1 +6->2 +7->0 +8->3 +9->11 +} diff --git a/test-shared/src/test/resources/correctness/tree/SALang/linear/input.dot b/test-shared/src/test/resources/correctness/tree/SALang/linear/input.dot new file mode 100644 index 000000000..8a497fee0 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/SALang/linear/input.dot @@ -0,0 +1,6 @@ +digraph Input { + start -> 0; + 0 -> 1 [label = "a"]; + 1 -> 2 [label = "b"]; + 2 -> 3 [label = "c"]; +} \ No newline at end of file diff --git a/test-shared/src/test/resources/correctness/tree/SALang/linear/result.dot b/test-shared/src/test/resources/correctness/tree/SALang/linear/result.dot new file mode 100644 index 000000000..056a94163 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/SALang/linear/result.dot @@ -0,0 +1,35 @@ +digraph g { +labelloc="t" +label="" +0 [label = "Intermediate input: 1, rsm: A_1", shape = plain] +1 [label = "Intermediate input: 1, rsm: S_2", shape = plain] +2 [label = "Intermediate input: 2, rsm: S_1", shape = plain] +3 [label = "Nonterminal A, input: [0, 2]", shape = invtrapezium] +4 [label = "Range , input: [0, 1], rsm: [A_0, A_1]", shape = ellipse] +5 [label = "Range , input: [0, 1], rsm: [S_0, S_2]", shape = ellipse] +6 [label = "Range , input: [0, 2], rsm: [A_0, A_2]", shape = ellipse] +7 [label = "Range , input: [0, 2], rsm: [S_0, S_1]", shape = ellipse] +8 [label = "Range , input: [0, 3], rsm: [S_0, S_3]", shape = ellipse] +9 [label = "Range , input: [1, 2], rsm: [A_1, A_2]", shape = ellipse] +10 [label = "Range , input: [1, 2], rsm: [S_2, S_1]", shape = ellipse] +11 [label = "Range , input: [2, 3], rsm: [S_1, S_3]", shape = ellipse] +12 [label = "Terminal 'a', input: [0, 1]", shape = rectangle] +13 [label = "Terminal 'b', input: [1, 2]", shape = rectangle] +14 [label = "Terminal 'c', input: [2, 3]", shape = rectangle] +0->4 +0->9 +1->10 +1->5 +10->13 +11->14 +2->11 +2->7 +3->6 +4->12 +5->12 +6->0 +7->1 +7->3 +8->2 +9->13 +} diff --git a/test-shared/src/test/resources/correctness/tree/SimplifiedDyck/linear/input.dot b/test-shared/src/test/resources/correctness/tree/SimplifiedDyck/linear/input.dot new file mode 100644 index 000000000..15d31f953 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/SimplifiedDyck/linear/input.dot @@ -0,0 +1,5 @@ +digraph Input { + start -> 0; + 0 -> 1 [label = "("]; + 1 -> 2 [label = ")"]; +} \ No newline at end of file diff --git a/test-shared/src/test/resources/correctness/tree/SimplifiedDyck/linear/result.dot b/test-shared/src/test/resources/correctness/tree/SimplifiedDyck/linear/result.dot new file mode 100644 index 000000000..57fd89a37 --- /dev/null +++ b/test-shared/src/test/resources/correctness/tree/SimplifiedDyck/linear/result.dot @@ -0,0 +1,27 @@ +digraph g { +labelloc="t" +label="" +0 [label = "Epsilon RSM: S_0, input: [1, 1]", shape = invhouse] +1 [label = "Intermediate input: 1, rsm: S_1", shape = plain] +2 [label = "Intermediate input: 1, rsm: S_2", shape = plain] +3 [label = "Nonterminal S, input: [1, 1]", shape = invtrapezium] +4 [label = "Range , input: [0, 1], rsm: [S_0, S_1]", shape = ellipse] +5 [label = "Range , input: [0, 1], rsm: [S_0, S_2]", shape = ellipse] +6 [label = "Range , input: [0, 2], rsm: [S_0, S_3]", shape = ellipse] +7 [label = "Range , input: [1, 1], rsm: [S_0, S_0]", shape = ellipse] +8 [label = "Range , input: [1, 1], rsm: [S_1, S_2]", shape = ellipse] +9 [label = "Range , input: [1, 2], rsm: [S_2, S_3]", shape = ellipse] +10 [label = "Terminal '(', input: [0, 1]", shape = rectangle] +11 [label = "Terminal ')', input: [1, 2]", shape = rectangle] +1->4 +1->8 +2->5 +2->9 +3->7 +4->10 +5->1 +6->2 +7->0 +8->3 +9->11 +}