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

jlg at codespeak.net jlg at codespeak.net
Mon Aug 6 11:56:10 CEST 2007


Author: jlg
Date: Mon Aug  6 11:56:09 2007
New Revision: 45512

Modified:
   pypy/dist/pypy/lang/scheme/execution.py
   pypy/dist/pypy/lang/scheme/object.py
   pypy/dist/pypy/lang/scheme/test/test_macro.py
Log:
OPERATION_MAP generated based on _symbol_name class atribute; macros tuning, most test are passing

Modified: pypy/dist/pypy/lang/scheme/execution.py
==============================================================================
--- pypy/dist/pypy/lang/scheme/execution.py	(original)
+++ pypy/dist/pypy/lang/scheme/execution.py	Mon Aug  6 11:56:09 2007
@@ -1,72 +1,25 @@
-from pypy.lang.scheme.object import *
+import pypy.lang.scheme.object as ssobject
+#from pypy.lang.scheme.object import *
 
 class Location(object):
     def __init__(self, w_obj=None):
         self.obj = w_obj
 
-##
-# dict mapping operations to W_Xxx objects
-# callables must have 2 arguments
-# - ctx = execution context
-# - lst = list of arguments
-##
-OMAP = \
-    {
-            #arithmetic operations
-        '+': Add,
-        '-': Sub,
-        '*': Mul,
-        '/': Div,
-            #list operations
-        'cons': Cons,
-        'car': Car,
-        'cdr': Cdr,
-        'set-car!': SetCar,
-        'set-cdr!': SetCdr,
-        'list': List,
-        'quit': Quit,
-            #comparisons
-        '=': Equal,
-            #predicates
-        'integer?': IntegerP,
-        'rational?': RealP,
-        'real?': RealP,
-        'complex?': NumberP,
-        'number?': NumberP,
-        'exact?': ExactP,
-        'inexact?': InexactP,
-        'zero?': ZeroP,
-        'odd?': OddP,
-        'even?': EvenP,
-            #delayed evaluation
-        'force': Force,
-        'delay': Delay, #macro
-            #macros
-        'define': Define,
-        'set!': Sete,
-        'if': MacroIf,
-        'lambda': Lambda,
-        'begin': Begin,
-        'let': Let,
-        'let*': LetStar,
-        'letrec': Letrec,
-        'quote': Quote,
-        'quasiquote': QuasiQuote,
-        'syntax-rules': SyntaxRules,
-        'define-syntax': DefineSyntax,
-        'let-syntax': LetSyntax,
-    }
-
 OPERATION_MAP = {}
-for name, cls in OMAP.items():
-    OPERATION_MAP[name] = cls(name)
+for obj_name in dir(ssobject):
+    obj = getattr(ssobject, obj_name)
+    try:
+        issubclass(obj, ssobject.W_Callable)
+        OPERATION_MAP[obj._symbol_name] = obj(obj._symbol_name)
+    except (TypeError, AttributeError):
+        pass
 
 class ExecutionContext(object):
     """Execution context implemented as a dict.
 
     { "IDENTIFIER": Location(W_Root()) }
     """
-    def __init__(self, globalscope=None, scope=None, closure=False, macro=False):
+    def __init__(self, globalscope=None, scope=None, closure=False):
         if globalscope is None:
             self.globalscope = {}
             for name, oper in OPERATION_MAP.items():
@@ -83,13 +36,13 @@
         self.closure = closure
 
     def _dispatch(self, symb):
-        if isinstance(symb, SymbolClosure):
+        if isinstance(symb, ssobject.SymbolClosure):
             return (symb.closure, symb.name)
 
-        elif isinstance(symb, W_Symbol):
+        elif isinstance(symb, ssobject.W_Symbol):
             return (self, symb.name)
 
-        raise SchemeSyntaxError
+        raise ssobject.SchemeSyntaxError
 
     def copy(self):
         return ExecutionContext(self.globalscope, self.scope.copy(), True)
@@ -98,16 +51,16 @@
         loc = self.scope.get(name, None)
         if loc is not None:
             if loc.obj is None:
