[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