All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.maryk.yaml.FlowSequenceReader.kt Maven / Gradle / Ivy

package maryk.yaml

import maryk.json.ArrayType
import maryk.json.JsonToken
import maryk.json.JsonToken.EndArray
import maryk.json.JsonToken.EndComplexFieldName
import maryk.json.JsonToken.EndObject
import maryk.json.JsonToken.FieldName
import maryk.json.JsonToken.SimpleStartArray
import maryk.json.JsonToken.SimpleStartObject
import maryk.json.JsonToken.StartArray
import maryk.json.JsonToken.StartComplexFieldName
import maryk.json.JsonToken.StartObject
import maryk.json.MapType
import maryk.json.TokenType
import maryk.lib.exceptions.ParseException
import maryk.yaml.FlowSequenceState.COMPLEX_KEY
import maryk.yaml.FlowSequenceState.EXPLICIT_KEY
import maryk.yaml.FlowSequenceState.KEY
import maryk.yaml.FlowSequenceState.MAP_END
import maryk.yaml.FlowSequenceState.MAP_VALUE
import maryk.yaml.FlowSequenceState.MAP_VALUE_AFTER_COMPLEX_KEY
import maryk.yaml.FlowSequenceState.START
import maryk.yaml.FlowSequenceState.STOP
import maryk.yaml.FlowSequenceState.VALUE_START
import maryk.yaml.PlainStyleMode.FLOW_SEQUENCE

private enum class FlowSequenceState {
    START,
    VALUE_START,
    EXPLICIT_KEY,
    COMPLEX_KEY,
    MAP_VALUE_AFTER_COMPLEX_KEY,
    KEY,
    VALUE,
    MAP_VALUE,
    MAP_END,
    STOP
}

/** Reader for flow sequences [item1, item2, item3] */
internal class FlowSequenceReader(
    yamlReader: YamlReaderImpl,
    parentReader: P,
    private val indentToAdd: Int
) : YamlCharWithParentAndIndentReader

