#lang racket
(require rackunit racket/unsafe/ops )
(provide (all-defined-out))
(define (new-state)
(values (make-vector 30000 0)
0))
(define-syntax-rule (raise-range-errors! a-state caller-name stx)
(if stx
(raise-syntax-error #f "pointer went out of range of data" stx)
(error caller-name "pointer went out of range of data")))
(define-syntax-rule (increment-ptr data ptr stx)
(begin
(set! ptr (unsafe-fx+ ptr 1))
(when (unsafe-fx>= ptr (unsafe-vector-length data))
(raise-range-errors! a-state 'increment-ptr stx))))
(define-syntax-rule (decrement-ptr data ptr stx)
(begin
(set! ptr (unsafe-fx- ptr 1))
(when (unsafe-fx< ptr 0)
(raise-range-errors! a-state 'decrement-ptr stx))))
(define-syntax-rule (increment-byte data ptr)
(unsafe-vector-set! data ptr (unsafe-fx+ (unsafe-vector-ref data ptr) 1)))
(define-syntax-rule (decrement-byte data ptr)
(unsafe-vector-set! data ptr (unsafe-fx- (unsafe-vector-ref data ptr) 1)))
(define-syntax-rule (write-byte-to-stdout data ptr)
(write-byte (unsafe-vector-ref data ptr) (current-output-port)))
(define-syntax-rule (read-byte-from-stdin data ptr)
(unsafe-vector-set! data ptr (let ([v (read-byte (current-input-port))])
(if (eof-object? v)
0
v))))
(define-syntax-rule (loop data ptr body ...)
(let loop ()
(unless (unsafe-fx= (unsafe-vector-ref data ptr)
0)
body ...
(loop))))
(let-values ([(data ptr) (new-state)])
(increment-byte data ptr)
(check-equal? 1 (vector-ref data 0))
(increment-byte data ptr)
(check-equal? 2 (vector-ref data 0))
(decrement-byte data ptr)
(check-equal? 1 (vector-ref data 0)))
(let-values ([(data ptr) (new-state)])
(increment-ptr data ptr #f)
(increment-byte data ptr)
(check-equal? 0 (vector-ref data 0))
(check-equal? 1 (vector-ref data 1))
(decrement-ptr data ptr #f)
(increment-byte data ptr)
(check-equal? 1 (vector-ref data 0))
(check-equal? 1 (vector-ref data 1)))
(let-values ([(data ptr) (new-state)])
(parameterize ([current-input-port
(open-input-bytes (bytes 3 1 4))])
(read-byte-from-stdin data ptr)
(increment-ptr data ptr #f)
(read-byte-from-stdin data ptr)
(increment-ptr data ptr #f)
(read-byte-from-stdin data ptr))
(check-equal? 3 (vector-ref data 0))
(check-equal? 1 (vector-ref data 1))
(check-equal? 4 (vector-ref data 2)))
(let-values ([(data ptr) (new-state)])
(set! data (vector 80 76 84))
(let ([simulated-stdout (open-output-string)])
(parameterize ([current-output-port simulated-stdout])
(write-byte-to-stdout data ptr)
(increment-ptr data ptr #f)
(write-byte-to-stdout data ptr)
(increment-ptr data ptr #f)
(write-byte-to-stdout data ptr))
(check-equal? "PLT" (get-output-string simulated-stdout))))
(let-values ([(data ptr) (new-state)])
(set! data (vector 0 104 101 108 112 109 101 105
109 109 101 108 116 105 110 103 ))
(set! ptr 15)
(loop data ptr
(loop data ptr (decrement-byte data ptr))
(decrement-ptr data ptr #f))
(check-equal? 0 ptr)
(check-equal? (make-vector 16 0) data))