61 lines
1.5 KiB
Common Lisp
61 lines
1.5 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 #:data-stack #:exec))
|
|
|
|
(in-package :scopes/forge)
|
|
|
|
(defvar *builtins* (make-hash-table :test 'equalp))
|
|
|
|
(defclass forge-env ()
|
|
((data-stack :initform nil
|
|
:reader data-stack
|
|
:accessor data-stack!)
|
|
(vocabulary :initform (list *builtins*)
|
|
:accessor vocabulary)))
|
|
|
|
(defun forge-env ()
|
|
(make-instance 'forge-env))
|
|
|
|
(defun exec (fe code)
|
|
(dolist (x code)
|
|
(if (symbolp x)
|
|
(funcall (find-word (vocabulary fe) x) fe)
|
|
(pushd fe x))))
|
|
|
|
(defun register (voc key fn)
|
|
(let ((k (if (symbolp key) (symbol-name key) key)))
|
|
(setf (gethash (string-downcase k) voc) fn)))
|
|
|
|
(defun find-word (vocab key)
|
|
(let ((k (string-downcase (symbol-name key))) (result nil))
|
|
(dolist (voc vocab)
|
|
(let ((v (gethash k voc)))
|
|
(if v (return v))))))
|
|
|
|
; built-in primitives
|
|
|
|
(defun reg-b (key fn) (register *builtins* key fn))
|
|
|
|
(reg-b 'add #'(lambda (fe) (pushd fe (+ (popd fe) (popd fe)))))
|
|
(reg-b 'mul #'(lambda (fe) (pushd fe (* (popd fe) (popd fe)))))
|
|
|
|
(reg-b 'dup #'(lambda (fe) (pushd fe (car (data-stack fe)))))
|
|
|
|
(reg-b 'def #'(lambda (fe)
|
|
(let ((voc (car(vocabulary fe)))
|
|
(name (popd fe))
|
|
(code (popd fe)))
|
|
(register voc name #'(lambda (fe) (exec fe code))))))
|
|
|
|
; internal definitions
|
|
|
|
(defun popd (fe)
|
|
(pop (data-stack! fe)))
|
|
|
|
(defun pushd (fe v)
|
|
(push v (data-stack! fe)))
|
|
|