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

jlg at codespeak.net jlg at codespeak.net
Fri Jul 27 13:43:09 CEST 2007


Author: jlg
Date: Fri Jul 27 13:43:08 2007
New Revision: 45400

Modified:
   pypy/dist/pypy/lang/scheme/object.py
   pypy/dist/pypy/lang/scheme/test/test_macro.py
Log:
macro recursive expansion; expand, expand_eval, match, substitute takes as 2nd arg ctx

Modified: pypy/dist/pypy/lang/scheme/object.py
==============================================================================
--- pypy/dist/pypy/lang/scheme/object.py	(original)
+++ pypy/dist/pypy/lang/scheme/object.py	Fri Jul 27 13:43:08 2007
@@ -865,7 +865,7 @@
     def __str__(self):
         return self.pattern.to_string() + " -> " + self.template.to_string()
 
-    def match(self, w_expr, ctx, pattern=None):
+    def match(self, ctx, w_expr, pattern=None):
         if pattern is None:
             w_patt = self.pattern
         else:
@@ -890,7 +890,7 @@
                 if not isinstance(w_exprcar, W_Pair):
                     return (False, {})
 
-                (matched, match_nested) = self.match(w_exprcar, ctx, w_pattcar)
+                (matched, match_nested) = self.match(ctx, w_exprcar, w_pattcar)
                 if not matched:
                     return (False, {})
 
@@ -925,12 +925,9 @@
         self.match_dict = {}
         self.closure = ctx
 
-    def match(self, w_expr, ctx=None):
-        if ctx is None:
-            ctx = self.closure
-
+    def match(self, ctx, w_expr):
         for rule in self.syntax_lst:
-            (matched, temp_dict) = rule.match(w_expr, ctx)
+            (matched, temp_dict) = rule.match(ctx, w_expr)
             if matched:
                 self.match_dict = temp_dict
                 return rule.template
@@ -938,41 +935,67 @@
         self.match_dict = {}
         return None
 
-    def expand(self, w_expr, ctx):
-        template = self.match(w_expr, ctx)
+    def expand(self, ctx, w_expr):
+        template = self.match(ctx, w_expr)
 
         if template is None :
             raise SchemeSyntaxError
 
-        return self.substitute(template, ctx)
+        return self.substitute(ctx, template)
 
-    def substitute(self, sexpr, ctx):
+    def substitute(self, ctx, sexpr):
         if isinstance(sexpr, W_Symbol):
-            w_sub = self.match_dict.get(sexpr.to_string(), None)
+            w_sub = self.match_dict.get(sexpr.name, None)
             if w_sub is not None:
                 # Hygenic macros close their input forms in the syntactic
                 # enviroment at the point of use
+
+                #not always needed, because w_sub can have no W_Symbols inside
                 return SyntacticClosure(ctx, w_sub)
 
             return sexpr
 
         elif isinstance(sexpr, W_Pair):
-            return W_Pair(self.substitute(sexpr.car, ctx),
-                    self.substitute(sexpr.cdr, ctx))
+            w_pair = W_Pair(self.substitute(ctx, sexpr.car),
+                    self.substitute(ctx, sexpr.cdr))
+
+            w_paircar = w_pair.car
+            if isinstance(w_paircar, W_Symbol):
+                try:
+                    w_macro = ctx.get(w_paircar.name)
+                    # recursive macro expansion
+                    if isinstance(w_macro, W_DerivedMacro):
+                        return w_macro.expand(ctx, w_pair)
+                except UnboundVariable:
+                    pass
+
+            elif isinstance(w_paircar, SyntacticClosure) and \
+                    isinstance(w_paircar.sexpr, W_Symbol):
+                try:
+                    #ops, which context?
+                    w_macro = ctx.get(w_paircar.sexpr.name)
+
+                    # recursive macro expansion
+                    if isinstance(w_macro, W_DerivedMacro):
+                        return w_macro.expand(ctx, w_pair)
+                except UnboundVariable:
+                    pass
+
+            return w_pair
 
         return sexpr
             
