RosettaCodeData/Task/Compiler-virtual-machine-in.../Scheme/compiler-virtual-machine-in...

163 lines
6.1 KiB
Scheme

(import (scheme base)
(scheme char)
(scheme file)
(scheme process-context)
(scheme write)
(only (srfi 13) string-contains string-delete string-filter
string-replace string-tokenize))
(define *word-size* 4)
;; Mappings from operation symbols to internal procedures.
;; We define operations appropriate to virtual machine:
;; e.g. division must return an int, not a rational
;; boolean values are treated as numbers: 0 is false, other is true
(define *unary-ops*
(list (cons 'neg (lambda (a) (- a)))
(cons 'not (lambda (a) (if (zero? a) 1 0)))))
(define *binary-ops*
(let ((number-comp (lambda (op) (lambda (a b) (if (op a b) 1 0)))))
(list (cons 'add +)
(cons 'sub -)
(cons 'mul *)
(cons 'div (lambda (a b) (truncate (/ a b)))) ; int division
(cons 'mod modulo)
(cons 'lt (number-comp <))
(cons 'gt (number-comp >))
(cons 'le (number-comp <=))
(cons 'ge (number-comp >=))
(cons 'eq (lambda (a b) (if (= a b) 1 0)))
(cons 'ne (lambda (a b) (if (= a b) 0 1)))
(cons 'and (lambda (a b) ; make "and" work on numbers
(if (and (not (zero? a)) (not (zero? b))) 1 0)))
(cons 'or (lambda (a b) ; make "or" work on numbers
(if (or (not (zero? a)) (not (zero? b))) 1 0))))))
;; read information from file, returning vectors for data and strings
;; and a list of the code instructions
(define (read-code filename)
(define (setup-definitions str)
(values ; return vectors for (data strings) of required size
(make-vector (string->number (list-ref str 1)) #f)
(make-vector (string->number (list-ref str 3)) #f)))
(define (read-strings strings) ; read constant strings into data structure
(define (replace-newlines chars) ; replace newlines, obeying \\n
(cond ((< (length chars) 2) ; finished list
chars)
((and (>= (length chars) 3) ; preserve \\n
(char=? #\\ (car chars))
(char=? #\\ (cadr chars))
(char=? #\n (cadr (cdr chars))))
(cons (car chars)
(cons (cadr chars)
(cons (cadr (cdr chars))
(replace-newlines (cdr (cdr (cdr chars))))))))
((and (char=? #\\ (car chars)) ; replace \n with newline
(char=? #\n (cadr chars)))
(cons #\newline (replace-newlines (cdr (cdr chars)))))
(else ; keep char and look further
(cons (car chars) (replace-newlines (cdr chars))))))
(define (tidy-string str) ; remove quotes, map newlines to actual newlines
(list->string
(replace-newlines
(string->list
(string-delete #\" str))))) ; " (needed to satisfy rosettacode's scheme syntax highlighter)
;
(do ((i 0 (+ i 1)))
((= i (vector-length strings)) )
(vector-set! strings i (tidy-string (read-line)))))
(define (read-code)
(define (cleanup-code opn) ; tidy instructions, parsing numbers
(let ((addr (string->number (car opn)))
(instr (string->symbol (cadr opn))))
(cond ((= 2 (length opn))
(list addr instr))
((= 3 (length opn))
(list addr
instr
(string->number
(string-filter char-numeric? (list-ref opn 2)))))
(else ; assume length 4, jump instructions
(list addr instr (string->number (list-ref opn 3)))))))
;
(let loop ((result '()))
(let ((line (read-line)))
(if (eof-object? line)
(reverse (map cleanup-code result))
(loop (cons (string-tokenize line) result))))))
;
(with-input-from-file
filename
(lambda ()
(let-values (((data strings)
(setup-definitions (string-tokenize (read-line)))))
(read-strings strings)
(values data
strings
(read-code))))))
;; run the virtual machine
(define (run-program data strings code)
(define (get-instruction n)
(if (assq n code)
(cdr (assq n code))
(error "Could not find instruction")))
;
(let loop ((stack '())
(pc 0))
(let ((op (get-instruction pc)))
(case (car op)
((fetch)
(loop (cons (vector-ref data (cadr op)) stack)
(+ pc 1 *word-size*)))
((store)
(vector-set! data (cadr op) (car stack))
(loop (cdr stack)
(+ pc 1 *word-size*)))
((push)
(loop (cons (cadr op) stack)
(+ pc 1 *word-size*)))
((add sub mul div mod lt gt le eq ne and or)
(let ((instr (assq (car op) *binary-ops*)))
(if instr
(loop (cons ((cdr instr) (cadr stack) ; replace top two with result
(car stack))
(cdr (cdr stack)))
(+ pc 1))
(error "Unknown binary operation"))))
((neg not)
(let ((instr (assq (car op) *unary-ops*)))
(if instr
(loop (cons ((cdr instr) (car stack)) ; replace top with result
(cdr stack))
(+ pc 1))
(error "Unknown unary operation"))))
((jmp)
(loop stack
(cadr op)))
((jz)
(loop (cdr stack)
(if (zero? (car stack))
(cadr op)
(+ pc 1 *word-size*))))
((prtc)
(display (integer->char (car stack)))
(loop (cdr stack)
(+ pc 1)))
((prti)
(display (car stack))
(loop (cdr stack)
(+ pc 1)))
((prts)
(display (vector-ref strings (car stack)))
(loop (cdr stack)
(+ pc 1)))
((halt)
#t)))))
;; create and run virtual machine from filename passed on command line
(if (= 2 (length (command-line)))
(let-values (((data strings code) (read-code (cadr (command-line)))))
(run-program data strings code))
(display "Error: pass a .asm filename\n"))