Source code for curry.backends.py.currylib.prelude.read

'''
Functions for parsing literals.
'''
# FIXME: the functions in this module may not handle set guards properly.

from ... import graph
from ..... import inspect
from . import string
from .math import INT_MAX, INT_MIN

__all__ = [
    'readCharLiteral', 'readFloatLiteral', 'readNatLiteral'
  , 'readStringLiteral'
  ]

[docs] def readCharLiteral(rts, s): ''' Parse a character literal from a Curry string. The literal begins and ends with a single quote. The body contains a character other than a backslash or single quote, or a backslash followed by one of the following: - Any character in "bfnrtv". - A single quote. - A backslash. - A natural number less than 1114112. - A hexadecimal integer less than 0x110000. Yields: A list whose head is a pair of the parsed character and remaining string and whose tail is this function applied to the tail. ''' s_in = s try: # Get the character. c, s = _getchar(rts, s) if c != "'": raise ParseError() c, s = _getchar(rts, s) if c == '\\': c_out, s = _parseEscapeCode(rts, s) elif c != "'": c_out = c else: raise ParseError() # Eat the closing quote. c, s = _getchar(rts, s) if c != "'": raise ParseError() yield rts.prelude.Cons yield graph.Node( rts.prelude.Pair , graph.Node(rts.prelude.Char, c_out) , s ) yield graph.Node(rts.prelude.Nil) except ParseError: yield rts.Failure
[docs] def readFloatLiteral(rts, s): ''' Parse a floating-point number from a Curry string. Returns a pair consisting of the value and the remaining string. ''' num = [] Cons = rts.prelude.Cons if inspect.isa(s.target, Cons): _1 = rts.variable(s, 0) c = _1.unboxed_value if c in '+-': num.append(c) s = rts.variable(s, 1) have_dot = False while inspect.isa(s.target, Cons): _1 = rts.variable(s, 0) c = _1.unboxed_value if c.isdigit(): num.append(c) s = rts.variable(s, 1) elif c == '.' and not have_dot: have_dot = True num.append(c) s = rts.variable(s, 1) else: yield rts.Failure return if not num: yield rts.Failure return value = float(''.join(num)) yield rts.prelude.Cons yield graph.Node( rts.prelude.Pair , graph.Node(rts.prelude.Float, value) , s ) yield graph.Node(rts.prelude.Nil)
[docs] def readNatLiteral(rts, s): ''' Parse a natural number from a Curry string. Returns a pair consisting of the value and the remaining string. ''' num = [] Cons = rts.prelude.Cons while inspect.isa(s.target, Cons): _1 = rts.variable(s, 0) c = _1.unboxed_value if c.isdigit(): num.append(c) s = rts.variable(s, 1) else: yield rts.Failure return if not num: yield rts.Failure return value = int(''.join(num)) # A numeral outside the range of Int fails the parse, as the C++ step does # when strtol reports ERANGE (issue #105). if not INT_MIN <= value <= INT_MAX: yield rts.Failure return yield rts.prelude.Cons yield graph.Node( rts.prelude.Pair , graph.Node(rts.prelude.Int, value) , s ) yield graph.Node(rts.prelude.Nil)
[docs] def readStringLiteral(rts, s): ''' Parse a string literal from a Curry string. The literal begins and ends with a double quote. The body contains a string of characters and uses the same escape codes as for character literals. Yields: A list whose head is a pair of the parsed string and remaining string and whose tail is this function applied to the tail. ''' s_in = s try: s_out = [] c, s = _getchar(rts, s) if c != '"': raise ParseError() while True: c, s = _getchar(rts, s) if c == '\\': c, s = _parseEscapeCode(rts, s) s_out.append(c) elif c == '"': break else: s_out.append(c) yield rts.prelude.Cons yield graph.Node( rts.prelude.Pair , string.pystring(rts, ''.join(s_out)) , s ) yield graph.Node(rts.prelude.Nil) except ParseError: yield rts.Failure
class ParseError(BaseException): '''Used internally.''' def _getchar(rts, s): ''' Get and unbox the head character of a string. Return a pair of it and the tail. ''' if inspect.isa(s.target, rts.prelude.Cons): _1 = rts.variable(s, 0) _2 = rts.variable(s, 1) return _1.unboxed_value, _2 raise ParseError() def _parseDecChar(rts, s, digits=None): ''' Parses a string of decimal digits. Returns the corresponding character and string tail. ''' digits = digits or [] s_prev = s while True: c, s = _getchar(rts, s_prev) if c.isdigit(): digits.append(c) elif not digits: raise ParseError() else: return chr(int(''.join(digits), 10)), s_prev s_prev = s ESCAPE_CODES = { '\\':'\\' # \\ -> \ , '"':'"' # \" -> " , "'":"'" # \' -> ' , 'a':'\a' , 'b':'\b' , 'f':'\f' , 'n':'\n' , 'r':'\r' , 't':'\t' , 'v':'\v' } def _parseEscapeCode(rts, s): c, s = _getchar(rts, s) if c in ESCAPE_CODES: return ESCAPE_CODES[c], s elif c == 'x': # hex escape code return _parseHexChar(rts, s) elif c == 'o': # octal escape code return _parseOctChar(rts, s) elif c.isdigit(): # decimal escape code return _parseDecChar(rts, s, [c]) else: raise ParseError() HEXDIGITS = set('0123456789abcdefABCDEF') def _parseHexChar(rts, s, digits=None): ''' Parses a string of hexadecimal digits. Returns the corresponding character and string tail. ''' digits = digits or [] s_prev = s while True: c, s = _getchar(rts, s_prev) if c in HEXDIGITS: digits.append(c) elif not digits: raise ParseError() else: return chr(int(''.join(digits), 16)), s_prev s_prev = s OCTDIGITS = set('01234567') def _parseOctChar(rts, s, digits=None): ''' Parses a string of octal digits. Returns the corresponding character and string tail. ''' digits = digits or [] s_prev = s while True: c, s = _getchar(rts, s_prev) if c in OCTDIGITS: digits.append(c) elif not digits: raise ParseError() else: return chr(int(''.join(digits), 8)), s_prev s_prev = s