-    def expand_eval(self, sexpr, ctx):
+    def expand_eval(self, ctx, sexpr):
         #we have lexical scopes:
         # 1. in which macro was defined - self.closure
         # 2. in which macro is called   - ctx
         # 3. in which macro is expanded, can introduce new bindings - expand_ctx 
-        expanded = self.expand(sexpr, ctx)
+        expanded = self.expand(ctx, sexpr)
         expand_ctx = self.closure.copy()
         return expanded.eval(expand_ctx)
 
     def procedure(self, ctx, lst):
-        return self.expand_eval(lst[0], ctx)
+        return self.expand_eval(ctx, lst[0])
 
 class DefineSyntax(W_Macro):
     def call(self, ctx, lst):
@@ -996,5 +1019,12 @@
         self.name = name
         self.transformer = transformer
 
+    def to_string(self):
+        return "#<derived-macro %s>" % (self.name,)
+
     def call(self, ctx, lst):
-        return self.transformer.expand_eval(W_Pair(W_Symbol(self.name), lst), ctx)
+        return self.transformer.expand_eval(ctx, W_Pair(W_Symbol(self.name), lst))
+
+    def expand(self, ctx, lst):
+        return self.transformer.expand(ctx, lst)
+

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	Fri Jul 27 13:43:08 2007
@@ -10,36 +10,37 @@
     return parse(expr)[0].eval(ExecutionContext())
 
 def test_syntax_rules_match():
+    ctx = ExecutionContext()
     py.test.raises(SchemeSyntaxError, eval_noctx, "(syntax-rules 1)")
     py.test.raises(SchemeSyntaxError, eval_noctx, "(syntax-rules () 1)")
 
     w_transformer = eval_noctx("(syntax-rules ())")
     w_expr = parse("(foo)")[0]
-    assert not w_transformer.match(w_expr)
+    assert not w_transformer.match(ctx, w_expr)
 
     w_transformer = eval_noctx("(syntax-rules () ((foo) #t))")
     w_expr = parse("(bar)")[0]
-    assert w_transformer.match(w_expr)
+    assert w_transformer.match(ctx, w_expr)
     w_expr = parse("(foo bar)")[0]
-    assert not w_transformer.match(w_expr)
+    assert not w_transformer.match(ctx, w_expr)
 
     w_transformer = eval_noctx("""(syntax-rules () ((_) #t)
                                                    ((_ foo) foo))""")
     w_expr = parse("(foo)")[0]
-    assert w_transformer.match(w_expr).to_boolean()
+    assert w_transformer.match(ctx, w_expr).to_boolean()
     w_expr = parse("(foo bar)")[0]
-    assert w_transformer.match(w_expr).to_string() == "foo"
+    assert w_transformer.match(ctx, w_expr).to_string() == "foo"
     assert w_transformer.match_dict["foo"].to_string() == "bar"
 
     w_expr = parse("(foo bar boo)")[0]
-    assert not w_transformer.match(w_expr)
+    assert not w_transformer.match(ctx, w_expr)
     assert w_transformer.match_dict == {}
 
     w_transformer = eval_noctx("(syntax-rules () ((foo (bar)) bar))")
     w_expr = parse("(_ fuzz)")[0]
-    assert not w_transformer.match(w_expr)
+    assert not w_transformer.match(ctx, w_expr)
     w_expr = parse("(_ (fuzz))")[0]
-    assert w_transformer.match(w_expr)
+    assert w_transformer.match(ctx, w_expr)
     assert w_transformer.match_dict["bar"].to_string() == "fuzz"
 
 def test_syntax_rules_literals():
@@ -50,13 +51,13 @@
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
 
     w_expr = parse("(foo bar boo)")[0]
-    assert not w_transformer.match(w_expr, ctx)
+    assert not w_transformer.match(ctx, w_expr)
 
     # exact match
     w_expr = parse("(foo => boo)")[0]
 
     # within the same context
