aboutsummaryrefslogtreecommitdiff
path: root/src/eval.lisp
blob: b2c399cf46c7f0775560ab63b43da6f00e171b0b (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
(in-package #:chains)

(defun group-if (predicate sequence)
  (loop :for (head . tail) :on sequence
        :while (funcall predicate head)
        :collect head :into group
        :finally
           (return (values group (if (not (funcall predicate head))
                                     (cons head tail)
                                     tail)))))

(defun group (item sequence &key (test #'eql))
  (group-if #'(lambda (x) (funcall test item x)) sequence))

(defun group-if-not (predicate sequence)
  (group-if (complement predicate) sequence))

(defun link-closure (link)
  "Returns the function object inside the first link of a chain"
  (setf link (remove-if #'whitespace-text-p link))
  (assert (and (not (null link))
               (closurep (first link))
               (null (rest link)))
          (link)
          "Link is not callable: ~A" link)
  (first link))

(defun eval-chain (environment chain siblings)
  "Evaluate CHAIN inside ENVIRONMENT and possibly consume SIBLINGS. Returns a
link with the evaluated body of the function under the ENVIRONMENT"
  (destructuring-bind (head &rest arguments)
      (chain-links chain)
    (with-accessors ((function closure-function)
                     (type closure-type)
                     (group-predicate closure-group-p))
        ;; Evaluate all elements of first link and get closure object
        (link-closure (eval-tree environment head))
      (multiple-value-bind (consumed-siblings rest)
          (group-if group-predicate siblings)
        (values
         (apply function
                environment
                consumed-siblings
                (ecase type
                  (:special
                   arguments)
                  (:function
                   (mapcar (curry #'eval-tree environment) arguments))))
         rest)))))

(defun eval-tree (environment node)
  (typecase node
    (object-symbol
     (get-var environment (object-value node)))
    (cons
     (destructuring-bind (head &rest tail)
         node
       (if (chainp head)
           (multiple-value-bind (result rest)
               (eval-chain environment head tail)
             (append (ensure-list result) (eval-tree environment rest)))
           (cons (eval-tree environment head) (eval-tree environment tail)))))
    (t
     node)))