Documentation
(defun atom? (x)
  (or (number? x)
      (bool? x)
      (string? x)
      (nil? x)))

(defun product (xs)
  (let ((p 1))
    (dolist (x xs)
      (set p (* p x)))
    p))

(defun evil-r (expr env)
  (let ((rec (lambda (e)
               (evil-r e env))))
    (cond ((atom? expr)
           expr)
          ((symbol? expr)
           (env expr))
          ((cons? expr)
           (let ((op (car expr))
                 (args (cdr expr)))
             (case op
               ('= (= (evil-r (car args) env)
                      (evil-r (cadr args) env)))
               ('+ (sum (map rec args)))
               ('- (- (evil-r (car args) env)
                      (sum (map rec (cdr args)))))
               ('* (product (map rec args)))
               ('/ (/ (evil-r (car args) env)
                      (product (map rec (cdr args)))))
               ('cons (cons (evil-r (car args) env)
                            (evil-r (cadr args) env)))
               ('car (car (evil-r (car args) env)))
               ('cdr (cdr (evil-r (car args) env)))
               ('quote (car args))
               ('if (if (evil-r (car args) env)
                        (evil-r (cadr args) env)
                        (and (cddr args)
                             (evil-r (caddr args) env))))
               ('progn (let ((res nil))
                         (dolist (arg args)
                           (set res (evil-r arg env)))
                         res))
               ('lambda (let ((x (caar args))
                              (body `(progn ,@(cdr args))))
                          (lambda (arg)
                            (evil-r body (lambda (sym)
                                           (if (= sym x)
                                               arg
                                               (env sym)))))))
               (_ ((evil-r op env)
                   (evil-r (car args) env))))))
          (true (progn
                  (println "Undefined pattern: " expr)
                  (+ "1" 1))))))

(defun evil (expr)
  (evil-r expr (lambda (x)
                 (println "Undefined variable: " x)
                 (+ "1" 1))))

(defun evil-examples ()
  (println (evil '((lambda (x) (+ x 1330)) 7)))

  (println (evil '((((lambda (x)
                       (lambda (y)
                         (lambda (z)
                           (+ (* x (* 2 y)) z))))
                     4) 5) 6)))

  (println (evil '((lambda (f) (f 2))
                   (lambda (x) (+ 10 x)))))

  (println (evil '((lambda (f)
                     ((f f) 20))
                   (lambda (f)
                     (lambda (n)
                       (if (= n 0)
                           1
                           (* n ((f f) (- n 1)))))))))

  (println (evil '((((lambda (f)
                       (f f))
                     (lambda (f)
                       (lambda (g)
                         (lambda (xs)
                           (if xs
                               (cons (g (car xs))
                                     (((f f) g) (cdr xs))))))))
                    (lambda (x)
                      (* x 2)))
                   '(2 4 6 8 10 12))))

  (println (evil '(((lambda (map)
                      (lambda (factorial)
                        ((map factorial) '(1 2 3 4 5 6 7 8 9 10))))
                    ((lambda (f)
                       (f f))
                     (lambda (f)
                       (lambda (g)
                         (lambda (xs)
                           (if xs
                               (cons (g (car xs))
                                     (((f f) g) (cdr xs)))))))))
                   ((lambda (f)
                      (f f))
                    (lambda (f)
                      (lambda (n)
                        (if (= n 0)
                            1
                            (* n ((f f) (- n 1)))))))))))

(defun do-factorial ()
  (evil '(((lambda (map)
             (lambda (factorial)
               ((map factorial) '(1 2 3 4 5 6 7 8 9 10))))
           ((lambda (f)
              (f f))
            (lambda (f)
              (lambda (g)
                (lambda (xs)
                  (if xs
                      (cons (g (car xs))
                            (((f f) g) (cdr xs)))))))))
          ((lambda (f)
             (f f))
           (lambda (f)
             (lambda (n)
               (if (= n 0)
                   1
                   (* n ((f f) (- n 1))))))))))

(defun do-factorial-d ()
  (((lambda (map)
      (lambda (factorial)
        ((map factorial) '(1 2 3 4 5 6 7 8 9 10))))
    ((lambda (f)
       (f f))
     (lambda (f)
       (lambda (g)
         (lambda (xs)
           (if xs
               (cons (g (car xs))
                     (((f f) g) (cdr xs)))))))))
   ((lambda (f)
      (f f))
    (lambda (f)
      (lambda (n)
        (if (= n 0)
            1
            (* n ((f f) (- n 1)))))))))