158 lines
3.8 KiB
Common Lisp
158 lines
3.8 KiB
Common Lisp
;;; cl-scopes/forge - may the forge be with you!
|
|
|
|
;;;; A Forth-like interpreter implemented in Common Lisp.
|
|
|
|
(defpackage :scopes/forge
|
|
(:use :common-lisp)
|
|
(:export #:*forge-env*
|
|
#:forge-env #:dstack #:exec #:exec-str #:repl
|
|
#:make-iseq #:isq-all #:isq-cur #:isq-next #:isq-end #:isq-add))
|
|
|
|
(defpackage :sf-builtin)
|
|
(defpackage :sf-user)
|
|
|
|
(in-package :scopes/forge)
|
|
|
|
;;; iseq: iterable sequence
|
|
|
|
(defclass iseq ()
|
|
((start :reader start :initform (list nil) :initarg :start)
|
|
(cur :accessor cur)
|
|
(end :accessor end)))
|
|
|
|
(defun make-iseq (&optional start)
|
|
(let* ((start (cons nil start))
|
|
(seq (make-instance 'iseq :start start)))
|
|
(setf (cur seq) (setf (end seq) start))
|
|
seq))
|
|
|
|
(defun isq-all (seq)
|
|
(cdr (start seq)))
|
|
|
|
(defun isq-cur (seq)
|
|
(car (cur seq)))
|
|
|
|
(defun isq-end (seq)
|
|
(car (end seq)))
|
|
|
|
(defun isq-next (seq)
|
|
(pop (cur seq))
|
|
(car (cur seq)))
|
|
|
|
(defun isq-add (seq v)
|
|
(setf (cdr (end seq)) (list v))
|
|
(pop (end seq)))
|
|
|
|
;;; forge environment
|
|
|
|
(defclass forge-env ()
|
|
((data-stack :initform nil :accessor data-stack)
|
|
(words :initform (make-hash-table))
|
|
(comp-words :initform (make-hash-table))
|
|
(packages :initform '(:sf-user :sf-builtin))
|
|
(current-package :initform :sf-user)
|
|
(rp :initform (make-iseq))
|
|
(cp :initform (make-iseq))
|
|
(ip :initform (make-iseq))))
|
|
|
|
(defun forge-env ()
|
|
(let ((fe (make-instance 'forge-env)))
|
|
fe))
|
|
|
|
(defvar *forge-env* (forge-env))
|
|
|
|
(defun dstack()
|
|
(data-stack *forge-env*))
|
|
|
|
(defun define-comp-word (sym fn)
|
|
(setf (gethash sym (slot-value *forge-env* 'comp-words)) fn))
|
|
|
|
(defun register (p sym fn)
|
|
(let ((w (intern (string sym) p)))
|
|
(setf (gethash w (slot-value *forge-env* 'words)) fn)))
|
|
|
|
;;; builtins
|
|
|
|
(defun reg-b (sym fn)
|
|
(register :sf-builtin sym fn))
|
|
|
|
(reg-b 'add #'(lambda () (pushd (+ (popd) (popd)))))
|
|
|
|
(reg-b 'dup #'(lambda () (pushd (car (dstack)))))
|
|
|
|
(defvar lit #'(lambda () (pushd (isq-next (fip)))))
|
|
(reg-b 'lit lit)
|
|
|
|
(define-comp-word 'def
|
|
#'(lambda ()
|
|
(let* ((name (isq-next (frp)))
|
|
(code (isq-next (frp))))
|
|
(print name)
|
|
(print code)
|
|
;(eval `(defvar ,name (comp code)))
|
|
)))
|
|
|
|
;;; compiler, interpreter
|
|
|
|
(defun exec-str (s)
|
|
(exec (read-from-string
|
|
(concatenate 'string "(" s ")"))))
|
|
|
|
(defun repl ()
|
|
(do ((input (read-line) (read-line))) ((string= input "q") nil)
|
|
(exec-str input)))
|
|
|
|
(defun exec (code)
|
|
(call (comp code)))
|
|
|
|
(defun call (code)
|
|
(let ((ip (make-iseq code)))
|
|
(setf (slot-value *forge-env* 'ip) ip)
|
|
(do ((item (isq-next ip) (isq-next ip)))
|
|
((null item))
|
|
(funcall item))))
|
|
|
|
(defun comp (slist)
|
|
(setf (slot-value *forge-env* 'cp) (make-iseq))
|
|
(let ((inp (make-iseq slist)))
|
|
(setf (slot-value *forge-env* 'rp) inp)
|
|
(do ((item (isq-next inp) (isq-next inp)))
|
|
((null item))
|
|
(typecase item
|
|
(symbol (comp-symbol item))
|
|
(cons (comp-item lit) (comp-item (comp item)))
|
|
(t (comp-item lit) (comp-item item)))))
|
|
(isq-all (fcp)))
|
|
|
|
(defun comp-symbol (sym)
|
|
(let* ((w (get-word sym))
|
|
(comp-fn (get-comp-word w)))
|
|
(if comp-fn
|
|
(funcall comp-fn)
|
|
(comp-item (gethash w (slot-value *forge-env* 'words))))))
|
|
|
|
(defun comp-item (item)
|
|
(isq-add (fcp) item))
|
|
|
|
(defun get-word (sym)
|
|
(let ((name (string sym)))
|
|
(dolist (p (slot-value *forge-env* 'packages))
|
|
(let ((w (find-symbol name p)))
|
|
(if w
|
|
(return-from get-word w))))
|
|
(intern name (slot-value *forge-env* 'current-package))))
|
|
|
|
(defun get-comp-word (w)
|
|
(gethash w (slot-value *forge-env* 'comp-words)))
|
|
|
|
;;; internal definitions
|
|
|
|
(defun popd () (pop (data-stack *forge-env*)))
|
|
|
|
(defun pushd (v) (push v (data-stack *forge-env*)))
|
|
|
|
(defun frp () (slot-value *forge-env* 'rp))
|
|
|
|
(defun fcp () (slot-value *forge-env* 'cp))
|
|
|
|
(defun fip () (slot-value *forge-env* 'ip))
|