/
parse_context.cr
63 lines (53 loc) · 2.04 KB
/
parse_context.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# Parsing context that holds anchors and what they refer to.
#
# When implementing `new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)`
# to deserialize an object from a node, `Reference` types must invoke
# both `read_alias` and `record_anchor` in order to support parsing
# recursive data structures.
#
# - `read_alias` must be invoked before an instance is created
# - `record_anchor` must be invoked after an instance is created and
# before its members are deserialized.
class YAML::ParseContext
def initialize
# Recorded anchors: anchor => {object_id, crystal_type_id}
@anchors = {} of String => {UInt64, Int32}
end
# Associates an object with an anchor.
def record_anchor(node, object : T) : Nil forall T
return unless object.is_a?(Reference)
record_anchor(node.anchor, object.object_id, object.crystal_type_id)
end
private def record_anchor(anchor, object_id, crystal_type_id)
return unless anchor
@anchors[anchor] = {object_id, crystal_type_id}
end
# Tries to read an alias from `node` of type `T`. Invokes
# the block if successful, and invokers must return this object
# instead of deserializing their members.
def read_alias(node, type : T.class) forall T
if ptr = read_alias_impl(node, T.crystal_instance_type_id, raise_on_alias: true)
yield ptr.unsafe_as(T)
end
end
# Similar to `read_alias` but doesn't raise if an alias exists
# but an instance of type T isn't associated with the current anchor.
def read_alias?(node, type : T.class) forall T
if ptr = read_alias_impl(node, T.crystal_instance_type_id, raise_on_alias: false)
yield ptr.unsafe_as(T)
end
end
private def read_alias_impl(node, expected_crystal_type_id, raise_on_alias)
if node.is_a?(YAML::Nodes::Alias)
value = @anchors[node.anchor]?
if value
object_id, crystal_type_id = value
if crystal_type_id == expected_crystal_type_id
return Pointer(Void).new(object_id)
end
end
raise("Error deserailizing alias") if raise_on_alias
end
nil
end
end