532 lines
22 KiB
Scheme
532 lines
22 KiB
Scheme
#cs(module compile mzscheme
|
|
(require "parse.ss"
|
|
(lib "match.ss")
|
|
(lib "list.ss"))
|
|
|
|
(provide compile-simplified)
|
|
|
|
;; The compiler generates references to prim.ss and
|
|
;; runtime.ss exports, as well as MzScheme forms
|
|
;; and functions. The `ctx' argument provides
|
|
;; an appropriate context for those bindings (in
|
|
;; the form of a syntax object to use with d->s-o).
|
|
(define (compile-simplified stmt ctx)
|
|
(datum->syntax-object
|
|
ctx
|
|
(parameterize ([current-compile-context ctx])
|
|
(compile-a60 stmt 'void (empty-context) #t))))
|
|
|
|
(define current-compile-context (make-parameter #f))
|
|
|
|
(define (compile-a60 stmt next-label context add-to-top-level?)
|
|
(match stmt
|
|
[($ a60:block decls statements)
|
|
(compile-block decls statements next-label context add-to-top-level?)]
|
|
[else
|
|
(compile-statement stmt next-label context)]))
|
|
|
|
(define (compile-block decls statements next-label context add-to-top-level?)
|
|
(let* ([labels-with-numbers (map car statements)]
|
|
[labels (map (lambda (l)
|
|
(if (stx-number? l)
|
|
(datum->syntax-object
|
|
l
|
|
(string->symbol (format "~a" (syntax-e l)))
|
|
l
|
|
l)
|
|
l))
|
|
labels-with-numbers)]
|
|
;; Build environment by adding labels, then decls:
|
|
[context (foldl (lambda (decl context)
|
|
(match decl
|
|
[($ a60:proc-decl result-type var arg-vars by-value-vars arg-specs body)
|
|
(add-procedure context var result-type arg-vars by-value-vars arg-specs)]
|
|
[($ a60:type-decl type ids)
|
|
(add-atoms context ids type)]
|
|
[($ a60:array-decl type arrays)
|
|
(add-arrays context
|
|
(map car arrays) ; names
|
|
(map cdr arrays) ; dimensions
|
|
type)]
|
|
[($ a60:switch-decl name exprs)
|
|
(add-switch context name)]))
|
|
(add-labels
|
|
context
|
|
labels)
|
|
decls)])
|
|
;; Generate bindings and initialization for all decls,
|
|
;; plus all statements (thunked):
|
|
(let ([bindings
|
|
(append
|
|
(apply
|
|
append
|
|
;; Decls:
|
|
(map (lambda (decl)
|
|
(match decl
|
|
[($ a60:proc-decl result-type var arg-vars by-value-vars arg-specs body)
|
|
(let ([code
|
|
`(lambda (kont . ,arg-vars)
|
|
;; Extract by-value variables
|
|
(let ,(map (lambda (var)
|
|
`[,var (get-value ,var)])
|
|
by-value-vars)
|
|
;; Set up the result variable and done continuation:
|
|
,(let ([result-var (gensym 'prec-result)]
|
|
[done (gensym 'done)])
|
|
`(let* ([,result-var undefined]
|
|
[,done (lambda () (kont ,result-var))])
|
|
;; Include the compiled body:
|
|
,(compile-a60 body done
|
|
(add-settable-procedure
|
|
(add-bindings
|
|
context
|
|
arg-vars
|
|
by-value-vars
|
|
arg-specs)
|
|
var
|
|
result-type
|
|
result-var)
|
|
#f)))))])
|
|
(if add-to-top-level?
|
|
`([,var
|
|
(let ([tmp ,code])
|
|
(namespace-set-variable-value! ',var tmp)
|
|
tmp)])
|
|
`([,var
|
|
,code])))]
|
|
[($ a60:type-decl type ids)
|
|
(map (lambda (id) `[,id undefined]) ids)]
|
|
[($ a60:array-decl type arrays)
|
|
(map (lambda (array)
|
|
`[,(car array) (make-array
|
|
,@(apply
|
|
append
|
|
(map
|
|
(lambda (bp)
|
|
(list
|
|
(compile-expression (car bp) context 'num)
|
|
(compile-expression (cdr bp) context 'num)))
|
|
(cdr array))))])
|
|
arrays)]
|
|
[($ a60:switch-decl name exprs)
|
|
`([,name (make-switch ,@(map (lambda (e) `(lambda () ,(compile-expression e context 'des)))
|
|
exprs))])]
|
|
[else (error "can't compile decl")]))
|
|
decls))
|
|
;; Statements: most of the work is in `compile-statement', but
|
|
;; we provide the continuation label:
|
|
(cdr
|
|
(foldr (lambda (stmt label next-label+compiled)
|
|
(cons label
|
|
(cons
|
|
`[,label
|
|
(lambda ()
|
|
,(compile-statement (cdr stmt)
|
|
(car next-label+compiled)
|
|
context))]
|
|
(cdr next-label+compiled))))
|
|
(cons next-label null)
|
|
statements
|
|
labels)))])
|
|
;; Check for duplicate bindings:
|
|
(let ([dup (check-duplicate-identifier (filter identifier? (map car bindings)))])
|
|
(when dup
|
|
(raise-syntax-error
|
|
#f
|
|
"name defined twice"
|
|
dup)))
|
|
;; Generate code; body of leterec jumps to the first statement label.
|
|
`(letrec ,bindings
|
|
(,(caar statements))))))
|
|
|
|
(define (compile-statement statement next-label context)
|
|
(match statement
|
|
[($ a60:block decls statements)
|
|
(compile-block decls statements next-label context #f)]
|
|
[($ a60:branch test ($ a60:goto then) ($ a60:goto else))
|
|
`(if (check-boolean ,(compile-expression test context 'bool))
|
|
(goto ,(check-label then context))
|
|
(goto ,(check-label else context)))]
|
|
[($ a60:goto label)
|
|
(at (expression-location label)
|
|
`(goto ,(compile-expression label context 'des)))]
|
|
[($ a60:dummy)
|
|
`(,next-label)]
|
|
[($ a60:call proc args)
|
|
(at (expression-location proc)
|
|
`(,(compile-expression proc context 'func)
|
|
(lambda (val)
|
|
(,next-label))
|
|
,@(map (lambda (arg) (compile-argument arg context))
|
|
args)))]
|
|
[($ a60:assign vars val)
|
|
;; >>>>>>>>>>>>>>> Start clean-up here <<<<<<<<<<<<<<<<<
|
|
;; Lift out the spec-finding part, and use it to generate
|
|
;; an expected type that is passed to `compile-expression':
|
|
`(begin
|
|
(let ([val ,(compile-expression val context 'numbool)])
|
|
,@(map (lambda (avar)
|
|
(let ([var (a60:variable-name avar)])
|
|
(at var
|
|
(cond
|
|
[(null? (a60:variable-indices avar))
|
|
(cond
|
|
[(call-by-name-variable? var context)
|
|
=> (lambda (spec)
|
|
`(set-target! ,var ',var (coerce ',(spec-coerce-target spec) val)))]
|
|
[(procedure-result-variable? var context)
|
|
`(set! ,(procedure-result-variable-name var context)
|
|
(coerce ',(spec-coerce-target (procedure-result-spec var context)) val))]
|
|
[(or (settable-variable? var context)
|
|
(array-element? var context))
|
|
=> (lambda (spec)
|
|
`(,(if (own-variable? var context) 'set-box! 'set!)
|
|
,var
|
|
(coerce ',(spec-coerce-target spec) val)))]
|
|
[else (raise-syntax-error #f "confused by assignment" (expression-location var))])]
|
|
[else
|
|
(let ([spec (or (array-element? var context)
|
|
(call-by-name-variable? var context))])
|
|
`(array-set! ,(compile-expression (make-a60:variable var null) context 'numbool)
|
|
(coerce ',(spec-coerce-target spec) val)
|
|
,@(map (lambda (e) (compile-expression e context 'num))
|
|
(a60:variable-indices avar))))]))))
|
|
vars))
|
|
(,next-label))]
|
|
[else (error "can't compile statement")]))
|
|
|
|
(define (compile-expression expr context type)
|
|
(match expr
|
|
[(? (lambda (x) (and (syntax? x) (number? (syntax-e x)))) n)
|
|
(if (eq? type 'des)
|
|
;; Need a label:
|
|
(check-label (datum->syntax-object expr
|
|
(string->symbol (number->string (syntax-e expr)))
|
|
expr
|
|
expr)
|
|
context)
|
|
;; Normal use of a number:
|
|
(begin
|
|
(check-type 'num type expr)
|
|
(as-builtin n)))]
|
|
[(? (lambda (x) (and (syntax? x) (boolean? (syntax-e x)))) n) (check-type 'bool type expr) (as-builtin n)]
|
|
[(? (lambda (x) (and (syntax? x) (string? (syntax-e x)))) n) (check-type 'string type expr) (as-builtin n)]
|
|
[(? identifier? i) (compile-expression (make-a60:variable i null) context type)]
|
|
[(? symbol? i) ; either a generated label or 'val:
|
|
(unless (eq? expr 'val)
|
|
(check-type 'des type expr))
|
|
(datum->syntax-object #f i)]
|
|
[($ a60:subscript array index)
|
|
;; Maybe a switch index, or maybe an array reference
|
|
(at array
|
|
(cond
|
|
[(array-element? array context)
|
|
`(array-ref ,array ,(compile-expression index context 'num))]
|
|
[(switch-variable? array context)
|
|
`(switch-ref ,array ,(compile-expression index context 'num))]
|
|
[else (raise-syntax-error
|
|
#f
|
|
"confused by variable"
|
|
array)]))]
|
|
[($ a60:binary t argt op e1 e2)
|
|
(check-type t type expr)
|
|
(at op
|
|
`(,(as-builtin op) ,(compile-expression e1 context argt) ,(compile-expression e2 context argt)))]
|
|
[($ a60:unary t argt op e1)
|
|
(check-type t type expr)
|
|
(at op
|
|
`(,(as-builtin op) ,(compile-expression e1 context argt)))]
|
|
[($ a60:variable var subscripts)
|
|
(let ([sub (lambda (wrap v)
|
|
(wrap
|
|
(if (null? subscripts)
|
|
v
|
|
`(array-ref ,v ,@(map (lambda (e) (compile-expression e context 'num)) subscripts)))))])
|
|
(cond
|
|
[(call-by-name-variable? var context)
|
|
=> (lambda (spec)
|
|
(check-spec-type spec type var)
|
|
(sub (lambda (val) `(coerce ',(spec-coerce-target spec) ,val)) `(get-value ,var)))]
|
|
[(primitive-variable? var context)
|
|
=> (lambda (name)
|
|
(sub values
|
|
(datum->syntax-object
|
|
(current-compile-context)
|
|
name
|
|
var
|
|
var)))]
|
|
[(and (procedure-result-variable? var context)
|
|
(not (eq? type 'func)))
|
|
(unless (null? subscripts)
|
|
(raise-syntax-error "confused by subscripts" var))
|
|
(let ([spec (procedure-result-spec var context)])
|
|
(check-spec-type spec type var)
|
|
(at var
|
|
`(coerce
|
|
',(spec-coerce-target spec)
|
|
,(procedure-result-variable-name var context))))]
|
|
[(or (procedure-result-variable? var context)
|
|
(procedure-variable? var context)
|
|
(label-variable? var context)
|
|
(settable-variable? var context)
|
|
(array-element? var context))
|
|
=> (lambda (spec)
|
|
(let ([spec (if (or (procedure-result-variable? var context)
|
|
(procedure-variable? var context)
|
|
(and (array-element? var context)
|
|
(null? subscripts)))
|
|
#f ;; need just the proc or array...
|
|
spec)])
|
|
(check-spec-type spec type var)
|
|
(let ([target (spec-coerce-target spec)])
|
|
(sub (if target
|
|
(lambda (v) `(coerce ',target ,v))
|
|
values)
|
|
(if (own-variable? var context)
|
|
`(unbox ,var)
|
|
var)))))]
|
|
[else (raise-syntax-error
|
|
#f
|
|
"confused by expression"
|
|
(expression-location var))]))]
|
|
|
|
[($ a60:app func args)
|
|
(at (expression-location func)
|
|
`(,(compile-expression func context 'func)
|
|
values
|
|
,@(map (lambda (e) (compile-argument e context))
|
|
args)))]
|
|
[($ a60:if test then else)
|
|
`(if (check-boolean ,(compile-expression test context 'bool))
|
|
,(compile-expression then context type)
|
|
,(compile-expression else context type))]
|
|
[else (error 'compile-expression "can't compile expression ~a" expr)]))
|
|
|
|
(define (expression-location expr)
|
|
(if (syntax? expr)
|
|
expr
|
|
(match expr
|
|
[($ a60:subscript array index) (expression-location array)]
|
|
[($ a60:binary type argtype op e1 e2) op]
|
|
[($ a60:unary type argtype op e1) op]
|
|
[($ a60:variable var subscripts) (expression-location var)]
|
|
[($ a60:app func args)
|
|
(expression-location func)]
|
|
[else #f])))
|
|
|
|
(define (compile-argument arg context)
|
|
(cond
|
|
[(and (a60:variable? arg)
|
|
(not (let ([v (a60:variable-name arg)])
|
|
(or (procedure-variable? v context)
|
|
(label-variable? v context)
|
|
(primitive-variable? v context)))))
|
|
`(case-lambda
|
|
[() ,(compile-expression arg context 'any)]
|
|
[(val) ,(compile-statement (make-a60:assign (list arg) 'val) 'void context)])]
|
|
[(identifier? arg)
|
|
(compile-argument (make-a60:variable arg null) context)]
|
|
[else `(lambda () ,(compile-expression arg context 'any))]))
|
|
|
|
(define (check-type got expected expr)
|
|
(or (eq? expected 'any)
|
|
(case got
|
|
[(num) (memq expected '(num numbool))]
|
|
[(bool) (memq expected '(bool numbool))]
|
|
[(des) (memq expected '(des))]
|
|
[(func) (memq expected '(func))]
|
|
[else #f])
|
|
(raise-syntax-error #f
|
|
(format "type mismatch (~a != ~a)" got expected)
|
|
expr)))
|
|
|
|
(define (check-spec-type spec type expr)
|
|
(let ([target (spec-coerce-target spec)])
|
|
(when target
|
|
(case (syntax-e target)
|
|
[(integer real) (check-type 'num type expr)]
|
|
[(boolean) (check-type 'bool type expr)]
|
|
[(procedure) (check-type 'func type expr)]))))
|
|
|
|
|
|
(define (check-label l context)
|
|
(if (or (symbol? l)
|
|
(label-variable? l context))
|
|
l
|
|
(raise-syntax-error
|
|
#f
|
|
"undefined label"
|
|
l)))
|
|
|
|
(define (at stx expr)
|
|
(if (syntax? stx)
|
|
(datum->syntax-object (current-compile-context) expr stx)
|
|
expr))
|
|
|
|
(define (as-builtin stx)
|
|
;; Preserve source loc, but change to reference to
|
|
;; a builtin operation by changing the context:
|
|
(datum->syntax-object
|
|
(current-compile-context)
|
|
(syntax-e stx)
|
|
stx
|
|
stx))
|
|
|
|
;; --------------------
|
|
|
|
(define (empty-context)
|
|
`(((sign prim sign)
|
|
(entier prim entier)
|
|
|
|
(sin prim a60:sin)
|
|
(cos prim a60:cos)
|
|
(acrtan prim a60:arctan)
|
|
(sqrt prim a60:sqrt)
|
|
(abs prim a60:abs)
|
|
(ln prim a60:ln)
|
|
(exp prim a60:exp)
|
|
|
|
(prints prim prints)
|
|
(printn prim printn)
|
|
(printsln prim printsln)
|
|
(printnln prim printnln))))
|
|
|
|
(define (add-labels context l)
|
|
(cons (map (lambda (lbl) (cons (if (symbol? lbl)
|
|
(datum->syntax-object #f lbl)
|
|
lbl)
|
|
'label)) l)
|
|
context))
|
|
|
|
(define (add-procedure context var result-type arg-vars by-value-vars arg-specs)
|
|
(cons (list (cons var 'procedure))
|
|
context))
|
|
|
|
(define (add-settable-procedure context var result-type result-var)
|
|
(cons (list (cons var `(settable-procedure ,result-var ,result-type)))
|
|
context))
|
|
|
|
(define (add-atoms context ids type)
|
|
(cons (map (lambda (id) (cons id type)) ids)
|
|
context))
|
|
|
|
(define (add-arrays context names dimensionses type)
|
|
(cons (map (lambda (name dimensions)
|
|
(cons name `(array ,type ,(length dimensions))))
|
|
names dimensionses)
|
|
context))
|
|
|
|
(define (add-switch context name)
|
|
(cons (list (cons name 'switch))
|
|
context))
|
|
|
|
(define (add-bindings context arg-vars by-value-vars arg-specs)
|
|
(cons (map (lambda (var)
|
|
(let ([spec (or (ormap (lambda (spec)
|
|
(and (ormap (lambda (x) (bound-identifier=? var x))
|
|
(cdr spec))
|
|
(car spec)))
|
|
arg-specs)
|
|
#'unknown)])
|
|
(cons var
|
|
(if (ormap (lambda (x) (bound-identifier=? var x)) by-value-vars)
|
|
spec
|
|
(list 'by-name spec)))))
|
|
arg-vars)
|
|
context))
|
|
|
|
;; var-binding : syntax context -> symbol
|
|
;; returns an identifier indicating where the var is
|
|
;; bound, or 'free if it isn't. The compiler inserts
|
|
;; top-level procedure definitions into the namespace; if
|
|
;; the variable is bound there, it is a procedure.
|
|
(define (var-binding var context)
|
|
(cond
|
|
[(null? context)
|
|
(let/ec k
|
|
(namespace-variable-value (syntax-e var)
|
|
#t
|
|
(lambda () (k 'free)))
|
|
'procedure)]
|
|
[else
|
|
(let ([m (var-in-rib var (car context))])
|
|
(or m (var-binding var (cdr context))))]))
|
|
|
|
(define (var-in-rib var rib)
|
|
(ormap (lambda (b)
|
|
(if (symbol? (car b))
|
|
;; primitives:
|
|
(and (eq? (syntax-e var) (car b))
|
|
(cdr b))
|
|
;; everything else:
|
|
(and (bound-identifier=? var (car b))
|
|
(cdr b))))
|
|
rib))
|
|
|
|
(define (primitive-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(and (pair? v)
|
|
(eq? (car v) 'prim)
|
|
(cadr v))))
|
|
|
|
(define (call-by-name-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(and (pair? v)
|
|
(eq? (car v) 'by-name)
|
|
(cadr v))))
|
|
|
|
(define (procedure-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(eq? v 'procedure)))
|
|
|
|
(define (procedure-result-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(and (pair? v)
|
|
(eq? (car v) 'settable-procedure)
|
|
(cdr v))))
|
|
|
|
(define (procedure-result-variable-name var context)
|
|
(let ([v (procedure-result-variable? var context)])
|
|
(car v)))
|
|
|
|
(define (procedure-result-spec var context)
|
|
(let ([v (procedure-result-variable? var context)])
|
|
(cadr v)))
|
|
|
|
(define (label-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(eq? v 'label)))
|
|
|
|
(define (switch-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(eq? v 'switch)))
|
|
|
|
(define (settable-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(or (box? v)
|
|
(and (syntax? v)
|
|
(memq (syntax-e v) '(integer real boolean))
|
|
v))))
|
|
|
|
(define (own-variable? var context)
|
|
(let ([v (var-binding var context)])
|
|
(box? v)))
|
|
|
|
(define (array-element? var context)
|
|
(let ([v (var-binding var context)])
|
|
(and (pair? v)
|
|
(eq? (car v) 'array)
|
|
(or (cadr v)
|
|
#'unknown))))
|
|
|
|
(define (spec-coerce-target spec)
|
|
(cond
|
|
[(and (syntax? spec) (memq (syntax-e spec) '(string label switch real integer boolean unknown))) spec]
|
|
[(and (syntax? spec) (memq (syntax-e spec) '(unknown))) #f]
|
|
[(or (not spec) (not (pair? spec))) #f]
|
|
[(eq? (car spec) 'array) (cadr spec)]
|
|
[(eq? (car spec) 'procedure) #'procedure]
|
|
[else #f]))
|
|
|
|
(define (stx-number? a) (and (syntax? a) (number? (syntax-e a)))))
|