Compare commits
8 Commits
5915c42fe3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e2f4e3d746 | |||
| 3ad9159969 | |||
| 69b6ccbce0 | |||
| 7eb1ede3d9 | |||
| b5d3438e79 | |||
| 4f8459ae64 | |||
| 095ced6f03 | |||
| e31483a76e |
@@ -6,7 +6,7 @@
|
|||||||
#:use-module ((scheme base) #:select (write-bytevector))
|
#:use-module ((scheme base) #:select (write-bytevector))
|
||||||
#:export ((make-assembler* . make-assembler)
|
#:export ((make-assembler* . make-assembler)
|
||||||
assembler?
|
assembler?
|
||||||
assembler-pos
|
assembler-pos assembler-pos-set!
|
||||||
assembler-buf
|
assembler-buf
|
||||||
assembler-labels
|
assembler-labels
|
||||||
emit-label
|
emit-label
|
||||||
|
|||||||
@@ -13,8 +13,7 @@
|
|||||||
|
|
||||||
(define (lookup-instruction inst)
|
(define (lookup-instruction inst)
|
||||||
(define inst-obj (assq (or-alias inst) *instruction-set*))
|
(define inst-obj (assq (or-alias inst) *instruction-set*))
|
||||||
(if inst-obj
|
(or inst-obj
|
||||||
inst-obj
|
|
||||||
(error (format #f "could not find instruction ~a" inst))))
|
(error (format #f "could not find instruction ~a" inst))))
|
||||||
|
|
||||||
(define label? (negate pair?))
|
(define label? (negate pair?))
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
(define-module (scmvm language cps)
|
|
||||||
#:use-module (scmvm assembler)
|
|
||||||
#:use-module (ice-9 match)
|
|
||||||
#:export (compile-cps))
|
|
||||||
|
|
||||||
(define (compile-atom asm atom)
|
|
||||||
(cond
|
|
||||||
[(number? atom) (emit-literal asm atom)]))
|
|
||||||
|
|
||||||
(define (compile-cps asm soup)
|
|
||||||
(when (not (null? soup))
|
|
||||||
(match (car soup)
|
|
||||||
[(? (negate pair?) atom) (compile-atom asm atom)])
|
|
||||||
(compile-cps asm (cdr soup))))
|
|
||||||
194
scmvm/language/scheme.scm
Normal file
194
scmvm/language/scheme.scm
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
(define-module (scmvm language scheme)
|
||||||
|
#:use-module (scmvm assembler)
|
||||||
|
#:use-module (srfi srfi-1)
|
||||||
|
#:use-module (srfi srfi-26)
|
||||||
|
#:use-module (ice-9 match)
|
||||||
|
#:export (compile decompile ir-convert))
|
||||||
|
|
||||||
|
;; Scheme compiler
|
||||||
|
;; Scheme subset we're targeting
|
||||||
|
;; <prgm> ::= <top> ...
|
||||||
|
;; <top> ::= <def> | <exp>
|
||||||
|
;; <def> ::= (define <var> <exp>)
|
||||||
|
;; | (define (<var> <var> ...) <exp> ...)
|
||||||
|
;; <exp> ::= (lambda (<var> ...) <exp> ...)
|
||||||
|
;; | (if <exp> <exp> <exp>)
|
||||||
|
;; | (<exp> <exp> ...)
|
||||||
|
;; | (let ((<var> <exp>) ...) <exp> ...)
|
||||||
|
;; | (begin <exp> ...)
|
||||||
|
;; | <num> | <sym> | <var> | #t | #f
|
||||||
|
|
||||||
|
(define (atomic? x)
|
||||||
|
(or (number? x)
|
||||||
|
(symbol? x)
|
||||||
|
(boolean? x)))
|
||||||
|
|
||||||
|
(define (primitive? x)
|
||||||
|
(memq x '(+ - * / = < > <= >=)))
|
||||||
|
|
||||||
|
(define-syntax-rule (define-cps-loop name unit)
|
||||||
|
(define (name v* k)
|
||||||
|
(if (null? v*)
|
||||||
|
(k '())
|
||||||
|
(unit (car v*)
|
||||||
|
(lambda (t)
|
||||||
|
(name (cdr v*)
|
||||||
|
(lambda (t*)
|
||||||
|
(k (cons t t*)))))))))
|
||||||
|
|
||||||
|
;; Desugaring
|
||||||
|
;; Transforms to simplify the language
|
||||||
|
;; - lambdas and lets can only have 1 expression in body position
|
||||||
|
;; - define is always simple binds, function defs bind a lambda
|
||||||
|
;; <prgm> ::= <top> ...
|
||||||
|
;; <top> ::= <def> | <exp>
|
||||||
|
;; <def> ::= (define <var> <exp>)
|
||||||
|
;; <exp> ::= (lambda (<var> ...) <exp>)
|
||||||
|
;; | (if <exp> <exp> <exp>)
|
||||||
|
;; | (<exp> <exp> ...)
|
||||||
|
;; | (let ((<var> <exp>) ...) <exp>)
|
||||||
|
;; | (begin <exp> ...)
|
||||||
|
;; | <num> | <sym> | <var> | #t | #f
|
||||||
|
|
||||||
|
(define (desugar-prgm prgm)
|
||||||
|
(map (lambda (top)
|
||||||
|
(if (and (pair? top) (eq? (car top) 'define))
|
||||||
|
(desugar-define top)
|
||||||
|
(desugar-exp top)))
|
||||||
|
prgm))
|
||||||
|
|
||||||
|
(define (desugar-define def)
|
||||||
|
(match def
|
||||||
|
[`(define ,(name params ...) . ,e*)
|
||||||
|
`(define ,name ,(desugar-exp `(lambda ,params ,@e*)))]
|
||||||
|
[`(define ,name ,exp)
|
||||||
|
`(define ,name ,(desugar-exp exp))]))
|
||||||
|
|
||||||
|
(define (desugar-exp exp)
|
||||||
|
(match exp
|
||||||
|
[`(lambda ,params . ,body)
|
||||||
|
`(lambda ,params ,(desugar-body body))]
|
||||||
|
[`(if ,exp1 ,exp2 ,exp3)
|
||||||
|
`(if ,(desugar-exp exp1) ,(desugar-exp exp2) ,(desugar-exp exp3))]
|
||||||
|
[`(,f . ,args)
|
||||||
|
`(,(desugar-exp f) ,@(map desugar-exp args))]
|
||||||
|
[`(let ,((v* e*) ...) . ,body)
|
||||||
|
`(let (,(map (lambda (v e) `(,v ,(desugar-exp e))) v* e*))
|
||||||
|
,(desugar-body body))]
|
||||||
|
[`(begin . ,body) (desugar-body body)]
|
||||||
|
[(? atomic?) exp]))
|
||||||
|
|
||||||
|
(define (desugar-body body)
|
||||||
|
(match body
|
||||||
|
['() '()]
|
||||||
|
[(e) (desugar-exp e)]
|
||||||
|
[(e* ...) `(begin ,@(map desugar-exp e*))]))
|
||||||
|
|
||||||
|
|
||||||
|
;; CPS conversion
|
||||||
|
;; Re-structure the program into "Continuation Passing Style", where non-atomic
|
||||||
|
;; expressions must pass their continuations explicitly, changing to a very
|
||||||
|
;; "lambda-like" format
|
||||||
|
;; - begin expressions are decomposed
|
||||||
|
;; - let expressions are transformed into closed function applications
|
||||||
|
;; <prgm> ::= <top> ...
|
||||||
|
;; <top> ::= <def> | <exp>
|
||||||
|
;; <def> ::= (define <var> <exp>)
|
||||||
|
;; <exp> ::= <aexp>
|
||||||
|
;; | <cexp>
|
||||||
|
;; <cexp> ::= (<aexp> <aexp> ...)
|
||||||
|
;; | (if <aexp> <cexp> <cexp>)
|
||||||
|
;; | (set-then! <var> <aexp> <cexp>)
|
||||||
|
;; | (define-then! <var> <aexp> <cexp>)
|
||||||
|
;; <aexp> ::= (lambda (<var> ...) exp)
|
||||||
|
;; | <num> | <var> | #t | #f
|
||||||
|
;;
|
||||||
|
;; We choose a hybrid transformation based on https://matt.might.net/articles/cps-conversion/
|
||||||
|
|
||||||
|
(define undefined-value (make-symbol "undefined"))
|
||||||
|
|
||||||
|
(define (M expr)
|
||||||
|
;; M dispatches to the appropriate transformer
|
||||||
|
(match expr
|
||||||
|
[('lambda (var ...) e)
|
||||||
|
(let ([$k (gensym "$k")])
|
||||||
|
`(lambda (,@var ,$k) ,(T-c e $k)))]
|
||||||
|
[(? atomic?) expr]))
|
||||||
|
|
||||||
|
(define (T-k expr k)
|
||||||
|
;; T-k takes an explicit continuation and calls it when done
|
||||||
|
;; As an invariant, T-k cannot nest a T-c call directly
|
||||||
|
(match expr
|
||||||
|
[`(lambda . ,_) (k (M expr))]
|
||||||
|
[ (? atomic?) (k (M expr))]
|
||||||
|
[ ('define v e) (T-k `(define-then! ,v ,e) k)]
|
||||||
|
[ ('begin e) (T-k e k)]
|
||||||
|
[ ('begin e e* ...)
|
||||||
|
(T-k e (lambda _ (T-k `(begin ,@e*) k)))]
|
||||||
|
[ ('let ([v* e*] ...) body)
|
||||||
|
(T-k `((lambda (,@v*) ,body) ,@e*) k)]
|
||||||
|
[ ('if exp1 exp2 exp3)
|
||||||
|
(T-k exp1 (lambda ($exp1)
|
||||||
|
`(if ,$exp1
|
||||||
|
,(T-k exp2 k)
|
||||||
|
,(T-k exp3 k))))]
|
||||||
|
[ ('set! var expr)
|
||||||
|
(T-k expr (lambda ($expr)
|
||||||
|
`(set-then! ,var ,$expr ,(k undefined-value))))]
|
||||||
|
[((? primitive? f) e* ...)
|
||||||
|
(let* ([$rv (gensym "$rv")]
|
||||||
|
[cont `(lambda (,$rv) ,(k $rv))])
|
||||||
|
(T*-k e* (lambda ($e*)
|
||||||
|
`((cps ,f) ,@$e* ,cont))))]
|
||||||
|
[(f e* ...)
|
||||||
|
(let* ([$rv (gensym "$rv")]
|
||||||
|
[cont `(lambda (,$rv) ,(k $rv))])
|
||||||
|
(T-k f (lambda ($f)
|
||||||
|
(T*-k e* (lambda ($e*)
|
||||||
|
`(,$f ,@$e* ,cont))))))]))
|
||||||
|
|
||||||
|
(define (T-c expr c)
|
||||||
|
;; T-c takes a symbolic continuation, and uses it to construct CPS
|
||||||
|
(match expr
|
||||||
|
[`(lambda . ,_) `(,c ,(M expr))]
|
||||||
|
[ (? atomic?) `(,c ,(M expr))]
|
||||||
|
[ ('define v e) (T-c `(define-then! ,v ,e) c)]
|
||||||
|
[ ('begin e) (T-c e c)]
|
||||||
|
[ ('begin e e* ...)
|
||||||
|
(T-k e (lambda _ (T-c `(begin ,@e*) c)))]
|
||||||
|
[ ('let ([v* e*] ...) body)
|
||||||
|
(T-c `((lambda (,@v*) ,body) ,@e*) c)]
|
||||||
|
[ ('if exp1 exp2 exp3)
|
||||||
|
(let ([$k (gensym "$k")]) ;; Bind cont to avoid blow up
|
||||||
|
`((lambda (,$k)
|
||||||
|
,(T-k exp1 (lambda (aexp)
|
||||||
|
`(if ,aexp
|
||||||
|
,(T-c exp2 $k)
|
||||||
|
,(T-c exp3 $k)))))
|
||||||
|
,c))]
|
||||||
|
[ ('set! var expr)
|
||||||
|
(T-k expr (lambda ($expr)
|
||||||
|
`(set-then! ,var ,$expr (,c ,undefined-value))))]
|
||||||
|
[ ((? primitive? f) e* ...)
|
||||||
|
(T*-k e* (lambda ($e*)
|
||||||
|
`((cps ,f) ,@$e* ,c)))]
|
||||||
|
[ (f e* ...)
|
||||||
|
(T-k f (lambda ($f)
|
||||||
|
(T*-k e* (lambda ($e*)
|
||||||
|
`(,$f ,@$e* ,c)))))]))
|
||||||
|
|
||||||
|
(define (cps-convert-prgm prgm)
|
||||||
|
(T-c `(begin ,@prgm) 'ktail))
|
||||||
|
|
||||||
|
(define-cps-loop T*-k T-k)
|
||||||
|
|
||||||
|
(define (ir-convert prgm)
|
||||||
|
(cps-convert-prgm (desugar-prgm prgm)))
|
||||||
|
|
||||||
|
;; Useful for testing
|
||||||
|
;; (define (cps prim)
|
||||||
|
;; (lambda vars
|
||||||
|
;; (let* ([rev (reverse vars)]
|
||||||
|
;; [k (car rev)]
|
||||||
|
;; [args (reverse (cdr rev))])
|
||||||
|
;; (k (apply prim args)))))
|
||||||
12
scmvm/vm.scm
12
scmvm/vm.scm
@@ -58,6 +58,9 @@
|
|||||||
[(->list)
|
[(->list)
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(reverse-vector->list the-stack 0 top))]
|
(reverse-vector->list the-stack 0 top))]
|
||||||
|
[(set!)
|
||||||
|
(lambda (k obj)
|
||||||
|
(vector-set! the-stack k obj))]
|
||||||
[else (error "stack dispatch unknown value")])))
|
[else (error "stack dispatch unknown value")])))
|
||||||
|
|
||||||
(define (push stack v)
|
(define (push stack v)
|
||||||
@@ -81,6 +84,9 @@
|
|||||||
(define (stack->list stack)
|
(define (stack->list stack)
|
||||||
((stack '->list)))
|
((stack '->list)))
|
||||||
|
|
||||||
|
(define (stack-set! stack k obj)
|
||||||
|
((stack 'set!) k obj))
|
||||||
|
|
||||||
|
|
||||||
;;; IO
|
;;; IO
|
||||||
(define (read-word)
|
(define (read-word)
|
||||||
@@ -116,6 +122,7 @@
|
|||||||
(rot #x18)
|
(rot #x18)
|
||||||
(over #x19)
|
(over #x19)
|
||||||
(not #x1a)
|
(not #x1a)
|
||||||
|
(set! #x1b)
|
||||||
(bye #xff)))
|
(bye #xff)))
|
||||||
|
|
||||||
(define instruction-name car)
|
(define instruction-name car)
|
||||||
@@ -243,6 +250,11 @@
|
|||||||
(push data-stack b)
|
(push data-stack b)
|
||||||
(push data-stack a)
|
(push data-stack a)
|
||||||
(push data-stack b))]
|
(push data-stack b))]
|
||||||
|
[(set!)
|
||||||
|
;; use let* to induce an order of evaluation
|
||||||
|
(let* ([idx (pop data-stack)]
|
||||||
|
[obj (pop data-stack)])
|
||||||
|
(stack-set! data-stack idx obj))]
|
||||||
[(bye) (set! exit? #t)])
|
[(bye) (set! exit? #t)])
|
||||||
(when (not exit?)
|
(when (not exit?)
|
||||||
(run-vm vm)))
|
(run-vm vm)))
|
||||||
|
|||||||
12
tests.scm
12
tests.scm
@@ -3,7 +3,7 @@
|
|||||||
(scmvm vm)
|
(scmvm vm)
|
||||||
(scmvm debugger)
|
(scmvm debugger)
|
||||||
(scmvm language assembly)
|
(scmvm language assembly)
|
||||||
(scmvm language cps)
|
(scmvm language scheme)
|
||||||
(rnrs bytevectors)
|
(rnrs bytevectors)
|
||||||
(rnrs io ports)
|
(rnrs io ports)
|
||||||
((scheme base)
|
((scheme base)
|
||||||
@@ -167,13 +167,3 @@
|
|||||||
(debugger-continue my-debugger)
|
(debugger-continue my-debugger)
|
||||||
(assert-equal 23 (vm-pc my-vm)) ;; continue stops stepping
|
(assert-equal 23 (vm-pc my-vm)) ;; continue stops stepping
|
||||||
))
|
))
|
||||||
|
|
||||||
(define-test-suite "cps"
|
|
||||||
(define-test "atomics"
|
|
||||||
(define asm (make-assembler))
|
|
||||||
(compile-cps asm '(1))
|
|
||||||
(assert-equal 1 (bytevector-u32-native-ref (assembler-buf asm) 0)))
|
|
||||||
(define-test "atomics"
|
|
||||||
(define asm (make-assembler))
|
|
||||||
(compile-cps asm '(1))
|
|
||||||
(assert-equal 1 (bytevector-u32-native-ref (assembler-buf asm) 0))))
|
|
||||||
|
|||||||
Reference in New Issue
Block a user