-                raise UnboundVariable(name)
+                raise ssobject.UnboundVariable(name)
             return loc.obj
 
         loc = self.globalscope.get(name, None)
         if loc is not None:
             if loc.obj is None:
-                raise UnboundVariable(name)
+                raise ssobject.UnboundVariable(name)
             return loc.obj
 
-        raise UnboundVariable(name)
+        raise ssobject.UnboundVariable(name)
 
     def ssete(self, symb, obj):
         (ctx, name) = self._dispatch(symb)
@@ -117,7 +70,7 @@
         """update existing location or raise
         directly used by (set! <var> <expr>) macro
         """
-        assert isinstance(obj, W_Root)
+        assert isinstance(obj, ssobject.W_Root)
         loc = self.scope.get(name, None)
         if loc is not None:
             loc.obj = obj
@@ -128,11 +81,11 @@
             loc.obj = obj
             return obj
 
-        raise UnboundVariable(name)
+        raise ssobject.UnboundVariable(name)
 
     def set(self, name, obj):
         """update existing location or create new location"""
-        assert isinstance(obj, W_Root)
+        assert isinstance(obj, ssobject.W_Root)
         if self.closure:
             loc = self.scope.get(name, None)
         else:
@@ -149,7 +102,7 @@
 
     def put(self, name, obj):
         """create new location"""
-        assert isinstance(obj, W_Root)
+        assert isinstance(obj, ssobject.W_Root)
         if self.closure:
             self.scope[name] = Location(obj)
         else:
@@ -179,3 +132,4 @@
             return loc
 
         return None
+

Modified: pypy/dist/pypy/lang/scheme/object.py
==============================================================================
--- pypy/dist/pypy/lang/scheme/object.py	(original)
+++ pypy/dist/pypy/lang/scheme/object.py	Mon Aug  6 11:56:09 2007
@@ -440,6 +440,7 @@
         Op.default_result = W_Integer(default_result)
 
     Op.__name__ = "Op" + title
+    Op._symbol_name = oper
     return Op
 
 Add = create_op_class('+', '', "Add", 0)
@@ -448,6 +449,8 @@
 Div = create_op_class('/', '1 /', "Div")
 
 class Equal(W_Procedure):
+    _symbol_name = "="
+
     def procedure(self, ctx, lst):
         if len(lst) < 2:
             return W_Boolean(True)
@@ -467,10 +470,14 @@
         return W_Boolean(True)
 
 class List(W_Procedure):
+    _symbol_name = "list"
+
     def procedure(self, ctx, lst):
         return plst2lst(lst)
 
 class Cons(W_Procedure):
+    _symbol_name = "cons"
+
     def procedure(self, ctx, lst):
         w_car = lst[0]
         w_cdr = lst[1]
@@ -478,6 +485,8 @@
         return W_Pair(w_car, w_cdr)
 
 class Car(W_Procedure):
+    _symbol_name = "car"
+
     def procedure(self, ctx, lst):
         w_pair = lst[0]
         if not isinstance(w_pair, W_Pair):
@@ -485,6 +494,8 @@
         return w_pair.car
 
 class Cdr(W_Procedure):
+    _symbol_name = "cdr"
+
     def procedure(self, ctx, lst):
         w_pair = lst[0]
         if not isinstance(w_pair, W_Pair):
@@ -492,6 +503,8 @@
         return w_pair.cdr
 
 class SetCar(W_Procedure):
+    _symbol_name = "set-car!"
+
     def procedure(self, ctx, lst):
         w_pair = lst[0]
         w_obj = lst[1]
@@ -502,6 +515,8 @@
         return w_undefined
 
 class SetCdr(W_Procedure):
+    _symbol_name = "set-cdr!"
+
     def procedure(self, ctx, lst):
         w_pair = lst[0]
         w_obj = lst[1]
@@ -512,10 +527,14 @@
         return w_undefined
 
 class Quit(W_Procedure):
+    _symbol_name = "quit"
+
     def procedure(self, ctx, lst):
         raise SchemeQuit
 
 class Force(W_Procedure):
+    _symbol_name = "force"
+
     def procedure(self, ctx, lst):
         if len(lst) != 1:
             raise WrongArgsNumber