-    assert w_transformer.match(w_expr, ctx)
+    assert w_transformer.match(ctx, w_expr)
 
     w_42 = W_Number(42)
 
@@ -64,19 +65,19 @@
     closure = ctx.copy()
     closure.put("=>", w_42)
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
-    assert not w_transformer.match(w_expr, closure)
+    assert not w_transformer.match(closure, w_expr)
 
     # different lexical scope, not the same bindings for => in ctx and closure
     ctx.put("=>", W_Number(12))
     assert ctx.get("=>") is not closure.get("=>")
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
-    assert not w_transformer.match(w_expr, closure)
+    assert not w_transformer.match(closure, w_expr)
 
     # the same binding for => in ctx and closure
     ctx.put("=>", w_42)
     assert ctx.get("=>") is closure.get("=>")
     w_transformer = eval_(ctx, "(syntax-rules (=>) ((foo => bar) #t))")
-    assert w_transformer.match(w_expr, closure)
+    assert w_transformer.match(closure, w_expr)
 
 def test_syntax_rules_expand_simple():
     ctx = ExecutionContext()
@@ -85,12 +86,12 @@
                                                    ((_ foo) foo))""")
 
     w_expr = parse("(foo)")[0]
-    w_expanded = w_transformer.expand(w_expr, ctx)
+    w_expanded = w_transformer.expand(ctx, w_expr)
     assert isinstance(w_expanded, W_Boolean)
     assert w_expanded.to_boolean() == True
 
     w_expr = parse("(foo bar)")[0]
-    w_expanded = w_transformer.expand(w_expr, ctx)
+    w_expanded = w_transformer.expand(ctx, w_expr)
     assert w_expanded.to_string() == "bar"
 
     w_transformer = eval_(ctx, """(syntax-rules ()
@@ -98,7 +99,7 @@
                                          (let ((var val)) body)))""")
 
     w_expr = parse("(let1 var 12 (+ 1 var))")[0]
-    w_expanded = w_transformer.expand(w_expr, ctx)
+    w_expanded = w_transformer.expand(ctx, w_expr)
     assert isinstance(w_expanded, W_Pair)
     assert w_expanded.to_string() == "(let ((var 12)) (+ 1 var))"
 
@@ -107,7 +108,7 @@
                                          (let ((var val)) body)))""")
 
     w_expr = parse("(let1 (var 12) (+ 1 var))")[0]
-    w_expanded = w_transformer.expand(w_expr, ctx)
+    w_expanded = w_transformer.expand(ctx, w_expr)
     assert isinstance(w_expanded, W_Pair)
     assert w_expanded.to_string() == "(let ((var 12)) (+ 1 var))"
 
@@ -119,25 +120,25 @@
                                         (let ((temp 1)) (+ var temp))))""")
 
     w_expr = parse("(_ 12)")[0]
-    w_expanded = w_transformer.expand(w_expr, ctx)
+    w_expanded = w_transformer.expand(ctx, w_expr)
     assert w_expanded.to_string() == "(let ((temp 1)) (+ 12 temp))"
-    assert w_transformer.expand_eval(w_expr, ctx).to_number() == 13
+    assert w_transformer.expand_eval(ctx, w_expr).to_number() == 13
 
     #transparency
     eval_(ctx, "(define temp 12)")
     w_expr = parse("(_ temp)")[0]
-    w_expanded = w_transformer.expand(w_expr, ctx)
+    w_expanded = w_transformer.expand(ctx, w_expr)
     assert w_expanded.to_string() == "(let ((temp 1)) (+ temp temp))"
-    assert w_transformer.expand_eval(w_expr, ctx).to_number() == 13
+    assert w_transformer.expand_eval(ctx, w_expr).to_number() == 13
 
     #define in closure, should not affect macro eval
     closure = ctx.copy()
     eval_(closure, "(define + -)")
-    assert w_transformer.expand_eval(w_expr, closure).to_number() == 13
+    assert w_transformer.expand_eval(closure, w_expr).to_number() == 13
 
     #define in top level - should affect macro eval
     eval_(ctx, "(define + -)")
-    assert w_transformer.expand_eval(w_expr, ctx).to_number() == 11
+    assert w_transformer.expand_eval(ctx, w_expr).to_number() == 11
 
 def test_syntax_rules_hygenic_expansion():
     ctx = ExecutionContext()
@@ -154,11 +155,11 @@
                                         (loop count))))""")
 
     w_expr = parse("(dotimes 5 (set! counter (+ counter 1)))")[0]
