#lang racket
(provide read-program)
(define names '#(ret ex jump call unext next if -if @p @+ @b @ !p !+ !b ! +*
2* 2/ - + and or drop dup pop over a nop push b! a!))
(define (to-opcode name)
(if (eof-object? name) (to-opcode 'nop)
(or (vector-member name names) (raise (format "~s is not a valid name!" name)))))
(define slot-3 '(ret +* unext + @p dup !p nop))
(define jumps '(jump call next if -if))
(define (read-18bit-word in)
(define (pack size instr . rest)
(if (null? rest)
(begin
(when (not (number? instr)) (raise (format "~s is not a valid jump address!" instr)))
(bitwise-bit-field instr 0 (+ size 5)))
(bitwise-ior (arithmetic-shift (to-opcode instr) size)
(apply pack (cons (- size 5) rest)))))
(let/cc end
(define a (read in))
(cond [(eof-object? a) (end #f)]
[(number? a) (end a)]
[(member a jumps) (end (pack 13 a (read in)))])
(define b (read in))
(when (member b jumps) (end (pack 13 a b (read in))))
(define c (read in))
(when (member c jumps) (end (pack 13 a b c (read in))))
(define d (read in))
(when (not (member d slot-3)) (raise (format "~s cannot go in the last slot!" d)))
(end (pack 13 a b c (bitwise-bit-field (to-opcode d) 2 5)))))
(define (read-program in)
(when (string? in) (set! in (open-input-string in)))
(define (go)
(let ([next (read-18bit-word in)])
(if next (cons next (go)) '())))
(go))