@@ -81,37 +81,81 @@ private[mill] object Util {
8181 }
8282
8383 def parseYaml (fileName : String , headerData : String ): Result [ujson.Value ] =
84+ parseYaml0(fileName, headerData).map(upickle.core.BufferedValue .transform(_, ujson.Value ))
85+
86+ def parseYaml0 (fileName : String , headerData : String ): Result [upickle.core.BufferedValue ] =
8487 try Result .Success {
85- import org .snakeyaml .engine .v2 .api .{Load , LoadSettings }
86- val loaded = new Load (LoadSettings .builder().build()).loadFromString(headerData)
87-
88- // recursively convert java data structure to ujson.Value
89- def rec (x : Any ): ujson.Value = {
90- x match {
91- case d : java.util.Date => ujson.Str (d.toString)
92- case s : String => ujson.Str (s)
93- case d : Double => ujson.Num (d)
94- case d : Int => ujson.Num (d)
95- case d : Long => ujson.Num (d)
96- case true => ujson.True
97- case false => ujson.False
98- case null => ujson.Null
99- case m : java.util.Map [Object , Object ] =>
100- import scala .jdk .CollectionConverters ._
101- val scalaMap = m.asScala
102- ujson.Obj .from(scalaMap.map { case (k, v) => (k.toString, rec(v)) })
103- case l : java.util.List [Object ] =>
104- import scala .jdk .CollectionConverters ._
105- val scalaList : collection.Seq [Object ] = l.asScala
106- ujson.Arr .from(scalaList.map(rec))
88+ import org .snakeyaml .engine .v2 .api .{LoadSettings }
89+ import org .snakeyaml .engine .v2 .composer .Composer
90+ import org .snakeyaml .engine .v2 .parser .ParserImpl
91+ import org .snakeyaml .engine .v2 .scanner .StreamReader
92+ import org .snakeyaml .engine .v2 .nodes ._
93+ import scala .jdk .CollectionConverters ._
94+ import scala .collection .mutable .ArrayBuffer
95+
96+ val settings = LoadSettings .builder().build()
97+ val reader = new StreamReader (settings, headerData)
98+ val parser = new ParserImpl (settings, reader)
99+ val composer = new Composer (settings, parser)
100+
101+ // recursively convert Node to upickle.core.BufferedValue, preserving character offsets
102+ def rec (node : Node ): upickle.core.BufferedValue = {
103+ val index = node.getStartMark.map(_.getIndex.intValue()).orElse(0 )
104+
105+ node match {
106+ case scalar : ScalarNode =>
107+ val value = scalar.getValue
108+ val tag = scalar.getTag.getValue
109+ tag match {
110+ case " tag:yaml.org,2002:null" => upickle.core.BufferedValue .Null (index)
111+ case " tag:yaml.org,2002:bool" =>
112+ if (value == " true" ) upickle.core.BufferedValue .True (index)
113+ else upickle.core.BufferedValue .False (index)
114+ case " tag:yaml.org,2002:int" =>
115+ upickle.core.BufferedValue .Num (value, - 1 , - 1 , index)
116+ case " tag:yaml.org,2002:float" =>
117+ upickle.core.BufferedValue .Num (value, - 1 , - 1 , index)
118+ case _ => upickle.core.BufferedValue .Str (value, index)
119+ }
120+
121+ case mapping : MappingNode =>
122+ val pairs = mapping.getValue.asScala.map { tuple =>
123+ val keyNode = tuple.getKeyNode
124+ val valueNode = tuple.getValueNode
125+ val key = keyNode match {
126+ case s : ScalarNode => upickle.core.BufferedValue .Str (
127+ s.getValue,
128+ keyNode.getStartMark.map(_.getIndex.intValue()).orElse(0 )
129+ )
130+ case _ => upickle.core.BufferedValue .Str (
131+ keyNode.toString,
132+ keyNode.getStartMark.map(_.getIndex.intValue()).orElse(0 )
133+ )
134+ }
135+ (key, rec(valueNode))
136+ }
137+ upickle.core.BufferedValue .Obj (ArrayBuffer .from(pairs), jsonableKeys = true , index)
138+
139+ case sequence : SequenceNode =>
140+ val items = sequence.getValue.asScala.map(rec)
141+ upickle.core.BufferedValue .Arr (ArrayBuffer .from(items), index)
107142 }
108143 }
109144
110- // Treat a top-level `null` as an empty object, so that an empty YAML header
111- // block is treated gracefully rather than blowing up with a NPE
112- rec(loaded) match {
113- case ujson.Null => ujson.Obj ()
114- case v => v
145+ // Treat a top-level `null` or empty document as an empty object
146+ if (composer.hasNext) {
147+ val node = composer.next()
148+ rec(node) match {
149+ case nullValue @ upickle.core.BufferedValue .Null (_) =>
150+ upickle.core.BufferedValue .Obj (
151+ ArrayBuffer .empty,
152+ jsonableKeys = true ,
153+ nullValue.index
154+ )
155+ case v => v
156+ }
157+ } else {
158+ upickle.core.BufferedValue .Obj (ArrayBuffer .empty, jsonableKeys = true , 0 )
115159 }
116160 }
117161 catch {
0 commit comments