#lang racket
(require (only-in racket [equal? equal-case-sensitive?]))
(require "circular-stack.rkt" "forth_num_convert.rkt" "forth_read.rkt"
"rvector.rkt")
(provide (all-defined-out))
(define (equal? a b)
(if (and (string? a) (string? b))
(string-ci=? a b)
(equal-case-sensitive? a b)))
(define address-required-on-dstack '("next" "if" "-if"))
(define address-required (append '("jump" "call") address-required-on-dstack))
(define last-slot-instructions
'(";" "ret" "unext" "@p" "!p" "+*" "+" "dup" "." "nop"))
(define instructions-preceded-by-nops '("+" "+*"))
(define instructions-using-entire-word '(";" "ret" "ex" "unext"))
(define core%
(class object%
(super-new)
(field (dstack (make-stack 2 8 (integer->integer-bytes -1 4 #t)))
(rstack (make-stack 1 8 (integer->integer-bytes -1 4 #t)))
(pc 0)
(next-word 1)
(rega 0)
(regb 0)
(memory (make-rvector 100 ".")))))
(define interpreter%
(class object%
(super-new)
(field (num-cores 144)
(used-cores '())
(cores (make-vector num-cores))
(state-index 0)
(send-recv-table (make-rvector 100 -1)))
(for [(i (in-range 0 num-cores))]
(vector-set! cores i (new core%)))
(for ([i (in-range 100)])
(rvector-set! send-recv-table i #f))
(define/public (get name)
(with-handlers
([exn:fail:object?
(lambda (e)
(dynamic-get-field name
(vector-ref cores state-index)))])
(dynamic-get-field name this)))
(define/public (set name value)
(with-handlers
([exn:fail:object?
(lambda (e)
(dynamic-set-field! name
(vector-ref cores state-index)
value))])
(dynamic-set-field! name this value)))
(define/public (increment-pc!)
(if (= (remainder (get 'pc) 4) 3)
(begin (set 'pc (* 4 (get 'next-word)))
(set 'next-word (add1 (get 'next-word))))
(set 'pc (add1 (get 'pc)))))
(define/public (read-and-increment-pc!)
(let [(old-pc (get 'pc))]
(increment-pc!)
(rvector-ref (get 'memory) old-pc)))
(define/public (set-pc! addr)
(set 'pc (* 4 addr))
(set 'next-word (add1 addr)))
(define/public (single-step core)
(set 'state-index core)
(let [(memory (get 'memory))
(pc (get 'pc))]
(if (or (< pc 0) (>= pc (rvector-length memory)))
(set 'used-cores (remove core (get 'used-cores)))
(let [(name (read-and-increment-pc!))]
(unless (string? name)
(raise "Not a string -- single-step"))
(let [(proc (get-instruction-proc name))]
(if (member name address-required)
(proc this (read-and-increment-pc!))
(proc this)))))))
(define/public (step)
(for [(core (get 'used-cores))]
(single-step core)))
(define/public (interpret)
(step)
(unless (null? (get 'used-cores))
(interpret)))))
(define instructions (make-hash))
(define (is-instruction? name)
(hash-has-key? instructions name))
(define (add-instruction! name code)
(hash-set! instructions name code))
(define (get-instruction-proc name)
(and (is-instruction? name)
(hash-ref instructions name)))
(define directives (make-hash))
(define (is-directive? name)
(hash-has-key? directives name))
(define (add-directive! name code)
(hash-set! directives name code))
(define (get-directive-proc name)
(and (is-directive? name)
(hash-ref directives name)))
(define (make-instruction-synonym a b)
(let [(a-present (is-instruction? a))
(b-present (is-instruction? b))]
(cond [(or (and a-present b-present) (not (or a-present b-present)))
(raise "Cannot make synonym")]
[a-present
(add-instruction! b (hash-ref instructions a))]
[else
(add-instruction! a (hash-ref instructions b))])))
(define compiler%
(class object%
(super-new)
(field (location-counter 1)
(i-register 0)
(dict (make-hash))
(execute? #f)
(dstack (make-infinite-stack))
(interpreter (new interpreter%)))
(define/public (get name)
(with-handlers
([exn:fail:object?
(lambda (e)
(send interpreter get name))])
(dynamic-get-field name this)))
(define/public (set name value)
(with-handlers
([exn:fail:object?
(lambda (e)
(send interpreter set name value))])
(dynamic-set-field! name this value)))
(define/public (set-pc! value)
(send interpreter set-pc! value))
(define/public (increment-pc!)
(send interpreter increment-pc!))
(define/public (add-word! name code)
(hash-set! dict name code))
(define/public (get-word-address name)
(and (hash-has-key? dict name)
(hash-ref dict name)))
(define/public (add-compiled-data! data)
(let [(memory (get 'memory))
(i-register (get 'i-register))]
(unless (= (remainder i-register 4) 0)
(fill-rest-with-nops))
(rvector-set! memory (get 'i-register) data)
(set 'i-register (add1 (get 'i-register)))
(fill-rest-with-false)))
(define/public (add-compiled-code! elmt)
(let [(memory (get 'memory))]
(define (standard-compile! thing)
(rvector-set! memory i-register elmt)
(if (= (remainder i-register 4) 3)
(begin (set 'i-register (* 4 (get 'location-counter)))
(set 'location-counter
(add1 (get 'location-counter))))
(set 'i-register (add1 i-register))))
(cond [(not elmt)
(standard-compile! elmt)]
[(bytes? elmt)
(rvector-set! memory (* 4 (get 'location-counter)) elmt)
(for [(i (in-range 1 4))]
(rvector-set! memory (+ (* 4 (get 'location-counter)) i) #f))
(set 'location-counter (add1 (get 'location-counter)))
(add-compiled-code! "@p")]
[(string? elmt)
(when (or (and (member elmt instructions-preceded-by-nops)
(not (equal? (rvector-ref memory (sub1 i-register)) ".")))
(and (= (remainder i-register 4) 3)
(not (member elmt last-slot-instructions))))
(add-compiled-code! "."))
(standard-compile! elmt)]
[(number? elmt)
(when (not (member (rvector-ref memory (sub1 i-register)) address-required))
(raise "Tried to compile a number that was not an address --- add-compiled-code!"))
(standard-compile! elmt)
(fill-rest-with-false)]
[else (raise "Unknown thing to compile --- add-compiled-code!")])))
(define/public (compile-address! addr)
(add-compiled-code! addr))
(define/public (compile-constant! const)
(add-compiled-code! const))
(define/public (fill-rest-with-nops)
(unless (= (remainder i-register 4) 0)
(add-compiled-code! ".")
(fill-rest-with-nops)))
(define/public (fill-rest-with-false)
(unless (= (remainder i-register 4) 0)
(add-compiled-code! #f)
(fill-rest-with-false)))
(define/public (port->number str)
(cond
[(equal? str "up") (int->bytes 325)]
[(equal? str "down") (int->bytes 277)]
[(equal? str "left") (int->bytes 373)]
[(equal? str "right") (int->bytes 469)]
[(equal? str "io") (int->bytes 349)]
[else #f]))
(define/public (compile-loop)
(let [(token (forth_read))]
(unless (eof-object? token)
(unless (eq? token #\newline)
(compile-token token))
(compile-loop))))
(define/public (compile-token token)
(let [(directive (get-directive-proc token))
(instruction (get-instruction-proc token))
(address (get-word-address token))]
(cond [directive
(directive this)]
[(and instruction execute?)
(instruction (get 'interpreter))]
[instruction
(add-compiled-code! token)
(when (member token instructions-using-entire-word)
(fill-rest-with-nops))]
[address
(let [(nxt (forth_read))]
(if (equal? nxt ";")
(add-compiled-code! "jump")
(begin (forth_read 'put-back nxt)
(add-compiled-code! "call")))
(compile-address! address))]
[else
(let [(num (or (port->number token)
(string->bytes token)))]
(if num
(if execute?
(push-cells! (get 'dstack) num)
(compile-constant! num))
(raise (string-append token " ?"))))])))))
(define push-cells! push!)
(define (push-int! stack num)
(push-cells! stack (int->bytes num)))
(define pop-cells! pop!)
(define (pop-int! stack signed?)
(integer-bytes->integer (pop-cells! stack) signed? #t))
(define get-cells peek)
(define (get-int stack signed? [pos 0])
(integer-bytes->integer (get-cells stack pos) signed? #t))
(define (print-stack stack)
(define (loop pos)
(print (get-int stack #t pos))
(display " ")
(unless (= pos 0) (loop (sub1 pos))))
(display "| ")
(loop (sub1 (stack-length stack)))
(display ">"))