You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
741 lines
24 KiB
Scheme
741 lines
24 KiB
Scheme
6 years ago
|
;; Expands to variables prepended with enum name and a period (.). See
|
||
|
;; enum 'err' for an example.
|
||
|
(define-syntax enum
|
||
|
(lambda (x r c)
|
||
|
(let ((name (symbol->string (cadr x))))
|
||
|
`(,(r 'begin)
|
||
|
,@(map (lambda (var/val)
|
||
|
`(,(r 'define) ,(string->symbol
|
||
|
(string-append name
|
||
|
"."
|
||
|
(symbol->string (car var/val))))
|
||
|
,(cadr var/val)))
|
||
|
(caddr x))))))
|
||
|
|
||
|
(define-syntax define*
|
||
|
(syntax-rules ()
|
||
|
((_ x)
|
||
|
(syntax-error "invalid define* form " x))
|
||
|
((_ var val)
|
||
|
(define var val))
|
||
|
((_ var val rest ...)
|
||
|
(begin (define var val)
|
||
|
(define* rest ...)))))
|
||
|
|
||
|
;; nan: not a number
|
||
|
;; nap: not a pair
|
||
|
;; nar: not a record
|
||
|
;; example: err.nan-add
|
||
|
(enum err
|
||
|
((nan-add 0)
|
||
|
(nan-sub 1)
|
||
|
(nan-mult 2)
|
||
|
(nan-quotient 3)
|
||
|
(nan-remainder 4)
|
||
|
(nap-car 5)
|
||
|
(nap-cdr 6)
|
||
|
(nap-set-car! 7)
|
||
|
(nap-set-cdr! 8)
|
||
|
(unknown-inst 9)
|
||
|
(bad-arg-count-apply 10)
|
||
|
(ran-out-of-fuel 11)
|
||
|
(nar-record-type 12)
|
||
|
(nar-record-value 13)
|
||
|
(nan-untag-number 14)
|
||
|
(primitive-not-found 15)
|
||
|
(operation-not-found 16)
|
||
|
(variable-not-found 17)
|
||
|
(type-not-found 18)
|
||
|
(oom 19)
|
||
|
(nap-record-type 20) ; for record objs
|
||
|
))
|
||
|
|
||
|
(define* pair-mask #b1111
|
||
|
pair-spec #b0000
|
||
|
number-mask #b1
|
||
|
number-spec #b1 ; also in tag-number define-records macro
|
||
|
record-mask #b10
|
||
|
record-spec #b10)
|
||
|
|
||
|
;; NOTE! if word-size is not half of block-size update the algorithm
|
||
|
;; in (cons).
|
||
|
(define* *word-size* 8 ; bytes
|
||
|
*block-size* 16) ; bytes
|
||
|
|
||
|
;;;;;;; PreScheme ;;;;;;;
|
||
|
(cond-expand
|
||
|
(prescheme
|
||
|
(define* halt (external "PS_HALT" (=> () null))
|
||
|
vm-init (external "vm_init" (=> () null)))
|
||
|
|
||
|
; also in tag-number define-records macro
|
||
|
(define (twos-complement obj)
|
||
|
(if (>= obj 0)
|
||
|
(shift-left obj 1)
|
||
|
(shift-left (+ (bitwise-not (abs obj)) 1) 1)))
|
||
|
|
||
|
; also in tag-number define-records macro
|
||
|
(define* (tag-number obj) (bitwise-xor (twos-complement obj) number-spec)
|
||
|
(tag-record obj) (bitwise-xor (shift-left obj 2) record-spec)
|
||
|
(tag-nil) (tag-record (tag-number 0)) ; TODO check if prescheme inlines this
|
||
|
(nil) (tag-nil))
|
||
|
|
||
|
(define (matches-spec? obj mask spec) (= (bitwise-and obj mask) spec))
|
||
|
|
||
|
(define* (pair? obj) (matches-spec? obj pair-mask pair-spec)
|
||
|
(number? obj) (matches-spec? obj number-mask number-spec)
|
||
|
(record? obj) (matches-spec? obj record-mask record-spec)
|
||
|
(nil? obj) (= obj (tag-nil))) ; TODO make sure prescheme inlines
|
||
|
|
||
|
(define (untag-number obj)
|
||
|
(if (>= obj 0)
|
||
|
(arithmetic-shift-right obj 1)
|
||
|
(- 0 (+ (arithmetic-shift-right (bitwise-not obj) 1) 1))))
|
||
|
(define (untag-record obj) (arithmetic-shift-right obj 2))
|
||
|
|
||
|
(define *error* 0)
|
||
|
(define *error-obj* 999)
|
||
|
|
||
|
(define-syntax assert
|
||
|
(syntax-rules ()
|
||
|
((_ test good err-num print-obj err-val)
|
||
|
(if test good (begin (set! *error* err-num) (set! *error-obj* print-obj) err-val)))))
|
||
|
|
||
|
(define (untag-number/chk n)
|
||
|
(assert (number? n) (untag-number n) err.nan-untag-number n 0))
|
||
|
;; TODO check for overflows
|
||
|
;; TODO this doesn't work for negative numbers!
|
||
|
(define (twos-complement-add n n2)
|
||
|
(assert (and (number? n) (number? n2)) (+ n n2 -1) err.nan-add (list n n2) 0))
|
||
|
|
||
|
(define (twos-complement-sub n n2)
|
||
|
(assert (and (number? n) (number? n2)) (+ (- n n2) 1) err.nan-sub (list n n2) 0))
|
||
|
|
||
|
(define (mult n1 n2)
|
||
|
(assert (and (number? n1) (number? n2))
|
||
|
(tag-number (* (untag-number n1) (untag-number n1)))
|
||
|
err.nan-mult
|
||
|
(list n1 n2)
|
||
|
0))
|
||
|
|
||
|
(define (quotient/chk n1 n2)
|
||
|
(assert (and (number? n1) (number? n2))
|
||
|
(tag-number (quotient (untag-number n1) (untag-number n2)))
|
||
|
err.nan-quotient
|
||
|
(list n1 n2)
|
||
|
0))
|
||
|
(define (remainder/chk n1 n2)
|
||
|
(assert (and (number? n1) (number? n2))
|
||
|
(tag-number (remainder (untag-number n1) (untag-number n2)))
|
||
|
err.nan-remainder
|
||
|
(list n1 n2)
|
||
|
0))
|
||
|
|
||
|
;; (define (display-error n)
|
||
|
;; (unsigned-byte-set! (integer->address 753664) 69)
|
||
|
;; (unsigned-byte-set! (integer->address (+ 753664 2)) (+ n 48)))
|
||
|
|
||
|
(define (display-error n obj)
|
||
|
(terminal:put-char #\E)
|
||
|
(terminal:put-number n)
|
||
|
(terminal:put-char #\<)
|
||
|
(print* obj)
|
||
|
(terminal:put-char #\>)
|
||
|
(terminal:put-char #\space))
|
||
|
|
||
|
(define *alloc-address* null-address)
|
||
|
(define *end-of-memory* 0)
|
||
|
(define *oom-pair* 1) ; ??? 0 test as a "tagged" pair, so we aren't going with that
|
||
|
|
||
|
;; cons overshoots *end-of-memory* by block size
|
||
|
(define (cons a b)
|
||
|
(let ((start (address->integer *alloc-address*)))
|
||
|
(assert (< start *end-of-memory*)
|
||
|
(begin
|
||
|
(word-set! *alloc-address* a)
|
||
|
(word-set! (address+ *alloc-address* *word-size*) b)
|
||
|
(set! *alloc-address* (address+ *alloc-address* *block-size*))
|
||
|
start)
|
||
|
err.oom (tag-number -1) *oom-pair*)))
|
||
|
|
||
|
(define (car pair)
|
||
|
(assert (pair? pair) (word-ref (integer->address pair)) err.nap-car pair (tag-nil)))
|
||
|
(define (cdr pair)
|
||
|
(assert (pair? pair) (word-ref (address+ (integer->address pair) *word-size*))
|
||
|
err.nap-cdr pair (tag-nil)))
|
||
|
|
||
|
(define (car/chk pair)
|
||
|
(assert (pair? pair) (word-ref (integer->address pair)) err.nap-car pair (tag-nil)))
|
||
|
(define (cdr/chk pair)
|
||
|
(assert (pair? pair) (word-ref (address+ (integer->address pair) *word-size*))
|
||
|
err.nap-cdr pair (tag-nil)))
|
||
|
|
||
|
(define (set-car! pair val)
|
||
|
(assert (pair? pair) (word-set! (integer->address pair) val)
|
||
|
;; TODO find a better way to get prescheme to have the
|
||
|
;; correct return value for the error condition.
|
||
|
err.nap-set-car! (list pair val) (word-set! (integer->address #xffff00) 0)))
|
||
|
(define (set-cdr! pair val)
|
||
|
(assert (pair? pair) (word-set! (address+ (integer->address pair) *word-size*) val)
|
||
|
err.nap-set-cdr! (list pair val) (word-set! (integer->address #xffff00) 0)))
|
||
|
|
||
|
(define (alloc bytes)
|
||
|
(let ((beginning-of-block (address->integer *alloc-address*)))
|
||
|
(set! *alloc-address*
|
||
|
(address+ *alloc-address*
|
||
|
(if (<= bytes *block-size*)
|
||
|
*block-size*
|
||
|
(+ bytes (- *block-size* (remainder bytes *block-size*))))))
|
||
|
beginning-of-block))
|
||
|
|
||
|
(define (record-obj type val)
|
||
|
(cons (tag-record type) val))
|
||
|
|
||
|
(define (record-obj? obj)
|
||
|
(and (pair? obj) (record? (car obj))))
|
||
|
|
||
|
;; If OBJ is a pair that points to a record return its type.
|
||
|
(define (record-obj-type obj)
|
||
|
(assert (pair? obj)
|
||
|
(let ((rec (car obj)))
|
||
|
(assert (record? rec) rec
|
||
|
err.nar-record-type rec 0))
|
||
|
err.nap-record-type obj 0))
|
||
|
|
||
|
;; If OBJ is a pair that points to a record return its value.
|
||
|
(define (record-obj-value obj)
|
||
|
(assert (pair? obj)
|
||
|
(assert (record? (car obj)) (cdr obj)
|
||
|
err.nar-record-value obj 0)
|
||
|
err.nap-record-type obj 0))
|
||
|
|
||
|
(define null? nil?)
|
||
|
|
||
|
(define-syntax list
|
||
|
(syntax-rules ()
|
||
|
((_) (tag-nil))
|
||
|
((_ element)
|
||
|
(cons element (tag-nil)))
|
||
|
((_ element r)
|
||
|
(cons element (list r)))
|
||
|
((_ element r ...)
|
||
|
(cons element (list r ...)))))
|
||
|
|
||
|
(define (list-tail x k)
|
||
|
(if (= k 0)
|
||
|
x
|
||
|
(list-tail (cdr x) (- k 1))))
|
||
|
|
||
|
(define (list-ref lis k)
|
||
|
(car (list-tail lis k)))
|
||
|
|
||
|
(define (reverse lis)
|
||
|
(if (null? lis)
|
||
|
lis
|
||
|
(let loop ((remaining lis) (new-head (tag-nil)))
|
||
|
(if (pair? (cdr remaining))
|
||
|
(loop (cdr remaining)
|
||
|
(cons (car remaining) new-head))
|
||
|
(cons (car remaining) new-head)))))
|
||
|
|
||
|
(define (cadr x) (car (cdr x))) (define (caddr x) (car (cdr (cdr x))))
|
||
|
(define (cadddr x) (car (cdr (cdr (cdr x)))))
|
||
|
(define (cddr x) (cdr (cdr x))) (define (cdddr x) (cdr (cdr (cdr x))))
|
||
|
) ; end cond-expand prescheme
|
||
|
|
||
|
|
||
|
(else ; cond-expand else
|
||
|
|
||
|
(define (twos-complement obj)
|
||
|
(if (>= obj 0)
|
||
|
obj
|
||
|
(+ (bitwise-not (abs obj)) 1)))
|
||
|
(define (tag-number obj)
|
||
|
(bitwise-xor (arithmetic-shift (twos-complement obj) 1) number-spec))
|
||
|
(define (untag-number obj)
|
||
|
(if (>= obj 0)
|
||
|
(arithmetic-shift obj -1)
|
||
|
(- 0 (+ (arithmetic-shift (bitwise-not obj) -1) 1))))
|
||
|
(define untag-number/chk untag-number)
|
||
|
(define (twos-complement-add n n2)
|
||
|
(+ n n2 -1))
|
||
|
(define (twos-complement-sub n n2)
|
||
|
(+ (- n n2) 1))
|
||
|
|
||
|
(define quotient/chk quotient)
|
||
|
(define remainder/chk remainder)
|
||
|
|
||
|
;;; If you want easier debugging.
|
||
|
;; (define (twos-complement-add n n2) (+ n n2))
|
||
|
;; (define (twos-complement-sub n n2) (- n n2))
|
||
|
;; (define (tag-number n) n)
|
||
|
;; (define (untag-number n) n)
|
||
|
(define (tag-nil) '())
|
||
|
(define (nil) (tag-nil))
|
||
|
(define nil? null?)
|
||
|
(define-record-type record (tag-record value) record?
|
||
|
(value untag-record))
|
||
|
|
||
|
(define-record-printer record
|
||
|
(lambda (obj port)
|
||
|
(print "#<record " (untag-record obj) ">")))
|
||
|
|
||
|
(define (record-obj type val)
|
||
|
(cons (tag-record type) val))
|
||
|
|
||
|
(define (record-obj? obj)
|
||
|
(and (pair? obj) (record? (car obj))))
|
||
|
|
||
|
;; If OBJ is a pair that points to a record return its type.
|
||
|
(define (record-obj-type obj)
|
||
|
(if (record-obj? obj)
|
||
|
(car obj)
|
||
|
(error "not a record obj")))
|
||
|
|
||
|
;; If OBJ is a pair that points to a record return its value.
|
||
|
(define (record-obj-value obj)
|
||
|
(if (record-obj? obj)
|
||
|
(cdr obj)
|
||
|
(error "not a record obj")))
|
||
|
|
||
|
(define (word-set! addr val) (void))
|
||
|
(define (unsigned-byte-set! addr val)
|
||
|
(display val) (void))
|
||
|
(define (integer->address n) n)
|
||
|
|
||
|
(define *error* 0)
|
||
|
(define *error-obj* 999)
|
||
|
(define (put-number n) (display n))
|
||
|
(define (put-char c) (display c))
|
||
|
|
||
|
(define (halt) '())
|
||
|
(define (initialize) '())
|
||
|
|
||
|
(define-syntax goto
|
||
|
(syntax-rules ()
|
||
|
((_ body ...)
|
||
|
(begin (body ...)))))
|
||
|
|
||
|
(define (display-error n obj)
|
||
|
(error (conc "VM error: " n "<" obj ">")))
|
||
|
|
||
|
(define (mult n1 n2) (tag-number (* (untag-number n1) (untag-number n2))))
|
||
|
|
||
|
)) ; end cond-expand
|
||
|
|
||
|
(define-syntax define-records
|
||
|
(lambda (x r c)
|
||
|
;; TODO move iota and conc in to another pre-scheme module and
|
||
|
;; import for syntax
|
||
|
(define (iota k . start)
|
||
|
(define (iota* k i)
|
||
|
(if (< i k)
|
||
|
(cons i (iota* k (+ i 1)))
|
||
|
'()))
|
||
|
(let ((start (if (null? start) 0 (car start))))
|
||
|
(iota* (+ k start) start)))
|
||
|
(define (conc . r)
|
||
|
(apply string-append (map (lambda (x) (if (symbol? x) (symbol->string x) x)) r)))
|
||
|
(define (tag-number n)
|
||
|
(bitwise-xor (twos-complement n) #b1))
|
||
|
(define (twos-complement obj)
|
||
|
(if (>= obj 0)
|
||
|
(arithmetic-shift obj 1)
|
||
|
(arithmetic-shift (+ (bitwise-not (abs obj)) 1) 1)))
|
||
|
`(begin
|
||
|
,@(map
|
||
|
(lambda (inst n)
|
||
|
(let* ((has-args (list? inst))
|
||
|
(name (if has-args (car inst) inst))
|
||
|
(args (if has-args (cdr inst) '())))
|
||
|
`(begin (,(r 'define) (,(string->symbol (conc 'make- name)) ,@args)
|
||
|
,(if has-args
|
||
|
`(record-obj ,n (list ,@args))
|
||
|
`(tag-record ,n)))
|
||
|
,(if has-args
|
||
|
`(,(r 'define) (,(string->symbol (string-append (symbol->string name) "-type?")) ,(r 'obj))
|
||
|
(,(r 'and) (record-obj? ,(r 'obj))
|
||
|
(,(r '=) (untag-record (record-obj-type ,(r 'obj))) ,n)))
|
||
|
`(,(r 'define) (,(string->symbol (string-append (symbol->string name) "-type?")) ,(r 'obj))
|
||
|
(,(r 'and) (record? ,(r 'obj))
|
||
|
(,(r '=) (untag-record ,(r 'obj)) ,n))))
|
||
|
,@(if has-args
|
||
|
(map (lambda (arg slot)
|
||
|
`(,(r 'define) (,(string->symbol
|
||
|
(conc name '- arg))
|
||
|
,(r 'obj))
|
||
|
(list-ref (record-obj-value ,(r 'obj)) ,slot)))
|
||
|
args (iota (length args)))
|
||
|
'()))))
|
||
|
(cdr x) (map tag-number (iota (length (cdr x)) 1)))))) ; start at 1, 0 is nil
|
||
|
|
||
|
(define-records
|
||
|
variable ;; TODO not used?
|
||
|
quote
|
||
|
assign
|
||
|
call/cc
|
||
|
(symbol char-list)
|
||
|
type
|
||
|
value
|
||
|
tag
|
||
|
untag
|
||
|
eq?
|
||
|
nil
|
||
|
env
|
||
|
word-set!
|
||
|
word-ref
|
||
|
byte-set!
|
||
|
byte-ref
|
||
|
alloc
|
||
|
add
|
||
|
sub
|
||
|
mult
|
||
|
quotient
|
||
|
remainder
|
||
|
abs
|
||
|
cons
|
||
|
car
|
||
|
cdr
|
||
|
eval
|
||
|
;; (macro proc)
|
||
|
(operative vars dyn-var has-dyn-var? proc static-env)
|
||
|
(applicative obj)
|
||
|
wrap
|
||
|
unwrap
|
||
|
set-car!
|
||
|
set-cdr!
|
||
|
primitive
|
||
|
apply
|
||
|
record
|
||
|
vau
|
||
|
type-eq?
|
||
|
)
|
||
|
|
||
|
(cond-expand (prescheme 0) (else (define symbol-type? symbol?)))
|
||
|
|
||
|
(define (list-of-values exps env)
|
||
|
(if (null? exps)
|
||
|
(nil)
|
||
|
(cons (ev (car exps) env)
|
||
|
(list-of-values (cdr exps) env))))
|
||
|
|
||
|
(define (apply-primitive-proc proc args env)
|
||
|
;; TODO check num args
|
||
|
;; In process of converting from all primitives having all args
|
||
|
;; being evaled to just some of them.
|
||
|
(cond ((add-type? proc) (twos-complement-add (ev (car args) env)
|
||
|
(ev (car (cdr args)) env)))
|
||
|
((sub-type? proc) (twos-complement-sub (ev (car args) env)
|
||
|
(ev (car (cdr args)) env)))
|
||
|
((car-type? proc) (car/chk (ev (car args) env)))
|
||
|
((cdr-type? proc) (cdr/chk (ev (car args) env)))
|
||
|
((env-type? proc) env)
|
||
|
((eval-type? proc) (ev (car args) (ev (car (cdr args)) env)))
|
||
|
;; ((list-of-values-type? proc))
|
||
|
((cons-type? proc) (cons (ev (car args) env) (ev (car (cdr args)) env)))
|
||
|
((nil-type? proc) (nil))
|
||
|
((wrap-type? proc) (make-applicative (ev (car args) env)))
|
||
|
;; TODO this doesn't even make sense. Someone could
|
||
|
;; (tag-record -1) and then this would say it is a number!
|
||
|
;; ((type-type? proc) (cond ((number? (car args)) (tag-number -1))
|
||
|
;; ((record? (car args))
|
||
|
;; (untag-record (car args)))
|
||
|
;; ((nil? (car args)) (tag-number -2))
|
||
|
;; ((pair? (car args)) (tag-number -3)) ; TODO possible?
|
||
|
;; (else
|
||
|
;; (assert #f -3 err.type-not-found (tag-number -99)))))
|
||
|
((value-type? proc)
|
||
|
(let ((args (list-of-values args env)))
|
||
|
(cond ((record? (car args)) (untag-record (car args)))
|
||
|
((or (number? (car args)) (pair? args))
|
||
|
(car args))
|
||
|
(else
|
||
|
(assert #f -3 err.type-not-found (car args) (tag-number -98))))))
|
||
|
((record-type? proc) (tag-record (ev (car args) env)))
|
||
|
;; ((tag-type? proc) (record (car args) (car (cdr args))))
|
||
|
;; ((untag-type? proc) (cond ((number? (car args)) (car args))
|
||
|
;; ((record? (car args)) (record-value (car args)))))
|
||
|
;; ((set-car!-type? proc) (set-car! (car args) (car (cdr args))))
|
||
|
((byte-set!-type? proc)
|
||
|
(unsigned-byte-set!
|
||
|
(integer->address (untag-number/chk (ev (car args) env)))
|
||
|
(untag-number/chk (ev (car (cdr args)) env)))
|
||
|
(tag-number 0))
|
||
|
((eq?-type? proc)
|
||
|
(if (= (ev (car args) env) (ev (car (cdr args)) env))
|
||
|
(car (cdr (cdr args)))
|
||
|
(car (cdr (cdr (cdr args))))))
|
||
|
((type-eq?-type? proc)
|
||
|
(let ((a (ev (car args) env))
|
||
|
(b (ev (cadr args) env)))
|
||
|
(cond ((and (pair? a) (pair? b))
|
||
|
(car (cdr (cdr args))))
|
||
|
((and (record? a) (record? b)
|
||
|
(= (untag-record a) (untag-record b)))
|
||
|
(car (cdr (cdr args))))
|
||
|
((= a b)
|
||
|
(car (cdr (cdr args))))
|
||
|
(else (car (cdr (cdr (cdr args))))))))
|
||
|
(else
|
||
|
args
|
||
|
;; (assert #f #f err.primitive-not-found (tag-number 0))
|
||
|
))
|
||
|
)
|
||
|
|
||
|
(define (ev-sequence exps env)
|
||
|
(if (null? (cdr exps))
|
||
|
(ev (car exps) env)
|
||
|
(begin (ev (car exps) env)
|
||
|
(ev-sequence (cdr exps) env))))
|
||
|
|
||
|
;; (define (ev-apply proc args env)
|
||
|
;; (if (pair? proc)
|
||
|
;; (ev-sequence (cdr (cdr proc)) (extend-environment (car (cdr proc)) args (car proc)))
|
||
|
;; (apply-primitive-proc proc args env)))
|
||
|
|
||
|
(define (ev-args args env)
|
||
|
(let loop ((args args) (res (nil)))
|
||
|
(if (null? args)
|
||
|
(reverse res) ;; TODO store applicative and/or operative args
|
||
|
;; in reverse so we don't have to do a reverse here
|
||
|
(loop (cdr args) (cons (ev (car args) env) res)))))
|
||
|
|
||
|
(define (empty-list seed)
|
||
|
(let loop ((acc (nil)) (seed seed))
|
||
|
(if (null? seed) acc (loop (cons (nil) acc) (cdr seed)))))
|
||
|
|
||
|
(define (make-frame variables values)
|
||
|
(cons variables values))
|
||
|
(define (frame-variables frame) (car frame))
|
||
|
(define (frame-values frame) (cdr frame))
|
||
|
(define (add-binding-to-frame! var val frame)
|
||
|
(set-car! frame (cons var (car frame)))
|
||
|
(set-cdr! frame (cons val (cdr frame))))
|
||
|
|
||
|
(define (assignment? op) (assign-type? op))
|
||
|
(define (assignment-variable exp) (car (cdr exp)))
|
||
|
(define (assignment-value exp) (car (cdr (cdr exp))))
|
||
|
|
||
|
(define (self-evaluating? exp)
|
||
|
(or (number? exp) (nil? exp) (operative-type? exp) (applicative-type? exp)
|
||
|
(and (not (symbol-type? exp)) (record? exp))))
|
||
|
|
||
|
(define (extend-environment vars vals base-env)
|
||
|
(cons (make-frame vars vals) base-env))
|
||
|
|
||
|
(define (find-variable var env)
|
||
|
(define (env-loop env)
|
||
|
(define (scan vars vals)
|
||
|
(cond ((null? vars)
|
||
|
(env-loop (cdr env)))
|
||
|
((eq? var (car vars))
|
||
|
(values vals #t))
|
||
|
(else (scan (cdr vars) (cdr vals)))))
|
||
|
(if (null? env)
|
||
|
(values var #f)
|
||
|
(let ((frame (car env)))
|
||
|
(scan (frame-variables frame)
|
||
|
(frame-values frame)))))
|
||
|
(env-loop env))
|
||
|
|
||
|
(define (lookup-variable-value var env)
|
||
|
(receive (vals found) (find-variable var env)
|
||
|
(if found
|
||
|
(car vals)
|
||
|
(begin
|
||
|
;; (display-error var)
|
||
|
(display-error err.variable-not-found var)
|
||
|
err.variable-not-found))))
|
||
|
|
||
|
(define (set-variable-value! var val env)
|
||
|
(receive (vals found) (find-variable var env)
|
||
|
(if found
|
||
|
(set-car! vals val)
|
||
|
(add-binding-to-frame! var val (car env)))
|
||
|
(nil)))
|
||
|
|
||
|
(define-syntax guard
|
||
|
(syntax-rules (*error*)
|
||
|
((_ body ...)
|
||
|
(if (= *error* 0)
|
||
|
(begin body ...)
|
||
|
(begin
|
||
|
(display-error *error* *error-obj*)
|
||
|
(tag-number 999))))))
|
||
|
|
||
|
(define (extend-operative-environment op x e)
|
||
|
(let* ((not-list-arg (pair? (car (operative-vars op))))
|
||
|
(operative-var-list
|
||
|
(if not-list-arg
|
||
|
(operative-vars op)
|
||
|
(list (operative-vars op))))
|
||
|
(vals (if not-list-arg (cdr x) (list (cdr x)))))
|
||
|
(if (= (operative-has-dyn-var? op) 1)
|
||
|
(extend-environment (cons (operative-dyn-var op)
|
||
|
operative-var-list)
|
||
|
(cons e vals)
|
||
|
(operative-static-env op))
|
||
|
(extend-environment operative-var-list
|
||
|
vals
|
||
|
(operative-static-env op)))))
|
||
|
|
||
|
(define (construct-operative x e)
|
||
|
(if (nil? (cdr (car x)))
|
||
|
(make-operative (car (car x)) (nil) 0 (cdr x) e)
|
||
|
;; Include dynamic environment.
|
||
|
(make-operative (car (car x)) (car (cdr (car x))) 1 (cdr x) e)))
|
||
|
|
||
|
(define (ev x e)
|
||
|
(guard
|
||
|
(cond ((self-evaluating? x) x)
|
||
|
((symbol-type? x) (lookup-variable-value x e))
|
||
|
(else
|
||
|
(let ((op (ev (car x) e)))
|
||
|
(guard
|
||
|
(cond ((assignment? op)
|
||
|
(set-variable-value! (car (cdr x)) (ev (car (cdr (cdr x))) e) e))
|
||
|
((operative-type? op)
|
||
|
(ev-sequence
|
||
|
(operative-proc op)
|
||
|
(extend-operative-environment op x e)))
|
||
|
((applicative-type? op)
|
||
|
(ev (cons (applicative-obj op) (ev-args (cdr x) e)) e))
|
||
|
((vau-type? op)
|
||
|
(construct-operative (cdr x) e))
|
||
|
(else (apply-primitive-proc op (cdr x) e)))))))))
|
||
|
|
||
|
;; (define *prim-env*
|
||
|
;; (extend-environment
|
||
|
;; '(+ ev vau eval wrap set! nil cons env)
|
||
|
;; `(,(make-add)
|
||
|
;; ,(make-eval)
|
||
|
;; ,(make-vau)
|
||
|
;; ,(make-eval)
|
||
|
;; ,(make-wrap)
|
||
|
;; ,(make-assign)
|
||
|
;; ,(make-nil)
|
||
|
;; ,(make-cons)
|
||
|
;; ,(make-env))
|
||
|
;; (nil)))
|
||
|
|
||
|
;; (ev '((vau (elements) elements) 1 2 3)
|
||
|
;; *prim-env*)
|
||
|
|
||
|
;; ;; (ev '(set! quote* (vau ((x)) x))
|
||
|
;; ;; *prim-env*)
|
||
|
;; ;; (ev '(quote* xyz)
|
||
|
;; ;; *prim-env*)
|
||
|
|
||
|
(cond-expand (prescheme 0)
|
||
|
(else
|
||
|
(define (precompile-sexp x)
|
||
|
(cond ((number? x) (tag-number x))
|
||
|
((symbol? x)
|
||
|
(make-symbol
|
||
|
(map (compose tag-number char->integer)
|
||
|
(string->list (symbol->string x)))))))
|
||
|
(precompile-sexp 'set!)))
|
||
|
|
||
|
(define *env* #x220000)
|
||
|
;; (define *oblist* #x220010)
|
||
|
(define *exps* #x220008)
|
||
|
|
||
|
(define s-add 0)
|
||
|
(define s-set! 0)
|
||
|
(define s-x 0)
|
||
|
|
||
|
(define (print-debug-table start words)
|
||
|
(let loop ((i 0))
|
||
|
(if (< i words)
|
||
|
(begin (terminal:put-number (+ start (* i 8)))
|
||
|
(terminal:put-char #\:)
|
||
|
(terminal:put-char #\space)
|
||
|
(terminal:put-number
|
||
|
(word-ref (integer->address (+ start (* i 8)))))
|
||
|
(terminal:put-char #\newline)
|
||
|
(loop (+ i 1))))))
|
||
|
|
||
|
(define (print* obj)
|
||
|
(cond ((number? obj)
|
||
|
(terminal:put-number (untag-number obj)))
|
||
|
((symbol-type? obj)
|
||
|
(let loop ((o (symbol-char-list obj)))
|
||
|
(terminal:put-char (ascii->char (untag-number (car/chk o))))
|
||
|
(if (not (nil? (cdr o)))
|
||
|
(loop (cdr/chk o)))))
|
||
|
((record? obj)
|
||
|
(terminal:put-char #\#)
|
||
|
(terminal:put-char #\<)
|
||
|
(terminal:put-number (untag-number (untag-record obj)))
|
||
|
(terminal:put-char #\>))
|
||
|
((pair? obj)
|
||
|
;; must not be in a function or prescheme could make it in to
|
||
|
;; a dependency loop
|
||
|
(terminal:put-char #\()
|
||
|
(let loop ((o obj))
|
||
|
(print* (car o))
|
||
|
(if (not (nil? (cdr o)))
|
||
|
(begin (terminal:put-char #\space)
|
||
|
(if (pair? (cdr o))
|
||
|
(loop (cdr o))
|
||
|
(begin (terminal:put-char #\.)
|
||
|
(terminal:put-char #\space)
|
||
|
(print* (cdr o)))))))
|
||
|
(terminal:put-char #\)))
|
||
|
((nil? obj)
|
||
|
(terminal:put-char #\()
|
||
|
(terminal:put-char #\)))
|
||
|
(else (terminal:put-char #\E))))
|
||
|
|
||
|
(define (run-ev)
|
||
|
(set! *alloc-address* (integer->address #x300000))
|
||
|
;; a single pair will be consed after this location
|
||
|
(set! *end-of-memory* #x1000000) ; 32 MiB
|
||
|
(set! *oom-pair* (cons (make-nil) (make-nil)))
|
||
|
|
||
|
|
||
|
(set! *env* (word-ref (integer->address *env*)))
|
||
|
(set! *exps* (word-ref (integer->address *exps*)))
|
||
|
|
||
|
(let loop ((exps (car *exps*)))
|
||
|
(if (null? (cdr exps))
|
||
|
(ev (car exps) *env*)
|
||
|
(begin (ev (car exps) *env*)
|
||
|
(loop (cdr exps)))))
|
||
|
|
||
|
)
|
||
|
|
||
|
(define (keyboard-handler key)
|
||
|
(+ key 0))
|
||
|
|
||
|
(define (kmain)
|
||
|
;; see note in prescheme.h
|
||
|
(vm-init)
|
||
|
;; (init)
|
||
|
|
||
|
(terminal:initialize)
|
||
|
|
||
|
|
||
|
(run-ev)
|
||
|
(let loop ()
|
||
|
(halt)
|
||
|
(loop))
|
||
|
)
|
||
|
|
||
|
;; NOTE!
|
||
|
;; dynamic variable binding can be accomplished by looking up a
|
||
|
;; variable in the dynamic environment instead of the lexical
|
||
|
;; environment that was stored when the procedure was created.
|
||
|
;; if one wants a variable to be dynamic scoped for calls later on it
|
||
|
;; can bind a variable in the static environment to the one in the
|
||
|
;; dynamic environment.
|
||
|
|
||
|
;; (define bar `(,(env) (x) (baz x)))
|
||
|
;; (bar (lookup-variable x (current-env))) ; x is dynamic for the
|
||
|
;; ; duration of the execution of
|
||
|
;; ; bar's body
|
||
|
|
||
|
;; NOTE! is it possible to do tail-recursion with the current
|
||
|
;; evaluator if the code in the tail position is always evaluated in
|
||
|
;; its own loop?
|