@@ -544,6 +563,8 @@
         raise NotImplementedError
 
 class IntegerP(PredicateNumber):
+    _symbol_name = "integer?"
+
     def predicate(self, w_obj):
         if not w_obj.exact:
             return w_obj.is_integer()
@@ -551,26 +572,44 @@
         return True
 
 class RealP(PredicateNumber):
+    _symbol_name = "real?"
+
     def predicate(self, w_obj):
         return isinstance(w_obj, W_Real)
 
+class RationalP(RealP):
+    _symbol_name = "rational?"
+
 class NumberP(PredicateNumber):
+    _symbol_name = "number?"
+
     def predicate(self, w_obj):
         return isinstance(w_obj, W_Number)
 
+class ComplexP(NumberP):
+    _symbol_name = "complex?"
+
 class ExactP(PredicateNumber):
+    _symbol_name = "exact?"
+
     def predicate(self, w_obj):
         return w_obj.exact
 
 class InexactP(PredicateNumber):
+    _symbol_name = "inexact?"
+
     def predicate(self, w_obj):
         return not w_obj.exact
 
 class ZeroP(PredicateNumber):
+    _symbol_name = "zero?"
+
     def predicate(self, w_obj):
         return w_obj.to_number() == 0.0
 
 class OddP(PredicateNumber):
+    _symbol_name = "odd?"
+
     def predicate(self, w_obj):
         if not w_obj.is_integer():
             raise WrongArgType(w_obj, "Integer")
@@ -578,6 +617,8 @@
         return w_obj.round() % 2 != 0
 
 class EvenP(PredicateNumber):
+    _symbol_name = "even?"
+
     def predicate(self, w_obj):
         if not w_obj.is_integer():
             raise WrongArgType(w_obj, "Integer")
@@ -588,6 +629,8 @@
 # Macro
 ##
 class Define(W_Macro):
+    _symbol_name = "define"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -612,6 +655,8 @@
             raise SchemeSyntaxError
 
 class Sete(W_Macro):
+    _symbol_name = "set!"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -622,6 +667,8 @@
         return w_val #undefined
 
 class MacroIf(W_Macro):
+    _symbol_name = "if"
+
     def call_tr(self, ctx, lst):
         #if needs to be tail-recursive aware
         if not isinstance(lst, W_Pair):
@@ -641,6 +688,8 @@
             return (w_else, ctx)
 
 class Lambda(W_Macro):
+    _symbol_name = "lambda"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError(lst, "Pair")
@@ -649,11 +698,15 @@
         return W_Lambda(w_args, w_body, ctx)
 
 class Begin(W_Macro):
+    _symbol_name = "begin"
+
     def call_tr(self, ctx, lst):
         #begin uses eval_body, so it is tail-recursive aware
         return self.eval_body(ctx, lst)
 
 class Let(W_Macro):
+    _symbol_name = "let"
+
     def call_tr(self, ctx, lst):
         #let uses eval_body, so it is tail-recursive aware
         if not isinstance(lst, W_Pair):
@@ -670,6 +723,8 @@
         return self.eval_body(local_ctx, lst.cdr)
 
 class LetStar(W_Macro):
+    _symbol_name = "let*"
+
     def call_tr(self, ctx, lst):
         #let* uses eval_body, so it is tail-recursive aware
         if not isinstance(lst, W_Pair):
@@ -686,6 +741,8 @@
         return self.eval_body(local_ctx, lst.cdr)
 
 class Letrec(W_Macro):
+    _symbol_name = "letrec"
+
     def call_tr(self, ctx, lst):
         """let uses eval_body, so it is tail-recursive aware"""
         if not isinstance(lst, W_Pair):
@@ -724,6 +781,8 @@
     return W_Pair(W_Symbol('unquote-splicing'), W_Pair(sexpr, w_nil))
 
 class Quote(W_Macro):
+    _symbol_name = "quote"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -731,6 +790,8 @@
         return lst.car
 
 class QuasiQuote(W_Macro):
+    _symbol_name = "quasiquote"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -809,6 +870,8 @@
         return w_lst
 
 class Delay(W_Macro):
