aboutsummaryrefslogtreecommitdiff
(in-package #:chains)

(defparameter *default-environment* '()
  "")

(let ((true (make-object :symbol (intern "TRUE" *interpreter-package*)))
      (false (make-object :symbol (intern "FALSE" *interpreter-package*))))
  (push (cons (intern "TRUE" *interpreter-package*) true) *default-environment*)
  (push (cons (intern "FALSE" *interpreter-package*) false) *default-environment*))

(defun get-var (env key)
  (if-let ((slot (assoc key env)))
    (cdr slot)
    (error "var ~A not found~% ~A~%" key env)))

;; Built-in functions and variables used inside the interpreter use their own
;; package. This allows the language to have keywords like lambda which
;; otherwise would be reserved words from the cl package

(defmacro define (name arguments
                  (environment &key specialp siblings group-predicate)
                  &body body)
  (setf name (intern (symbol-name name) *interpreter-package*))
  `(push (cons ',name
               (make-closure #'(lambda (,environment ,siblings)
                                 (declare (ignorable ,siblings))
                                 (let ,(mapcar #'(lambda (x)
                                                   `(,x (get-var ,environment (find-symbol (symbol-name ',x) *interpreter-package*))))
                                        arguments)
                                   ,@body))
                             ',(mapcar #'(lambda (x) (intern (symbol-name x) *interpreter-package*))
                                       arguments)
                             ,(if group-predicate
                                  group-predicate
                                  '(complement #'identity))
                             nil
                             ,specialp))
         *default-environment*))

(defmacro define-function (name arguments &body body)
  (let ((environment (gensym)))
    `(define ,name ,arguments (,environment) ,@body)))

(defmacro define-special (name arguments &body body)
  (let ((environment (gensym)))
    `(define ,name ,arguments (,environment :specialp t) ,@body)))

(defmacro clambda ((environment lambda-list) &body body)
  (let ((siblings (gensym)))
    `(make-instance 'closure :function #'(lambda (,environment ,siblings)
                                           (declare (ignorable ,siblings))
                                           ,@body)
                             :lambda-list ,lambda-list
                             :groupp (complement #'identity)
                             :environment nil
                             :specialp nil)))

(define lambda (lambda-list body)
    (captured-environment :siblings siblings :specialp t)
  (setf lambda-list (remove-if #'whitespace-text-p lambda-list))
  (assert (every #'object-symbol-p lambda-list))
  (setf lambda-list (mapcar #'object-value lambda-list))
  (clambda (environment lambda-list)
    ;; Splice arguments into resulting link
    (flatten (eval-tree (append environment captured-environment) body))))

;; (define-special lambda* (arglist group-predicate body)
;;   (setf arglist (remove-if #'whitespace-text-p arglist))
;;   (assert (every #'object-symbol-p arglist))
;;   (setf arglist (mapcar #'object-value arglist))

;;   (cond ((and (listp group-predicate))
;;          ;; TODO: Check arglist of predicate
;;          (setf group-predicate (first (eval-tree environment group-predicate)))

;;          (assert (closurep group-predicate))
;;          (let ((function (closure-function group-predicate)))
;;            (setf group-predicate #'(lambda (object)
;;                                      (funcall function nil nil (list object))))))
;;         ((not (functionp group-predicate))
;;          (error "~A" group-predicate)))

;;   (let ((env environment))
;;     (make-closure  #'(lambda (environment siblings &rest args)
;;                        (eval-tree (append (pairlis arglist args)
;;                                           (list (cons (intern "SIBLINGS" *interpreter-package*)
;;                                                       siblings))
;;                                           env
;;                                           environment)
;;                                   body))
;;                    arglist group-predicate
;;                    env nil)))

(define define (name value)
    (environment :siblings siblings :group-predicate #'identity :specialp t)
  (assert (object-symbol-p (first name)))
  (setf name (object-value (first name)))
  (setf value (first (eval-tree environment value)))
  (eval-tree (acons name value environment)
             siblings))

;;; TODO: Macro for checking arguments types, eliminate whitespace and so on
;;; TODO: Docstrings for functions
;;; TODO: More advance lambda-lists (optional values, etc)