-    py.test.raises(UnboundVariable, w_transformer.expand_eval, w_expr, ctx)
+    py.test.raises(UnboundVariable, w_transformer.expand_eval, ctx, w_expr)
 
     eval_(ctx, "(define counter 0)")
     w_expr = parse("(dotimes 5 (set! counter (+ counter 1)))")[0]
-    w_transformer.expand_eval(w_expr, ctx)
+    w_transformer.expand_eval(ctx, w_expr)
     assert ctx.get("counter").to_number() == 5
 
 def test_shadow():
@@ -169,10 +170,10 @@
                                       (let ((used-arg 5)) body)))""")
 
     w_expr = parse("(shadow test test)")[0]
-    assert w_transformer.expand_eval(w_expr, ctx).to_number() == 5
+    assert w_transformer.expand_eval(ctx, w_expr).to_number() == 5
 
     eval_(ctx, "(define test 7)")
-    assert w_transformer.expand_eval(w_expr, ctx).to_number() == 5
+    assert w_transformer.expand_eval(ctx, w_expr).to_number() == 5
 
 def test_transformer_eval():
     ctx = ExecutionContext()
@@ -180,20 +181,37 @@
                                      ((_) #t)
                                      ((_ bar) bar)))""")
 
-    w_foo = eval_(ctx, "(foo '(_))")
-    assert w_foo.to_boolean()
-
-    w_foobar = eval_(ctx, """(foo '(_ 42))""")
-    assert w_foobar.to_number() == 42
+    assert eval_(ctx, "(foo '(_))").to_boolean()
+    assert eval_(ctx, "(foo '(_ 42))").to_number() == 42
 
 def test_define_syntax():
     ctx = ExecutionContext()
-    eval_(ctx, """(define-syntax foo (syntax-rules ()
-                                     ((_) #t)
-                                     ((_ bar) bar)))""")
-    w_foo = eval_(ctx, """(foo)""")
-    assert w_foo.to_boolean()
-
-    w_foobar = eval_(ctx, """(foo 42)""")
-    assert w_foobar.to_number() == 42
+    eval_(ctx, """(define-syntax foo
+                                 (syntax-rules ()
+                                    ((_) #t)
+                                    ((_ bar) bar)))""")
+
+    assert eval_(ctx, "(foo)").to_boolean()
+    assert eval_(ctx, "(foo 42)").to_number() == 42
+
+def test_recursive_macro():
+    ctx = ExecutionContext()
+    eval_(ctx, """(define-syntax my-or
+                                 (syntax-rules ()
+                                    ((my-or) #f)
+                                    ((my-or arg) arg)
+                                    ((my-or arg1 arg2)
+                                     (if arg1
+                                         arg1
+                                         (my-or arg2)))))""")
+
+    assert eval_(ctx, "(my-or)").to_boolean() is False
+    assert eval_(ctx, "(my-or 12)").to_number() == 12
+
+    #should expand recursively and after that eval, how to check it?
+    w_expr = parse("(my-or 12 42)")[0]
+    assert ctx.get("my-or").expand(ctx, w_expr).to_string() == \
+            "(if 12 12 42)"
+    assert eval_(ctx, "(my-or 12 42)").to_number() == 12
+    assert eval_(ctx, "(my-or #f 42)").to_number() == 42
 



More information about the Pypy-commit mailing list