Commits

Amaury Forgeot d'Arc committed 563cb51

Really start the Decimal type: some constructors.

Comments (0)

Files changed (6)

pypy/module/_decimal/__init__.py

 from pypy.interpreter.mixedmodule import MixedModule
 from rpython.rlib import rmpdec
+from pypy.module._decimal import interp_signals
+
 
 class Module(MixedModule):
     appleveldefs = {
         }
     for name in rmpdec.ROUND_CONSTANTS:
         interpleveldefs[name] = 'space.wrap(%r)' % name
-    for name in ('DecimalException', 'Clamped', 'Rounded', 'Inexact',
-                 'Subnormal', 'Underflow', 'Overflow', 'DivisionByZero',
-                 'InvalidOperation', 'FloatOperation'):
+    for name in interp_signals.SIGNAL_NAMES:
         interpleveldefs[name] = 'interp_signals.get(space).w_%s' % name
         

pypy/module/_decimal/interp_context.py

 from pypy.interpreter.typedef import (
     TypeDef, GetSetProperty, interp_attrproperty_w)
 from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.module._decimal import interp_signals
 
 
 # The SignalDict is a MutableMapping that provides access to the
 
 class W_Context(W_Root):
     def __init__(self, space):
-        self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, 1, flavor='raw',
+        self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, flavor='raw',
+                                 zero=True,
                                  track_allocation=False)
         self.w_flags = space.call_function(state_get(space).W_SignalDict)
 
     def __del__(self):
         if self.ctx:
-            lltype.free(self.ctx, flavor='raw')
+            lltype.free(self.ctx, flavor='raw', track_allocation=False)
+
+    def addstatus(self, space, status):
+        "Add resulting status to context, and eventually raise an exception."
+        self.ctx.c_status |= status
+        if status & rmpdec.MPD_Malloc_error:
+            raise OperationError(space.w_MemoryError, space.w_None)
+        if status & self.ctx.c_traps:
+            raise interp_signals.flags_as_exception(
+                space, self.ctx.c_traps & status)
 
     def copy_w(self, space):
         w_copy = W_Context(space)
 def setcontext(space, w_context):
     ec = space.getexecutioncontext()
     ec.decimal_context = w_context
+
+class ConvContext:
+    def __init__(self, context, exact):
+        self.exact = exact
+        if self.exact:
+            self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, flavor='raw',
+                                     zero=True)
+            rmpdec.mpd_maxcontext(self.ctx)
+        else:
+            self.ctx = context.ctx
+
+    def __enter__(self):
+        return self.ctx
+
+    def __exit__(self, *args):
+        if self.exact:
+            lltype.free(self.ctx, flavor='raw')

pypy/module/_decimal/interp_decimal.py

 from rpython.rlib import rmpdec
+from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
 from pypy.interpreter.typedef import (TypeDef, GetSetProperty, descr_get_dict,
     descr_set_dict, descr_del_dict)
+from pypy.module._decimal import interp_context
 
 
 IEEE_CONTEXT_MAX_BITS = rmpdec.MPD_IEEE_CONTEXT_MAX_BITS
 MAX_PREC = rmpdec.MPD_MAX_PREC
+# DEC_MINALLOC >= MPD_MINALLOC
+DEC_MINALLOC = 4
+
+def ensure_context(space, w_context):
+    context = space.interp_w(interp_context.W_Context, w_context,
+                             can_be_None=True)
+    if context is None:
+        context = interp_context.getcontext(space)
+    return context
 
 class W_Decimal(W_Root):
