연산자를 먼저 컴파일하고 인자 순서를 보존한 뒤 apply 경계를 한 번 건넌다.
중첩 조합식의 정확한 명령 순서를 살펴보고 상태로 피연산자 평가를 드러내며 원시·해석·컴파일 프로시저를 하나의 호출 규약으로 연결합니다.
컴파일된 적용 코드가 보존해야 할 순서와 표현 계약은 무엇일까요?
- 피연산자 식보다 연산자를 먼저 컴파일하기
- 인자 순서를 보존하며 피연산자를 왼쪽부터 컴파일하기
- 완전한 조합 상태가 준비된 뒤 call 명령 하나 내보내기
- 불명확한 일정에 의존하지 않고 소스 순서의 효과 관찰하기
- 프로시저 표현 사이에 하나의 어휘 환경과 인자 규약 공유하기
- primitive·interpreted·compiled 디스패치 구분하기
수업용 컴파일러는 뺄셈 연산자의 load를 먼저 내보내고 왼쪽 record 호출 전체, 오른쪽 record 호출 전체, 마지막 바깥 call을 차례로 내보냅니다. record가 observations에 레이블을 덧붙이므로 (left right)가 실행도 방출된 소스 순서를 따른다는 사실을 보여 줍니다.
두 번째 프로그램에서는 평가기가 compiled add-three를 호출하고 compiled 코드가 interpreted double을 호출합니다. 두 경로 모두 정렬된 인자 목록을 apply-any에 넘기며 dispatch-log는 실제 primitive·interpreted·compiled 태그를 남깁니다. 이 실행은 수업에서 사용하는 호출 규약을 기록합니다.
(begin
(define (tagged-list? expression tag)
(and (pair? expression) (eq? (car expression) tag)))
(define (self-evaluating? expression)
(or (number? expression)
(string? expression)
(boolean? expression)))
(define (quoted? expression) (tagged-list? expression 'quote))
(define (assignment? expression) (tagged-list? expression 'set!))
(define (definition? expression) (tagged-list? expression 'define))
(define (if? expression) (tagged-list? expression 'if))
(define (lambda? expression) (tagged-list? expression 'lambda))
(define (begin? expression) (tagged-list? expression 'begin))
(define (definition-variable expression)
(if (symbol? (cadr expression))
(cadr expression)
(car (cadr expression))))
(define (definition-value expression)
(if (symbol? (cadr expression))
(caddr expression)
(cons 'lambda
(cons (cdr (cadr expression))
(cddr expression)))))
(define (compile-sequence expressions)
(cond ((null? expressions)
'((literal ok)))
((null? (cdr expressions))
(compile-expression (car expressions)))
(else
(append
(compile-expression (car expressions))
(cons '(discard)
(compile-sequence (cdr expressions)))))))
(define (compile-operands operands)
(if (null? operands)
'()
(append
(compile-expression (car operands))
(compile-operands (cdr operands)))))
(define (compile-expression expression)
(cond
((self-evaluating? expression)
(list (list 'literal expression)))
((symbol? expression)
(list (list 'load expression)))
((quoted? expression)
(list (list 'literal (cadr expression))))
((assignment? expression)
(append
(compile-expression (caddr expression))
(list (list 'set (cadr expression)))))
((definition? expression)
(append
(compile-expression (definition-value expression))
(list (list 'define
(definition-variable expression)))))
((if? expression)
(append
(compile-expression (cadr expression))
(list
(list 'branch
(compile-expression (caddr expression))
(compile-expression (cadddr expression))))))
((lambda? expression)
(list
(list 'closure
(cadr expression)
(compile-sequence (cddr expression)))))
((begin? expression)
(compile-sequence (cdr expression)))
((pair? expression)
(append
(compile-expression (car expression))
(append
(compile-operands (cdr expression))
(list (list 'call (length (cdr expression)))))))
(else
(error "unknown expression for compiler" expression))))
(define (pair-bindings variables values)
(cond ((and (null? variables) (null? values)) '())
((null? variables) (error "too many arguments"))
((null? values) (error "too few arguments"))
(else
(cons (cons (car variables) (car values))
(pair-bindings (cdr variables)
(cdr values))))))
(define (make-frame variables values)
(cons '*frame* (pair-bindings variables values)))
(define (frame-bindings frame) (cdr frame))
(define (extend-environment variables values environment)
(cons (make-frame variables values) environment))
(define (lookup-variable-value variable environment)
(if (null? environment)
(error "unbound compiled variable" variable)
(let ((binding
(assoc variable
(frame-bindings (car environment)))))
(if binding
(cdr binding)
(lookup-variable-value variable
(cdr environment))))))
(define (define-variable! variable value environment)
(let* ((frame (car environment))
(binding (assoc variable (frame-bindings frame))))
(if binding
(set-cdr! binding value)
(set-cdr! frame
(cons (cons variable value)
(frame-bindings frame)))))
(list 'defined variable))
(define (set-variable-value! variable value environment)
(if (null? environment)
(error "unbound compiled assignment" variable)
(let ((binding
(assoc variable
(frame-bindings (car environment)))))
(if binding
(begin
(set-cdr! binding value)
(list 'assigned variable))
(set-variable-value! variable
value
(cdr environment))))))
(define (make-primitive implementation)
(list 'primitive implementation))
(define (primitive? procedure)
(tagged-list? procedure 'primitive))
(define (primitive-implementation procedure) (cadr procedure))
(define (make-compiled-procedure parameters code environment)
(list 'compiled parameters code environment))
(define (compiled-procedure? procedure)
(tagged-list? procedure 'compiled))
(define (compiled-parameters procedure) (cadr procedure))
(define (compiled-code procedure) (caddr procedure))
(define (compiled-environment procedure) (cadddr procedure))
(define (split-call stack count)
(define (loop remaining-stack remaining-count arguments)
(if (= remaining-count 0)
(list (car remaining-stack)
arguments
(cdr remaining-stack))
(loop (cdr remaining-stack)
(- remaining-count 1)
(cons (car remaining-stack) arguments))))
(loop stack count '()))
(define (run-code code stack environment)
(if (null? code)
(list stack environment)
(let* ((instruction (car code))
(tag (car instruction)))
(cond
((eq? tag 'literal)
(run-code (cdr code)
(cons (cadr instruction) stack)
environment))
((eq? tag 'load)
(run-code
(cdr code)
(cons
(lookup-variable-value
(cadr instruction)
environment)
stack)
environment))
((eq? tag 'closure)
(run-code
(cdr code)
(cons
(make-compiled-procedure
(cadr instruction)
(caddr instruction)
environment)
stack)
environment))
((eq? tag 'define)
(let ((result
(define-variable!
(cadr instruction)
(car stack)
environment)))
(run-code (cdr code)
(cons result (cdr stack))
environment)))
((eq? tag 'set)
(let ((result
(set-variable-value!
(cadr instruction)
(car stack)
environment)))
(run-code (cdr code)
(cons result (cdr stack))
environment)))
((eq? tag 'discard)
(run-code (cdr code)
(cdr stack)
environment))
((eq? tag 'branch)
(let* ((selected
(if (car stack)
(cadr instruction)
(caddr instruction)))
(branch-state
(run-code selected
(cdr stack)
environment)))
(run-code (cdr code)
(car branch-state)
(cadr branch-state))))
((eq? tag 'call)
(let* ((parts
(split-call stack (cadr instruction)))
(procedure (car parts))
(arguments (cadr parts))
(caller-stack (caddr parts)))
(if (primitive? procedure)
(run-code
(cdr code)
(cons
(apply
(primitive-implementation procedure)
arguments)
caller-stack)
environment)
(if (compiled-procedure? procedure)
(let* ((call-environment
(extend-environment
(compiled-parameters procedure)
arguments
(compiled-environment procedure)))
(call-state
(run-code
(compiled-code procedure)
'()
call-environment))
(value (car (car call-state))))
(run-code (cdr code)
(cons value caller-stack)
environment))
(error "not a compiled procedure"
procedure)))))
(else
(error "unknown compiled instruction"
instruction))))))
(define (make-global-environment)
(list
(cons '*frame*
(list
(cons '+ (make-primitive +))
(cons '- (make-primitive -))
(cons '* (make-primitive *))
(cons '/ (make-primitive /))
(cons '= (make-primitive =))
(cons '< (make-primitive <))
(cons '> (make-primitive >))
(cons 'list (make-primitive list))
(cons 'cons (make-primitive cons))
(cons 'car (make-primitive car))
(cons 'cdr (make-primitive cdr))
(cons 'null? (make-primitive null?))))))
(define (compile-and-run expression)
(let* ((code (compile-expression expression))
(environment (make-global-environment))
(state (run-code code '() environment)))
(list (car (car state))
code
environment)))
(define combination
'(- (record (quote left) 20)
(record (quote right) 3)))
(define program
'(begin
(define observations (quote ()))
(define (append-list left right)
(if (null? left)
right
(cons (car left)
(append-list (cdr left) right))))
(define (record label value)
(begin
(set! observations
(append-list observations (list label)))
value))
(define result
(- (record (quote left) 20)
(record (quote right) 3)))
(list result observations)))
(define result (compile-and-run program))
(list (car result)
(map car (compile-expression combination)))
)- 출력
- —
- 값
- —
- 진단
- —
순서 프로그램은 ((17 (left right)) (load load literal literal call load literal literal call call))을 반환합니다. 인터페이스 프로그램은 ((evaluator-to-compiled 8 (compiled primitive)) (compiled-to-evaluator 14 (interpreted primitive) #t) (representations #t #t))를 반환합니다.
첫 실행에서 바깥 연산자 load와 왼쪽 record 호출의 모든 명령과 오른쪽 호출의 모든 명령과 마지막 subtraction call을 차례로 따라가세요. 인터페이스 실행에서는 인자 준비와 apply-any 표현 디스패치를 분리하고 각 본문이 선언한 매개변수 순서로 인자를 받는지 확인하세요.
프로그램을 수정하고 결과를 비교해 보세요.
서로 다른 레이블을 기록하는 피연산자 세 개의 list 호출을 컴파일하고 한 피연산자를 compiled closure 호출로 감싸세요. 명령 태그와 시간순 dispatch-log를 예상하세요.
힌트 보기
연산자 코드를 먼저 끝내고 각 피연산자의 완전한 코드를 소스 순서대로 붙입니다. 마지막 call은 완성된 정렬 인자 목록을 사용합니다.