diff --git a/assemble.rkt b/assemble.rkt index d3a8fcb..cba1910 100644 --- a/assemble.rkt +++ b/assemble.rkt @@ -1,5 +1,5 @@ -#lang racket/base -(require "structs.rkt" +#lang typed/racket/base +(require "typed-structs.rkt" racket/string racket/list) @@ -7,43 +7,46 @@ ;; assemble/write-invoke: (listof statement) output-port -> void +(: assemble/write-invoke ((Listof Statement) Output-Port -> Void)) (define (assemble/write-invoke stmts op) (let ([basic-blocks (fracture stmts)]) (fprintf op "function(k) {\n") - (for-each (lambda (basic-block) + (for-each (lambda: ([basic-block : BasicBlock]) (displayln (assemble-basic-block basic-block) op) (newline op)) basic-blocks) (fprintf op "MACHINE.cont = k;\n") (fprintf op "trampoline(~a, function() {}); }" - (basic-block-name (first basic-blocks))))) + (BasicBlock-name (first basic-blocks))))) ;; fracture: (listof stmt) -> (listof basic-block) +(: fracture ((Listof Statement) -> (Listof BasicBlock))) (define (fracture stmts) (let* ([first-block-label (make-label 'start)] [jump-targets (cons first-block-label (collect-general-jump-targets stmts))]) - (let loop ([name first-block-label] - [acc '()] - [basic-blocks '()] - [stmts stmts] - [last-stmt-goto? #f]) + (let: loop : (Listof BasicBlock) + ([name : Symbol first-block-label] + [acc : (Listof UnlabeledStatement) '()] + [basic-blocks : (Listof BasicBlock) '()] + [stmts : (Listof Statement) stmts] + [last-stmt-goto? : Boolean #f]) (cond [(null? stmts) - (reverse (cons (make-basic-block name (reverse acc)) + (reverse (cons (make-BasicBlock name (reverse acc)) basic-blocks))] [(symbol? (car stmts)) (cond [(member (car stmts) jump-targets) (loop (car stmts) '() - (cons (make-basic-block name + (cons (make-BasicBlock name (if last-stmt-goto? (reverse acc) - (reverse (append `((goto (label ,(car stmts)))) + (reverse (append `(,(make-GotoStatement (make-Label (car stmts)))) acc)))) basic-blocks) (cdr stmts) @@ -59,37 +62,39 @@ (cons (car stmts) acc) basic-blocks (cdr stmts) - (tagged-list? (car stmts) 'goto))])))) + (GotoStatement? (car stmts)))])))) ;; unique: (listof symbol -> listof symbol) +(: unique ((Listof Symbol) -> (Listof Symbol))) (define (unique los) - (let ([ht (make-hasheq)]) + (let: ([ht : (HashTable Symbol Boolean) (make-hasheq)]) (for ([l los]) (hash-set! ht l #t)) - (for/list ([k (in-hash-keys ht)]) - k))) + (hash-map ht (lambda: ([k : Symbol] [v : Boolean]) k)))) + ;; collect-general-jump-targets: (listof stmt) -> (listof label) ;; collects all the labels that are potential targets for GOTOs or branches. +(: collect-general-jump-targets ((Listof Statement) -> (Listof Symbol))) (define (collect-general-jump-targets stmts) (define (collect-input an-input) (cond - [(reg? an-input) + [(Reg? an-input) empty] - [(const? an-input) + [(Const? an-input) empty] - [(label? an-input) - (list (label-name an-input))] + [(Label? an-input) + (list (Label-name an-input))] [else (error 'collect-input "~e" an-input)])) (define (collect-location a-location) (cond - [(reg? a-location) + [(Reg? a-location) empty] - [(label? a-location) - (list (label-name a-location))] + [(Label? a-location) + (list (Label-name a-location))] [else (error 'collect-location "~e" a-location)])) (unique (let loop ([stmts stmts]) @@ -100,32 +105,29 @@ (append (cond [(symbol? stmt) empty] - [(tagged-list? stmt 'assign) - (cond - [(reg? (caddr stmt)) - empty] - [(label? (caddr stmt)) - (list (label-name (caddr stmt)))] - [(const? (caddr stmt)) - empty] - [(op? (caddr stmt)) - (apply append (map collect-input (cdddr stmt)))] - [else - (error 'assemble "~a" stmt)])] - [(tagged-list? stmt 'perform) - (apply append (map collect-input (cddr stmt)))] - [(tagged-list? stmt 'test) - (apply append (map collect-input (cddr stmt)))] - [(tagged-list? stmt 'branch) - (collect-location (cadr stmt))] - [(tagged-list? stmt 'goto) - (collect-location (cadr stmt))] - [(tagged-list? stmt 'save) + [(AssignImmediateStatement? stmt) + (let ([v (AssignImmediateStatement-value stmt)]) + (cond + [(Reg? v) + empty] + [(Label? v) + (list (Label-name v))] + [(Const? v) + empty]))] + [(AssignPrimOpStatement? stmt) + (apply append (map collect-input (AssignPrimOpStatement-rands stmt)))] + [(PerformStatement? stmt) + (apply append (map collect-input (PerformStatement-rands stmt)))] + [(TestStatement? stmt) empty] - [(tagged-list? stmt 'restore) + [(BranchLabelStatement? stmt) + (list (BranchLabelStatement-label stmt))] + [(GotoStatement? stmt) + (collect-location (GotoStatement-target stmt))] + [(SaveStatement? stmt) empty] - [else - (error 'assemble "~a" stmt)]) + [(RestoreStatement? stmt) + empty]) (loop (rest stmts))))])))) @@ -135,7 +137,8 @@ ;; indirect jumps. ;; The only interesting case should be where there's a register assignment ;; whose value is a label. -(define (collect-indirect-jump-targets stmts) +#;(: collect-indirect-jump-targets ((Listof Statement) -> (Listof Symbol))) +#;(define (collect-indirect-jump-targets stmts) (define (collect-input an-input) (cond [(reg? an-input) @@ -196,70 +199,75 @@ ;; assemble-basic-block: basic-block -> string +(: assemble-basic-block (BasicBlock -> String)) (define (assemble-basic-block a-basic-block) (format "var ~a=function(){\nif(--MACHINE.callsBeforeTrampoline < 0) { throw ~a; }\n~a};" - (basic-block-name a-basic-block) - (basic-block-name a-basic-block) - (string-join (map assemble-stmt (basic-block-stmts a-basic-block)) + (BasicBlock-name a-basic-block) + (BasicBlock-name a-basic-block) + (string-join (map assemble-stmt (BasicBlock-stmts a-basic-block)) "\n"))) ;; assemble-stmt: stmt -> string +(: assemble-stmt (UnlabeledStatement -> String)) (define (assemble-stmt stmt) (cond - [(tagged-list? stmt 'assign) - (cond - [(reg? (caddr stmt)) - (format "MACHINE.~a=~a" - (cadr stmt) - (assemble-reg (caddr stmt)))] - [(label? (caddr stmt)) - (format "MACHINE.~a=~a;" (cadr stmt) - (assemble-label (caddr stmt)))] - [(const? (caddr stmt)) - (format "MACHINE.~a=~a;" - (cadr stmt) - (assemble-const (caddr stmt)))] - [(op? (caddr stmt)) - (format "MACHINE.~a=~a;" - (cadr stmt) - (assemble-op-expression (op-name (caddr stmt)) - (cdddr stmt)))] - [else - (error 'assemble "~a" stmt)])] - [(tagged-list? stmt 'perform) - (assemble-op-statement (op-name (cadr stmt)) - (cddr stmt))] - [(tagged-list? stmt 'test) + [(AssignImmediateStatement? stmt) + (let ([v (AssignImmediateStatement-value stmt)]) + (cond + [(Reg? v) + (format "MACHINE.~a=~a" + (AssignImmediateStatement-target stmt) + (assemble-reg v))] + [(Label? v) + (format "MACHINE.~a=~a;" + (AssignImmediateStatement-target stmt) + (assemble-label v))] + [(Const? v) + (format "MACHINE.~a=~a;" + (AssignImmediateStatement-target stmt) + (assemble-const v))]))] + + [(AssignPrimOpStatement? stmt) + (format "MACHINE.~a=~a;" + (AssignPrimOpStatement-target stmt) + (assemble-op-expression (AssignPrimOpStatement-op stmt) + (AssignPrimOpStatement-rands stmt)))] + [(PerformStatement? stmt) + (assemble-op-statement (PerformStatement-op stmt) + (PerformStatement-rands stmt))] + [(TestStatement? stmt) (format "if(~a){" - (assemble-op-expression (op-name (cadr stmt)) - (cddr stmt)))] - [(tagged-list? stmt 'branch) + (assemble-op-expression (TestStatement-op stmt) + (list (make-Reg (TestStatement-register-rand stmt)))))] + [(BranchLabelStatement? stmt) ;; the unbalanced } is deliberate: test and branch always follow each other. (format "return ~a();}" - (assemble-location (cadr stmt)))] - [(tagged-list? stmt 'goto) + (assemble-location (make-Label (BranchLabelStatement-label stmt))))] + [(GotoStatement? stmt) (format "return ~a();" - (assemble-location (cadr stmt)))] - [(tagged-list? stmt 'save) + (assemble-location (GotoStatement-target stmt)))] + [(SaveStatement? stmt) (format "MACHINE.stack.push(MACHINE.~a);" - (cadr stmt))] - [(tagged-list? stmt 'restore) + (SaveStatement-reg stmt))] + [(RestoreStatement? stmt) (format "MACHINE.~a=MACHINE.stack.pop();" - (cadr stmt))] - [else (error 'assemble "~a" stmt)])) + (RestoreStatement-reg stmt))])) + ;; fixme: use js->string +(: assemble-const (Const -> String)) (define (assemble-const stmt) - (let loop ([val (cadr stmt)]) - (cond [(symbol? val) - (format "~s" (symbol->string val))] - [(list? val) - (format "_list(~a)" (string-join (map loop val) - ","))] - [else - (format "~s" val)]))) + (let: loop : String ([val : Any (Const-const stmt)]) + (cond [(symbol? val) + (format "~s" (symbol->string val))] + [(list? val) + (format "_list(~a)" (string-join (map loop val) + ","))] + [else + (format "~s" val)]))) +(: assemble-op-expression (Symbol (Listof OpArg) -> String)) (define (assemble-op-expression op-name inputs) (let ([assembled-inputs (map assemble-input inputs)]) (case op-name @@ -280,7 +288,7 @@ (cond [(empty? inputs) "undefined"] [else - (let loop ([assembled-inputs assembled-inputs]) + (let: loop : String ([assembled-inputs : (Listof String) assembled-inputs]) (cond [(empty? assembled-inputs) "undefined"] @@ -311,7 +319,7 @@ [else (error 'assemble "~e" op-name)]))) - +(: assemble-op-statement (Symbol (Listof OpArg) -> String)) (define (assemble-op-statement op-name inputs) (let ([assembled-inputs (map assemble-input inputs)]) (case op-name @@ -341,28 +349,28 @@ - +(: assemble-input ((U Reg Const Label) -> String)) (define (assemble-input an-input) (cond - [(reg? an-input) + [(Reg? an-input) (assemble-reg an-input)] - [(const? an-input) + [(Const? an-input) (assemble-const an-input)] - [(label? an-input) - (assemble-label an-input)] - [else (error 'assemble-input "~e" an-input)])) - + [(Label? an-input) + (assemble-label an-input)])) + +(: assemble-location ((U Reg Label) -> String)) (define (assemble-location a-location) (cond - [(reg? a-location) + [(Reg? a-location) (assemble-reg a-location)] - [(label? a-location) - (assemble-label a-location)] - [else (error 'assemble-location "~e" a-location)])) + [(Label? a-location) + (assemble-label a-location)])) +(: assemble-reg (Reg -> String)) (define (assemble-reg a-reg) - (string-append "MACHINE." (symbol->string (cadr a-reg)))) - + (string-append "MACHINE." (symbol->string (Reg-name a-reg)))) +(: assemble-label (Label -> String)) (define (assemble-label a-label) - (symbol->string (label-name a-label))) \ No newline at end of file + (symbol->string (Label-name a-label))) \ No newline at end of file diff --git a/cm.rkt b/compile.rkt similarity index 72% rename from cm.rkt rename to compile.rkt index f2d34cc..c8ef070 100644 --- a/cm.rkt +++ b/compile.rkt @@ -1,7 +1,6 @@ #lang typed/racket/base (require "typed-structs.rkt" - #;"assemble.rkt" racket/list) (provide compile) @@ -19,6 +18,8 @@ (define-type CompileTimeEnvironment (Listof (Listof Symbol))) (define-type LexicalAddress (U (List Number Number) 'not-found)) + + ;; find-variable: symbol compile-time-environment -> lexical-address ;; Find where the variable should be located. (: find-variable (Symbol CompileTimeEnvironment -> LexicalAddress)) @@ -80,12 +81,12 @@ (define (compile-linkage linkage) (cond [(eq? linkage 'return) - (make-instruction-sequence '(cont) '() '((goto (reg cont))))] + (make-instruction-sequence '(cont) '() `(,(make-GotoStatement (make-Reg 'cont))))] [(eq? linkage 'next) empty-instruction-sequence] [else (make-instruction-sequence '() '() - `((goto (label ,linkage))))])) + `(,(make-GotoStatement (make-Label linkage))))])) (: end-with-linkage (Linkage InstructionSequence -> InstructionSequence)) (define (end-with-linkage linkage instruction-sequence) @@ -99,7 +100,7 @@ (make-instruction-sequence '() (list target) - `((assign ,target (const ,exp)))))) + `(,(make-AssignImmediateStatement target (make-Const exp)))))) (: compile-quoted (Quote CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-quoted exp cenv target linkage) @@ -107,7 +108,7 @@ (make-instruction-sequence '() (list target) - `((assign ,target (const ,(Quote-text exp))))))) + `(,(make-AssignImmediateStatement target (make-Const (Quote-text exp))))))) (: compile-variable (Var CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-variable exp cenv target linkage) @@ -120,23 +121,23 @@ (list target) ;; Slight modification: explicitly testing for ;; global variable binding before lookup. - `((perform (op check-bound-global!) - (const ,(Var-id exp)) - (reg env)) - (assign ,target - (op lookup-variable-value) - (const ,(Var-id exp)) - (reg env)))))] + `(,(make-PerformStatement 'check-bound-global! + (list (make-Const (Var-id exp)) + (make-Reg 'env))) + ,(make-AssignPrimOpStatement target + 'lookup-variable-value + (list (make-Const (Var-id exp)) + (make-Reg 'env))))))] [else (end-with-linkage linkage (make-instruction-sequence '(env) (list target) - `((assign ,target - (op lexical-address-lookup) - (const ,(first lexical-pos)) - (const ,(second lexical-pos)) - (reg env)))))]))) + `(,(make-AssignPrimOpStatement target + 'lexical-address-lookup + (list (make-Const (first lexical-pos)) + (make-Const (second lexical-pos)) + (make-Reg 'env))))))]))) (: compile-assignment (Assign CompileTimeEnvironment Target Linkage -> InstructionSequence)) @@ -155,11 +156,11 @@ (make-instruction-sequence '(env val) (list target) - `((perform (op set-variable-value!) - (const ,var) - (reg val) - (reg env)) - (assign ,target (const ok))))))] + `(,(make-PerformStatement 'set-variable-value! + (list (make-Const var) + (make-Reg 'val) + (make-Reg 'env))) + ,(make-AssignImmediateStatement target (make-Const 'ok))))))] [else (end-with-linkage linkage @@ -168,12 +169,12 @@ (make-instruction-sequence '(env val) (list target) - `((perform (op lexical-address-set!) - (const ,(first lexical-address)) - (const ,(second lexical-address)) - (reg env) - (reg val)) - (assign ,target (const ok))))))]))) + `(,(make-PerformStatement 'lexical-address-set! + (list (make-Const (first lexical-address)) + (make-Const (second lexical-address)) + (make-Reg 'env) + (make-Reg 'val))) + ,(make-AssignImmediateStatement target (make-Const 'ok))))))]))) ;; FIXME: exercise 5.43 @@ -190,11 +191,11 @@ (make-instruction-sequence '(env val) (list target) - `((perform (op define-variable!) - (const ,var) - (reg val) - (reg env)) - (assign ,target (const ok)))))))) + `(,(make-PerformStatement 'define-variable! + (list (make-Const var) + (make-Reg 'val) + (make-Reg 'env))) + ,(make-AssignImmediateStatement target (make-Const 'ok)))))))) (: compile-if (Branch CompileTimeEnvironment Target Linkage -> InstructionSequence)) @@ -215,8 +216,8 @@ (make-instruction-sequence '(val) '() - `((test (op false?) (reg val)) - (branch (label ,f-branch)))) + `(,(make-TestStatement 'false? 'val) + ,(make-BranchLabelStatement f-branch))) (parallel-instruction-sequences (append-instruction-sequences t-branch c-code) (append-instruction-sequences f-branch a-code)) @@ -246,16 +247,16 @@ (make-instruction-sequence '(env) (list target) - `((assign ,target - (op make-compiled-procedure) - (label ,proc-entry) - ;; TODO: rather than capture the whole - ;; environment, we may instead - ;; just capture the free variables. - ;; But that requires that we box - ;; up all set!-ed variables, in order - ;; to preserve semantics of set! - (reg env))))) + `(,(make-AssignPrimOpStatement target + 'make-compiled-procedure + (list (make-Label proc-entry) + ;; TODO: rather than capture the whole + ;; environment, we may instead + ;; just capture the free variables. + ;; But that requires that we box + ;; up all set!-ed variables, in order + ;; to preserve semantics of set! + (make-Reg 'env)))))) (compile-lambda-body exp cenv proc-entry)) after-lambda)))) @@ -269,11 +270,13 @@ '(env proc argl) '(env) `(,proc-entry - (assign env (op compiled-procedure-env) (reg proc)) - (assign env - (op extend-environment) - (reg argl) - (reg env)))) + ,(make-AssignPrimOpStatement 'env + 'compiled-procedure-env + (list (make-Reg 'proc))) + ,(make-AssignPrimOpStatement 'env + 'extend-environment + (list (make-Reg 'argl) + (make-Reg 'env))))) (compile-sequence (Lam-body exp) extended-cenv 'val 'return)))) (: compile-application (App CompileTimeEnvironment Target Linkage -> InstructionSequence)) @@ -294,12 +297,13 @@ (if (null? operand-codes) (make-instruction-sequence '() '(argl) - '((assign argl (const ())))) + `(,(make-AssignImmediateStatement 'argl (make-Const '())))) (let ([code-to-get-last-arg (append-instruction-sequences (car operand-codes) (make-instruction-sequence '(val) '(argl) - '((assign argl (op list) (reg val)))))]) + `(,(make-AssignPrimOpStatement 'argl 'list + (list (make-Reg 'val))))))]) (if (null? (cdr operand-codes)) code-to-get-last-arg (preserving '(env) @@ -314,7 +318,10 @@ (make-instruction-sequence '(val argl) '(argl) - '((assign argl (op cons) (reg val) (reg argl)))))]) + `(,(make-AssignPrimOpStatement 'argl + 'cons + (list (make-Reg 'val) + (make-Reg 'argl))))))]) (if (null? (cdr operand-codes)) code-for-next-arg (preserving '(env) @@ -330,8 +337,8 @@ (if (eq? linkage 'next) after-call linkage)]) (append-instruction-sequences (make-instruction-sequence '(proc) '() - `((test (op primitive-procedure?) (reg proc)) - (branch (label ,primitive-branch)))) + `(,(make-TestStatement 'primitive-procedure? 'proc) + ,(make-BranchLabelStatement primitive-branch))) (parallel-instruction-sequences (append-instruction-sequences compiled-branch @@ -342,10 +349,10 @@ (make-instruction-sequence '(proc argl) (list target) - `((assign ,target - (op apply-primitive-procedure) - (reg proc) - (reg argl))))))) + `(,(make-AssignPrimOpStatement target + 'apply-primitive-procedure + (list (make-Reg 'proc) + (make-Reg 'argl)))))))) after-call)))) (: compile-proc-appl (Target Linkage -> InstructionSequence)) @@ -355,31 +362,31 @@ (make-instruction-sequence '(proc) all-regs - `((assign cont (label ,linkage)) - (assign val (op compiled-procedure-entry) - (reg proc)) - (goto (reg val))))] + `(,(make-AssignImmediateStatement 'cont (make-Label linkage)) + ,(make-AssignPrimOpStatement 'val 'compiled-procedure-entry + (list (make-Reg 'proc))) + ,(make-GotoStatement (make-Reg 'val))))] [(and (not (eq? target 'val)) (not (eq? linkage 'return))) (let ([proc-return (make-label 'procReturn)]) (make-instruction-sequence '(proc) all-regs - `((assign cont (label ,proc-return)) - (assign val (op compiled-procedure-entry) - (reg proc)) - (goto (reg val)) + `(,(make-AssignImmediateStatement 'cont (make-Label proc-return)) + ,(make-AssignPrimOpStatement 'val 'compiled-procedure-entry + (list (make-Reg 'proc))) + ,(make-GotoStatement (make-Reg 'val)) ,proc-return - (assign ,target (reg val)) - (goto (label ,linkage)))))] + ,(make-AssignImmediateStatement target (make-Reg 'val)) + ,(make-GotoStatement (make-Label linkage)))))] [(and (eq? target 'val) (eq? linkage 'return)) (make-instruction-sequence '(proc cont) all-regs - '((assign val (op compiled-procedure-entry) - (reg proc)) - (goto (reg val))))] + `(,(make-AssignPrimOpStatement 'val 'compiled-procedure-entry + (list (make-Reg 'proc))) + ,(make-GotoStatement (make-Reg 'val))))] [(and (not (eq? target 'val)) (eq? linkage 'return)) (error 'compile "return linkage, target not val: ~s" target)])) @@ -413,9 +420,9 @@ (registers-needed seq1)) (list-difference (registers-modified seq1) (list first-reg)) - (append `((save ,first-reg)) + (append `(,(make-SaveStatement first-reg)) (statements seq1) - `((restore ,first-reg)))) + `(,(make-RestoreStatement first-reg)))) seq2) (preserving (cdr regs) seq1 seq2))))) diff --git a/typed-structs.rkt b/typed-structs.rkt index 72e575c..9efb901 100644 --- a/typed-structs.rkt +++ b/typed-structs.rkt @@ -30,11 +30,50 @@ + ;; instruction sequences +(define-type UnlabeledStatement (U + AssignImmediateStatement + AssignPrimOpStatement + PerformStatement + TestStatement + BranchLabelStatement + GotoStatement + SaveStatement + RestoreStatement)) +(define-type Statement (U UnlabeledStatement + Symbol ;; label + )) +(define-struct: AssignImmediateStatement ([target : Symbol] + [value : (U Const Reg Label)]) + #:transparent) +(define-struct: AssignPrimOpStatement ([target : Symbol] + [op : Symbol] + [rands : (Listof (U Label Reg Const))]) + #:transparent) +(define-struct: PerformStatement ([op : Symbol] + [rands : (Listof (U Label Reg Const))]) #:transparent) +(define-struct: TestStatement ([op : (U 'false? 'primitive-procedure?)] + [register-rand : Symbol]) #:transparent) +(define-struct: BranchLabelStatement ([label : Symbol]) #:transparent) +(define-struct: GotoStatement ([target : (U Label Reg)]) #:transparent) +(define-struct: SaveStatement ([reg : Symbol]) #:transparent) +(define-struct: RestoreStatement ([reg : Symbol]) #:transparent) + +(define-struct: Label ([name : Symbol])) +(define-struct: Reg ([name : Symbol])) +(define-struct: Const ([const : Any])) + +(define-type OpArg (U Const Label Reg)) + + + + + (define-type InstructionSequence (U Symbol instruction-sequence)) (define-struct: instruction-sequence ([needs : (Listof Symbol)] [modifies : (Listof Symbol)] - [statements : (Listof Any)]) #:transparent) + [statements : (Listof Statement)]) #:transparent) (define empty-instruction-sequence (make-instruction-sequence '() '() '())) (: make-label (Symbol -> Symbol)) @@ -53,7 +92,7 @@ (define (registers-modified s) (if (symbol? s) '() (instruction-sequence-modifies s))) -(: statements (InstructionSequence -> (Listof Any))) +(: statements (InstructionSequence -> (Listof Statement))) (define (statements s) (if (symbol? s) (list s) (instruction-sequence-statements s))) @@ -63,4 +102,9 @@ (define-type Target Symbol) ;; Linkage -(define-type Linkage (U 'return 'next Symbol)) \ No newline at end of file +(define-type Linkage (U 'return 'next Symbol)) + + + +(define-struct: BasicBlock ([name : Symbol] + [stmts : (Listof UnlabeledStatement)]) #:transparent)