-    pass
+    hash = -1
+
+    def __init__(self, space):
+        self.mpd = lltype.malloc(rmpdec.MPD_PTR.TO, flavor='raw')
+        self.data = lltype.malloc(rffi.UINTP.TO, DEC_MINALLOC, flavor='raw')
+        rffi.setintfield(self.mpd, 'c_flags',
+                         rmpdec.MPD_STATIC | rmpdec.MPD_STATIC_DATA)
+        self.mpd.c_exp = 0
+        self.mpd.c_digits = 0
+        self.mpd.c_len = 0
+        self.mpd.c_alloc = DEC_MINALLOC
+        self.mpd.c_data = self.data
+
+    def __del__(self):
+        if self.mpd:
+            lltype.free(self.mpd, flavor='raw')
+        if self.data:
+            lltype.free(self.data, flavor='raw')
+
+    def compare(self, space, w_other, op):
+        if not isinstance(w_other, W_Decimal):  # So far
+            return space.w_NotImplemented
+        with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as status_ptr:
+            r = rmpdec.mpd_qcmp(self.mpd, w_other.mpd, status_ptr)
+        if op == 'eq':
+            return space.wrap(r == 0)
+        else:
+            return space.w_NotImplemented
+
+    def descr_eq(self, space, w_other):
+        return self.compare(space, w_other, 'eq')
+
+
+# Constructors
+def decimal_from_ssize(space, w_subtype, value, context, exact=True):
+    w_result = space.allocate_instance(W_Decimal, w_subtype)
+    W_Decimal.__init__(w_result, space)
+    with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as status_ptr:
+        with interp_context.ConvContext(context, exact) as ctx:
+            rmpdec.mpd_qset_ssize(w_result.mpd, value, ctx, status_ptr)
+        context.addstatus(space, status_ptr[0])
+    
+    return w_result
+
+def decimal_from_cstring(space, w_subtype, value, context, exact=True):
+    w_result = space.allocate_instance(W_Decimal, w_subtype)
+    W_Decimal.__init__(w_result, space)
+
+    with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as status_ptr:
+        with interp_context.ConvContext(context, exact) as ctx:
+            rmpdec.mpd_qset_string(w_result.mpd, value, ctx, status_ptr)
+        context.addstatus(space, status_ptr[0])
+    return w_result
+
+def decimal_from_unicode(space, w_subtype, w_value, context, exact=True,
+                         strip_whitespace=True):
+    s = space.str_w(w_value)  # XXX numeric_as_ascii() is different
+    if strip_whitespace:
+        s = s.strip()
+    return decimal_from_cstring(space, w_subtype, s, context, exact=exact)
+
+def decimal_from_object(space, w_subtype, w_value, context, exact=True):
+    if w_value is None:
+        return decimal_from_ssize(space, w_subtype, 0, context, exact=exact)
+    elif space.isinstance_w(w_value, space.w_unicode):
+        return decimal_from_unicode(space, w_subtype, w_value, context,
+                                    exact=True, strip_whitespace=True)
+
+@unwrap_spec(w_context=WrappedDefault(None))
+def descr_new_decimal(space, w_subtype, w_value=None, w_context=None):
+    context = ensure_context(space, w_context)
+    return decimal_from_object(space, w_subtype, w_value, context,
+                               exact=True)
 
 W_Decimal.typedef = TypeDef(
-    'Decimal')
+    'Decimal',
+    __new__ = interp2app(descr_new_decimal),
+    __eq__ = interp2app(W_Decimal.descr_eq),
+    )

pypy/module/_decimal/interp_signals.py

