"""Contains routines for printing protocol messages in text format.""" import cStringIO import re from google.net.proto2.python.internal import type_checkers from google.net.proto2.python.public import descriptor from google.net.proto2.python.public import text_encoding __all__ = [ 'MessageToString', 'PrintMessage', 'PrintField', 'PrintFieldValue', 'Merge' ] _INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), type_checkers.Int32ValueChecker(), type_checkers.Uint64ValueChecker(), type_checkers.Int64ValueChecker()) _FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE) _FLOAT_NAN = re.compile('nanf?', re.IGNORECASE) _FLOAT_TYPES = frozenset([ descriptor.FieldDescriptor.CPPTYPE_FLOAT, descriptor.FieldDescriptor.CPPTYPE_DOUBLE ]) class Error(Exception): """Top-level module error for text_format.""" class ParseError(Error): """Thrown in case of ASCII parsing error."""
class _Tokenizer(object): """Protocol buffer ASCII representation tokenizer. This class handles the lower level string parsing by splitting it into meaningful tokens. It was directly ported from the Java protocol buffer API. """ _WHITESPACE = re.compile('(\\s|(#.*$))+', re.MULTILINE) _TOKEN = re.compile('[a-zA-Z_][0-9a-zA-Z_+-]*|' '[0-9+-][0-9a-zA-Z_.+-]*|' '\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|' '\'([^\'\n\\\\]|\\\\.)*(\'|\\\\?$)') _IDENTIFIER = re.compile('\w+') _INTEGER_CHECKERS = [ type_checkers.Uint32ValueChecker(), type_checkers.Int32ValueChecker(), type_checkers.Uint64ValueChecker(), type_checkers.Int64ValueChecker() ] _FLOAT_INFINITY = re.compile('-?inf(inity)?f?', re.IGNORECASE) _FLOAT_NAN = re.compile("nanf?", re.IGNORECASE) def __init__(self, text_message): self._text_message = text_message self._position = 0 self._line = -1 self._column = 0 self._token_start = None self.token = '' self._lines = deque(text_message.split('\n')) self._current_line = '' self._previous_line = 0 self._previous_column = 0 self._SkipWhitespace() self.NextToken() def AtEnd(self): """Checks the end of the text was reached. Returns: True iff the end was reached. """ return self.token == '' def _PopLine(self): while len(self._current_line) <= self._column: if not self._lines: self._current_line = '' return self._line += 1 self._column = 0 self._current_line = self._lines.popleft() def _SkipWhitespace(self): while True: self._PopLine() match = self._WHITESPACE.match(self._current_line, self._column) if not match: break length = len(match.group(0)) self._column += length def TryConsume(self, token): """Tries to consume a given piece of text. Args: token: Text to consume. Returns: True iff the text was consumed. """ if self.token == token: self.NextToken() return True return False def Consume(self, token): """Consumes a piece of text. Args: token: Text to consume. Raises: ParseError: If the text couldn't be consumed. """ if not self.TryConsume(token): raise self._ParseError('Expected "%s".' % token) def LookingAtInteger(self): """Checks if the current token is an integer. Returns: True iff the current token is an integer. """ if not self.token: return False c = self.token[0] return (c >= '0' and c <= '9') or c == '-' or c == '+' def ConsumeIdentifier(self): """Consumes protocol message field identifier. Returns: Identifier string. Raises: ParseError: If an identifier couldn't be consumed. """ result = self.token if not self._IDENTIFIER.match(result): raise self._ParseError('Expected identifier.') self.NextToken() return result def ConsumeInt32(self): """Consumes a signed 32bit integer number. Returns: The integer parsed. Raises: ParseError: If a signed 32bit integer couldn't be consumed. """ try: result = self._ParseInteger(self.token, is_signed=True, is_long=False) except ValueError, e: raise self._IntegerParseError(e) self.NextToken() return result