+    _symbol_name = "delay"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -819,6 +882,8 @@
 # DerivedMacros
 ##
 class SyntaxRules(W_Macro):
+    _symbol_name = "syntax-rules"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -832,11 +897,9 @@
             if not isinstance(w_literals.car, W_Symbol):
                 raise SchemeSyntaxError
 
+            #XXX locations here
             literal_name = w_literals.car.to_string()
-            try:
-                w_temp = ctx.get(literal_name)
-            except UnboundVariable:
-                w_temp = w_literals.car
+            w_temp = ctx.get_location(literal_name)
 
             literals_map[literal_name] = w_temp
 
@@ -890,10 +953,11 @@
         return self.pattern.to_string() + " -> " + self.template.to_string()
 
     def match(self, ctx, w_expr, pattern=None):
+        #we use .cdr here, because keyword should not be a macro variable
         if pattern is None:
-            return self.matchr(ctx, self.pattern, w_expr)
+            return self.matchr(ctx, self.pattern.cdr, w_expr.cdr)
 
-        return self.matchr(ctx, pattern, w_expr)
+        return self.matchr(ctx, pattern.cdr, w_expr.cdr)
 
     def matchr(self, ctx, w_patt, w_expr):
         if isinstance(w_patt, W_Pair):
@@ -931,16 +995,22 @@
             raise EllipsisPattern
 
         if isinstance(w_patt, W_Symbol):
-            w_literal = self.literals.get(w_patt.name, None)
-            if w_literal is not None:
-                try:
-                    w_form = ctx.get(w_expr.to_string())
-                except UnboundVariable:
-                    w_form = w_expr
+            try:
+                w_literal = self.literals[w_patt.name]
+                if not isinstance(w_expr, W_Symbol):
+                    raise MatchError
+
+                if w_patt.name != w_expr.name:
+                    raise MatchError
+
+                w_form = ctx.get_location(w_expr.name)
 
                 if w_form is not w_literal:
                     raise MatchError
 
+            except KeyError:
+                pass
+
             return {w_patt.name: w_expr}
 
         if w_patt is w_nil and w_expr is w_nil:
@@ -1100,7 +1170,8 @@
                     w_macro = ctx.get(w_car.name)
                     # recursive macro expansion
                     if isinstance(w_macro, W_DerivedMacro):
-                        return w_macro.expand(ctx, w_pair)
+                        if w_macro.transformer is self:
+                            return w_macro.expand(ctx, w_pair)
                 except UnboundVariable:
                     pass
 
@@ -1161,6 +1232,8 @@
         return self.transformer.expand(ctx, lst)
 
 class DefineSyntax(W_Macro):
+    _symbol_name = "define-syntax"
+
     def call(self, ctx, lst):
         if not isinstance(lst, W_Pair):
             raise SchemeSyntaxError
@@ -1179,6 +1252,8 @@
         return w_macro #undefined
  
 class LetSyntax(W_Macro):
+    _symbol_name = "let-syntax"
+
     def call_tr(self, ctx, lst):
         #let uses eval_body, so it is tail-recursive aware
         if not isinstance(lst, W_Pair):

Modified: pypy/dist/pypy/lang/scheme/test/test_macro.py
==============================================================================
--- pypy/dist/pypy/lang/scheme/test/test_macro.py	(original)
+++ pypy/dist/pypy/lang/scheme/test/test_macro.py	Mon Aug  6 11:56:09 2007
@@ -54,6 +54,9 @@
     # w_transformer created in ctx
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
 
+    w_expr = parse_("(foo 12 boo)")
+    py.test.raises(MatchError, w_transformer.match, ctx, w_expr)
+
     w_expr = parse_("(foo bar boo)")
     py.test.raises(MatchError, w_transformer.match, ctx, w_expr)
 
@@ -77,29 +80,35 @@
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
     py.test.raises(MatchError, w_transformer.match, closure, w_expr)
 
-    # the same binding for => in ctx and closure
-    # XXX
+    # the same object for => in ctx and closure, but different bindings
     # <arigo> I think this should also raise MatchError.  When R5RS says
     # "same binding" it probably means bound to the same *location*, not
