#! /usr/bin/env racket
#lang racket
(require srfi/1 srfi/13 srfi/48 2htdp/batch-io)
;SRFI 1: リストライブラリ
;https://w...content-available-to-author-only...s.com/ja/tech/srfi/srfi-1/srfi-1.html#drop
;SRFI 13: 文字列ライブラリ
;https://w...content-available-to-author-only...s.com/ja/tech/srfi/srfi-13/srfi-13.html
;SRFI 48: 中級の書式文字列
;https://w...content-available-to-author-only...s.com/ja/tech/srfi/srfi-48/srfi-48.html
;Batch Input/Output: "batch-io.rkt"
;https://d...content-available-to-author-only...g.org/teachpack/2htdpbatch-io.html#%28def._%28%28lib._2htdp%2Fbatch-io..rkt%29._read-file%29%29
;;; Stack machine for running whitespace programs
(define (vm prog stack cstack heap pc)
(let ((instr (list-ref prog pc)))
((doInstr prog stack cstack heap (+ pc 1)) instr)))
;; Running individual instructions
(define (doInstr prog stack cs heap pc)
(lambda (instr)
(case (car instr)
((Push) (let ((n (cadr instr)))
(vm prog (cons n stack) cs heap pc)))
((Dup) (let ((n (car stack)))
(vm prog (cons n stack) cs heap pc)))
((Ref) (let ((i (cadr instr)))
(vm prog
(cons (list-ref stack i) stack)
cs heap pc)))
((Slide) (let ((i (cadr instr)) (n (car stack)))
(vm prog
(cons n (drop (cdr stack) i))
cs heap pc)))
((Swap) (let ((n (car stack)) (m (cadr stack)))
(vm prog
(cons m (cons n (cddr stack)))
cs heap pc)))
((Discard) (vm prog (cdr stack) cs heap pc))
((Infix) (let ((op (cadr instr))
(y (car stack))
(x (cadr stack)))
(vm prog
(cons ((case op
((Plus) +)
((Minus) -)
((Times) *)
((Divide) /)
((Modulo) modulo))
x y) (cddr stack))
cs heap pc)))
((OutputChar) (let ((n (car stack)))
(write-char (integer->char n))
(flush-output)
(vm prog (cdr stack) cs heap pc)))
((ReadChar) (let ((loc (car stack))
(ch (read-char)))
(let ((hp
(store (char->integer ch) loc heap)))
(vm prog (cdr stack) cs hp pc))))
((ReadNum) (let ((loc (car stack))
(ch (read-line)))
(let ((num (string->number ch)))
(let ((hp (store num loc heap)))
(vm prog (cdr stack) cs hp pc)))))
((OutputNum) (let ((n (car stack)))
(write-string (number->string n))
(flush-output)
(vm prog (cdr stack) cs heap pc)))
((Label) (let ((_ (cadr instr)))
(vm prog stack cs heap pc)))
((Call) (let ((l (cadr instr)))
(let ((loc (findLabel l prog)))
(vm prog stack (cons pc cs) heap loc))))
((Jump) (let ((l (cadr instr)))
(let ((loc (findLabel l prog)))
(vm prog stack cs heap loc))))
((If) (let ((t (cadr instr))
(l (caddr instr))
(n (car stack)))
(if ((case t
((Zero) zero?)
((Negative) negative?)) n)
(let ((loc (findLabel l prog)))
(vm prog (cdr stack) cs heap loc))
(vm prog (cdr stack) cs heap pc))))
((Return) (let ((c (car cs)))
(vm prog stack (cdr cs) heap c)))
((Store) (let ((n (car stack)) (loc (cadr stack)))
(let ((hp (store n loc heap)))
(vm prog (cddr stack) cs hp pc))))
((Retrieve) (let ((loc (car stack)))
(let ((val (retrieve loc heap)))
(vm prog (cons val (cdr stack)) cs heap pc))))
((End) (format #t "Done.~%Stack size ~a~%Heap size ~a~%"
(length cs) (length heap)))
(else (error "Can't do " (car instr))))))
;; Digging out labels from wherever they are
(define (findLabel l p)
(let loop ((p p) (i 0))
(if (null? p)
(error "Undefined label" l)
(let ((m (car p)))
(if (and (eq? (car m) 'Label) (eq? (cadr m) l))
i
(loop (cdr p) (+ i 1)))))))
;; Heap management
(define (retrieve x heap) (list-ref heap x))
;(define (store x n Heap)
; (match (list x n Heap)
; ((list x 0 (cons h hs)) (cons x hs))
; ((list x n (cons h hs)) (let ((hp (store x (- n 1) hs)))
; (cons h hp)))
; ((list x 0 '()) (cons x '()))
; ((list x n '()) (let ((hp (store x (- n 1) '())))
; (cons 0 hp)))))
(define (store x n Heap)
(let ((h (length Heap)))
(if (< n h)
(let-values (((head tail) (split-at Heap n)))
(append head `(,x) (cdr tail)))
(append Heap (make-list (- n h) 0) `(,x)))))
;input to the whitespace VM.
;For convinience, three input characters
;#\space, #\tab, #\newline
;
;Numbers are binary (#\space = 0, #\tab = 1, #\newline = terminator)
;Strings are sequences of binary characters, terminated by #\newline .
;
;We have:
;
;* Stack instructions (Preceded by #\space)
; Push (Integer) #\space
; Dup #\newline #\space
; Swap #\newline #\tab
; Discart #\newline #\newline
;
;* Arithmetic (Preceded by #\tab #\space)
; Plus #\space #\space
; Minus #\space #\tab
; Times #\space #\newline
; Divide #\tab #\space
; Modulo #\tab #\tab
;
;* Heap access (Preceded by #\tab #\tab)
; Store #\space
; Retrieve #\tab
;
;* Control (Preceded by #\newline)
; Label Symbol #\space #\space
; Call Label #\space #\tab
; Jump Label #\space #\newline
; If Zero Label #\tab #\space
; If Neg Label #\tab #\tab
; Return #\tab #\newline
; End #\newline #\newline
;
;* IO instructions (Preceded by #\tab #\newline)
; OutputChar #\space #\space
; OutputNum #\space #\tab
; ReadChar #\tab #\space
; ReadNum #\tab #\tab
(define (execute fname)
(let ((prog (string-append (read-file fname) "\n")))
(let ((tokens (tokenize prog)))
(let ((runtime (parse tokens)))
(vm runtime '() '() '() 0)))))
(define (tokenize str)
(filter (lambda (x)
(or (char=? x #\space)
(char=? x #\tab)
(char=? x #\newline))) (string->list str)))
(define (parse ls)
(let loop ((ls ls) (acc '()))
(if (null? ls)
(reverse acc)
(match ls
((list #\space #\space xs ...)
(let-values (((num rest) (parseNumber xs)))
(loop rest (cons `(Push ,num) acc))))
((list #\space #\newline #\space xs ...)
(loop xs (cons '(Dup) acc)))
((list #\space #\tab #\space xs ...)
(let-values (((num rest) (parseNumber xs)))
(loop rest (cons `(Ref ,num) acc))))
((list #\space #\tab #\newline xs ...)
(let-values (((num rest) (parseNumber xs)))
(loop rest (cons `(Slide ,num) acc))))
((list #\space #\newline #\tab xs ...)
(loop xs (cons '(Swap) acc)))
((list #\space #\newline #\newline xs ...)
(loop xs (cons '(Discard) acc)))
((list #\tab #\space #\space #\space xs ...)
(loop xs (cons '(Infix Plus) acc)))
((list #\tab #\space #\space #\tab xs ...)
(loop xs (cons '(Infix Minus) acc)))
((list #\tab #\space #\space #\newline xs ...)
(loop xs (cons '(Infix Times) acc)))
((list #\tab #\space #\tab #\space xs ...)
(loop xs (cons '(Infix Divide) acc)))
((list #\tab #\space #\tab #\tab xs ...)
(loop xs (cons '(infix Modulo) acc)))
((list #\tab #\tab #\space xs ...)
(loop xs (cons '(Store) acc)))
((list #\tab #\tab #\tab xs ...)
(loop xs (cons '(Retrieve) acc)))
((list #\newline #\space #\space xs ...)
(let-values (((symbol rest) (parseSymbol xs)))
(loop rest (cons `(Label ,symbol) acc))))
((list #\newline #\space #\tab xs ...)
(let-values (((symbol rest) (parseSymbol xs)))
(loop rest (cons `(Call ,symbol) acc))))
((list #\newline #\space #\newline xs ...)
(let-values (((symbol rest) (parseSymbol xs)))
(loop rest (cons `(Jump ,symbol) acc))))
((list #\newline #\tab #\space xs ...)
(let-values (((symbol rest) (parseSymbol xs)))
(loop rest (cons `(If Zero ,symbol) acc))))
((list #\newline #\tab #\tab xs ...)
(let-values (((symbol rest) (parseSymbol xs)))
(loop rest (cons `(If Negative ,symbol) acc))))
((list #\newline #\tab #\newline xs ...)
(loop xs (cons '(Return) acc)))
((list #\newline #\newline #\newline xs ...)
(loop xs (cons '(End) acc)))
((list #\tab #\newline #\space #\space xs ...)
(loop xs (cons '(OutputChar) acc)))
((list #\tab #\newline #\space #\tab xs ...)
(loop xs (cons '(OutputNum) acc)))
((list #\tab #\newline #\tab #\space xs ...)
(loop xs (cons '(ReadChar) acc)))
((list #\tab #\newline #\tab #\tab xs ...)
(loop xs (cons '(ReadNum) acc)))
(else (error "Unrecognized input"))))))
(define (parseNumber ts)
(let loop ((ts ts) (acc '()))
(let ((x (car ts)) (rest (cdr ts)))
(if (char=? x #\newline)
(values (makeNumber acc) rest)
(loop rest (cons x acc))))))
(define (parseSymbol ts)
(let loop ((ts ts) (acc '()))
(let ((x (car ts)) (rest (cdr ts)))
(if (char=? x #\newline)
(values (makeSymbol acc) rest)
(loop rest (cons x acc))))))
(define (makeNumber t)
(let ((sign (last t)) (ls (reverse (take t (- (length t) 1)))))
(if (null? ls)
0
(let ((num (string->number
(list->string
(map (lambda (x)
(cond ((char=? x #\space) #\0)
((char=? x #\tab) #\1))) ls)) 2)))
(if (char=? sign #\space)
num
(- num))))))
(define (makeSymbol ls)
(let loop ((s (string-map (lambda (x)
(cond ((char=? x #\space) #\0)
((char=? x #\tab) #\1)))
(list->string (reverse ls))))
(acc '()))
(if (string-null? s)
(string->symbol (list->string (reverse acc)))
(loop (string-drop s 8)
(cons (integer->char (string->number (string-take s 8) 2))
acc)))))
(define (main args)
(if (not (= (length args) 1))
(usage)
(execute (car args))))
(define (usage)
(display "wspace with Racket 0.1 (c) 2021 Cametan\n")
(display "-------------------------------\n")
(display "Usage: wspace [file]\n"))
(main (vector->list (current-command-line-arguments)))