summaryrefslogtreecommitdiff
path: root/lib/yaml/resolver.py
blob: e3706c5eaa8b3460c253a597b5a1f0700a1352ba (plain)
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

__all__ = ['BaseResolver', 'Resolver', 'ResolverError']

from error import MarkedYAMLError
from nodes import *

import re

# Not really used.
class ResolverError(MarkedYAMLError):
    pass

class BaseResolver:

    DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
    DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
    DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'

    def __init__(self, composer):
        self.composer = composer
        self.resolved_nodes = {}

    def check(self):
        # If there are more documents available?
        return self.composer.check()

    def get(self):
        # Resolve and return the root node of the next document.
        if self.composer.check():
            return self.resolve_document(self.composer.get())

    def __iter__(self):
        # Iterator protocol.
        while self.composer.check():
            yield self.resolve_document(self.composer.get())

    def resolve_document(self, node):
        self.resolve_node([], node)
        return node
        self.resolved_nodes = {}

    def resolve_node(self, path, node):
        if node in self.resolved_nodes:
            return
        self.resolved_nodes[node] = None
        if isinstance(node, ScalarNode):
            self.resolve_scalar(path, node)
        elif isinstance(node, SequenceNode):
            self.resolve_sequence(path, node)
            for index in range(len(node.value)):
                self.resolve_node(path+[(node, index)], node.value[index])
        elif isinstance(node, MappingNode):
            self.resolve_mapping(path, node)
            for key in node.value:
                self.resolve_node(path+[node, None], key)
                self.resolve_node(path+[node, key], node.value[key])

    def resolve_scalar(self, path, node):
        if node.tag is None:
            node.tag = self.detect_scalar(node.value)
        if node.tag is None or node.tag == u'!':
            node.tag = self.DEFAULT_SCALAR_TAG

    def resolve_sequence(self, path, node):
        if node.tag is None or node.tag == u'!':
            node.tag = self.DEFAULT_SEQUENCE_TAG

    def resolve_mapping(self, path, node):
        if node.tag is None or node.tag == u'!':
            node.tag = self.DEFAULT_MAPPING_TAG

    def detect_scalar(self, value):
        if value == u'':
            detectors = self.yaml_detectors.get(u'', [])
        else:
            detectors = self.yaml_detectors.get(value[0], [])
        detectors += self.yaml_detectors.get(None, [])
        for tag, regexp in detectors:
            if regexp.match(value):
                return tag

    def add_detector(cls, tag, regexp, first):
        if not 'yaml_detectors' in cls.__dict__:
            cls.yaml_detectors = cls.yaml_detectors.copy()
        for ch in first:
            cls.yaml_detectors.setdefault(ch, []).append((tag, regexp))
    add_detector = classmethod(add_detector)

    yaml_detectors = {}

class Resolver(BaseResolver):
    pass

Resolver.add_detector(
        u'tag:yaml.org,2002:bool',
        re.compile(ur'''^(?:y|Y|yes|Yes|YES|n|N|no|No|NO
                    |true|True|TRUE|false|False|FALSE
                    |on|On|ON|off|Off|OFF)$''', re.X),
        list(u'yYnNtTfFoO'))

Resolver.add_detector(
        u'tag:yaml.org,2002:float',
        re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*(?:[eE][-+][0-9]+)?
                    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
                    |[-+]?\.(?:inf|Inf|INF)
                    |\.(?:nan|NaN|NAN))$''', re.X),
        list(u'-+0123456789.'))

Resolver.add_detector(
        u'tag:yaml.org,2002:int',
        re.compile(ur'''^(?:[-+]?0b[0-1_]+
                    |[-+]?0[0-7_]+
                    |[-+]?(?:0|[1-9][0-9_]*)
                    |[-+]?0x[0-9a-fA-F_]+
                    |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
        list(u'-+0123456789'))

Resolver.add_detector(
        u'tag:yaml.org,2002:merge',
        re.compile(ur'^(?:<<)$'),
        ['<'])

Resolver.add_detector(
        u'tag:yaml.org,2002:null',
        re.compile(ur'''^(?: ~
                    |null|Null|NULL
                    | )$''', re.X),
        [u'~', u'n', u'N', u''])

Resolver.add_detector(
        u'tag:yaml.org,2002:timestamp',
        re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
                    |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
                     (?:[Tt]|[ \t]+)[0-9][0-9]?
                     :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
                     (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
        list(u'0123456789'))

Resolver.add_detector(
        u'tag:yaml.org,2002:value',
        re.compile(ur'^(?:=)$'),
        ['='])

# The following detector is only for documentation purposes. It cannot work
# because plain scalars cannot start with '!', '&', or '*'.
Resolver.add_detector(
        u'tag:yaml.org,2002:yaml',
        re.compile(ur'^(?:!|&|\*)$'),
        list(u'!&*'))