"""The [preserves.values][] module implements the core representations of Preserves [`Value`s](https://preserves.dev/preserves.html#semantics) as Python values. """ import re import sys import struct import math from .error import DecodeError def preserve(v): """Converts `v` to a representation of a Preserves `Value` by (repeatedly) setting ```python v = v.__preserve__() ``` while `v` has a `__preserve__` method. Parsed [Schema][preserves.schema] values are able to render themselves to their serialized representations this way. """ while hasattr(v, '__preserve__'): v = v.__preserve__() return v def float_to_int(v): return struct.unpack('>Q', struct.pack('>d', v))[0] def cmp_floats(a, b): """Implements the `totalOrder` predicate defined in section 5.10 of [IEEE Std 754-2008](https://dx.doi.org/10.1109/IEEESTD.2008.4610935). """ a = float_to_int(a) b = float_to_int(b) if a & 0x8000000000000000: a = a ^ 0x7fffffffffffffff if b & 0x8000000000000000: b = b ^ 0x7fffffffffffffff return a - b # FIXME: This regular expression is conservatively correct, but Anglo-chauvinistic. RAW_SYMBOL_RE = re.compile(r'^[-a-zA-Z0-9~!$%^&*?_=+/.]+$') def _eq(a, b): from .compare import eq return eq(a, b) class Symbol(object): """Representation of Preserves `Symbol`s. ```python >>> Symbol('xyz') #xyz >>> Symbol('xyz').name 'xyz' >>> repr(Symbol('xyz')) '#xyz' >>> str(Symbol('xyz')) 'xyz' >>> import preserves >>> preserves.stringify(Symbol('xyz')) 'xyz' >>> preserves.stringify(Symbol('hello world')) '|hello world|' >>> preserves.parse('xyz') #xyz >>> preserves.parse('|hello world|') #hello world ``` Attributes: name (str | Symbol): The symbol's text label. If an existing [Symbol][preserves.values.Symbol] is passed in, the existing Symbol's `name` is used as the `name` for the new Symbol. """ def __init__(self, name): self.name = name.name if isinstance(name, Symbol) else name def __eq__(self, other): other = _unwrap(other) return isinstance(other, Symbol) and self.name == other.name def __ne__(self, other): return not self.__eq__(other) def __lt__(self, other): return self.name < other.name def __le__(self, other): return self.name <= other.name def __gt__(self, other): return self.name > other.name def __ge__(self, other): return self.name >= other.name def __hash__(self): return hash(self.name) def __repr__(self): return '#' + self.name def __str__(self): return self.name def __preserve_write_binary__(self, encoder): bs = self.name.encode('utf-8') encoder.buffer.append(0xb3) encoder.varint(len(bs)) encoder.buffer.extend(bs) def __preserve_write_text__(self, formatter): if RAW_SYMBOL_RE.match(self.name): formatter.chunks.append(self.name) else: formatter.chunks.append('|') for c in self.name: if c == '|': formatter.chunks.append('\\|') else: formatter.write_stringlike_char(c) formatter.chunks.append('|') class Record(object): """Representation of Preserves `Record`s, which are a pair of a *label* `Value` and a sequence of *field* `Value`s. ```python >>> r = Record(Symbol('label'), ['field1', ['field2item1', 'field2item2']]) >>> r #label('field1', ['field2item1', 'field2item2']) >>> r.key #label >>> r.fields ('field1', ['field2item1', 'field2item2']) >>> import preserves >>> preserves.stringify(r) '