1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00
mal/elisp/core.el
2016-06-16 09:32:55 +02:00

252 lines
11 KiB
EmacsLisp

(defun mal-seq-p (mal-object)
(let ((type (mal-type mal-object)))
(if (or (eq type 'list) (eq type 'vector))
mal-true
mal-false)))
(defun mal-listify (mal-object)
(let ((type (mal-type mal-object)))
(if (eq type 'vector)
(append (mal-value mal-object) nil)
(mal-value mal-object))))
(defun mal-= (a b)
(let ((a-type (mal-type a))
(b-type (mal-type b)))
(cond
((and (and (not (eq a-type 'map))
(not (eq a-type 'list))
(not (eq a-type 'vector)))
(and (not (eq b-type 'map))
(not (eq b-type 'list))
(not (eq b-type 'vector))))
(mal-atom-= a b))
((and (or (eq a-type 'list) (eq a-type 'vector))
(or (eq b-type 'list) (eq b-type 'vector)))
(mal-seq-= a b))
((and (eq a-type 'map) (eq b-type 'map))
(mal-map-= a b))
(t
;; incompatible types
nil))))
(defun mal-atom-= (a b)
(equal (mal-value a) (mal-value b)))
(defun mal-seq-= (a b)
(when (= (length (mal-value a))
(length (mal-value b)))
(when (everyp 'mal-= (mal-listify a) (mal-listify b))
t)))
(defun everyp (predicate list-a list-b)
(let ((everyp t))
(while (and everyp list-a list-b)
(let ((item-a (pop list-a))
(item-b (pop list-b)))
(when (not (funcall predicate item-a item-b))
(setq everyp nil))))
everyp))
(defun mal-map-= (a b)
(catch 'return
(let ((a* (mal-value a))
(b* (mal-value b)))
(when (= (hash-table-count a*)
(hash-table-count b*))
(maphash (lambda (key a-value)
(let ((b-value (gethash key b*)))
(if b-value
(when (not (mal-= a-value b-value))
(throw 'return nil))
(throw 'return nil))))
a*)
;; if we made it this far, the maps are equal
t))))
(define-hash-table-test 'mal-= 'mal-= 'sxhash)
(defun mal-conj (seq &rest args)
(let ((type (mal-type seq))
(value (mal-value seq)))
(if (eq type 'vector)
(mal-vector (vconcat (append (append value nil) args)))
(while args
(push (pop args) value))
(mal-list value))))
(defun elisp-to-mal (arg)
(cond
((not arg)
mal-nil)
((eq arg t)
mal-true)
((numberp arg)
(mal-number arg))
((stringp arg)
(mal-string arg))
((keywordp arg)
(mal-keyword arg))
((symbolp arg)
(mal-symbol arg))
((consp arg)
(mal-list (mapcar 'elisp-to-mal arg)))
((vectorp arg)
(mal-vector (vconcat (mapcar 'elisp-to-mal arg))))
((hash-table-p arg)
(let ((output (make-hash-table :test 'mal-=)))
(maphash
(lambda (key value)
(puthash (elisp-to-mal key) (elisp-to-mal value) output))
arg)
(mal-map output)))
(t
;; represent anything else as printed arg
(mal-string (format "%S" arg)))))
(defvar core-ns
`((+ . ,(mal-fn (lambda (a b) (mal-number (+ (mal-value a) (mal-value b))))))
(- . ,(mal-fn (lambda (a b) (mal-number (- (mal-value a) (mal-value b))))))
(* . ,(mal-fn (lambda (a b) (mal-number (* (mal-value a) (mal-value b))))))
(/ . ,(mal-fn (lambda (a b) (mal-number (/ (mal-value a) (mal-value b))))))
(< . ,(mal-fn (lambda (a b) (if (< (mal-value a) (mal-value b)) mal-true mal-false))))
(<= . ,(mal-fn (lambda (a b) (if (<= (mal-value a) (mal-value b)) mal-true mal-false))))
(> . ,(mal-fn (lambda (a b) (if (> (mal-value a) (mal-value b)) mal-true mal-false))))
(>= . ,(mal-fn (lambda (a b) (if (>= (mal-value a) (mal-value b)) mal-true mal-false))))
(= . ,(mal-fn (lambda (a b) (if (mal-= a b) mal-true mal-false))))
(list . ,(mal-fn (lambda (&rest args) (mal-list args))))
(list? . ,(mal-fn (lambda (mal-object) (if (mal-list-p mal-object) mal-true mal-false))))
(empty? . ,(mal-fn (lambda (seq) (if (zerop (length (mal-value seq))) mal-true mal-false))))
(count . ,(mal-fn (lambda (seq) (mal-number (if (mal-seq-p seq) (length (mal-value seq)) 0)))))
(pr-str . ,(mal-fn (lambda (&rest args) (mal-string (mapconcat (lambda (item) (pr-str item t)) args " ")))))
(str . ,(mal-fn (lambda (&rest args) (mal-string (mapconcat 'pr-str args "")))))
(prn . ,(mal-fn (lambda (&rest args) (println (mapconcat (lambda (item) (pr-str item t)) args " ")) mal-nil)))
(println . ,(mal-fn (lambda (&rest args) (println (mapconcat 'pr-str args " ")) mal-nil)))
(read-string . ,(mal-fn (lambda (input) (read-str (mal-value input)))))
(slurp . ,(mal-fn (lambda (file)
(with-temp-buffer
(insert-file-contents-literally (mal-value file))
(mal-string (buffer-string))))))
(atom . ,(mal-fn (lambda (arg) (mal-atom arg))))
(atom? . ,(mal-fn (lambda (mal-object) (if (mal-atom-p mal-object) mal-true mal-false))))
(deref . ,(mal-fn (lambda (atom) (mal-value atom))))
(reset! . ,(mal-fn (lambda (atom value) (setf (aref atom 1) value))))
(swap! . ,(mal-fn (lambda (atom fn &rest args)
(let* ((fn* (if (mal-func-p fn) (mal-func-fn fn) fn))
(args* (cons (mal-value atom) args))
(value (apply (mal-value fn*) args*)))
(setf (aref atom 1) value)))))
(cons . ,(mal-fn (lambda (arg list) (mal-list (cons arg (mal-listify list))))))
(concat . ,(mal-fn (lambda (&rest lists)
(let ((lists* (mapcar (lambda (item) (mal-listify item)) lists)))
(mal-list (apply 'append lists*))))))
(nth . ,(mal-fn (lambda (seq index)
(let ((i (mal-value index))
(list (mal-listify seq)))
(or (nth i list)
(error "Args out of range: %s, %d" (pr-str seq) i))))))
(first . ,(mal-fn (lambda (seq)
(if (mal-nil-p seq)
mal-nil
(let* ((list (mal-listify seq))
(value (car list)))
(or value mal-nil))))))
(rest . ,(mal-fn (lambda (seq) (mal-list (cdr (mal-listify seq))))))
(throw . ,(mal-fn (lambda (mal-object) (signal 'mal-custom (list mal-object)))))
(apply . ,(mal-fn (lambda (fn &rest args)
(let* ((butlast (butlast args))
(last (mal-listify (car (last args))))
(fn* (if (mal-func-p fn) (mal-func-fn fn) fn))
(args* (append butlast last)))
(apply (mal-value fn*) args*)))))
(map . ,(mal-fn (lambda (fn seq)
(let ((fn* (if (mal-func-p fn) (mal-func-fn fn) fn)))
(mal-list (mapcar (mal-value fn*) (mal-value seq)))))))
(nil? . ,(mal-fn (lambda (arg) (if (mal-nil-p arg) mal-true mal-false))))
(true? . ,(mal-fn (lambda (arg) (if (mal-true-p arg) mal-true mal-false))))
(false? . ,(mal-fn (lambda (arg) (if (mal-false-p arg) mal-true mal-false))))
(symbol? . ,(mal-fn (lambda (arg) (if (mal-symbol-p arg) mal-true mal-false))))
(keyword? . ,(mal-fn (lambda (arg) (if (mal-keyword-p arg) mal-true mal-false))))
(string? . ,(mal-fn (lambda (arg) (if (mal-string-p arg) mal-true mal-false))))
(vector? . ,(mal-fn (lambda (arg) (if (mal-vector-p arg) mal-true mal-false))))
(map? . ,(mal-fn (lambda (arg) (if (mal-map-p arg) mal-true mal-false))))
(symbol . ,(mal-fn (lambda (string) (mal-symbol (intern (mal-value string))))))
(keyword . ,(mal-fn (lambda (string) (mal-keyword (intern (concat ":" (mal-value string)))))))
(vector . ,(mal-fn (lambda (&rest args) (mal-vector (vconcat args)))))
(hash-map . ,(mal-fn (lambda (&rest args)
(let ((map (make-hash-table :test 'mal-=)))
(while args
(puthash (pop args) (pop args) map))
(mal-map map)))))
(sequential? . ,(mal-fn 'mal-seq-p))
(get . ,(mal-fn (lambda (map key) (if (mal-map-p map) (or (gethash key (mal-value map)) mal-nil) mal-nil))))
(contains? . ,(mal-fn (lambda (map key) (if (gethash key (mal-value map)) mal-true mal-false))))
(assoc . ,(mal-fn (lambda (map &rest args)
(let ((map* (copy-hash-table (mal-value map))))
(while args
(puthash (pop args) (pop args) map*))
(mal-map map*)))))
(dissoc . ,(mal-fn (lambda (map &rest args)
(let ((map* (copy-hash-table (mal-value map))))
(while args
(remhash (pop args) map*))
(mal-map map*)))))
(keys . ,(mal-fn (lambda (map) (let (keys)
(maphash (lambda (key value) (push key keys))
(mal-value map))
(mal-list keys)))))
(vals . ,(mal-fn (lambda (map) (let (vals)
(maphash (lambda (key value) (push value vals))
(mal-value map))
(mal-list vals)))))
(readline . ,(mal-fn (lambda (prompt)
(let ((ret (readln (mal-value prompt))))
(if ret
(mal-string ret)
mal-nil)))))
(meta . ,(mal-fn (lambda (mal-object) (or (mal-meta mal-object) mal-nil))))
(with-meta . ,(mal-fn (lambda (mal-object meta)
;; TODO: doesn't work on hashtables
(let ((mal-object* (copy-tree mal-object t)))
(setf (aref mal-object* 2) meta)
mal-object*))))
(time-ms . ,(mal-fn (lambda () (mal-number (floor (* (float-time) 1000))))))
(conj . ,(mal-fn 'mal-conj))
(seq . ,(mal-fn (lambda (mal-object)
(let ((type (mal-type mal-object))
(value (mal-value mal-object)))
(cond
((or (eq type 'list) (eq type 'vector))
(if (and value (not (zerop (length value))))
(mal-list (mal-listify mal-object))
mal-nil))
((eq type 'string)
(if (not (zerop (length value)))
(mal-list (mapcar (lambda (item) (mal-string (char-to-string item)))
(append value nil)))
mal-nil))
(t
mal-nil))))))
(elisp-eval . ,(mal-fn (lambda (string) (elisp-to-mal (eval (read (mal-value string)))))))
))