(in-package :core)
+(define-condition index-error (types:mal-error)
+ ((size :initarg :size :reader size)
+ (index :initarg :index :reader index)
+ (sequence :initarg :sequence :reader sequence))
+ (:report (lambda (condition stream)
+ (format stream
+ "Index out of range (~a), length is ~a but index given was ~a"
+ (printer:pr-str (sequence condition))
+ (size condition)
+ (index condition)))))
+
(defun get-file-contents (filename)
(with-open-file (stream filename)
(let ((data (make-string (file-length stream))))
(types:apply-unwrapped-values '* value1 value2))))
(cons (types:make-mal-symbol '/)
- (types:make-mal-builtin-fn (lambda (value1 value2)
+ (types:make-mal-builtin-fn ( lambda (value1 value2)
(types:apply-unwrapped-values '/ value1 value2))))
(cons (types:make-mal-symbol '|prn|)
(types:make-mal-builtin-fn (lambda (sequence index)
(or (nth (mal-value index)
(map 'list #'identity (mal-value sequence)))
- (error "Index out of range")))))
+ (error 'index-error
+ :size (length (mal-value sequence))
+ :index (mal-value index)
+ :sequence sequence)))))
(cons (types:make-mal-symbol '|first|)
(types:make-mal-builtin-fn (lambda (sequence)
(in-package :env)
-(define-condition undefined-symbol (error)
+(define-condition undefined-symbol (types:mal-error)
((symbol :initarg :symbol :reader symbol))
(:report (lambda (condition stream)
(format stream
"Symbol ~a is undefined"
(symbol condition)))))
-(define-condition arity-mismatch (error)
+(define-condition arity-mismatch (types:mal-error)
((required :initarg :required :reader required)
(provided :initarg :provided :reader provided))
(:report (lambda (condition stream)
(in-package :mal)
+(define-condition invalid-function (types:mal-error)
+ ((form :initarg :form :reader form)
+ (context :initarg :context :reader context))
+ (:report (lambda (condition stream)
+ (format stream
+ "Invalid function '~a' provided while ~a"
+ (printer:pr-str (form condition))
+ (if (string= (context condition) "apply")
+ "applying"
+ "defining macro")))))
+
(defvar *repl-env* (make-instance 'env:mal-environment))
(dolist (binding core:ns)
((mal-value= (make-mal-symbol '|defmacro!|) (first forms))
(let ((value (mal-eval (third forms) env)))
(return (if (types:mal-fn-p value)
- (env:set-env env (second forms)
+ (env:set-env env
+ (second forms)
(progn
(setf (cdr (assoc 'is-macro (types:mal-attrs value))) t)
value))
- (error "Not a function")))))
+ (error 'invalid-function
+ :form value
+ :context "macro")))))
((mal-value= (make-mal-symbol '|let*|) (first forms))
(let ((new-env (make-instance 'env:mal-environment
(t (let* ((evaluated-list (eval-ast ast env))
(function (car evaluated-list)))
;; If first element is a mal function unwrap it
- (if (not (types:mal-fn-p function))
- (return (apply (mal-value function)
- (cdr evaluated-list)))
- (let* ((attrs (types:mal-attrs function)))
- (setf ast (cdr (assoc 'ast attrs))
- env (make-instance 'env:mal-environment
- :parent (cdr (assoc 'env attrs))
- :binds (map 'list
- #'identity
- (mal-value (cdr (assoc 'params attrs))))
- :exprs (cdr evaluated-list)))))))))))))
+ (cond ((types:mal-fn-p function)
+ (let* ((attrs (types:mal-attrs function)))
+ (setf ast (cdr (assoc 'ast attrs))
+ env (make-instance 'env:mal-environment
+ :parent (cdr (assoc 'env attrs))
+ :binds (map 'list
+ #'identity
+ (mal-value (cdr (assoc 'params attrs))))
+ :exprs (cdr evaluated-list)))))
+ ((types:mal-builtin-fn-p function)
+ (return (apply (mal-value function)
+ (cdr evaluated-list))))
+ (t (error 'invalid-function
+ :form function
+ :context "apply")))))))))))
(defun mal-read (string)
(reader:read-str string))
(handler-case
(mal-print (mal-eval (mal-read string)
*repl-env*))
- (reader:eof (condition)
- (format nil
- "~a"
- condition))
- (env:undefined-symbol (condition)
+ (types:mal-error (condition)
(format nil
"~a"
condition))
(error (condition)
(format nil
- "~a"
+ "Internal error: ~a"
condition))))
(rep "(def! not (fn* (a) (if a false true)))")