Commits

Lucian Brănescu-Mihăilă committed 1e9ca85

More clear separation between macros and special forms.

Comments (0)

Files changed (5)

+import pdb
 import operator
 
 from lsp.types import *
 
 def make_list(*items):
     return List(items)
-
 import sys
+import pdb
 import operator
 from functools import partial
 
     'println': println,
     'input': input,
     'exit': sys.exit,
+
+    'debugger': pdb.set_trace,
 }, macros={
-    'if': if_macro,
-    'fn': fn_macro,
-    'def': def_macro,
-    'quote': quote_macro,
-    'quasiquote': quasiquote_macro,
-    'unquote': unquote_macro,
-    'unquote-splicing': unquote_splicing_macro,
-    'defmacro': defmacro_macro,
-    'do': do_macro,
+    # Special forms
+    'if': if_,
+    'fn': fn,
+    'def': def_,
+    'quote': quote,
+    'quasiquote': quasiquote,
+    'unquote': unquote,
+    'unquote-splicing': unquote_splicing,
+    'defmacro': defmacro,
+    'do': do,
     '.': call_method,
 })
 
 from lsp.types import *
 
 
-def if_macro(body, env):
+def if_(body, env):
     if eval(body[0], env):
         return eval(body[1], env)
     else:
         return eval(body[2], env)
 
 
-def def_macro(body, env):
-    if not isinstance(body[0], Symbol):
-        raise SyntaxError("Expected symbol, got {0}".format(body[0]))
+def def_(body, env):
+    name = body[0]
+    if not isinstance(name, Symbol):
+        raise SyntaxError("Expected symbol, got {0}".format(name))
 
-    name = body[0]
     val = eval(body[1], env)
-
     env[name] = eval(val, env)
 
     return val
 
 
-class defmacro_macro(object):
-    def __init__(self, body, env):
-        self.env = env
-
-        if not isinstance(body[0], Symbol):
-            raise SyntaxError("Expected symbol, got {0}".format(body[0]))
-
-        name = body[0]
-
-        if not isinstance(body[1], List):
-            raise SyntaxError("Expected argument list, got {0}".format(body[1]))
-
-        self.args = body[1]
-        self.body = body[2]
-
-        env.macros[name] = self
-
-    def __call__(self, args, env):
-        if len(args) != len(self.args):
-            raise SyntaxError("Expected {0} args, got {1}: {2}".format(
-                len(self.args), len(args), args))
-
-        return eval(eval(self.body,
-                        Env(zip(self.args, args),
-                            parent=self.env)),
-                    env)
-
-
-class fn_macro(object):
+class fn(object):
     def __init__(self, body, env):
         first = body[0]
 
         return '<lsp.lambda object at {0}>'.format(hex(id(self)))
 
 
-def quote_macro(body, env):
+class defmacro(object):
+    def __init__(self, body, env):
+        name = body[0]
+        if not isinstance(name, Symbol):
+            raise SyntaxError("Expected symbol, got {0}".format(name))
+
+        args = body[1]
+        if isinstance(args, List):
+            self.args = args
+        else:
+            raise SyntaxError("Expected argument list, got: {0}".format(args))
+
+        # Rest arguments
+        if '&' in self.args:
+            i = self.args.index('&')
+
+            self.rest_arg = self.args[i + 1]
+            if not isinstance(self.rest_arg, Symbol):
+                raise SyntaxError("Expected symbol as rest argument, got: {0}" \
+                    .format(self.rest_arg))
+
+            self.args = List(self.args[:i])
+        else:
+            self.rest_arg = None
+
+        self.body = body[2]
+        self.env = env
+
+        env.macros[name] = self
+
+    def __call__(self, args, env):
+        if self.rest_arg is not None:
+            if len(args) < len(self.args):
+                raise RuntimeError("Expected at least {0} args, got {1}: {2}" \
+                    .format(len(self.args), len(args), args))
+
+        elif len(args) != len(self.args):
+            raise RuntimeError("Expected {0} args, got {1}: {2}".format(
+                len(self.args), len(args), args))
+
+        loc = Env(zip(self.args, args), parent=self.env)
+
+        if self.rest_arg is not None:
+            loc[self.rest_arg] = List(args[len(self.args):])
+
+        return eval(eval(self.body, loc), env)
+
+
+def quote(body, env):
     if len(body) != 1:
         raise SyntaxError("quote expects 1 part")
 
     return body[0]
 
 
