-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #98 from VirtusLab/add-tags-to-nodes
Add tags to nodes and use them in decoding
- Loading branch information
Showing
21 changed files
with
363 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
yaml/shared/src/main/scala/org/virtuslab/yaml/LoadSettings.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.virtuslab.yaml | ||
|
||
case class LoadSettings(constructors: Map[Tag, YamlDecoder[?]]) | ||
object LoadSettings: | ||
val empty = LoadSettings(Map.empty) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,58 @@ | ||
package org.virtuslab.yaml | ||
|
||
import org.virtuslab.yaml.Range | ||
import org.virtuslab.yaml.Tag | ||
import org.virtuslab.yaml.syntax.YamlPrimitive | ||
|
||
/** | ||
* ADT that corresponds to the YAML representation graph nodes https://yaml.org/spec/1.2/spec.html#id2764044 | ||
*/ | ||
sealed trait Node: | ||
def pos: Option[Range] | ||
private[yaml] def pos: Option[Range] | ||
def tag: Tag | ||
def as[T](using | ||
c: YamlDecoder[T], | ||
settings: LoadSettings = LoadSettings.empty | ||
): Either[YamlError, T] = | ||
c.construct(this) | ||
|
||
object Node: | ||
final case class ScalarNode(value: String, pos: Option[Range] = None) extends Node | ||
final case class ScalarNode private[yaml] (value: String, tag: Tag, pos: Option[Range] = None) | ||
extends Node | ||
|
||
object ScalarNode: | ||
def apply(value: String): ScalarNode = new ScalarNode(value) | ||
def apply(value: String): ScalarNode = new ScalarNode(value, Tag.resolveTag(value)) | ||
def unapply(node: ScalarNode): Option[(String, Tag)] = Some((node.value, node.tag)) | ||
end ScalarNode | ||
|
||
final case class SequenceNode(nodes: Seq[Node], pos: Option[Range] = None) extends Node | ||
final case class SequenceNode private[yaml] ( | ||
nodes: Seq[Node], | ||
tag: Tag, | ||
pos: Option[Range] = None | ||
) extends Node | ||
object SequenceNode: | ||
def apply(nodes: Node*): SequenceNode = new SequenceNode(nodes, None) | ||
def apply(nodes: Node*): SequenceNode = new SequenceNode(nodes, Tag.seq, None) | ||
def apply(first: YamlPrimitive, rest: YamlPrimitive*): SequenceNode = | ||
val nodes: List[YamlPrimitive] = (first :: rest.toList) | ||
new SequenceNode(nodes.map(_.node), None) | ||
new SequenceNode(nodes.map(_.node), Tag.seq, None) | ||
def unapply(node: SequenceNode): Option[(Seq[Node], Tag)] = Some((node.nodes, node.tag)) | ||
end SequenceNode | ||
|
||
final case class MappingNode( | ||
mappings: Seq[KeyValueNode], | ||
final case class MappingNode private[yaml] ( | ||
mappings: Map[Node, Node], | ||
tag: Tag, | ||
pos: Option[Range] = None | ||
) extends Node | ||
|
||
object MappingNode: | ||
def apply(nodes: KeyValueNode*): MappingNode = MappingNode(nodes, None) | ||
def apply(mappings: Map[Node, Node]): MappingNode = MappingNode(mappings, Tag.map, None) | ||
def apply(mappings: (Node, Node)*): MappingNode = MappingNode(mappings.toMap, Tag.map, None) | ||
def apply( | ||
first: (YamlPrimitive, YamlPrimitive), | ||
rest: (YamlPrimitive, YamlPrimitive)* | ||
): MappingNode = | ||
val nodes = (first :: rest.toList) | ||
val kvn = nodes.map((k, v) => KeyValueNode(k.node, v.node)) | ||
new MappingNode(kvn, None) | ||
|
||
final case class KeyValueNode( | ||
key: Node, | ||
value: Node, | ||
pos: Option[Range] = None | ||
) extends Node | ||
|
||
val primitives = (first :: rest.toList) | ||
val mappings = primitives.map((k, v) => (k.node -> v.node)).toMap | ||
new MappingNode(mappings, Tag.map, None) | ||
def unapply(node: MappingNode): Option[(Map[Node, Node], Tag)] = Some((node.mappings, node.tag)) | ||
end MappingNode | ||
end Node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package org.virtuslab.yaml | ||
|
||
import scala.reflect.ClassTag | ||
|
||
sealed trait Tag: | ||
def value: String | ||
|
||
final case class CoreSchemaTag(value: String) extends Tag | ||
final case class CustomTag(value: String) extends Tag | ||
|
||
object Tag: | ||
def apply[T](implicit classTag: ClassTag[T]): Tag = CustomTag( | ||
s"!${classTag.runtimeClass.getName}" | ||
) | ||
|
||
private val default = "tag:yaml.org,2002:" | ||
val nullTag: Tag = CoreSchemaTag(s"${default}null") | ||
val boolean: Tag = CoreSchemaTag(s"${default}bool") | ||
val int: Tag = CoreSchemaTag(s"${default}int") | ||
val float: Tag = CoreSchemaTag(s"${default}float") | ||
val str: Tag = CoreSchemaTag(s"${default}str") | ||
val seq: Tag = CoreSchemaTag(s"${default}seq") | ||
val map: Tag = CoreSchemaTag(s"${default}map") | ||
|
||
val corePrimitives = Set(nullTag, boolean, int, float, str) | ||
val coreSchemaValues = (corePrimitives ++ Set(seq, map)).map(_.value) | ||
|
||
private val nullPattern = "null|Null|NULL|~".r | ||
private val booleanPattern = "true|True|TRUE|false|False|FALSE".r | ||
private val int10Pattern = "[-+]?[0-9]+".r | ||
private val int8Pattern = "0o[0-7]+".r | ||
private val int16Pattern = "0x[0-9a-fA-F]+".r | ||
private val floatPattern = "[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?".r | ||
private val minusInfinity = "-(\\.inf|\\.Inf|\\.INF)".r | ||
private val plusInfinity = "\\+?(\\.inf|\\.Inf|\\.INF)".r | ||
|
||
def resolveTag(value: String): Tag = { | ||
value match { | ||
case null => nullTag | ||
case nullPattern(_*) => nullTag | ||
case booleanPattern(_*) => boolean | ||
case int10Pattern(_*) => int | ||
case int8Pattern(_*) => int | ||
case int16Pattern(_*) => int | ||
case floatPattern(_*) => float | ||
case minusInfinity(_*) => float | ||
case plusInfinity(_*) => float | ||
case _ => str | ||
} | ||
} | ||
end Tag |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.