Skip to content

Instantly share code, notes, and snippets.

@ha2ne2
Created August 3, 2015 14:21
Show Gist options
  • Select an option

  • Save ha2ne2/5f1bd945d7719dbca5fe to your computer and use it in GitHub Desktop.

Select an option

Save ha2ne2/5f1bd945d7719dbca5fe to your computer and use it in GitHub Desktop.
;; scheme interpreter
;; PAIP p714より引用
;; CL-USER> (scheme)
;; ==> (set! fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1))))))
;; #<COMPILED-LEXICAL-CLOSURE (:INTERNAL INTERP) #x2100A5E22F>
;; ==> (fact 5)
;; 120
;; ==> ((if (= 1 1) * +) 3 4)
;; 12
(defun interp (x &optional env)
"Interpret (evaluate) the expression x in the envirionment env."
(cond
((symbolp x) (get-var x env))
((atom x) x)
((case (first x)
(QUOTE (second x))
(BEGIN (last1 (mapcar #'(lambda (y) (interp y env))
(rest x))))
(SET! (set-var! (second x) (interp (third x) env) env))
(IF (if (interp (second x) env)
(interp (third x) env)
(interp (fourth x) env)))
(LAMBDA (let ((parms (second x))
(code (maybe-add 'begin (rest2 x))))
#'(lambda (&rest args)
(interp code (extend-env parms args env)))))
(t ;; 手続き適用
(apply (interp (first x) env)
(mapcar #'(lambda (v) (interp v env))
(rest x))))))))
(defun rest2 (x)
"The rest of a list after the first TWO elements."
(rest (rest x)))
(defun set-var! (var val env)
"Set a variable to a value, in the given or global environment."
(if (assoc var env)
(setf (second (assoc var env)) val)
(set-global-var! var val))
val)
(defun get-var (var env)
"Get the value of a variable, from the given or global environment."
(if (assoc var env)
(second (assoc var env))
(get-global-var var)))
(defun set-global-var! (var val)
(setf (get var 'global-val) val))
(defun get-global-var (var)
(let* ((default "unbound")
(val (get var 'global-val default)))
(if (eq val default)
(error "Unbound scheme variable: ~a" var)
val)))
(defun extend-env (vars vals env)
"Add some variables and values to an environment."
(nconc (mapcar #'list vars vals) env))
(defparameter *scheme-procs*
'(+ - * / = < > <= >= cons car cdr not append list read member
(null? null) (eq? eq) (equal? equal) (eqv? eql)
(write prin1) (display princ) (newline terpri)))
(defun init-scheme-interp ()
"Initialize the scheme interpreter with some global variables."
(mapc #'init-scheme-proc *scheme-procs*)
(set-global-var! t t)
(set-global-var! nil nil))
(defun init-scheme-proc (f)
"Define a Scheme procedure as a corresponding CL function."
(if (listp f)
(set-global-var! (first f) (symbol-function (second f)))
(set-global-var! f (symbol-function f))))
(defun maybe-add (op exps &optional if-nil)
"For example, (maybe-add 'and exps t) returns
t if exps is nil, exps if there is only one,
and (and exp1 exp2...) if there are several exps."
(cond ((null exps) if-nil)
((length=1 exps) (first exps))
(t (cons op exps))))
(defun length=1 (x)
"Is x a list of length1?"
(and (consp x) (null (cdr x))))
(defun last1 (list)
"Return the last element (not last cons cell) of list"
(first (last list)))
(defun scheme (&optional x)
"A Scheme read-eval-print loop (using interp)"
(init-scheme-interp)
(loop (format t "~&==> ")
(print (interp (read) nil))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment