cl-forth/interpreter.lisp

82 lines
3.1 KiB
Common Lisp
Raw Permalink Normal View History

2024-09-06 10:58:05 -04:00
(in-package #:cl-forth)
(defparameter *compile-mode* nil)
(defparameter *current-definition* nil)
(define-condition unknown-word-error (error)
((token :initarg :token
:accessor token-of))
(:report (lambda (condition stream)
(format stream "Unknown word: ~a.~&" (token-of condition)))))
(defclass interpreter ()
((current-token :accessor current-token-of)
(stack :initform (make-instance 'stack)
:accessor stack-of)
(words :initform (make-hash-table :test 'equal)
:accessor words-of)
(line :accessor line-of)))
(defmethod initialize-instance :after ((interp interpreter) &key &allow-other-keys)
"Initialize a new interpreter with basic operations."
(setf (gethash "+" (words-of interp)) #'add
(gethash "-" (words-of interp)) #'subtract
(gethash "*" (words-of interp)) #'multiply
(gethash "/" (words-of interp)) #'divide
(gethash "mod" (words-of interp)) #'mod
(gethash "dup" (words-of interp)) #'dup
(gethash "swap" (words-of interp)) #'swap
(gethash "negate" (words-of interp)) #'negate
(gethash "drop" (words-of interp)) #'drop
(gethash "over" (words-of interp)) #'over
(gethash "rot" (words-of interp)) #'rot)
interp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod evaluate-line ((obj interpreter))
(let ((tokens (split-string #\Space (line-of obj))))
(dolist (token tokens)
(cond
((equal token ":")
(setf *compile-mode* t))
((equal token ";")
(setf *compile-mode* nil)
;; save the definition
(setf (gethash (car *current-definition*) (words-of obj))
(reverse (cdr *current-definition*))
*current-definition* nil))
(*compile-mode*
(if (null *current-definition*)
;; First token is the name of the new word
(setf *current-definition* (list token))
;; Subsequent tokens are the body of the word
(push token (cdr *current-definition*))))
(t
(setf (current-token-of obj) token)
(execute-word obj))))))
(defmethod execute-word ((obj interpreter))
(let ((token (current-token-of obj)))
(cond
((digit-char-p (elt token 0))
;; Push numbers onto the stack
(push-to-stack (stack-of obj) (parse-integer token)))
((gethash token (words-of obj))
;; If token is in dictionary, execute it
(let ((definition (gethash token (words-of obj))))
(if (functionp definition)
;; If it's a primitive word, just execute it
(funcall definition obj)
;; If it's a user-defined word, interpret each token in it
(dolist (word definition)
(setf (current-token-of obj) word)
(execute-word obj)))))
(t
(error 'unknown-word-error :token token)))))
(defmethod interpret ((obj interpreter) program)
"Interpret a PROGRAM using the INTERP."
(setf (line-of obj) program)
(evaluate-line obj)
(format t "Stack after execution: ~a~%" (stack-of obj)))