Skip to content

Commit

Permalink
Implementation of AST for ArrayOfTables (#104)
Browse files Browse the repository at this point in the history
### What's done:
- Changes in the logic of inserting Arrays of Tables
- Reworked the logic of insretion of tables in the AST
- Fixed small issues related to technical nodes in the tree
- Intentionally broke partial decoding as it is not applicable for array structures. Need to rework that in future
  • Loading branch information
orchestr7 committed Feb 16, 2022
1 parent 0bf142f commit a76f5e8
Show file tree
Hide file tree
Showing 18 changed files with 845 additions and 343 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ We are still developing and testing this library, so it has several limitations:
:x: Multiline Strings \
:x: Inline Tables \
:x: Array of Tables \
:x: Inline Array of Tables \
:x: Offset Date-Time, Local: Date-Time; Date; Time

## Dependency
Expand Down
35 changes: 2 additions & 33 deletions ktoml-core/src/commonMain/kotlin/com/akuleshov7/ktoml/Toml.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.akuleshov7.ktoml

import com.akuleshov7.ktoml.decoders.TomlMainDecoder
import com.akuleshov7.ktoml.exceptions.MissingRequiredPropertyException
import com.akuleshov7.ktoml.parsers.TomlParser
import com.akuleshov7.ktoml.tree.TableType
import com.akuleshov7.ktoml.tree.TomlFile
import com.akuleshov7.ktoml.writers.TomlWriter

Expand Down Expand Up @@ -90,7 +88,7 @@ public open class Toml(
tomlTableName: String,
config: TomlConfig = TomlConfig()
): T {
val fakeFileNode = generateFakeTomlStructureForPartialParsing(toml, tomlTableName, config, TableType.PRIMITIVE, TomlParser::parseString)
val fakeFileNode = TomlFile()
return TomlMainDecoder.decode(deserializer, fakeFileNode, this.config)
}

Expand All @@ -114,40 +112,11 @@ public open class Toml(
tomlTableName: String,
config: TomlConfig = TomlConfig()
): T {
val fakeFileNode = generateFakeTomlStructureForPartialParsing(
toml.joinToString("\n"),
tomlTableName,
config,
TableType.PRIMITIVE,
TomlParser::parseString,
)
val fakeFileNode = TomlFile()
return TomlMainDecoder.decode(deserializer, fakeFileNode, this.config)
}

// ================== other ===============
@Suppress("TYPE_ALIAS")
private fun generateFakeTomlStructureForPartialParsing(
toml: String,
tomlTableName: String,
config: TomlConfig = TomlConfig(),
type: TableType,
parsingFunction: (TomlParser, String) -> TomlFile
): TomlFile {
val parsedToml = parsingFunction(TomlParser(this.config), toml)
.findTableInAstByName(tomlTableName, tomlTableName.count { it == '.' } + 1, type)
?: throw MissingRequiredPropertyException(
"Cannot find table with name <$tomlTableName> in the toml input. " +
"Not able to decode this toml part."
)

// adding a fake file node to restore the structure and parse only the part of te toml
val fakeFileNode = TomlFile(config)
parsedToml.children.forEach {
fakeFileNode.appendChild(it)
}

return fakeFileNode
}

/**
* The default instance of [Toml] with the default configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public class TomlMainDecoder(
* Actually this method is not needed as serialization lib should do everything for us, but let's
* fail-fast in the very beginning if the structure is inconsistent and required properties are missing
*/
private fun checkMissingRequiredProperties(children: MutableSet<TomlNode>?, descriptor: SerialDescriptor) {
private fun checkMissingRequiredProperties(children: MutableList<TomlNode>?, descriptor: SerialDescriptor) {
val propertyNames = children?.map {
it.name
} ?: emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ public value class TomlParser(private val config: TomlConfig) {
@Suppress("TOO_LONG_FUNCTION")
public fun parseStringsToTomlTree(tomlLines: List<String>, config: TomlConfig): TomlFile {
var currentParentalNode: TomlNode = TomlFile(config)
// link to the head of the tree
val tomlFileHead = currentParentalNode as TomlFile
// need to trim empty lines BEFORE the start of processing
val mutableTomlLines = tomlLines.toMutableList().trimEmptyLines()
// here we always store the bucket of the latest created array of tables
var latestCreatedBucket: TomlArrayOfTablesElement? = null

mutableTomlLines.forEachIndexed { index, line ->
val lineNo = index + 1
Expand All @@ -48,13 +51,19 @@ public value class TomlParser(private val config: TomlConfig) {
if (line.isArrayOfTables()) {
// TomlArrayOfTables contains all information about the ArrayOfTables ([[array of tables]])
val tableArray = TomlArrayOfTables(line, lineNo, config)
val arrayOfTables = tomlFileHead.insertTableToTree(tableArray, TableType.ARRAY)
val arrayOfTables = tomlFileHead.insertTableToTree(tableArray, latestCreatedBucket)
// creating a new empty element that will be used as an element in array and the parent for next key-value records
val newArrayElement = TomlArrayOfTablesElement(lineNo, config)
// adding this element as a child to the array of tables
arrayOfTables.appendChild(newArrayElement)
// covering the case when the processed table does not contain nor key-value pairs neither tables (after our insertion)
// adding fake nodes to a previous table (it has no children because we have found another table right after)
if (currentParentalNode.hasNoChildren() && currentParentalNode !is TomlFile && currentParentalNode !is TomlArrayOfTablesElement) {
currentParentalNode.appendChild(TomlStubEmptyNode(currentParentalNode.lineNo, config))
}
// and setting this element as a current parent, so new key-records will be added to this bucket
currentParentalNode = newArrayElement
latestCreatedBucket = newArrayElement
} else {
val tableSection = TomlTablePrimitive(line, lineNo, config)
// if the table is the last line in toml, then it has no children, and we need to
Expand All @@ -64,10 +73,10 @@ public value class TomlParser(private val config: TomlConfig) {
}
// covering the case when the processed table does not contain nor key-value pairs neither tables (after our insertion)
// adding fake nodes to a previous table (it has no children because we have found another table right after)
if (currentParentalNode.hasNoChildren()) {
if (currentParentalNode.hasNoChildren() && currentParentalNode !is TomlFile && currentParentalNode !is TomlArrayOfTablesElement) {
currentParentalNode.appendChild(TomlStubEmptyNode(currentParentalNode.lineNo, config))
}
currentParentalNode = tomlFileHead.insertTableToTree(tableSection, TableType.PRIMITIVE)
currentParentalNode = tomlFileHead.insertTableToTree(tableSection)
}
} else {
val keyValue = line.parseTomlKeyValue(lineNo, config)
Expand All @@ -80,8 +89,9 @@ public value class TomlParser(private val config: TomlConfig) {
// in case parser has faced dot-separated complex key (a.b.c) it should create proper table [a.b],
// because table is the same as dotted key
val newTableSection = keyValue.createTomlTableFromDottedKey(currentParentalNode, config)

tomlFileHead
.insertTableToTree(newTableSection, TableType.PRIMITIVE)
.insertTableToTree(newTableSection)
.appendChild(keyValue)
} else {
// otherwise it should simply append the keyValue to the parent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ public class TomlArrayOfTables(
content: String,
lineNo: Int,
config: TomlConfig = TomlConfig(),
public val isSynthetic: Boolean = false
isSynthetic: Boolean = false
) : TomlTable(
content,
lineNo,
config
config,
isSynthetic
) {
public override val type: TableType = TableType.ARRAY

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public class TomlFile(config: TomlConfig = TomlConfig()) : TomlNode(
) {
override val name: String = "rootNode"

override fun getNeighbourNodes(): MutableSet<TomlNode> =
override fun getNeighbourNodes(): MutableList<TomlNode> =
throw InternalAstException("Invalid call to getNeighbourNodes() for TomlFile node")
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal interface TomlKeyValue {
// updating current KeyValue with this key
this.key = realKeyWithoutDottedPrefix
// tables should contain fully qualified name, so we need to add parental name
val parentalPrefix = if (parentNode is TomlTablePrimitive) "${parentNode.fullTableName}." else ""
val parentalPrefix = if (parentNode is TomlTable) "${parentNode.fullTableName}." else ""
// and creating a new table that will be created from dotted key
return TomlTablePrimitive(
"[$parentalPrefix${syntheticTablePrefix.joinToString(".")}]",
Expand Down
Loading

0 comments on commit a76f5e8

Please sign in to comment.