+SIGNAL_NAMES = (
+    'DecimalException', 'Clamped', 'Rounded', 'Inexact',
+    'Subnormal', 'Underflow', 'Overflow', 'DivisionByZero',
+    'InvalidOperation', 'FloatOperation')
+
+def flags_as_exception(space, flags):
+    raise ValueError(hex(flags))
+
+
 class SignalState:
     def __init__(self, space):
         self.w_DecimalException = space.call_function(

pypy/module/_decimal/test/test_decimal.py

+class AppTestExplicitConstruction:
+    spaceconfig = dict(usemodules=('_decimal',))
+
+    def test_explicit_empty(self):
+        import _decimal
+        Decimal = _decimal.Decimal
+        assert Decimal() == Decimal("0")
+

rpython/rlib/rmpdec.py

 import sys
 
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
-from rpython.rtyper.lltypesystem import rffi
+from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.tool import rffi_platform as platform
 from rpython.conftest import cdir
 
                            libdir.join('convolute.c'),
                            libdir.join('constants.c'),
                            libdir.join('context.c'),
+                           libdir.join('io.c'),
                            libdir.join('fourstep.c'),
                            libdir.join('sixstep.c'),
                            libdir.join('transpose.c'),
                            libdir.join('memory.c'),
                            ],
     export_symbols=[
-        "mpd_getprec", "mpd_getemin",  "mpd_getemax", "mpd_getround",
-        "mpd_getclamp",
-        "mpd_qsetprec", "mpd_qsetemin",  "mpd_qsetemax", "mpd_qsetround",
-        "mpd_qsetclamp",
+        "mpd_qset_ssize", "mpd_qset_string",
+        "mpd_getprec", "mpd_getemin",  "mpd_getemax", "mpd_getround", "mpd_getclamp",
+        "mpd_qsetprec", "mpd_qsetemin",  "mpd_qsetemax", "mpd_qsetround", "mpd_qsetclamp",
+        "mpd_maxcontext",
+        "mpd_qcmp",
         ],
     compile_extra=compile_extra,
     libraries=['m'],
     MPD_IEEE_CONTEXT_MAX_BITS = platform.ConstantInteger(
         'MPD_IEEE_CONTEXT_MAX_BITS')
     MPD_MAX_PREC = platform.ConstantInteger('MPD_MAX_PREC')
+    MPD_STATIC = platform.ConstantInteger('MPD_STATIC')
+    MPD_STATIC_DATA = platform.ConstantInteger('MPD_STATIC_DATA')
+
+    MPD_Malloc_error = platform.ConstantInteger('MPD_Malloc_error')
 
     for name in ROUND_CONSTANTS:
         name = 'MPD_' + name
         locals()[name] = platform.ConstantInteger(name)
 
+    MPD_T = platform.Struct('mpd_t',
+                            [('flags', rffi.UINT),
+                             ('exp', rffi.SSIZE_T),
+                             ('digits', rffi.SSIZE_T),
+                             ('len', rffi.SSIZE_T),
+                             ('alloc', rffi.SSIZE_T),
+                             ('data', rffi.UINTP),
+                             ])
     MPD_CONTEXT_T = platform.Struct('mpd_context_t',
-                                    [])
+                                    [('traps', rffi.UINT),
+                                     ('status', rffi.UINT),
+                                     ])
+
 
 globals().update(platform.configure(CConfig))
 
 def external(name, args, result, **kwds):
     return rffi.llexternal(name, args, result, compilation_info=eci, **kwds)
 
-MPD_CONTEXT_PTR = rffi.CArrayPtr(MPD_CONTEXT_T)
+MPD_PTR = lltype.Ptr(MPD_T)
+MPD_CONTEXT_PTR = lltype.Ptr(MPD_CONTEXT_T)
 
+# Initialization
+mpd_qset_ssize = external(
+    'mpd_qset_ssize', [MPD_PTR, rffi.SSIZE_T, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
+mpd_qset_string = external(
+    'mpd_qset_string', [MPD_PTR, rffi.CCHARP, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
+
+# Context operations
 mpd_getprec = external(
     'mpd_getprec', [MPD_CONTEXT_PTR], rffi.SSIZE_T)
 mpd_getemin = external(
 mpd_qsetclamp = external(
     'mpd_qsetclamp', [MPD_CONTEXT_PTR, rffi.INT], rffi.INT)
 
+mpd_maxcontext = external(
+    'mpd_maxcontext', [MPD_CONTEXT_PTR], lltype.Void)
+
+# Operations
+mpd_qcmp = external(
+    'mpd_qcmp', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)