8章のVM変換器完成

コンピュータシステムの理論と実装8章のVM変換器完成しました。
7章で作ったものに、関数の定義をできるようにしたり、goto文などを追加しています。

(use file.util)

(define (read-vfile file)
   (call-with-input-file file
    (lambda (i)
      (let loop ((c (read-line i)) (lis '()))
	(if (eof-object? c)
	    (reverse lis)
	    (loop (read-line i) (cons c lis)))))))

(define (vmtranslator1 path)
  (vm-file-name-set! path);スタティック変数の定義で使う
  (let1 lis (read-vfile path)
	(apply string-append 
	       (map translator lis))))


(define (translator str)
  (let1 command (parse (string->list str))
	(cond ((null? command) "")
	      ((equal? command '("add")) vm-add)
	      ((equal? command '("sub")) vm-sub)
	      ((equal? command '("neg")) vm-neg)
	      ((equal? command '("eq")) (vm-eq))
	      ((equal? command '("gt")) (vm-gt))
	      ((equal? command '("lt")) (vm-lt))
	      ((equal? command '("and")) vm-and)
	      ((equal? command '("or")) vm-or)
	      ((equal? command '("not")) vm-not)

	      ((vm-push-constant? command)
	       (push_constant (string->number (caddr command))))

	      ((vm-pop-local? command)
	       (vm-pop_ "LCL" (string->number (caddr command))))
	      ((vm-pop-argument? command)
	       (vm-pop_ "ARG" (string->number (caddr command))))
	      ((vm-pop-this? command)
	       (vm-pop_ "THIS" (string->number (caddr command))))
	      ((vm-pop-that? command)
	       (vm-pop_ "THAT" (string->number (caddr command))))
	      ((vm-pop-temp? command)
	       (vm-pop-temp (string->number (caddr command))))
	      ((vm-pop-pointer? command)
	       (vm-pop-pointer (string->number (caddr command))))
	      ((vm-pop-static? command)
	       (vm-pop-static (caddr command) vm-file-name))

	      ((vm-push-local? command)
	       (vm-push_ "LCL" (caddr command)))
	      ((vm-push-argument? command)
	       (vm-push_ "ARG" (caddr command)))
	      ((vm-push-this? command)
	       (vm-push_ "THIS" (caddr command)))
	      ((vm-push-that? command)
	       (vm-push_ "THAT" (caddr command)))
	      ((vm-push-temp? command)
	       (vm-push-temp (string->number (caddr command))))
	      ((vm-push-pointer? command)
	       (vm-push-pointer (string->number (caddr command))))
	      ((vm-push-static? command)
	       (vm-push-static (caddr command) vm-file-name))

	      ((vm-label? command) 
	       (vm-label (cadr command)))
	      ((vm-goto? command)
	       (vm-goto (cadr command)))
	      ((vm-if-goto? command)
	       (vm-if-goto (cadr command)))

	      ((vm-function? command)
	       (vm-function (cadr command) (caddr command)))

	      ((vm-return? command)
	       vm-return)

	      ((vm-call? command)
	       (vm-call (cadr command) (caddr command)))

	      (else (error str))
	      )))
(define (vmtranslator path)
  (cond ((and (path-extension path) (string=? (path-extension path) "vm"))
	 (call-with-output-file (path-swap-extension path "asm")
				(lambda (p)
				  (display (string-append
					     "@256\n"
					     "D=A\n"
					     "@SP\n"
					     "M=D\n"
					     (vm-call "Sys.init" "0")
					     vm-true
					     vm-false
					     (vmtranslator1 path) )
					   p))))
	(else 
	  (let1 file-list (remove  (lambda (s) (not (and (path-extension s) 
							 (string=? (path-extension s) "vm")))) 
				   (sys-readdir path))
		(call-with-output-file (path-swap-extension path "asm")
				       (lambda (p) 
					 (display (string-append
						    "@256\n"
						    "D=A\n"
						    "@SP\n"
						    "M=D\n"
						    (vm-call "Sys.init" "0")
						    vm-true
						    vm-false
						    )
						  p)
					 (for-each (lambda (s) 
						     (display 
						       (vmtranslator1 (string-append path "/" s)) 
						       p))
						   file-list)))))



	))
	 
(define (vm-push-constant? lst)
  (and (string=? (car lst) "push")
       (string=? (cadr lst) "constant")
       ((string->regexp "^\\d+$") (caddr lst))))

(define (vm-pop?-make sym)   
  (lambda (s)
    (and (string=? (car s) "pop")
	 (string=? (cadr s) sym)
	 ((string->regexp "\\d+$") (caddr s)))))

(define vm-pop-local? (vm-pop?-make "local"))
(define vm-pop-argument? (vm-pop?-make "argument"))
(define vm-pop-this? (vm-pop?-make "this"))
(define vm-pop-that? (vm-pop?-make "that"))
(define vm-pop-temp? (vm-pop?-make "temp"))
(define vm-pop-pointer? (vm-pop?-make "pointer"))
(define vm-pop-static? (vm-pop?-make "static"))

(define (vm-push?-make sym)   
  (lambda (s)
    (and (string=? (car s) "push")
	 (string=? (cadr s) sym)
	 ((string->regexp "\\d+$") (caddr s)))))

(define vm-push-local? (vm-push?-make "local"))
(define vm-push-argument? (vm-push?-make "argument"))
(define vm-push-this? (vm-push?-make "this"))
(define vm-push-that? (vm-push?-make "that"))
(define vm-push-temp? (vm-push?-make "temp"))
(define vm-push-pointer? (vm-push?-make "pointer"))
(define vm-push-static? (vm-push?-make "static"))

(define (vm-label? command)
  (string=? (car command) "label"))
    
(define (vm-label labe)
  (string-append "(" function-name "$" labe ")\n"))

(define (vm-goto? command)
  (string=? (car command) "goto"))

(define (vm-goto labe)
  (string-append "@" function-name "$" labe "\n"
		 "0;JMP\n"))

(define (vm-if-goto? command)
  (string=? (car command) "if-goto"))

(define (vm-if-goto labe)
  (string-append "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"
		 "@" function-name "$" labe "\n"
		 "D;JNE\n"))

(define (vm-call? command)
  (string=? (car command) "call"))

(define return-address-n 0)

(define (return-count!)
  (set! return-address-n (+ return-address-n 1)))

(define (make-return-address)
  (let1 return-address (string-append "return$" (number->string return-address-n))
	(return-count!)
	return-address))

(define (vm-call f n)
  (let ((push (lambda (s)
	       (string-append "@" s "\n"
			      "D=M\n"
			      "@SP\n"
			      "M=M+1\n"
			      "A=M-1\n"
			      "M=D\n")))
	(return-address (make-return-address)))
    (string-append
      "@" return-address "\n"
      "D=A\n"
      "@SP\n"
      "M=M+1\n"
      "A=M-1\n"
      "M=D\n"
      (apply string-append (map push (list "LCL" "ARG" "THIS" "THAT")))
      "@SP\n"
      "D=M\n"
      "@" n "\n"
      "D=D-A\n"
      "@5\n"
      "D=D-A\n"
      "@ARG\n"
      "M=D\n"
      "@SP\n"
      "D=M\n"
      "@LCL\n"
      "M=D\n"
      "@" f "\n"
      "0;JMP\n"
      "(" return-address ")\n")))

(define (vm-return? command)
  (string=? (car command) "return"))
(define vm-return
  (string-append
    "@LCL\n"
    "D=M\n"
    "@R13\n"
    "M=D\n"

    ;RET = *(FRAME-5)
    "@5\n"
    "D=A\n"
    "@R13\n"
    "A=M-D\n"
    "D=M\n"
    "@R14\n"
    "M=D\n"


    "@SP\n"
    "A=M-1\n"
    "D=M\n"
    "@ARG\n"
    "A=M\n"
    "M=D\n"

    "D=A+1\n"
    "@SP\n"
    "M=D\n"

    "@R13\n"
    "A=M-1\n"
    "D=M\n"
    "@THAT\n"
    "M=D\n"

    "@2\n"
    "D=A\n"
    "@R13\n"
    "A=M-D\n"
    "D=M\n"
    "@THIS\n"
    "M=D\n"

    "@3\n"
    "D=A\n"
    "@R13\n"
    "A=M-D\n"
    "D=M\n"
    "@ARG\n"
    "M=D\n"

    "@4\n"
    "D=A\n"
    "@R13\n"
    "A=M-D\n"
    "D=M\n"
    "@LCL\n"
    "M=D\n"

    ;goto RET
    "@R14\n"
    "A=M\n"
    "0;JMP\n"))

(define (skip-space str)
  (cond ((null? str) str) 
    	((or  (char=? (car str) #\space) (char=? (car str) #\tab))
      	 (skip-space (cdr str)))
	(else str)))


(define (parse1 str lis s)
  (cond
    ((null? str) (reverse 
		   (cons (list->string (reverse s)) lis)))
    ((or (char=? (car str) #\space) (char=? (car str) #\tab))
	 (parse1 (skip-space str) (cons (list->string (reverse s)) lis) '()))
    (else
      (parse1 (cdr str) lis (cons (car str) s)) 
	  

	)))

(define (parse str)
  (remove (lambda (a) (string=? a ""))
   	  (parse1 (skip-space (remove-coment str))'() '())))

;コメントを除去する
(define (remove-coment1 lis result)
  (cond ((null? lis) (reverse result))
	((char=? (car lis) #\/) (reverse result))
	(else
	 (remove-coment1 (cdr lis) (cons (car lis) result)))))

(define (remove-coment lis)
  (remove-coment1 lis '()))


(define t-f-label-n 0)


(define (count)
  (set! t-f-label-n (+ t-f-label-n 1)))

(define (t-f-label)
  (let1 label (string-append "$" (number->string t-f-label-n))
	(count)
	label))

(define (vm-function? command)
  (string=? (car command) "function"))

(define function-name "")

(define (vm-function fname k)
  (set! function-name fname)
  (string-append "(" fname ")\n"
		 (apply string-append 
			(map push_constant (make-list (string->number k) 0)))))

(define (push_constant n)
  (string-append "@" (number->string n)
		 "\nD=A"
		 "\n@SP"
		 "\nA=M"
		 "\nM=D"
		 "\n@SP"
		 "\nM=M+1\n"))
(define (vm-pop_ sym n)
  (string-append "@" (number->string n) "\n"
		 "D=A\n"
		 "@" sym "\n"
		 "D=M+D\n"
		 "@R13\n"
		 "M=D\n" ;r13にベースアドレス+nを保存
		 "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"

		 "@R13\n"
		 "A=M\n"
		 "M=D\n"))

(define (vm-push_ sym n)
  (string-append "@"  n "\n"
		 "D=A\n"
		 "@" sym "\n"
		 "A=M+D\n"
		 "D=M\n"
		 "@SP\n"
		 "A=M\n"
		 "M=D\n"
		 "D=A\n"
		 "@SP\n"
		 "M=D+1\n"))

(define (vm-pop-temp i)
  (if (or (< i 0) (> i 7))
    (error "pop temp i のiの値は0から7までです。")
    (string-append "@SP\n"
		   "M=M-1\n"
		   "A=M\n"
		   "D=M\n"
		   "@" (number->string (+ 5 i)) "\n"
		   "M=D\n")))

(define (vm-push-temp i)
  (if (or (< i 0) (> i 7))
    (error "push temp i のiの値は0から7までです。")
    (string-append "@" (number->string (+ 5 i)) "\n"
		   "D=M\n"
		   "@SP\n"
		   "A=M\n"
		   "M=D\n"
		   "@SP\n"
		   "M=M+1\n")))

(define (vm-pop-pointer i)
  (if (or (< i 0) (> i 1))
    (error "pop pointer i のiの値は0から1までです。")
    (string-append "@SP\n"
		   "M=M-1\n"
		   "A=M\n"
		   "D=M\n"
		   "@" (number->string (+ 3 i)) "\n"
		   "M=D\n")))

(define (vm-push-pointer i)
  (if (or (< i 0) (> i 1))
    (error "push temp i のiの値は0から1までです。")
    (string-append "@" (number->string (+ 3 i)) "\n"
		   "D=M\n"
		   "@SP\n"
		   "A=M\n"
		   "M=D\n"
		   "@SP\n"
		   "M=M+1\n")))

(define vm-file-name "")

(define (vm-file-name-set! path)
  (set! vm-file-name (get-file-name path)))

;ファイルのパスからファイル名だけを取り出す
(define (get-file-name path)
  (receive (p name k) 
	   (decompose-path path)
	   name))

(define (vm-pop-static i name)
  (string-append "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"
		 "@" name "." i "\n"
		 "M=D\n"))


(define (vm-push-static i name)
  (string-append "@" name "." i "\n"
		 "D=M\n"
		 "@SP\n"
		 "A=M\n"
		 "M=D\n"
		 "@SP\n"
		 "M=M+1\n"))
(define vm-neg
  (string-append "@SP\n"
		 "A=M-1\n"
		 "M=-M\n"))
(define vm-not
  (string-append "@SP\n"
		 "A=M-1\n"
		 "M=!M\n"))

(define vm-add
  (string-append "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"
		 "A=A-1\n"
		 "M=M+D\n"))
(define vm-sub
  (string-append "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"
		 "A=A-1\n"
		 "M=M-D\n"))
(define vm-and
  (string-append "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"
		 "A=A-1\n"
		 "M=M&D\n"))
(define vm-or
  (string-append "@SP\n"
		 "M=M-1\n"
		 "A=M\n"
		 "D=M\n"
		 "A=A-1\n"
		 "M=M|D\n"))


(define (vm-eq)
  (let1 r (t-f-label)
	(string-append
	 ;戻ってくるアドレスをR13に保存
	 "@" r "\n"
	 "D=A\n"
	 "@R13\n"
	 "M=D\n"
	 
	 ;ここからメイン
	 "@SP\n"
	 "M=M-1\n"
	 "A=M\n"
	 "D=M\n"
	 "A=A-1\n"
	 "D=M-D\n" ;x - y
	 "@TRUE\n"
	 "D;JEQ\n"
	 "@FALSE\n"
	 "0;JMP\n"
	 "(" r ")\n" ;ここに戻る
	 )))
(define (vm-gt)
  (let1 r (t-f-label)
	(string-append
	 ;戻ってくるアドレスをR13に保存
	 "@" r "\n"
	 "D=A\n"
	 "@R13\n"
	 "M=D\n"
	 
	 ;ここからメイン
	 "@SP\n"
	 "M=M-1\n"
	 "A=M\n"
	 "D=M\n"
	 "A=A-1\n"
	 "D=M-D\n" ;x - y
	 "@TRUE\n"
	 "D;JGT\n" ;Dが0より大きければジャンプ
	 "@FALSE\n"
	 "0;JMP\n"
	 "(" r ")\n" ;ここに戻る
	 )))

(define (vm-lt)
  (let1 r (t-f-label)
	(string-append
	 ;戻ってくるアドレスをR13に保存
	 "@" r "\n"
	 "D=A\n"
	 "@R13\n"
	 "M=D\n"
	 
	 ;ここからメイン
	 "@SP\n"
	 "M=M-1\n"
	 "A=M\n"
	 "D=M\n"
	 "A=A-1\n"
	 "D=M-D\n" ;x - y
	 "@TRUE\n"
	 "D;JLT\n" ;Dが0より小さければジャンプ
	 "@FALSE\n"
	 "0;JMP\n"
	 "(" r ")\n" ;ここに戻る
	 )))

;スタックにtrue(-1)を積む 	 
(define vm-true
  (string-append "(TRUE)\n"
                 "@SP\n"
		 "A=M-1\n"
		 "M=-1\n"
		 "@R13\n"
		 "A=M\n"
		 "0;JMP\n"))

;スタックにfalse(0)を積む
(define vm-false
  (string-append "(FALSE)\n"
                 "@SP\n"
		 "A=M-1\n"
		 "M=0\n"
		 "@R13\n"
		 "A=M\n"
		 "0;JMP\n"))