[pypy-svn] r45556 - in pypy/dist/pypy/lang/scheme: . test

jlg at codespeak.net jlg at codespeak.net
Wed Aug 8 18:02:37 CEST 2007


Author: jlg
Date: Wed Aug  8 18:02:36 2007
New Revision: 45556

Added:
   pypy/dist/pypy/lang/scheme/test/test_continuation.py   (contents, props changed)
Modified:
   pypy/dist/pypy/lang/scheme/TODO.txt
   pypy/dist/pypy/lang/scheme/execution.py
   pypy/dist/pypy/lang/scheme/object.py
Log:
ellipses in same templ. obj. with different match length -> error; naive, stack-only implementation of continuations

Modified: pypy/dist/pypy/lang/scheme/TODO.txt
==============================================================================
--- pypy/dist/pypy/lang/scheme/TODO.txt	(original)
+++ pypy/dist/pypy/lang/scheme/TODO.txt	Wed Aug  8 18:02:36 2007
@@ -1,7 +1,10 @@
 Do now
 ------
 
-- lambda called with wrong number of arguments issue
+- continuations
+
+continuation frame must be saved fot every non tail-call
+(for tail calls there is no cc)
 
 Do next
 -------
@@ -16,8 +19,8 @@
 
 Here starts the real fun!
 
-- macros
-  * macros are not first-class objects
-- continuations
+- lambda called with wrong number of arguments issue
+- macros *are* not first-class objects
+
 - switch to byte-code generation + eval instead of evaluating AST
 

