#lang racket
(require "forth_read.rkt" "forth_num_convert.rkt" "rvector.rkt")
(provide interpret run-file)
(define (displaynl arg)
(display arg)
(newline))
(define (displayspace arg)
(display arg)
(display " "))
(define stack (make-bytes 0))
(define rstack (make-bytes 0))
(define (set-appropriate-stack! type val)
(if (equal? type 'stack)
(set! stack val)
(set! rstack val)))
(define (push-cells! #:type [type 'stack] bstr [pos 0])
(let [(stack (if (equal? type 'stack) stack rstack))]
(set-appropriate-stack! type (bytes-append (subbytes stack 0 (* pos 4)) bstr (subbytes stack (* pos 4))))))
(define (push-int! #:type [type 'stack] num [pos 0])
(push-cells! #:type type (int->bytes num) pos))
(define (push-double! #:type [type 'stack] num [pos 0])
(push-cells! #:type type (double->bytes num) pos))
(define (get-cells #:stack [stack stack] [start 0] [end 1])
(if (< (bytes-length stack) (* end 4))
(raise "Stack underflow")
(subbytes stack (* start 4) (* end 4))))
(define (get-2cells #:stack [stack stack] [pos 0])
(get-cells #:stack stack pos (+ pos 2)))
(define (get-int #:stack [stack stack] signed? [pos 0])
(integer-bytes->integer (get-cells #:stack stack pos (+ pos 1)) signed? #t))
(define (get-double #:stack [stack stack] signed? [pos 0])
(integer-bytes->integer (get-2cells #:stack stack pos) signed? #t))
(define (pop-cells! #:type [type 'stack] [start 0] [end 1])
(let [(stack (if (equal? type 'stack) stack rstack))]
(if (< (bytes-length stack) (* end 4))
(raise "Stack underflow")
(let [(res (subbytes stack (* start 4) (* end 4)))]
(set-appropriate-stack! type (bytes-append (subbytes stack 0 (* start 4)) (subbytes stack (* end 4))))
res))))
(define (pop-2cells! #:type [type 'stack] [pos 0])
(pop-cells! #:type type pos (+ pos 2)))
(define (pop-int! #:type [type 'stack] signed? [pos 0])
(integer-bytes->integer (pop-cells! #:type type pos (+ pos 1)) signed? #t))
(define (pop-double! #:type [type 'stack] signed? [pos 0])
(integer-bytes->integer (pop-2cells! #:type type pos) signed? #t))
(define (print-stack stack)
(define (loop pos)
(if (>= pos 0)
(begin (print (get-int #:stack stack #t pos))
(display " ")
(loop (sub1 pos)))
(void)))
(display "| ")
(loop (sub1 (/ (bytes-length stack) 4)))
(display ">"))
(struct entry (primitive [precedence #:mutable] name [code #:mutable] [data #:mutable]))
(define dict (make-rvector 100))
(define visible_address 0)
(define next_address 1)
(define (add-entry! prim prec name code [data '()])
(let [(new (entry prim prec name code data))]
(rvector-set! dict next_address new)
(set! next_address (add1 next_address))
new))
(define codespace (make-rvector 500))
(define here-entry
(let [(addr next_address)]
(rvector-set! codespace 1 (lambda () (push-int! addr)))
(add-entry! #t #f "here" 1 2)))
(define (add-compiled-code! proc-or-addr)
(rvector-set! codespace (entry-data here-entry) proc-or-addr)
(set-entry-data! here-entry (add1 (entry-data here-entry))))
(define exit-addr 3)
(define (exit)
(pop-int! #:type 'rstack #f) (set! pc (pop-int! #:type 'rstack #f)))
(add-compiled-code! exit-addr)
(define (reveal-entry!)
(set! visible_address (sub1 next_address)))
(define (add-and-reveal-entry! prim prec name code data)
(let [(entry (add-entry! prim prec name code data))]
(reveal-entry!)
entry))
(define (add-word! prim prec name [data '()])
(add-and-reveal-entry! prim prec name (entry-data here-entry) '()))
(define (add-primitive-word! prec name code [data '()])
(add-word! #t prec name data)
(add-compiled-code! code)
(add-compiled-code! exit-addr)) (void (add-word! #f #f "exit"))
(add-compiled-code! exit)
(define (find-address name)
(define (loop address)
(let [(word (rvector-ref dict address))]
(cond [(string-ci=? name (entry-name word)) address]
[(= address 1) #f]
[else (loop (sub1 address))])))
(loop visible_address))
(define (find-entry name)
(let [(address (find-address name))]
(if address
(rvector-ref dict address)
#f)))
(define pc 1)
(define (code-loop)
(if (= pc 0)
'Exiting
(let [(code (rvector-ref codespace pc))]
(set! pc (add1 pc))
(with-handlers ([string? abort])
(if (number? code)
(execute-code code)
(code)))
(code-loop))))
(define (execute-code addr)
(push-int! #:type 'rstack pc) (set! pc addr))
(define execute (compose execute-code entry-code))
(add-primitive-word! #f "execute" (lambda () (execute (rvector-ref dict (pop-int! #f)))))
(define (abort msg)
(displaynl msg)
(set! stack (make-bytes 0))
(quit))
(define (quit)
(read-line) (set! rstack (make-bytes 0))
(set! pc interpreter-addr))
(add-primitive-word! #f "quit" quit)
(define (tick)
(push-int! (find-address (forth_read_no_eof))))
(add-primitive-word! #f "'" tick)
(define (interpret-proc)
(push-int! #:type 'rstack (sub1 pc)) (let [(name (forth_read))]
(if (eof-object? name)
(set! pc 0)
(if (eq? name #\newline)
(displaynl " ok")
(let [(entry (find-entry name))]
(if entry
(execute entry)
(let [(num (string->bytes name))]
(if num
(push-cells! num)
(raise (string-append name " ?"))))))))))
(add-primitive-word! #f "interpret" interpret-proc)
(define (interpret)
(set! stack (make-bytes 0))
(set! rstack (make-bytes 0))
(set! pc interpreter-addr)
(code-loop))
(define (colon-compiler)
(push-int! #:type 'rstack (sub1 pc)) (let [(to_compile (forth_read_no_eof))]
(if (not (eq? to_compile #\newline))
(let [(entry (find-entry to_compile))]
(cond [(not entry)
(let [(num (string->bytes to_compile))]
(if num
(add-compiled-code! (lambda () (push-cells! num)))
(raise (string-append to_compile " ?"))))]
[(entry-precedence entry)
(execute entry)]
[(entry-primitive entry)
(add-compiled-code! (rvector-ref codespace (entry-code entry)))]
[else
(add-compiled-code! (entry-code entry))] ))
(void))))
(add-primitive-word! #f "]" colon-compiler)
(define interpreter-addr (entry-code (find-entry "interpret")))
(define compiler-addr (entry-code (find-entry "]")))
(void (add-word! #f #t ":")) (add-compiled-code! (lambda () (add-entry! #f #f (forth_read_no_eof) (entry-data here-entry))))
(add-compiled-code! (entry-code (find-entry "]")))
(add-compiled-code! exit-addr)
(define (stop-compilation)
(define (loop pos)
(cond [(<= pos 0) (void)]
[(= (get-int #:stack rstack #f pos) compiler-addr)
(pop-double! #:type 'rstack #f pos)
(loop (- pos 2))] [else (loop (sub1 pos))]))
(loop (sub1 (/ (bytes-length rstack) 4))))
(add-primitive-word! #t "[" stop-compilation)
(void (add-word! #f #t ";")) (add-compiled-code! (lambda () (add-compiled-code! exit-addr)))
(add-compiled-code! reveal-entry!)
(add-compiled-code! stop-compilation)
(add-compiled-code! exit-addr)
(define (immediate)
(set-entry-precedence! (rvector-ref dict visible_address) #t))
(add-primitive-word! #f "immediate" immediate)
(define (postpone)
(let* [(name (forth_read_no_eof))
(entry (find-entry name))]
(cond [(not entry)
(raise (string-append name " ?"))]
[(entry-precedence entry)
(add-compiled-code! (entry-code entry))]
[else
(add-compiled-code! (lambda () (add-compiled-code! (entry-code entry))))])))
(add-primitive-word! #t "postpone" postpone)
(add-primitive-word! #f "reveal" reveal-entry!)
(add-primitive-word! #t "literal"
(lambda ()
(let [(num (pop-int! #t))]
(add-compiled-code! (lambda () (push-int! num))))))
(define (forget name)
(set! next_address (find-address name))
(set! visible_address (sub1 next_address)))
(add-primitive-word! #f "forget" (lambda () (forget (forth_read_no_eof))))
(define (marker name)
(let [(addr next_address)]
(add-primitive-word! #f name (lambda () (set! next_address addr) (set! visible_address (sub1 next_address))))))
(add-primitive-word! #f "marker" (lambda () (marker (forth_read_no_eof))))
(define (dummy-proc) (void))
(define (if-proc)
(add-compiled-code!
(lambda () (if (= (pop-int! #f) 0)
(void)
(set! pc (add1 pc)))))
(push-int! (entry-data here-entry))
(add-compiled-code! dummy-proc))
(add-primitive-word! #t "if" if-proc)
(define (else-proc)
(push-int! (entry-data here-entry) 1)
(add-compiled-code! dummy-proc)
(let [(here-addr (entry-data here-entry))]
(rvector-set! codespace (pop-int! #f) (lambda () (set! pc here-addr)))))
(add-primitive-word! #t "else" else-proc)
(define (then-proc)
(let [(here-addr (entry-data here-entry))]
(rvector-set! codespace (pop-int! #f) (lambda () (set! pc here-addr)))))
(add-primitive-word! #t "then" then-proc)
(define leave-stack '())
(define (resolve-leaves addr do-addr)
(if (or (null? leave-stack) (< (car leave-stack) do-addr)) (void)
(begin (rvector-set! codespace (car leave-stack) (lambda () (pop-double! #:type 'rstack #t)
(set! pc addr)))
(set! leave-stack (cdr leave-stack))
(resolve-leaves addr do-addr))))
(define (do-proc)
(add-compiled-code! (lambda () (push-cells! #:type 'rstack (pop-cells! 0 2))))
(push-int! (entry-data here-entry)))
(add-primitive-word! #t "do" do-proc)
(define (leave-proc)
(set! leave-stack (cons (entry-data here-entry) leave-stack))
(add-compiled-code! dummy-proc))
(add-primitive-word! #t "leave" leave-proc)
(define (loop-proc)
(let [(addr (pop-int! #f))]
(add-compiled-code!
(lambda ()
(if (= (add1 (get-int #:stack rstack #t)) (get-int #:stack rstack #t 1))
(pop-double! #:type 'rstack #t)
(begin (push-int! #:type 'rstack (add1 (pop-int! #:type 'rstack #t)))
(set! pc addr)))))
(resolve-leaves (entry-data here-entry) addr)))
(add-primitive-word! #t "loop" loop-proc)
(define (plusloop-proc)
(let [(addr (pop-int! #f))]
(add-compiled-code!
(lambda ()
(let [(n (pop-int! #t))
(old (pop-int! #:type 'rstack #t))
(limit (get-int #:stack rstack #t))]
(let [(new (+ n old))]
(if (and (< (min old new) limit) (>= (max old new) limit))
(pop-int! #:type 'rstack #t) (begin (push-int! #:type 'rstack new)
(set! pc addr)))))))
(resolve-leaves (entry-data here-entry) addr)))
(add-primitive-word! #t "+loop" plusloop-proc)
(add-primitive-word! #f "unloop" (lambda () (pop-double! #:type 'rstack #t)))
(add-primitive-word! #t "begin" (lambda () (push-int! (entry-data here-entry))))
(define (until-proc)
(let [(addr (pop-int! #f))]
(add-compiled-code! (lambda ()
(if (= (pop-int! #t) 0)
(set! pc addr)
(void))))))
(add-primitive-word! #t "until" until-proc)
(add-primitive-word! #t "while" if-proc)
(define (repeat-proc)
(let [(addr (pop-int! #f 1))] (add-compiled-code! (lambda () (set! pc addr)))
(then-proc)))
(add-primitive-word! #t "repeat" repeat-proc)
(define (comment)
(if (equal? (read-char) #\))
(void)
(comment)))
(add-primitive-word! #t "(" comment)
(define get-constant-value entry-data)
(define (constant)
(let* [(name (forth_read_no_eof))
(data (pop-cells!))]
(add-primitive-word! #f name
(lambda () (push-cells! data))
data)))
(add-primitive-word! #f "constant" constant)
(define (2constant)
(let* [(name (forth_read_no_eof))
(data (pop-2cells!))]
(add-primitive-word! #f name
(lambda () (push-cells! data))
data)))
(add-primitive-word! #f "2constant" 2constant)
(define (swap)
(let* [(arg1 (pop-cells!))
(arg2 (pop-cells!))]
(push-cells! arg1)
(push-cells! arg2)))
(add-primitive-word! #f "swap" swap)
(define (dup)
(push-cells! (get-cells))) (add-primitive-word! #f "dup" dup)
(define (over)
(push-cells! (get-cells 1 2)))
(add-primitive-word! #f "over" over)
(define (rot)
(push-cells! (pop-cells! 2 3)))
(add-primitive-word! #f "rot" rot)
(define (drop)
(pop-cells!))
(add-primitive-word! #f "drop" drop)
(define (2swap)
(let* [(arg1 (pop-2cells!))
(arg2 (pop-2cells!))]
(push-cells! arg1)
(push-cells! arg2)))
(add-primitive-word! #f "2swap" 2swap)
(define (2dup)
(push-cells! (get-cells 0 2))) (add-primitive-word! #f "2dup" 2dup)
(define (2over)
(push-cells! (get-cells 2 4)))
(add-primitive-word! #f "2over" 2over)
(define (2rot)
(push-cells! (pop-cells! 4 6)))
(add-primitive-word! #f "2rot" 2rot)
(define (2drop)
(pop-2cells!))
(add-primitive-word! #f "2drop" 2drop)
(add-primitive-word! #f ">r" (lambda () (push-cells! #:type 'rstack (pop-cells!))))
(add-primitive-word! #f "r>" (lambda () (push-cells! (pop-cells! #:type 'rstack))))
(add-primitive-word! #f "r@" (lambda () (push-cells! (get-cells #:stack rstack))))
(add-primitive-word! #f "i" (lambda () (push-cells! (get-cells #:stack rstack))))
(add-primitive-word! #f "j" (lambda () (push-cells! (get-cells #:stack rstack 2 3))))
(add-primitive-word! #f "2>r" (lambda () (push-cells! #:type 'rstack (pop-cells! 0 2))))
(add-primitive-word! #f "2r>" (lambda () (push-cells! (pop-cells! #:type 'rstack 0 2))))
(add-primitive-word! #f "2r@" (lambda () (push-cells! (get-cells #:stack rstack 0 2))))
(add-primitive-word! #f "+"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (+ arg1 arg2)))))
(add-primitive-word! #f "-"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (- arg2 arg1)))))
(add-primitive-word! #f "*"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (* arg1 arg2)))))
(add-primitive-word! #f "/"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (quotient arg2 arg1)))))
(add-primitive-word! #f "mod"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (remainder arg2 arg1)))))
(add-primitive-word! #f "/mod"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (remainder arg2 arg1))
(push-int! (quotient arg2 arg1)))))
(add-primitive-word! #f "*/"
(lambda ()
(let* [(n3 (pop-int! #t))
(n2 (pop-int! #t))
(n1 (pop-int! #t))
(intermediate (* n1 n2))]
(push-int! (quotient intermediate n3)))))
(add-primitive-word! #f "*/mod"
(lambda ()
(let* [(n3 (pop-int! #t))
(n2 (pop-int! #t))
(n1 (pop-int! #t))
(intermediate (* n1 n2))]
(push-int! (remainder intermediate n3))
(push-int! (quotient intermediate n3)))))
(add-primitive-word! #f "min"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (min arg2 arg1)))))
(add-primitive-word! #f "max"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (max arg2 arg1)))))
(add-primitive-word! #f "um*"
(lambda ()
(let* [(arg1 (pop-int! #f))
(arg2 (pop-int! #f))]
(push-double! (* arg2 arg1)))))
(add-primitive-word! #f "um/mod"
(lambda ()
(let* [(arg1 (pop-int! #f))
(arg2 (pop-double! #f))]
(push-int! (remainder arg2 arg1))
(push-int! (quotient arg2 arg1)))))
(add-primitive-word! #f "d+"
(lambda ()
(let* [(arg1 (pop-double! #t))
(arg2 (pop-double! #t))]
(push-double! (+ arg1 arg2)))))
(add-primitive-word! #f "d-"
(lambda ()
(let* [(arg1 (pop-double! #t))
(arg2 (pop-double! #t))]
(push-double! (- arg2 arg1)))))
(add-primitive-word! #f "." (lambda () (displayspace (pop-int! #t))))
(add-primitive-word! #f "u." (lambda () (displayspace (pop-int! #f))))
(add-primitive-word! #f "d." (lambda () (displayspace (pop-double! #t))))
(add-primitive-word! #f "du." (lambda () (displayspace (pop-double! #f))))
(define (read-string)
(define (iter lst)
(let [(new_char (read-char))]
(if (eq? new_char #\")
(list->string lst)
(iter (append lst (list new_char))))))
(iter '()))
(define (dot-quote)
(let [(str (read-string))]
(add-compiled-code! (lambda () (display str)))))
(add-primitive-word! #t ".\"" dot-quote)
(add-primitive-word! #f "cr" newline)
(add-primitive-word! #f "space" (lambda () (display " ")))
(define (spaces)
(define (loop num)
(if (= num 0)
'done
(begin (display " ") (loop (sub1 num)))))
(loop (pop-int! #f)))
(add-primitive-word! #f "spaces" spaces)
(add-primitive-word! #f "emit"
(lambda () (display (integer->char (pop-int! #f)))))
(define true -1)
(define false 0)
(add-primitive-word! #f "true" (lambda () (push-int! true)))
(add-primitive-word! #f "false" (lambda () (push-int! false)))
(add-primitive-word! #f ">"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (if (> arg2 arg1) true false)))))
(add-primitive-word! #f "<"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (if (< arg2 arg1) true false)))))
(add-primitive-word! #f "u<"
(lambda ()
(let* [(arg1 (pop-int! #f))
(arg2 (pop-int! #f))]
(push-int! (if (< arg2 arg1) true false)))))
(add-primitive-word! #f "="
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (if (= arg2 arg1) true false)))))
(add-primitive-word! #f "0=" (lambda () (push-int! (if (= (pop-int! #t) 0) true false))))
(add-primitive-word! #f "0<" (lambda () (push-int! (if (< (pop-int! #t) 0) true false))))
(add-primitive-word! #f "0>" (lambda () (push-int! (if (> (pop-int! #t) 0) true false))))
(add-primitive-word! #f "and"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (bitwise-and arg1 arg2)))))
(add-primitive-word! #f "or"
(lambda ()
(let* [(arg1 (pop-int! #t))
(arg2 (pop-int! #t))]
(push-int! (bitwise-ior arg1 arg2)))))
(add-primitive-word! #f "invert" (lambda () (push-int! (bitwise-not (pop-int! #t)))))
(add-primitive-word! #f "?stack" (lambda () false))
(add-primitive-word! #f "?dup" (lambda () (if (= 0 (get-int #f))
(void)
(push-cells! (get-cells)))))
(add-primitive-word! #t "abort\""
(lambda () (let [(str (read-string))]
(add-compiled-code!
(lambda () (if (= (pop-int! #t) false)
(void)
(raise str)))))))
(add-primitive-word! #f ".s" (lambda () (print-stack stack)))
(define (run-file name [out (current-output-port)])
(parameterize ([current-input-port (open-input-file name)]
[current-output-port out])
(interpret)
(close-input-port (current-input-port)))
(void))
(let [(out-str (open-output-string))]
(run-file "basewords.forth" out-str)
(close-output-port out-str))