diff --git a/compile.rkt b/compile.rkt index 7ea7f1a..d1c148b 100644 --- a/compile.rkt +++ b/compile.rkt @@ -116,9 +116,10 @@ (append (map (lambda: ([d : Natural]) (list-ref cenv d)) (Lam-closure-map lam)) - (build-list (Lam-num-parameters lam) (lambda: ([i : Natural]) '?)))) - - + (build-list (if (Lam-rest? lam) + (add1 (Lam-num-parameters lam)) + (Lam-num-parameters lam)) + (lambda: ([i : Natural]) '?)))) @@ -338,7 +339,9 @@ `(,(make-AssignPrimOpStatement target (make-MakeCompiledProcedure (Lam-entry-label exp) - (Lam-num-parameters exp) + (if (Lam-rest? exp) + (make-ArityAtLeast (Lam-num-parameters exp)) + (Lam-num-parameters exp)) (Lam-closure-map exp) (Lam-name exp))))))) @@ -354,7 +357,9 @@ `(,(make-AssignPrimOpStatement target (make-MakeCompiledProcedureShell (Lam-entry-label exp) - (Lam-num-parameters exp) + (if (Lam-rest? exp) + (make-ArityAtLeast (Lam-num-parameters exp)) + (Lam-num-parameters exp)) (Lam-name exp))))))) @@ -362,22 +367,29 @@ ;; Compiles the body of the lambda in the appropriate environment. ;; Closures will target their value to the 'val register, and use return linkage. (define (compile-lambda-body exp cenv) - (append-instruction-sequences - - (make-instruction-sequence - `(,(Lam-entry-label exp))) - - (if (not (empty? (Lam-closure-map exp))) - (make-instruction-sequence `(,(make-PerformStatement (make-InstallClosureValues!)))) - empty-instruction-sequence) - - (compile (Lam-body exp) - (append (map (lambda: ([d : Natural]) - (list-ref cenv d)) - (Lam-closure-map exp)) - (build-list (Lam-num-parameters exp) (lambda: ([i : Natural]) '?))) - 'val - return-linkage))) + (let: ([maybe-unsplice-rest-argument : InstructionSequence + (if (Lam-rest? exp) + ;; FIXME: we may need to unsplice the rest argument if this lambda is a rest + (error 'fixme) + empty-instruction-sequence)] + [maybe-install-closure-values : InstructionSequence + (if (not (empty? (Lam-closure-map exp))) + (make-instruction-sequence + `(,(make-PerformStatement (make-InstallClosureValues!)))) + empty-instruction-sequence)] + [lam-body-code : InstructionSequence + (compile (Lam-body exp) + (extract-lambda-cenv exp cenv) + 'val + return-linkage)]) + + (append-instruction-sequences + (make-instruction-sequence + `(,(Lam-entry-label exp))) + + maybe-unsplice-rest-argument + maybe-install-closure-values + lam-body-code))) diff --git a/parse.rkt b/parse.rkt index 7f3819e..4ddfa3f 100644 --- a/parse.rkt +++ b/parse.rkt @@ -183,12 +183,20 @@ (parse b body-cenv #f)) (lambda-body exp))) mutated-parameters)]) - (make-Lam (current-defined-name) - (length (lambda-parameters exp)) - #f - lam-body - (map env-reference-depth closure-references) - (fresh-lam-label))))) + (cond [(lambda-has-rest-parameter? exp) + (make-Lam (current-defined-name) + (sub1 (length (lambda-parameters exp))) + #t + lam-body + (map env-reference-depth closure-references) + (fresh-lam-label))] + [else + (make-Lam (current-defined-name) + (length (lambda-parameters exp)) + #f + lam-body + (map env-reference-depth closure-references) + (fresh-lam-label))])))) (define lam-label-counter 0) @@ -378,7 +386,31 @@ (define (lambda? exp) (tagged-list? exp 'lambda)) -(define (lambda-parameters exp) (cadr exp)) + + +;; lambda-parameters: lambda-expression -> (listof identifier) +(define (lambda-parameters exp) + (let loop ([params (cadr exp)]) + (cond + [(null? params) + empty] + [(pair? params) + (cons (car params) + (loop (cdr params)))] + [else + (list params)]))) + +;; Produces true if the lambda's last parameter is a rest parameter. +(define (lambda-has-rest-parameter? exp) + (let loop ([params (cadr exp)]) + (cond + [(null? params) + #f] + [(pair? params) + (loop (cdr params))] + [else + #t]))) + (define (lambda-body exp) (cddr exp)) (define (make-lambda parameters body) diff --git a/test-parse.rkt b/test-parse.rkt index 2bcb56f..da6c84b 100644 --- a/test-parse.rkt +++ b/test-parse.rkt @@ -87,6 +87,36 @@ (make-ToplevelRef 0 1) (make-Constant "ok")))) +(test (parse '(lambda () x)) + (make-Top (make-Prefix '(x)) + (make-Lam #f 0 #f (make-ToplevelRef 0 0) + '(0) 'lamEntry1))) + +(test (parse '(lambda args args)) + (make-Top (make-Prefix '()) + (make-Lam #f 0 #t (make-LocalRef 0 #f) + '() 'lamEntry1))) + +(test (parse '(lambda (x y . z) x)) + (make-Top (make-Prefix '()) + (make-Lam #f 2 #t + (make-LocalRef 0 #f) + '() 'lamEntry1))) + +(test (parse '(lambda (x y . z) y)) + (make-Top (make-Prefix '()) + (make-Lam #f 2 #t + (make-LocalRef 1 #f) + '() 'lamEntry1))) + + +(test (parse '(lambda (x y . z) z)) + (make-Top (make-Prefix '()) + (make-Lam #f 2 #t + (make-LocalRef 2 #f) + '() 'lamEntry1))) + + (test (parse '(lambda (x y z) x)) (make-Top (make-Prefix '()) (make-Lam #f 3 #f (make-LocalRef 0 #f) '() 'lamEntry1)))