Modified: pypy/dist/pypy/lang/scheme/execution.py
==============================================================================
--- pypy/dist/pypy/lang/scheme/execution.py	(original)
+++ pypy/dist/pypy/lang/scheme/execution.py	Wed Aug  8 18:02:36 2007
@@ -19,7 +19,8 @@
 
     { "IDENTIFIER": Location(W_Root()) }
     """
-    def __init__(self, globalscope=None, scope=None, closure=False):
+    def __init__(self, globalscope=None, scope=None, closure=False,
+            cont_stack=None):
         if globalscope is None:
             self.globalscope = {}
             for name, oper in OPERATION_MAP.items():
@@ -35,6 +36,11 @@
 
         self.closure = closure
 
+        if cont_stack is None:
+            self.cont_stack = []
+        else:
+            self.cont_stack = cont_stack
+
     def _dispatch(self, symb):
         if isinstance(symb, ssobject.SymbolClosure):
             return (symb.closure, symb.name)
@@ -45,7 +51,8 @@
         raise ssobject.SchemeSyntaxError
 
     def copy(self):
-        return ExecutionContext(self.globalscope, self.scope.copy(), True)
+        return ExecutionContext(self.globalscope, self.scope.copy(), True,
+                self.cont_stack)
 
     def get(self, name):
         loc = self.scope.get(name, None)

Modified: pypy/dist/pypy/lang/scheme/object.py
==============================================================================
--- pypy/dist/pypy/lang/scheme/object.py	(original)
+++ pypy/dist/pypy/lang/scheme/object.py	Wed Aug  8 18:02:36 2007
@@ -244,18 +244,34 @@
     def call(self, ctx, lst):
         raise NotImplementedError
 
-    def eval_body(self, ctx, body):
+    def eval_body(self, ctx, body, cnt=False):
         body_expression = body
-        while True:
-            if not isinstance(body_expression, W_Pair):
-                raise SchemeSyntaxError
-            elif body_expression.cdr is w_nil:
-                return (body_expression.car, ctx)
+        while isinstance(body_expression, W_Pair):
+            if body_expression.cdr is w_nil:
+                if cnt is False:
+                    return (body_expression.car, ctx)
+
+                if ctx is None:
+                    result = body_expression.car
+                else:
+                    result = body_expression.car.eval(ctx)
+
+                if len(ctx.cont_stack) == 0:
+                    raise ContinuationReturn(result)
+
+                cont = ctx.cont_stack.pop()
+                return cont.run(ctx, result)
+
             else:
+                ctx.cont_stack.append(
+                        ContinuationFrame(self, body_expression.cdr))
                 body_expression.car.eval(ctx)
+                ctx.cont_stack.pop()
 
             body_expression = body_expression.cdr
 
+        raise SchemeSyntaxError
+
 class W_Procedure(W_Callable):
     def __init__(self, pname=""):
         self.pname = pname
@@ -264,17 +280,45 @@
         return "#<primitive-procedure %s>" % (self.pname,)
 
     def call_tr(self, ctx, lst):
+        return self.continue_tr(ctx, lst, [], False)
+
+    def continue_tr(self, ctx, lst, elst, cnt=True):
         #evaluate all arguments into list
-        arg_lst = []
+        arg_lst = elst
         arg = lst
-        while not arg is w_nil:
-            if not isinstance(arg, W_Pair):
-                raise SchemeSyntaxError
+        while isinstance(arg, W_Pair):
+            #this is non tail-call, it should create continuation frame
+            # continuation frame consist:
+            #  - plst of arleady evaluated arguments
+            #  - arg (W_Pair) = arg.cdr as a pointer to not evaluated
+            #    arguments
+            #  - actual context
+            ctx.cont_stack.append(ContinuationFrame(self, arg.cdr, arg_lst))
             w_obj = arg.car.eval(ctx)
+            ctx.cont_stack.pop()
+
             arg_lst.append(w_obj)
             arg = arg.cdr
 
-        return self.procedure_tr(ctx, arg_lst)
+        if arg is not w_nil:
+            raise SchemeSyntaxError
+
+        procedure_result = self.procedure_tr(ctx, arg_lst)
+        if cnt is False:
+            return procedure_result
+
+        #if procedure_result still has to be evaluated
+        # this can happen in case if self isinstance of W_Lambda
+        if procedure_result[1] is None:
+            procedure_result = procedure_result[0]
+        else:
+            procedure_result = procedure_result[0].eval(procedure_result[1])
+
+        if len(ctx.cont_stack) == 0:
+            raise ContinuationReturn(procedure_result)
+
+        cont = ctx.cont_stack.pop()
+        return cont.run(ctx, procedure_result)
 
     def procedure(self, ctx, lst):
         raise NotImplementedError
@@ -291,6 +335,10 @@
     def to_string(self):
         return "#<primitive-macro %s>" % (self.pname,)
 
+    def continue_tr(self, ctx, lst, elst, cnt=True):
+        lst = W_Pair(elst[0], lst)
+        return self.eval_body(ctx, lst, cnt=True)
+
 class Formal(object):
     def __init__(self, name, islist=False):
         self.name = name
@@ -1083,6 +1131,7 @@
         return self.substitute(ctx, template, match_dict)
 
     def find_elli(self, expr, mdict):
+        #filter mdict, returning only ellipsis which appear in expr
         if isinstance(expr, W_Pair):
             edict_car = self.find_elli(expr.car, mdict)
             edict_cdr = self.find_elli(expr.cdr, mdict)
@@ -1137,12 +1186,17 @@
                     return self.substituter(ctx, w_outer, match_dict, True)
 
                 plst = []
-                #find_elli gets part of match_dict relevant to sexpr.car
+                #find_elli gets ellipses from match_dict relevant to sexpr.car
                 mdict_elli = self.find_elli(sexpr.car, match_dict)
                 elli_len = 0
                 for (key, val) in mdict_elli.items():
-                    assert elli_len == 0 or elli_len == len(val.mdict_lst)
-                    elli_len = len(val.mdict_lst)
+                    if elli_len == 0 or elli_len == len(val.mdict_lst):
+                        elli_len = len(val.mdict_lst)
+                    else:
+                        #we can treat is as an error if ellipsis has
+                        # different match length
+                        # # or get the shortest one
+                        raise SchemeSyntaxError
 
                 #generate elli_len substitutions for ellipsis
                 for i in range(elli_len):
@@ -1183,7 +1237,7 @@
 
             w_sub = match_dict.get(sexpr.name, None)
             if w_sub is not None:
-                # Hygenic macros close their input forms in the syntactic
+                #Hygenic macros close their input forms in the syntactic
                 # enviroment at the point of use
 
                 if isinstance(w_sub, Ellipsis):
@@ -1277,3 +1331,58 @@
 
         return self.eval_body(local_ctx, lst.cdr)
 
+class ContinuationReturn(SchemeException):
+    def __init__(self, result):
+        self.result = result
+
+class ContinuationFrame(object):
+    def __init__(self, callable, continuation, evaluated_args = []):
+        assert isinstance(callable, W_Callable)
+        self.callable = callable
+        assert isinstance(continuation, W_Root)
+        self.continuation = continuation
+        assert isinstance(evaluated_args, list)
+        #XXX copying of evaluated_args here is SLOW,
+        # it should ocur only on continuation capture
+        self.evaluated_args = evaluated_args[:]
+
+    def run(self, ctx, arg):
+        elst = self.evaluated_args[:]
+        elst.append(arg)
+        print self.callable.to_string(), elst, self.continuation
+        return self.callable.continue_tr(ctx, self.continuation, elst, True)
+
+class Continuation(W_Procedure):
+    def __init__(self, ctx, continuation):
+        self.closure = ctx #XXX to .copy() ot not to .copy()
+        #copy of continuation stack
+        self.cont_stack = continuation[:]
+        try:
+            self.continuation = self.cont_stack.pop()
+        except IndexError:
+            #continuation captured on top-level
+            self.continuation = None
+
+    def procedure_tr(self, ctx, lst):
+        if len(lst) == 0:
+            lst.append(w_undefined)
+
+        print "Continuation called"
+        self.closure.cont_stack = self.cont_stack[:]
+        cont = self.continuation
+        if cont is None:
+            raise ContinuationReturn(lst[0])
+
+        return cont.run(self.closure, lst[0])
+
+class CallCC(W_Procedure):
+    _symbol_name = "call/cc"
+
+    def procedure_tr(self, ctx, lst):
+        if len(lst) != 1 or not isinstance(lst[0], W_Procedure):
+            raise SchemeSyntaxError
+
+        w_lambda = lst[0]
+        cc = Continuation(ctx, ctx.cont_stack)
+        return w_lambda.call_tr(ctx, W_Pair(cc, w_nil))
+

Added: pypy/dist/pypy/lang/scheme/test/test_continuation.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lang/scheme/test/test_continuation.py	Wed Aug  8 18:02:36 2007
@@ -0,0 +1,109 @@
+import py
+from pypy.lang.scheme.ssparser import parse
+from pypy.lang.scheme.execution import ExecutionContext
+from pypy.lang.scheme.object import *
+
+def eval_(ctx, expr):
+    try:
+        return parse(expr)[0].eval(ctx)
+    except ContinuationReturn, e:
+        return e.result
+
+def test_callcc():
+    ctx = ExecutionContext()
+
+    eval_(ctx, "(define cont #f)")
+    w_result = eval_(ctx, """(call/cc (lambda (k) (set! cont k) 3))""")
+
+    w_result = eval_(ctx, "(cont 3)")
+    assert w_result.to_number() == 3
+    w_result = eval_(ctx, "(cont #f)")
+    assert w_result.to_boolean() is False
+
+    #this (+ 1 [...]) should be ingored
+    w_result = eval_(ctx, "(+ 1 (cont 3))")
+    assert w_result.to_number() == 3
+    w_result = eval_(ctx, "(+ 1 (cont #t))")
+    assert w_result.to_boolean() is True
+
+def test_simple_multi_shot():
+    ctx = ExecutionContext()
+
+    eval_(ctx, "(define cont #f)")
+    w_result = eval_(ctx, """
+        (+ 1 2 (call/cc (lambda (k) (set! cont k) 3)) 4)""")
+
+    assert w_result.to_number() == 10
+    assert isinstance(eval_(ctx, "cont"), W_Procedure)
+    w_result = eval_(ctx, "(cont 0)")
+    assert w_result.to_number() == 7
+    w_result = eval_(ctx, "(cont 3)")
+    assert w_result.to_number() == 10
+
+def test_nested_multi_shot():
+    ctx = ExecutionContext()
+
+    eval_(ctx, "(define cont #f)")
+    w_result = eval_(ctx, """
+        (* 2 (+ 1 2 (call/cc (lambda (k) (set! cont k) 3)) 4) 1)""")
+    assert w_result.to_number() == 20
+    w_result = eval_(ctx, "(cont 0)")
+    assert w_result.to_number() == 14
+    w_result = eval_(ctx, "(cont 3)")
+    assert w_result.to_number() == 20
+
+def test_as_lambda_arg():
+    ctx = ExecutionContext()
+
+    eval_(ctx, "(define cont #f)")
+    eval_(ctx, "(define (add3 a1 a2 a3) (+ a3 a2 a1))")
+    w_result = eval_(ctx, """
+            (add3 (call/cc (lambda (k) (set! cont k) 3)) 2 1)""")
+    assert w_result.to_number() == 6
+    w_result = eval_(ctx, "(cont 0)")
+    assert w_result.to_number() == 3
+    w_result = eval_(ctx, "(cont 3)")
+    assert w_result.to_number() == 6
+
+def test_the_continuation():
+    ctx = ExecutionContext()
+
+    eval_(ctx, "(define con #f)")
+    eval_(ctx, """
+        (define (test)
+          (let ((i 0))
+            (call/cc (lambda (k) (set! con k)))
+            ; The next time tc is called, we start here.
+            (set! i (+ i 1))
+            i))""")
+
+    assert eval_(ctx, "(test)").to_number() == 1
+    assert eval_(ctx, "(con)").to_number() == 2
+    assert eval_(ctx, "(con)").to_number() == 3
+    eval_(ctx, "(define con2 con)")
+    assert eval_(ctx, "(test)").to_number() == 1
+    assert eval_(ctx, "(con)").to_number() == 2
+    assert eval_(ctx, "(con2)").to_number() == 4
+    assert eval_(ctx, "(+ 1 (con2))").to_number() == 5
+
+def test_the_continuation_x2():
+    ctx = ExecutionContext()
+
+    eval_(ctx, "(define con #f)")
+    eval_(ctx, """
+        (define (test)
+           (* 2 
+              (let ((i 0))
+                (call/cc (lambda (k) (set! con k)))
+                (set! i (+ i 1))
+                i)))""")
+
+    assert eval_(ctx, "(test)").to_number() == 2
+    assert eval_(ctx, "(con)").to_number() == 4
+    assert eval_(ctx, "(con)").to_number() == 6
+    eval_(ctx, "(define con2 con)")
+    assert eval_(ctx, "(test)").to_number() == 2
+    assert eval_(ctx, "(con)").to_number() == 4
+    assert eval_(ctx, "(con2)").to_number() == 8
+    assert eval_(ctx, "(+ 1 (con2))").to_number() == 10
+



More information about the Pypy-commit mailing list