Skip to content

Commit 31cd2bc

Browse files
committed
Always attempt to print element first, Introduce metadata
1 parent 26a4573 commit 31cd2bc

File tree

13 files changed

+175
-154
lines changed

13 files changed

+175
-154
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ allprojects {
2020

2121
allprojects {
2222
group = "io.exoquery"
23-
version = "2.0.2"
23+
version = "3.0.0.D"
2424
}
2525

2626
subprojects {

pprint-kotlin-core/build.gradle.kts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ kotlin {
2222
val isWindows = platform == "windows"
2323

2424
// If we're not the CI build a limited set of standard targets
25+
jvm {
26+
jvmToolchain(11)
27+
}
28+
2529
if(!isCI) {
26-
jvm {
27-
jvmToolchain(11)
28-
}
2930
js {
3031
browser()
3132
nodejs()
@@ -38,9 +39,6 @@ kotlin {
3839

3940
// If we are a CI, build all the targets for the specified platform
4041
if (isLinux && isCI) {
41-
jvm {
42-
jvmToolchain(11)
43-
}
4442
js {
4543
browser()
4644
nodejs()

pprint-kotlin-core/src/commonMain/kotlin/io/exoquery/pprint/PPrinterBase.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ abstract class PPrinterBase<T>(open val config: PPrinterConfig): Walker<T> {
9494
// The three stages within the pretty-printing process:
9595

9696
// Convert the Any into a lazy Tree of `Apply`, `Infix` and `Lazy`/`Strict` literals
97-
val tree = this.treeify(x, escapeUnicode, showFieldNames)
97+
val tree = this.treeify(x, null, escapeUnicode, showFieldNames)
9898
// Render the `Any` into a stream of tokens, properly indented and wrapped
9999
// at the given width
100100
val renderer = Renderer(width, config.colorApplyPrefix, config.colorLiteral, indent)

pprint-kotlin-core/src/commonMain/kotlin/io/exoquery/pprint/Walker.kt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,34 @@ import io.exoquery.fansi.Attrs
1010
* `foo op bar`, and terminals `foo` in both lazy and eager forms
1111
*/
1212
sealed interface Tree {
13+
val elementName: String?
1314

1415
/**
1516
* Foo(aa, bbb, cccc)
1617
*/
17-
data class Apply(val prefix: String, val body: Iterator<Tree>): Tree
18+
data class Apply(val prefix: String, val body: Iterator<Tree>, override val elementName: String?): Tree
1819

1920
/**
2021
* LHS op RHS
2122
*/
22-
data class Infix(val lhs: Tree, val op: String, val rhs: Tree): Tree
23+
data class Infix(val lhs: Tree, val op: String, val rhs: Tree, override val elementName: String?): Tree
2324

2425
/**
2526
* "xyz"
2627
*/
27-
data class Literal(val body: String): Tree{
28+
data class Literal(val body: String, override val elementName: String?): Tree{
2829
val hasNewLine = body.any { c -> c == '\n' || c == '\r' }
2930
}
3031

3132
/**
3233
* x = y
3334
*/
34-
data class KeyValue(val key: String, val value: Tree): Tree
35+
data class KeyValue(val key: String, val value: Tree, override val elementName: String?): Tree
3536

3637
/**
3738
* xyz
3839
*/
39-
data class Lazy(val body0: (Ctx) -> Iterator<String>): Tree
40+
data class Lazy(val body0: (Ctx) -> Iterator<String>, override val elementName: String?): Tree
4041

4142
data class Ctx(
4243
val width: Int,
@@ -51,17 +52,17 @@ sealed interface Tree {
5152
abstract class EncodeHelper {
5253
abstract fun makeHexString(c: Char): String
5354

54-
fun encodeChar(x: Char, escapeUnicode: Boolean): Tree.Literal {
55+
fun encodeChar(x: Char, escapeUnicode: Boolean, elementName: String?): Tree.Literal {
5556
val sb = StringBuilder()
5657
sb.append('\'')
5758
Util.escapeChar(x, sb, escapeUnicode, ::makeHexString)
5859
sb.append('\'')
59-
return Tree.Literal(sb.toString())
60+
return Tree.Literal(sb.toString(), elementName)
6061
}
6162

62-
fun encodeString(x: String, escapeUnicode: Boolean): Tree.Literal {
63-
return if (x.any {c -> c == '\n' || c == '\r'}) Tree.Literal("\"\"\"" + x + "\"\"\"")
64-
else Tree.Literal(Util.literalize(x.toCharArray(), escapeUnicode, ::makeHexString))
63+
fun encodeString(x: String, escapeUnicode: Boolean, elementName: String?): Tree.Literal {
64+
return if (x.any {c -> c == '\n' || c == '\r'}) Tree.Literal("\"\"\"" + x + "\"\"\"", elementName)
65+
else Tree.Literal(Util.literalize(x.toCharArray(), escapeUnicode, ::makeHexString), elementName)
6566
}
6667
}
6768

@@ -73,7 +74,7 @@ interface Walker<T> {
7374
// No additional handlers. To extend, override the treeify function
7475
// fun additionalHandlers: PartialFunction<Any, Tree>
7576

76-
fun treeify(x: T, escapeUnicode: Boolean, showFieldNames: Boolean): Tree
77+
fun treeify(x: T, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree
7778
}
7879

7980

pprint-kotlin-kmp/build.gradle.kts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ kotlin {
2323
val isWindows = platform == "windows"
2424

2525
// If we're not the CI build a limited set of standard targets
26+
jvm {
27+
jvmToolchain(11)
28+
}
29+
2630
if(!isCI) {
27-
jvm {
28-
jvmToolchain(11)
29-
}
3031
js {
3132
browser()
3233
nodejs()
@@ -39,9 +40,6 @@ kotlin {
3940

4041
// If we are a CI, build all the targets for the specified platform
4142
if (isLinux && isCI) {
42-
jvm {
43-
jvmToolchain(11)
44-
}
4543
js {
4644
browser()
4745
nodejs()

pprint-kotlin-kmp/src/commonMain/kotlin/io/exoquery/kmp/pprint/PPrinter.kt

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,46 +38,62 @@ open class PPrinter<T>(open val serializer: SerializationStrategy<T>, override o
3838
}
3939

4040

41-
override fun treeify(x: T, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
42-
treeifyWith(Treeifyable.Elem(x, serializer), escapeUnicode, showFieldNames)
41+
override fun treeify(x: T, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
42+
treeifyElement(Treeifyable.Elem(x, serializer), elementName, escapeUnicode, showFieldNames)
4343

44-
open fun <R> treeifyWith(treeifyable: Treeifyable<R>, escapeUnicode: Boolean, showFieldNames: Boolean): Tree {
45-
val value = treeifyable.value
46-
return when {
47-
value == null -> Tree.Literal("null")
48-
value is Boolean -> Tree.Literal(value.toString())
49-
value is Byte -> Tree.Literal(value.toString())
50-
value is Short -> Tree.Literal(value.toString())
51-
value is Int -> Tree.Literal(value.toString())
52-
value is Long -> Tree.Literal("${value}L")
53-
value is Float -> Tree.Literal("${value}F")
54-
value is Double -> Tree.Literal(value.toString())
55-
value is Char -> EncodeHelperImpl.encodeChar(value, escapeUnicode)
56-
value is String -> EncodeHelperImpl.encodeString(value, escapeUnicode)
57-
// Empty iterator case which for some reason causes an exception with the regular kotlin serializer
58-
value is Iterator<*> && !value.hasNext() -> Tree.Literal("empty iterator")
59-
value is Iterator<*> && value.hasNext() -> Tree.Literal("non-empty iterator")
60-
// If it is a sequence and the user is using the correct serializer i.e. PPrintSequenceSerializer
61-
value is Sequence<*> && treeifyable is Treeifyable.Elem && treeifyable.serializer is PPrintSequenceSerializer<*> -> {
62-
@Suppress("UNCHECKED_CAST")
63-
val elementSerializer = treeifyable.serializer.element as KSerializer<Any?>
64-
Tree.Apply("Sequence", value.map { treeifyWith(Treeifyable.Elem(it, elementSerializer), escapeUnicode, showFieldNames) }.iterator())
65-
}
66-
treeifyable is Treeifyable.Elem -> {
67-
val encoder = TreeElementEncoder(this)
68-
treeifyable.serializer.serialize(encoder, treeifyable.value)
69-
val elements = encoder.retrieve()
70-
PPrinterHelper.encodeComposite(
71-
treeifyable.value,
72-
treeifyable.serializer.descriptor,
73-
elements,
74-
showFieldNames,
75-
config
76-
)
77-
}
78-
else -> Tree.Literal(value.toString())
44+
open fun <E> treeifyComposite(elem: Treeifyable.Elem<E>, elementName: String?, showFieldNames: Boolean) = run {
45+
val encoder = TreeElementEncoder(this)
46+
elem.serializer.serialize(encoder, elem.value)
47+
val elements = encoder.retrieve()
48+
PPrinterHelper.encodeComposite(
49+
elem.value,
50+
elementName,
51+
elem.serializer.descriptor,
52+
elements,
53+
showFieldNames,
54+
config
55+
)
56+
}
57+
58+
open fun <R> treeifyElement(treeifyable: Treeifyable<R>, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree {
59+
return when(treeifyable) {
60+
is Treeifyable.Elem<*> ->
61+
// try to treeify it with the treeifyValueOrNull function in case there's an override for the element
62+
treeifyValueOrNull(treeifyable.value, elementName, escapeUnicode, showFieldNames) ?: run {
63+
when {
64+
// If it is a sequence and the user is using the correct serializer i.e. PPrintSequenceSerializer
65+
treeifyable.value is Sequence<*> && treeifyable.serializer is PPrintSequenceSerializer<*> -> {
66+
@Suppress("UNCHECKED_CAST")
67+
val elementSerializer = treeifyable.serializer.element as KSerializer<Any?>
68+
Tree.Apply("Sequence", (treeifyable.value as Sequence<*>).map { treeifyElement(Treeifyable.Elem(it, elementSerializer), null, escapeUnicode, showFieldNames) }.iterator(), elementName)
69+
}
70+
// Otherwise it's a regular composite
71+
else -> treeifyComposite(treeifyable, elementName, showFieldNames)
72+
}
73+
}
74+
is Treeifyable.Leaf ->
75+
treeifyValueOrNull(treeifyable.value, elementName, escapeUnicode, showFieldNames) ?:
76+
Tree.Literal(treeifyable.value.toString(), elementName)
7977
}
8078
}
79+
80+
open fun <R> treeifyValueOrNull(value: R, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree? =
81+
when {
82+
value == null -> Tree.Literal("null", elementName)
83+
value is Boolean -> Tree.Literal(value.toString(), elementName)
84+
value is Byte -> Tree.Literal(value.toString(), elementName)
85+
value is Short -> Tree.Literal(value.toString(), elementName)
86+
value is Int -> Tree.Literal(value.toString(), elementName)
87+
value is Long -> Tree.Literal("${value}L", elementName)
88+
value is Float -> Tree.Literal("${value}F", elementName)
89+
value is Double -> Tree.Literal(value.toString(), elementName)
90+
value is Char -> EncodeHelperImpl.encodeChar(value, escapeUnicode, elementName)
91+
value is String -> EncodeHelperImpl.encodeString(value, escapeUnicode, elementName)
92+
// Empty iterator case which for some reason causes an exception with the regular kotlin serializer
93+
value is Iterator<*> && !value.hasNext() -> Tree.Literal("empty iterator", elementName)
94+
value is Iterator<*> && value.hasNext() -> Tree.Literal("non-empty iterator", elementName)
95+
else -> null
96+
}
8197
}
8298

8399
class PPrintSequenceSerializer<T>(val element: KSerializer<T>) : KSerializer<Sequence<T>> {

pprint-kotlin-kmp/src/commonMain/kotlin/io/exoquery/kmp/pprint/PPrinterHelper.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import kotlinx.serialization.descriptors.*
77

88
object PPrinterHelper {
99
@OptIn(ExperimentalSerializationApi::class)
10-
fun <T> encodeComposite(value: T, descriptor: SerialDescriptor, childrenRaw: List<ChildElement>, showFieldNames: Boolean, config: PPrinterConfig): Tree {
10+
fun <T> encodeComposite(value: T, elementName: String?, descriptor: SerialDescriptor, childrenRaw: List<ChildElement>, showFieldNames: Boolean, config: PPrinterConfig): Tree {
1111
// important to make the sequence lazy in case doing a call to ChildElement.tree() causes an infinite loop
1212
val children = childrenRaw.asSequence()
1313

@@ -18,7 +18,7 @@ object PPrinterHelper {
1818
if (showFieldNamesIfPossible)
1919
children.map { when (it) {
2020
is ChildElement.Atom -> it.tree()
21-
is ChildElement.Member -> Tree.KeyValue(it.name, it.tree())
21+
is ChildElement.Member -> Tree.KeyValue(it.name, it.tree(), it.name)
2222
} }
2323
else
2424
children.map { it.tree() }
@@ -32,7 +32,7 @@ object PPrinterHelper {
3232
val childTrees = processChildren(false)
3333
//require(childTrees.size % 2 == 0)
3434
childTrees.asSequence().chunked(2).map { (k, v) ->
35-
Tree.Infix(k, "->", v)
35+
Tree.Infix(k, "->", v, null)
3636
}
3737
}
3838
// If it's a list the keys would be the indexes, don't bother printing that
@@ -61,7 +61,7 @@ object PPrinterHelper {
6161
// sealed interface Colors { object Red: Colors; object Blue: Colors; data class Custom(val hex: String): Colors }
6262
// for Colors.Red we should just print "Red"
6363
kind == StructureKind.OBJECT && !childrenIterator.hasNext() ->
64-
Tree.Literal(serialNameParsed)
64+
Tree.Literal(serialNameParsed, elementName)
6565

6666

6767
// The kotlinx-serlization mechanism will attempt to identify a polymorphic type by adding a type-field
@@ -80,11 +80,11 @@ object PPrinterHelper {
8080
kind == PolymorphicKind.OPEN || kind == PolymorphicKind.SEALED -> {
8181
childrenRaw.find { it is ChildElement.Member && it.name == "value" }?.let {
8282
it.tree()
83-
} ?: Tree.Apply(serialNameParsed, childrenIterator)
83+
} ?: Tree.Apply(serialNameParsed, childrenIterator, elementName)
8484
}
8585

8686
else -> {
87-
Tree.Apply(serialNameParsed, childrenIterator)
87+
Tree.Apply(serialNameParsed, childrenIterator, elementName)
8888
}
8989
}
9090
}

pprint-kotlin-kmp/src/commonMain/kotlin/io/exoquery/kmp/pprint/TreeElementEncoder.kt

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import kotlinx.serialization.modules.SerializersModule
1414
sealed interface ChildElement {
1515
val tree: () -> Tree
1616

17-
data class Member(val name: String, override val tree: () -> Tree): ChildElement
17+
data class Member(val name: String, val makeTree: (String) -> Tree): ChildElement {
18+
override val tree = { makeTree(name) }
19+
}
1820
data class Atom(override val tree: () -> Tree): ChildElement
1921
}
2022

@@ -86,16 +88,16 @@ open class TreeElementEncoder private constructor (val pprinter: PPrinter<*>) :
8688

8789
protected val currChildren = mutableListOf<ChildElement>()
8890

89-
protected fun add(name: String, tree: () -> Tree): Unit {
91+
protected fun add(name: String, tree: (name: String) -> Tree): Unit {
9092
currChildren.add(ChildElement.Member(name, tree))
9193
}
9294

9395
protected fun addAtom(tree: () -> Tree): Unit {
9496
currChildren.add(ChildElement.Atom(tree))
9597
}
9698

97-
private fun treeifyLeaf(value: Any?): Tree =
98-
pprinter.treeifyWith<Any?>(PPrinter.Treeifyable.Leaf(value), escapeUnicode, showFieldNames)
99+
private fun treeifyLeaf(value: Any?, elementName: String?): Tree =
100+
pprinter.treeifyElement<Any?>(PPrinter.Treeifyable.Leaf(value), elementName, escapeUnicode, showFieldNames)
99101

100102
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = this
101103

@@ -113,34 +115,34 @@ open class TreeElementEncoder private constructor (val pprinter: PPrinter<*>) :
113115
/**
114116
* Invoked to encode a value when specialized `encode*` method was not overridden.
115117
*/
116-
public fun encodeValue(value: Any): Unit = addAtom { treeifyLeaf(value) }
118+
public fun encodeValue(value: Any): Unit = addAtom { treeifyLeaf(value, null) }
117119

118-
override fun encodeNull(): Unit = addAtom { treeifyLeaf(null) }
120+
override fun encodeNull(): Unit = addAtom { treeifyLeaf(null, null) }
119121

120122
override fun encodeInline(descriptor: SerialDescriptor): Encoder = this
121123

122124
// Delegating implementation of CompositeEncoder
123-
override fun encodeBooleanElement(desc: SerialDescriptor, index: Int, value: Boolean) = add(desc.getElementName(index), { treeifyLeaf(value) })
124-
override fun encodeByteElement(desc: SerialDescriptor, index: Int, value: Byte) = add(desc.getElementName(index), { treeifyLeaf(value) })
125-
override fun encodeShortElement(desc: SerialDescriptor, index: Int, value: Short) = add(desc.getElementName(index), { treeifyLeaf(value) })
126-
override fun encodeIntElement(desc: SerialDescriptor, index: Int, value: Int) = add(desc.getElementName(index), { treeifyLeaf(value) })
127-
override fun encodeLongElement(desc: SerialDescriptor, index: Int, value: Long) = add(desc.getElementName(index), { treeifyLeaf(value) })
128-
override fun encodeFloatElement(desc: SerialDescriptor, index: Int, value: Float) = add(desc.getElementName(index), { treeifyLeaf(value) })
129-
override fun encodeDoubleElement(desc: SerialDescriptor, index: Int, value: Double) = add(desc.getElementName(index), { treeifyLeaf(value) })
130-
override fun encodeCharElement(desc: SerialDescriptor, index: Int, value: Char) = add(desc.getElementName(index), { treeifyLeaf(value) })
131-
override fun encodeStringElement(desc: SerialDescriptor, index: Int, value: String) = add(desc.getElementName(index), { treeifyLeaf(value) })
132-
133-
override fun encodeBoolean(value: Boolean): Unit = addAtom { treeifyLeaf(value) }
134-
override fun encodeByte(value: Byte): Unit = addAtom { treeifyLeaf(value) }
135-
override fun encodeShort(value: Short): Unit = addAtom { treeifyLeaf(value) }
136-
override fun encodeInt(value: Int): Unit = addAtom { treeifyLeaf(value) }
137-
override fun encodeLong(value: Long): Unit = addAtom { treeifyLeaf(value) }
138-
override fun encodeFloat(value: Float): Unit = addAtom { treeifyLeaf(value) }
139-
override fun encodeDouble(value: Double): Unit = addAtom { treeifyLeaf(value) }
140-
override fun encodeChar(value: Char): Unit = addAtom { treeifyLeaf(value) }
141-
override fun encodeString(value: String): Unit = addAtom { treeifyLeaf(value) }
142-
143-
override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int): Unit = addAtom { treeifyLeaf(enumDescriptor.getElementName(index)) }
125+
override fun encodeBooleanElement(desc: SerialDescriptor, index: Int, value: Boolean) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
126+
override fun encodeByteElement(desc: SerialDescriptor, index: Int, value: Byte) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
127+
override fun encodeShortElement(desc: SerialDescriptor, index: Int, value: Short) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
128+
override fun encodeIntElement(desc: SerialDescriptor, index: Int, value: Int) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
129+
override fun encodeLongElement(desc: SerialDescriptor, index: Int, value: Long) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
130+
override fun encodeFloatElement(desc: SerialDescriptor, index: Int, value: Float) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
131+
override fun encodeDoubleElement(desc: SerialDescriptor, index: Int, value: Double) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
132+
override fun encodeCharElement(desc: SerialDescriptor, index: Int, value: Char) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
133+
override fun encodeStringElement(desc: SerialDescriptor, index: Int, value: String) = add(desc.getElementName(index), { treeifyLeaf(value, it) })
134+
135+
override fun encodeBoolean(value: Boolean): Unit = addAtom { treeifyLeaf(value, null) }
136+
override fun encodeByte(value: Byte): Unit = addAtom { treeifyLeaf(value, null) }
137+
override fun encodeShort(value: Short): Unit = addAtom { treeifyLeaf(value, null) }
138+
override fun encodeInt(value: Int): Unit = addAtom { treeifyLeaf(value, null) }
139+
override fun encodeLong(value: Long): Unit = addAtom { treeifyLeaf(value, null) }
140+
override fun encodeFloat(value: Float): Unit = addAtom { treeifyLeaf(value, null) }
141+
override fun encodeDouble(value: Double): Unit = addAtom { treeifyLeaf(value, null) }
142+
override fun encodeChar(value: Char): Unit = addAtom { treeifyLeaf(value, null) }
143+
override fun encodeString(value: String): Unit = addAtom { treeifyLeaf(value, null) }
144+
145+
override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int): Unit = addAtom { treeifyLeaf(enumDescriptor.getElementName(index), enumDescriptor.getElementName(index)) }
144146

145147
override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
146148
return super.beginCollection(descriptor, collectionSize)
@@ -159,8 +161,9 @@ open class TreeElementEncoder private constructor (val pprinter: PPrinter<*>) :
159161
value: T
160162
) {
161163
if (encodeElement(descriptor, index)) {
162-
add(descriptor.getElementName(index)) {
163-
pprinter.treeifyWith<T>(PPrinter.Treeifyable.Elem(value, serializer), escapeUnicode, showFieldNames)
164+
val elemName = descriptor.getElementName(index)
165+
add(elemName) {
166+
pprinter.treeifyElement<T>(PPrinter.Treeifyable.Elem(value, serializer), it, escapeUnicode, showFieldNames)
164167
}
165168
}
166169
}

pprint-kotlin-kmp/src/commonTest/kotlin/io/exoquery/pprint/DerivationTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ data class CustomToString(val value: Int = 1){
1515
}
1616
sealed interface Customs {
1717
data class A(val i: Int): Customs
18-
data class B(val s: String): Customs{
18+
data class B(val s: String): Customs {
1919
override fun toString() = "Beeee"
2020
}
2121
}

0 commit comments

Comments
 (0)