Skip to content

Commit c246953

Browse files
committed
Simplify parsing scenes
1 parent 9aaf6ef commit c246953

File tree

3 files changed

+66
-92
lines changed

3 files changed

+66
-92
lines changed

src/main/kotlin/com/tomwyr/core/Exceptions.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,5 @@ class UnexpectedNodeFormat(nodeLine: String) :
1111
class UnexpectedNodeParameters(nodeParams: NodeParams) :
1212
Exception("A node with unexpected set of parameters encountered: $nodeParams")
1313

14-
class UnexpectedResourceFormat(resourceLine: String) :
15-
Exception("A resource with unexpected format encountered: $resourceLine")
16-
1714
class UnexpectedSceneResource(instance: String) :
1815
Exception("A node pointing to an unknown scene resource encountered with id: $instance")
19-
20-
class DuplicatedSceneResources(duplicates: Map<String, List<String>>) :
21-
Exception("Found multiple scenes for following resource ids: $duplicates")

src/main/kotlin/com/tomwyr/core/Log.kt

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,33 +46,44 @@ interface ProcessorLog : Logger {
4646
}
4747

4848
interface ParserLog : Logger {
49-
fun parsedSceneNodes(nodes: List<String>) {
50-
info("${nodes.size} nodes found in the scene")
49+
fun parsingScenePaths() {
50+
info("Parsing scene paths by id from scene file")
5151
}
5252

53-
fun parsingNode(line: String) {
54-
debug("Parsing node $line")
53+
fun parsingSceneNodes() {
54+
info("Parsing scene nodes from scene file")
5555
}
5656

57-
fun skippingNode() {
57+
fun skippingNode(params: Map<String, String>) {
5858
info("Skipping node missing at least one of the required keys")
59+
debug("Received params were $params")
5960
}
6061

61-
fun parsedSceneResources(resources: List<String>) {
62-
info("${resources.size} scene resources found in the scene")
62+
fun skippingSceneResource(params: Map<String, String>) {
63+
info("Skipping scene resource missing at least one of the required keys")
64+
debug("Received params were $params")
6365
}
6466

65-
fun parsingSceneResource(line: String) {
66-
debug("Parsing scene resource $line")
67-
}
68-
69-
fun skippingSceneResource() {
70-
info("Skipping scene resource missing at least one of the required keys")
67+
fun duplicatedSceneResources(duplicates: Map<String, List<String>>) {
68+
warn("Found duplicated scenes for the following resource ids $duplicates")
69+
warn("Only the last resource id for each scene will be retained")
7170
}
7271

7372
fun creatingRootNode() {
7473
info("Creating node tree structure for the extracted nodes")
7574
}
75+
76+
fun splittingEntries(entryType: String) {
77+
info("Splitting file to entries of type $entryType")
78+
}
79+
80+
fun parsingEntryParams(entry: String) {
81+
info("Parsing params from entry $entry")
82+
}
83+
84+
fun parsingSceneName(scenePath: String) {
85+
info("Parsing scene name for path $scenePath")
86+
}
7687
}
7788

7889
interface RendererLog : Logger {

src/main/kotlin/com/tomwyr/generator/Parser.kt

Lines changed: 42 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,20 @@ class SceneNodesParser {
1111
}
1212

1313
private class ScenesParser {
14-
fun parse(sceneFileContent: String): Map<String, String> = sceneFileContent
15-
.let(::splitToSceneLines)
16-
.map(::parseSceneParams)
17-
.mapNotNull(::getSceneIdToPath)
18-
.let(::getScenePathsById)
19-
20-
private fun splitToSceneLines(data: String): List<String> {
21-
return data.split("\n").filter { line ->
22-
line.startsWith("[ext_resource type=\"PackedScene\"") && line.endsWith("]")
23-
}.also(Log::parsedSceneResources)
14+
fun parse(sceneFileContent: String): Map<String, String> {
15+
Log.parsingScenePaths()
16+
return splitToEntries(sceneFileContent, "ext_resource")
17+
.map(::parseEntryParams)
18+
.mapNotNull(::extractSceneIdToPath)
19+
.let(::getScenePathsById)
2420
}
2521

26-
private fun parseSceneParams(line: String): Map<String, String> {
27-
Log.parsingSceneResource(line)
28-
29-
val pattern = "^\\[ext_resource (.*)]$".toRegex()
30-
val groups = pattern.find(line)?.groups?.filterNotNull() ?: emptyList()
31-
val paramsLine = groups.takeIf { it.size == 2 }?.last()?.value
32-
paramsLine ?: throw UnexpectedResourceFormat(line)
33-
34-
val paramPattern = "\\w+=\".+?\"".toRegex()
35-
val segments = paramPattern.findAll(paramsLine).map { it.value }.toList()
36-
return segments.associate(::parseParamSegment)
37-
}
38-
39-
40-
private fun getSceneIdToPath(params: Map<String, String>): Pair<String, String>? {
41-
val id = params.extract("id")
42-
val path = params.extract("path")
22+
private fun extractSceneIdToPath(params: Map<String, String>): Pair<String, String>? {
23+
val id = params["id"]
24+
val path = params["path"]
4325

4426
if (id == null || path == null) {
45-
Log.skippingSceneResource()
27+
Log.skippingSceneResource(params)
4628
return null
4729
}
4830

@@ -51,54 +33,35 @@ private class ScenesParser {
5133

5234
private fun getScenePathsById(sceneIdsToPaths: List<Pair<String, String>>): Map<String, String> {
5335
val duplicates = sceneIdsToPaths
54-
.groupBy { it.first }
36+
.groupBy({ it.first }, { it.second })
5537
.filter { it.value.size > 1 }
56-
.mapValues { it.value.map { idToPath -> idToPath.second } }
5738

5839
if (duplicates.isNotEmpty()) {
59-
throw DuplicatedSceneResources(duplicates)
40+
Log.duplicatedSceneResources(duplicates)
6041
}
6142

6243
return sceneIdsToPaths.toMap()
6344
}
6445
}
6546

6647
private class NodesParser(val scenePathsById: Map<String, String>) {
67-
fun parse(sceneFileContent: String): Node = sceneFileContent
68-
.let(::splitToNodeLines)
69-
.map(::parseNodeParams)
70-
.mapNotNull(::createNodeParams)
71-
.let(::createRootNode)
72-
73-
74-
private fun splitToNodeLines(data: String): List<String> {
75-
return data.split("\n").filter { line ->
76-
line.startsWith("[node") && line.endsWith("]")
77-
}.also(Log::parsedSceneNodes)
78-
}
79-
80-
private fun parseNodeParams(line: String): Map<String, String> {
81-
Log.parsingNode(line)
82-
83-
val pattern = "^\\[node (.*)]$".toRegex()
84-
val groups = pattern.find(line)?.groups?.filterNotNull() ?: emptyList()
85-
val paramsLine = groups.takeIf { it.size == 2 }?.last()?.value
86-
paramsLine ?: throw UnexpectedNodeFormat(line)
87-
88-
val paramPattern = "\\w+=(\".+?\"|\\w+\\(\".+\"\\))".toRegex()
89-
val segments = paramPattern.findAll(paramsLine).map { it.value }.toList()
90-
return segments.associate(::parseParamSegment)
48+
fun parse(sceneFileContent: String): Node {
49+
Log.parsingSceneNodes()
50+
return splitToEntries(sceneFileContent, "node")
51+
.map(::parseEntryParams)
52+
.mapNotNull(::extractNodeParams)
53+
.let(::createRootNode)
9154
}
9255

9356

94-
private fun createNodeParams(params: Map<String, String>): NodeParams? {
95-
val name = params.extract("name")
96-
val type = params.extract("type")
97-
val instance = params.extractResource("instance")
98-
val parent = params.extract("parent")
57+
private fun extractNodeParams(params: Map<String, String>): NodeParams? {
58+
val name = params["name"]
59+
val type = params["type"]
60+
val instance = params["instance"]
61+
val parent = params["parent"]
9962

10063
if (name == null || (type == null && instance == null) || (type != null && instance != null)) {
101-
Log.skippingNode()
64+
Log.skippingNode(params)
10265
return null
10366
}
10467

@@ -140,10 +103,8 @@ data class NodeParams(
140103
}
141104

142105
type == null && instance != null -> {
143-
val pattern = "^res://(.*).tscn$".toRegex()
144-
val scenePath = scenePathsById[instance] ?: throw UnexpectedSceneResource(instance)
145-
val scene = pattern.find(scenePath)?.groupValues?.getOrNull(1)?.capitalize()
146-
scene ?: throw UnexpectedNodeFormat(scenePath)
106+
val scene = scenePathsById[instance]?.let(::parseSceneName)?.capitalize()
107+
scene ?: throw UnexpectedSceneResource(instance)
147108
NestedScene(name = name, scene = scene)
148109
}
149110

@@ -152,16 +113,24 @@ data class NodeParams(
152113
}
153114
}
154115

155-
private fun parseParamSegment(segment: String): Pair<String, String> {
156-
val paramPattern = "^(.+)=(.+)$".toRegex()
157-
val groups = paramPattern.find(segment)?.groups?.filterNotNull() ?: emptyList()
158-
return groups.map { it.value }.let { it[1] to it[2] }
116+
private fun splitToEntries(data: String, entryType: String): List<String> {
117+
Log.splittingEntries(entryType)
118+
val pattern = """\[${entryType} .*]""".toRegex()
119+
return pattern.findAll(data).mapNotNull { it.groupValues.firstOrNull() }.toList()
159120
}
160121

161-
private fun Map<String, String>.extract(key: String): String? {
162-
return this[key]?.trim('\"')
122+
private fun parseEntryParams(entry: String): Map<String, String> {
123+
Log.parsingEntryParams(entry)
124+
val pattern = """(?:(\w+)=(?:\w+\("(.+)"\)|"(.+?)"))+""".toRegex()
125+
return pattern.findAll(entry).associate { match ->
126+
val (key, argumentValue, plainValue) = match.destructured
127+
val value = argumentValue.ifEmpty { plainValue }
128+
key to value
129+
}
163130
}
164131

165-
private fun Map<String, String>.extractResource(key: String): String? {
166-
return this[key]?.removePrefix("ExtResource(\"")?.removeSuffix("\")")
132+
private fun parseSceneName(scenePath: String): String? {
133+
Log.parsingEntryParams(scenePath)
134+
val pattern = """^res://(.*).tscn$""".toRegex()
135+
return pattern.find(scenePath)?.groupValues?.getOrNull(1)
167136
}

0 commit comments

Comments
 (0)