0
0
.dotfiles/powerline-bin/powerline/lint/markedjson/constructor.py

286 lines
7.6 KiB
Python

# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import collections
import types
from functools import wraps
from powerline.lint.markedjson.error import MarkedError
from powerline.lint.markedjson import nodes
from powerline.lint.markedjson.markedvalue import gen_marked_value
from powerline.lib.unicode import unicode
def marked(func):
@wraps(func)
def f(self, node, *args, **kwargs):
return gen_marked_value(func(self, node, *args, **kwargs), node.start_mark)
return f
class ConstructorError(MarkedError):
pass
class BaseConstructor:
yaml_constructors = {}
def __init__(self):
self.constructed_objects = {}
self.state_generators = []
self.deep_construct = False
def check_data(self):
# If there are more documents available?
return self.check_node()
def get_data(self):
# Construct and return the next document.
if self.check_node():
return self.construct_document(self.get_node())
def get_single_data(self):
# Ensure that the stream contains a single document and construct it.
node = self.get_single_node()
if node is not None:
return self.construct_document(node)
return None
def construct_document(self, node):
data = self.construct_object(node)
while self.state_generators:
state_generators = self.state_generators
self.state_generators = []
for generator in state_generators:
for dummy in generator:
pass
self.constructed_objects = {}
self.deep_construct = False
return data
def construct_object(self, node, deep=False):
if node in self.constructed_objects:
return self.constructed_objects[node]
if deep:
old_deep = self.deep_construct
self.deep_construct = True
constructor = None
tag_suffix = None
if node.tag in self.yaml_constructors:
constructor = self.yaml_constructors[node.tag]
else:
raise ConstructorError(None, None, 'no constructor for tag %s' % node.tag)
if tag_suffix is None:
data = constructor(self, node)
else:
data = constructor(self, tag_suffix, node)
if isinstance(data, types.GeneratorType):
generator = data
data = next(generator)
if self.deep_construct:
for dummy in generator:
pass
else:
self.state_generators.append(generator)
self.constructed_objects[node] = data
if deep:
self.deep_construct = old_deep
return data
@marked
def construct_scalar(self, node):
if not isinstance(node, nodes.ScalarNode):
raise ConstructorError(
None, None,
'expected a scalar node, but found %s' % node.id,
node.start_mark
)
return node.value
def construct_sequence(self, node, deep=False):
if not isinstance(node, nodes.SequenceNode):
raise ConstructorError(
None, None,
'expected a sequence node, but found %s' % node.id,
node.start_mark
)
return [
self.construct_object(child, deep=deep)
for child in node.value
]
@marked
def construct_mapping(self, node, deep=False):
if not isinstance(node, nodes.MappingNode):
raise ConstructorError(
None, None,
'expected a mapping node, but found %s' % node.id,
node.start_mark
)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.Hashable):
self.echoerr(
'While constructing a mapping', node.start_mark,
'found unhashable key', key_node.start_mark
)
continue
elif type(key.value) != unicode:
self.echoerr(
'Error while constructing a mapping', node.start_mark,
'found key that is not a string', key_node.start_mark
)
continue
elif key in mapping:
self.echoerr(
'Error while constructing a mapping', node.start_mark,
'found duplicate key', key_node.start_mark
)
continue
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
@classmethod
def add_constructor(cls, tag, constructor):
if 'yaml_constructors' not in cls.__dict__:
cls.yaml_constructors = cls.yaml_constructors.copy()
cls.yaml_constructors[tag] = constructor
class Constructor(BaseConstructor):
def construct_scalar(self, node):
if isinstance(node, nodes.MappingNode):
for key_node, value_node in node.value:
if key_node.tag == 'tag:yaml.org,2002:value':
return self.construct_scalar(value_node)
return BaseConstructor.construct_scalar(self, node)
def flatten_mapping(self, node):
merge = []
index = 0
while index < len(node.value):
key_node, value_node = node.value[index]
if key_node.tag == 'tag:yaml.org,2002:merge':
del node.value[index]
if isinstance(value_node, nodes.MappingNode):
self.flatten_mapping(value_node)
merge.extend(value_node.value)
elif isinstance(value_node, nodes.SequenceNode):
submerge = []
for subnode in value_node.value:
if not isinstance(subnode, nodes.MappingNode):
raise ConstructorError(
'while constructing a mapping',
node.start_mark,
'expected a mapping for merging, but found %s' % subnode.id,
subnode.start_mark
)
self.flatten_mapping(subnode)
submerge.append(subnode.value)
submerge.reverse()
for value in submerge:
merge.extend(value)
else:
raise ConstructorError(
'while constructing a mapping',
node.start_mark,
('expected a mapping or list of mappings for merging, but found %s' % value_node.id),
value_node.start_mark
)
elif key_node.tag == 'tag:yaml.org,2002:value':
key_node.tag = 'tag:yaml.org,2002:str'
index += 1
else:
index += 1
if merge:
node.value = merge + node.value
def construct_mapping(self, node, deep=False):
if isinstance(node, nodes.MappingNode):
self.flatten_mapping(node)
return BaseConstructor.construct_mapping(self, node, deep=deep)
@marked
def construct_yaml_null(self, node):
self.construct_scalar(node)
return None
@marked
def construct_yaml_bool(self, node):
value = self.construct_scalar(node).value
return bool(value)
@marked
def construct_yaml_int(self, node):
value = self.construct_scalar(node).value
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
if value == '0':
return 0
else:
return sign * int(value)
@marked
def construct_yaml_float(self, node):
value = self.construct_scalar(node).value
sign = +1
if value[0] == '-':
sign = -1
if value[0] in '+-':
value = value[1:]
else:
return sign * float(value)
def construct_yaml_str(self, node):
return self.construct_scalar(node)
def construct_yaml_seq(self, node):
data = gen_marked_value([], node.start_mark)
yield data
data.extend(self.construct_sequence(node))
def construct_yaml_map(self, node):
data = gen_marked_value({}, node.start_mark)
yield data
value = self.construct_mapping(node)
data.update(value)
def construct_undefined(self, node):
raise ConstructorError(
None, None,
'could not determine a constructor for the tag %r' % node.tag,
node.start_mark
)
Constructor.add_constructor(
'tag:yaml.org,2002:null', Constructor.construct_yaml_null)
Constructor.add_constructor(
'tag:yaml.org,2002:bool', Constructor.construct_yaml_bool)
Constructor.add_constructor(
'tag:yaml.org,2002:int', Constructor.construct_yaml_int)
Constructor.add_constructor(
'tag:yaml.org,2002:float', Constructor.construct_yaml_float)
Constructor.add_constructor(
'tag:yaml.org,2002:str', Constructor.construct_yaml_str)
Constructor.add_constructor(
'tag:yaml.org,2002:seq', Constructor.construct_yaml_seq)
Constructor.add_constructor(
'tag:yaml.org,2002:map', Constructor.construct_yaml_map)
Constructor.add_constructor(
None, Constructor.construct_undefined)