-def quasiquote_macro(body, env):
+def quasiquote(body, env):
     if len(body) != 1:
         raise SyntaxError("quasiquote expects 1 part")
 
     return eval_unquote(body[0], env)
 
 
-def unquote_macro(body, env):
+def unquote(body, env):
     raise SyntaxError("unquote only valid in quasiquote")
 
 
-def unquote_splicing_macro(body, env):
+def unquote_splicing(body, env):
     raise SyntaxError("unquote-splicing only valid in quasiquote")
 
 
-def do_macro(body, env):
+def do(body, env):
     for i in body:
         result = eval(i, env)
 
             return m(sexp[1:], env)
 
         else:
-            vals = [eval(i, env) for i in sexp]
+            vals = List(eval(i, env) for i in sexp)
             fun = vals[0]
 
             if not hasattr(fun, '__call__'):
-(defmacro defn (name args body)
-  `(def ~name (fn (~@args) ~body)))
+(defmacro let (pairs exp)
+	`((fn ~(slice pairs 0 (len pairs) 2) ~exp)
+		~@(slice pairs 1 (len pairs) 2)))
 
-(defmacro let (pairs exp)
-  `((fn (~@(slice pairs 0 (len pairs) 2)) ~exp)
-    ~@(slice pairs 1 (len pairs) 2)))
+(defmacro defn (name args & body)
+	`(def ~name (fn ~args (do ~@body))))
 
 (defn comp (f g)
-  (fn (& xs)
-    (f (apply g xs))))
+	(fn (& xs)
+		(f (apply g xs))))
 
 (defn partial (f & args)
-  (fn (& xs)
-    (apply f (concat args xs))))
+	(fn (& xs)
+		(apply f (concat args xs))))
 
 (defn inc (x) (+ x 1))
 
 (defn dec (x) (- x 1))
 
 (defn first (coll)
-  (coll 0))
+	(coll 0))
 
 (defn second (coll)
-  (coll 1))
+	(coll 1))
 
 (defn rest (coll)
-  (slice coll 1 (len coll)))
+	(slice coll 1 (len coll)))
 
 (defn reduce (fun acc coll)
-  (if (empty? coll)
-    acc
-    (reduce fun (fun acc (first coll)) (rest coll))))
+	(if (empty? coll)
+		acc
+		(reduce fun (fun acc (first coll)) (rest coll))))
 
 (defn map (fun coll)
-  (reduce
-    (fn (acc e) (concat acc (list (fun e))))
-    '() coll))
+	(reduce
+		(fn (acc e) (concat acc (list (fun e))))
+		'() coll))
 
 (defn filter (pred coll)
-  (reduce
-    (fn (acc e)
-      (if (pred e)
-        (concat acc (list e))
-        acc))
-    '() coll))
+	(reduce
+		(fn (acc e)
+			(if (pred e)
+				(concat acc (list e))
+				acc))
+		'() coll))
 
 (defn range (start stop & args)
-  (let (step (if (empty? args) 1 (first args)))
-    ((fn gen-list (n)
-      (if (< n stop)
-        (concat (list n) (gen-list (+ n step)))
-        (list)))
-      start)))
+	(let (step (if (empty? args) 1 (first args)))
+		((fn gen-list (n)
+			(if (< n stop)
+				(concat (list n) (gen-list (+ n step)))
+				(list)))
+			start)))
 from fractions import Fraction
+from functools import partial
 
 from py.test import raises
 
 from lsp.parser import lex, parse, read
-from lsp.inter import eval, lsp
+from lsp.forms import eval
 from lsp.types import Symbol, List, Nil, Env
 from lsp.env import top
+from lsp import lsp
+
+eval = partial(eval, env=top)
 
 
 def test_lex():