Skip to content

Commit

Permalink
Improve code in JSON and partly YAML modules
Browse files Browse the repository at this point in the history
  • Loading branch information
jurmous committed Sep 21, 2024
1 parent 61d8e81 commit 4c58b04
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ sealed class JsonEmbedType(val isSimple: Boolean) {
/** Class to implement code which is generic among JSON like writers */
abstract class AbstractJsonLikeWriter : IsJsonLikeWriter {
protected var lastType: JsonType = START
protected var typeStack: MutableList<JsonEmbedType> = mutableListOf()
protected val typeStack = mutableListOf<JsonEmbedType>()

override fun writeStartObject(isCompact: Boolean) {
typeStack.add(JsonEmbedType.Object(isCompact))
Expand All @@ -57,7 +57,7 @@ abstract class AbstractJsonLikeWriter : IsJsonLikeWriter {
}

override fun writeEndObject() {
if (typeStack.isEmpty() || typeStack.last() !is JsonEmbedType.Object) {
if (typeStack.lastOrNull() !is JsonEmbedType.Object) {
throw IllegalJsonOperation("There is no object to close")
}
typeStack.removeAt(typeStack.lastIndex)
Expand Down Expand Up @@ -85,7 +85,7 @@ abstract class AbstractJsonLikeWriter : IsJsonLikeWriter {
}

override fun writeEndArray() {
if (typeStack.isEmpty() || typeStack.last() !is JsonEmbedType.Array) {
if (typeStack.lastOrNull() !is JsonEmbedType.Array) {
throw IllegalJsonOperation("Json: There is no array to close")
}
typeStack.removeAt(typeStack.lastIndex)
Expand Down
8 changes: 3 additions & 5 deletions json/src/commonMain/kotlin/maryk/json/IsJsonLikeReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,11 @@ sealed class JsonToken(val name: String) {

abstract class Stopped(name: String) : JsonToken(name)
object EndDocument : Stopped("EndDocument")
class Suspended(val lastToken: JsonToken, val storedValue: String?) : Stopped("Stopped reader")
class JsonException(val e: InvalidJsonContent) : Stopped("JsonException")
data class Suspended(val lastToken: JsonToken, val storedValue: String?) : Stopped("Stopped reader")
data class JsonException(val e: InvalidJsonContent) : Stopped("JsonException")

override fun toString() = when (this) {
is Value<*> -> this.value.let {
"$name(${this.value})"
}
is Value<*> -> "$name(${this.value})"
is FieldName -> "$name(${this.value})"
else -> name
}
Expand Down
26 changes: 11 additions & 15 deletions json/src/commonMain/kotlin/maryk/json/IsJsonLikeWriter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,23 @@ interface IsJsonLikeWriter {
fun writeValue(value: String)

/** Writes a [boolean] */
fun writeBoolean(boolean: Boolean) {
this.writeValue(boolean.toString())
}
fun writeBoolean(boolean: Boolean) = writeValue(boolean.toString())

/** Writes an [int] */
fun writeInt(int: Int) {
this.writeValue(int.toString())
}
fun writeInt(int: Int) = writeValue(int.toString())

/** Writes a [long] */
fun writeLong(long: Long) = writeValue(long.toString())

/** Writes a [double] */
fun writeDouble(double: Double) = writeValue(double.toString())

/** Writes a [float] */
fun writeFloat(float: Float) {
this.writeValue(float.toString())
}
fun writeFloat(float: Float) = writeValue(float.toString())

/** Writes a null */
fun writeNull() {
this.writeValue("null")
}
fun writeNull() = writeValue("null")
}

/** Exception for invalid JSON */
class IllegalJsonOperation(
description: String
) : Throwable(description)
class IllegalJsonOperation(description: String) : IllegalStateException(description)
8 changes: 2 additions & 6 deletions json/src/commonMain/kotlin/maryk/json/JsonReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import maryk.json.JsonToken.Value
import maryk.lib.extensions.HEX_CHARS
import maryk.lib.extensions.isLineBreak

private val skipArray = arrayOf(ObjectSeparator, ArraySeparator, StartDocument)
private val skipArray = setOf(ObjectSeparator, ArraySeparator, StartDocument)

/** Describes JSON complex types */
internal enum class JsonComplexType {
Expand Down Expand Up @@ -121,11 +121,7 @@ class JsonReader(
throw e
}

if (currentToken in skipArray) {
return nextToken()
}

return currentToken
return if (currentToken in skipArray) nextToken() else currentToken
}

private fun constructJsonValueToken(it: Any?) =
Expand Down
49 changes: 15 additions & 34 deletions json/src/commonMain/kotlin/maryk/json/JsonWriter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,12 @@ class JsonWriter(
private val pretty: Boolean = false,
private val writer: (String) -> Unit
) : AbstractJsonLikeWriter() {
private val indent = if (pretty) " " else ""
private val separator = if (pretty) ", " else ","
private val colonSpace = if (pretty) ": " else ":"

override fun writeStartObject(isCompact: Boolean) {
if (lastType != START_ARRAY
&& typeStack.isNotEmpty()
&& typeStack.last() is JsonEmbedType.Array
) {
writer(",")
if (pretty) {
writer(" ")
}
}
writeCommaIfNeeded()
super.writeStartObject(isCompact)
writer("{")
makePretty()
Expand All @@ -32,15 +28,7 @@ class JsonWriter(
}

override fun writeStartArray(isCompact: Boolean) {
if (lastType != START_ARRAY
&& typeStack.isNotEmpty()
&& typeStack.last() is JsonEmbedType.Array
) {
writer(",")
if (pretty) {
writer(" ")
}
}
writeCommaIfNeeded()
super.writeStartArray(isCompact)
writer("[")
}
Expand All @@ -57,10 +45,7 @@ class JsonWriter(
makePretty()
}
super.writeFieldName(name)
writer("\"$name\":")
if (pretty) {
writer(" ")
}
writer("\"$name\"$colonSpace")
}

/** Writes a string value including quotes */
Expand All @@ -74,12 +59,7 @@ class JsonWriter(
writer(value)
}
is JsonEmbedType.Array -> {
if (lastType != START_ARRAY) {
writer(",")
if (pretty) {
writer(" ")
}
}
writeCommaIfNeeded()
super.checkArrayValueAllowed()
writer(value)
}
Expand All @@ -91,14 +71,15 @@ class JsonWriter(
writer(value)
}

private fun writeCommaIfNeeded() {
if (lastType != START_ARRAY && typeStack.isNotEmpty() && typeStack.last() is JsonEmbedType.Array) {
writer(separator)
}
}

private fun makePretty() {
if (pretty) {
writer("\n")
for (it in typeStack) {
if (it is Object) {
writer(" ")
}
}
writer("\n${indent.repeat(typeStack.count { it is Object })}")
}
}
}
38 changes: 15 additions & 23 deletions json/src/commonMain/kotlin/maryk/json/PresetJsonTokenReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,35 @@ import maryk.json.JsonToken.Stopped
class PresetJsonTokenReader(
private val tokens: List<JsonToken>
) : IsJsonLikeReader {
override var currentToken: JsonToken = this.tokens[0]

override var currentToken: JsonToken = tokens.firstOrNull() ?: EndDocument
override var columnNumber = 0
override var lineNumber = 0

private var index = 1

private var typeStackCount: Int = 0
private var index = 0
private var typeStackCount = 0

override fun nextToken(): JsonToken {
if (index > tokens.lastIndex) {
this.currentToken = EndDocument
return this.currentToken
if (++index > tokens.lastIndex) {
return EndDocument.also { currentToken = it }
}

return this.tokens[index++].also {
this.currentToken = it
return tokens[index].also {
currentToken = it
when (it) {
is StartObject, is StartArray -> {
this.typeStackCount++
}
is EndObject, is EndArray -> {
this.typeStackCount--
}
else -> Unit
is StartObject, is StartArray -> typeStackCount++
is EndObject, is EndArray -> typeStackCount--
else -> {}
}
}
}

override fun skipUntilNextField(handleSkipToken: ((JsonToken) -> Unit)?) {
val startDepth = this.typeStackCount
val startDepth = typeStackCount
do {
nextToken()
handleSkipToken?.invoke(this.currentToken)
} while (
!((currentToken is FieldName || currentToken is EndObject) && this.typeStackCount <= startDepth)
&& currentToken !is Stopped
)
handleSkipToken?.invoke(currentToken)
} while (!(isFieldOrEndObject() && typeStackCount <= startDepth) && currentToken !is Stopped)
}

private fun isFieldOrEndObject() = currentToken is FieldName || currentToken is EndObject
}
27 changes: 13 additions & 14 deletions yaml/src/commonMain/kotlin/maryk/yaml/AliasReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,30 @@ import maryk.yaml.PlainStyleMode.FLOW_SEQUENCE
internal fun IsYamlCharReader.aliasReader(mode: PlainStyleMode): JsonToken {
var alias = ""

fun pushStoredTokens(): JsonToken {
val tokens = this.yamlReader.getTokensForAlias(alias)
for (index in 1 until tokens.size) {
this.yamlReader.pushToken(tokens[index])
}
return tokens[0]
}
fun pushStoredTokens(): JsonToken =
yamlReader.getTokensForAlias(alias).also { tokens ->
for (index in 1 until tokens.size) {
yamlReader.pushToken(tokens[index])
}
}.first()

try {
read()

val forbiddenChars = when (mode) {
FLOW_SEQUENCE -> arrayOf(' ', '\r', '\n', '\t', ',', ']')
FLOW_MAP -> arrayOf(' ', '\r', '\n', '\t', ',', '}')
else -> arrayOf(' ', '\r', '\n', '\t')
FLOW_SEQUENCE -> charArrayOf(' ', '\r', '\n', '\t', ',', ']')
FLOW_MAP -> charArrayOf(' ', '\r', '\n', '\t', ',', '}')
else -> charArrayOf(' ', '\r', '\n', '\t')
}

while (this.lastChar !in forbiddenChars) {
alias += this.lastChar
while (lastChar !in forbiddenChars) {
alias += lastChar
read()
}

return pushStoredTokens()
} catch (e: ExceptionWhileReadingJson) {
this.yamlReader.hasException = true
} catch (_: ExceptionWhileReadingJson) {
yamlReader.hasException = true
return pushStoredTokens()
}
}
33 changes: 14 additions & 19 deletions yaml/src/commonMain/kotlin/maryk/yaml/AnchorReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,40 @@ import maryk.json.JsonToken
internal class AnchorRecorder(
private val anchor: String
) {
private var storedValues = mutableListOf<JsonToken>()
private val storedValues = mutableListOf<JsonToken>()
private var tokenStartDepth: Int? = null

/** Records token from yamlReader and triggers [onEnd] if back at starting depth */
fun recordToken(token: JsonToken, tokenDepth: Int, onEnd: (String, Array<JsonToken>) -> Unit) {
this.storedValues.add(token)
storedValues.add(token)

if (this.tokenStartDepth == tokenDepth) {
onEnd(
this.anchor,
this.storedValues.toTypedArray()
)
if (tokenStartDepth == tokenDepth) {
onEnd(anchor, storedValues.toTypedArray())
}
}

/** Set [tokenDepth] on which this token reader started */
fun setTokenStartDepth(tokenDepth: Int) {
this.tokenStartDepth = tokenDepth
tokenStartDepth = tokenDepth
}
}

/** Reads an anchor and fires [onDone] when done */
internal fun IsYamlCharReader.anchorReader(onDone: () -> JsonToken): JsonToken {
var anchor = ""
read()

while (!this.lastChar.isWhitespace()) {
anchor += this.lastChar
read()
val anchor = buildString {
read() // Skip the '&' character
while (!lastChar.isWhitespace()) {
append(lastChar)
read()
}
}

if (anchor.trim().isEmpty()) {
if (anchor.isEmpty()) {
throw InvalidYamlContent("Name of anchor (&) needs at least 1 character")
}

// Pass this anchor reader to the YamlReader so it can start to pass tokens
this.yamlReader.recordAnchors(
AnchorRecorder(anchor)
)
// Pass this anchor recorder to the YamlReader so it can start to pass tokens
yamlReader.recordAnchors(AnchorRecorder(anchor))

return onDone()
}
4 changes: 2 additions & 2 deletions yaml/src/commonMain/kotlin/maryk/yaml/YamlReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ internal class YamlReaderImpl(
reader.readUntilToken(0)
}
}
} catch (e: ExceptionWhileReadingJson) {
} catch (_: ExceptionWhileReadingJson) {
this.hasException = true
currentReader.handleReaderInterrupt()
}
Expand Down Expand Up @@ -252,7 +252,7 @@ internal class YamlReaderImpl(
columnNumber += 1
}
lastChar = reader()
} catch (e: Throwable) { // Reached end or something bad happened
} catch (_: Throwable) { // Reached end or something bad happened
throw ExceptionWhileReadingJson()
}

Expand Down
Loading

0 comments on commit 4c58b04

Please sign in to comment.