-    # just that there is the same object in both locations; something like
-    # this:   (let ((x 42) (y 42))
-    #           (let-syntax ((foo (syntax-rules (x y)
-    #                          ((foo x) 123)
-    #                          ((foo y) 456)
-    #                          ((foo whatever) 789))))
-    #             (foo y)))
-    #                        ---> 456
-    # but if we change the last line:
-    #             (let ((y 42)) (foo y))))
-    #                        ---> 789
+    # just that there is the same object in both locations
     ctx.put("=>", w_42)
     assert ctx.get("=>") is closure.get("=>")
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
-    assert w_transformer.match(closure, w_expr)[0].to_boolean()
+    py.test.raises(MatchError, w_transformer.match, closure, w_expr)
 
-def test_syntax_rules_expand_simple():
+def test_literals_eval():
     ctx = ExecutionContext()
+    w_result = eval_(ctx, """(let ((x 42) (y 42))
+                                (let-syntax ((foo (syntax-rules (x y)
+                                              ((foo x) 123)
+                                              ((foo y) 456)
+                                              ((foo whatever) 789))))
+                                   (foo y)))""")
+    assert w_result.to_number() == 456
+
+    w_result = eval_(ctx, """(let ((x 42) (y 42))
+                                (let-syntax ((foo (syntax-rules (x y)
+                                              ((foo x) 123)
+                                              ((foo y) 456)
+                                              ((foo whatever) 789))))
+                                   (let ((y x)) (foo y))))""")
+    assert w_result.to_number() == 789
 
+def test_syntax_rules_expand_simple():
+    ctx = ExecutionContext()
     w_transformer = eval_(ctx, """(syntax-rules () ((_) #t)
                                                    ((_ foo) foo))""")
 
@@ -264,13 +273,9 @@
                                           ((bar arg) (foo arg))))""")
 
     w_expr = parse_("(bar 42)")
-    #should expand directly (recursively) to 42
-    assert ctx.get("bar").expand(ctx, w_expr).to_string() == "42"
-    # XXX <arigo> no, I believe it should not expand recursively...
-    # here is another example which returns (foo) in MIT-Scheme where
-    # we get 42 so far:
-    py.test.skip("XXX in-progress (or wrong test?)")
-    ctx = ExecutionContext()
+    assert ctx.get("bar").expand(ctx, w_expr).to_string() == "(foo 42)"
+    assert eval_(ctx, "(bar 42)").to_number() == 42
+
     eval_(ctx, """(define-syntax foo (syntax-rules ()
                                           ((foo) #t)))""")
     eval_(ctx, """(define-syntax bar (syntax-rules ()
@@ -440,11 +445,10 @@
             "(x y 1 2 3 4 + end)"
 
 def test_cornercase1():
-    py.test.skip("currently crashes")
     w_result = eval_noctx("""(let-syntax ((foo (syntax-rules ()
                                                  ((bar) 'bar))))
-                               (foo))
-                          """)
+                               (foo)) """)
+
     assert w_result.to_string() == 'bar'
     # <arigo> I think that the correct answer is 'bar, according to
     # the R5RS, because it says that the keyword bar in the syntax rule
@@ -461,8 +465,9 @@
 
 def test_pitfall_3_2():
     py.test.skip("(cond ...) not implemented yet")
+    #define inside macors can and sometimes can not introduce new binding
     w_result = eval_noctx("""(let-syntax ((foo (syntax-rules ()
-                                                   ((_ var) (define var 1)))))
+                                                 ((_ var) (define var 1)))))
                                  (let ((x 2))
                                    (begin (define foo +))
                                    (cond (else (foo x))) 
@@ -470,7 +475,6 @@
     assert w_result.to_number() == 2
 
 def test_pitfall_3_3():
-    py.test.skip("currently fails")
     w_result = eval_noctx("""
       (let ((x 1))
         (let-syntax
@@ -485,3 +489,4 @@
 def test_pitfall_3_4():
     w_result = eval_noctx("(let-syntax ((x (syntax-rules ()))) 1)")
     assert w_result.to_number() == 1
+



More information about the Pypy-commit mailing list