(yamlReader, parentReader) { private var state = START private var cachedCall: (() -> JsonToken)? = null override fun readUntilToken(extraIndent: Int, tag: TokenType?): JsonToken { val stateAtStart = this.state if (this.state == EXPLICIT_KEY) { this.state = KEY } return when (this.state) { START -> { this.state = VALUE_START tag?.let { StartArray( it as? ArrayType ?: throw InvalidYamlContent("Can only use sequence tags on sequences") ) } ?: SimpleStartArray } COMPLEX_KEY -> this.jsonTokenCreator(null, false, null, 0) else -> { while (this.lastChar.isWhitespace()) { read() } return when (this.lastChar) { '\'' -> this.singleQuoteString(tag, extraIndent, this::jsonTokenCreator) '\"' -> this.doubleQuoteString(tag, extraIndent, this::jsonTokenCreator) '{' -> this.flowMapReader(tag, 0).let(this::checkComplexFieldAndReturn) '[' -> this.flowSequenceReader(tag, 0).let(this::checkComplexFieldAndReturn) '!' -> this.tagReader { this.readUntilToken(extraIndent, it) } '&' -> this.anchorReader { this.readUntilToken(extraIndent, tag) } '*' -> this.aliasReader(FLOW_SEQUENCE) '-' -> { read() if (this.lastChar.isWhitespace()) { throw InvalidYamlContent("Expected a comma") } else { this.plainStringReader("-", tag, FLOW_SEQUENCE, 0, this::jsonTokenCreator) } } ',' -> { if (this.state != MAP_END && this.state != VALUE_START) { this.jsonTokenCreator(null, false, null, 0) } else { read() tokenReturner(tag) { this.readUntilToken(0) } } } ':' -> { read() if (this.state != MAP_VALUE_AFTER_COMPLEX_KEY) { this.state = KEY } return tokenReturner(tag) { this.readUntilToken(0) } } ']' -> { tokenReturner(tag) { this.state = STOP read() this.currentReader = this.parentReader EndArray } } '}' -> { read() // This should be handled in Map reader. Otherwise incorrect content throw InvalidYamlContent("Invalid char $lastChar at this position") } '?' -> { read() if (this.lastChar.isWhitespace()) { if (stateAtStart == EXPLICIT_KEY) { throw InvalidYamlContent("Cannot have two ? explicit keys in a row") } this.state = EXPLICIT_KEY this.jsonTokenCreator(null, false, tag, 0) } else if (this.lastChar == ',' || this.lastChar == ':') { this.state = EXPLICIT_KEY this.jsonTokenCreator(null, false, null, 0) } else { this.plainStringReader("?", tag, FLOW_SEQUENCE, 0, this::jsonTokenCreator) } } '|', '>', '@', '`' -> throw InvalidYamlContent("Unsupported character $lastChar in flow array") else -> this.plainStringReader( "", tag, FLOW_SEQUENCE, indentToAdd, this::jsonTokenCreator ) } } } } override fun endIndentLevel( indentCount: Int, tag: TokenType?, tokenToReturn: (() -> JsonToken)? ) = throw InvalidYamlContent("Missing a comma") private fun checkComplexFieldAndReturn(jsonToken: JsonToken): JsonToken { if (this.state == KEY) { this.state = COMPLEX_KEY this.yamlReader.pushToken(jsonToken) return StartComplexFieldName } return jsonToken } private fun tokenReturner(tag: TokenType?, doIfNoToken: () -> JsonToken): JsonToken { this.cachedCall?.let { this.cachedCall = null if (this.state == VALUE_START) { this.state = FlowSequenceState.VALUE } return it() } return when (this.state) { MAP_END -> { this.state = VALUE_START EndObject } KEY, MAP_VALUE -> this.jsonTokenCreator(null, false, null, 0) VALUE_START -> { if (tag == null) { doIfNoToken() } else { this.createTokensFittingTag(tag) } } else -> doIfNoToken() } } private fun jsonTokenCreator( value: String?, isPlainStringReader: Boolean, tag: TokenType?, @Suppress("UNUSED_PARAMETER") extraIndentAtStart: Int ): JsonToken = when (this.state) { START -> throw ParseException("Sequence cannot be in start mode") EXPLICIT_KEY -> this.startObject(tag) VALUE_START -> { this.cachedCall = { this.jsonTokenCreator(value, isPlainStringReader, tag, 0) } this.readUntilToken(0) } KEY -> { this.state = MAP_VALUE checkAndCreateFieldName(value, isPlainStringReader) } COMPLEX_KEY -> { this.state = MAP_VALUE_AFTER_COMPLEX_KEY EndComplexFieldName } FlowSequenceState.VALUE -> { this.state = VALUE_START createYamlValueToken(value, tag, isPlainStringReader) } MAP_VALUE, MAP_VALUE_AFTER_COMPLEX_KEY -> { this.state = MAP_END createYamlValueToken(value, tag, isPlainStringReader) } MAP_END, STOP -> throw ParseException("Not a content token creator") } override fun foundMap(tag: TokenType?, startedAtIndent: Int) = if (this.state == VALUE_START) { startObject(tag) } else { null }.also { this.state = MAP_VALUE } private fun startObject(tag: TokenType?): JsonToken { return tag?.let { StartObject( tag as? MapType ?: throw InvalidYamlContent("$tag should be a map type") ) } ?: SimpleStartObject } // Sequences can only return single item maps override fun checkAndCreateFieldName(fieldName: String?, isPlainStringReader: Boolean) = FieldName(fieldName) override fun handleReaderInterrupt(): JsonToken { if (this.state != STOP) { throw InvalidYamlContent("Sequences started with [ should always end with a ]") } this.currentReader = this.parentReader return EndArray } } /** * Creates a FlowSequenceReader within a YamlCharReader with [tag] as typing. * Reads until first token and returns it */ internal fun P.flowSequenceReader(tag: TokenType?, indentToAdd: Int): JsonToken { read() return FlowSequenceReader( yamlReader = this.yamlReader, parentReader = this, indentToAdd = indentToAdd ).let { this.currentReader = it it.readUntilToken(0, tag) } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy