
compile-argv-hook                        := ();

compile-include-argv := λ. if compile-argv-hook () (set compile-argv-hook (compile-argv-hook (
   \t 'pop \s '%r10                    \n # put argc in '%r10
   \t 'mov \s '$argv, \s '%r9          \n
   \t 'movq \s '$0, \s '0 \[ '%r9 \]   \n
   \t 'movq \s '$0, \s '8 \[ '%r9 \]   \n

   'before_main_argv:                  \n
   \t 'cmp \s '$0, \s '%r10            \n
   \t 'je \s 'before_main_end          \n

   \t 'pop \s '%r12                    \n #this = argv[i]
   \t 'mov \s '$0, \s '%r13            \n
   (close())                              #r8 is argv[i]

   \t 'mov \s '%r8, \s '0 \[ '%r9 \]   \n #set t.head = argv[i]
   \t 'mov \s '$0, \s '%r12            \n
   \t 'mov \s '$0, \s '%r13            \n
   (close())
   \t 'mov \s '%r8, \s '8 \[ '%r9 \]   \n #set t.tail = ()
   \t 'mov \s '%r8, \s '%r9            \n #set t = t.tail

   \t 'dec \s '%r10                    \n #dec argc
   \t 'jmp \s 'before_main_argv        \n

   'before_main_end:                   \n
)));

compile-append := λoutput_e gctx e . (tail(
   (set e (compile-expr( gctx e (expr::get-offset output_e) Unused )))
   (Expr(
      ((expr::get-frame output_e) (expr::get-frame e))
      ((expr::get-prog output_e) (expr::get-prog e))
      ((expr::get-unframe output_e) (expr::get-unframe e))
      ((expr::get-text output_e) (expr::get-text e))
      ((expr::get-data output_e) (expr::get-data e))
      (expr::get-context e)
      (expr::get-offset e)
   ))
));

compile-expr := λctx e offset used . (tail(
   (assert-typeof( 'compile-expr-dynamic::ctx ctx Context ))
   (if config-strict (compile-expr-strict( ctx e (if offset offset 0) used ))
                     (compile-expr-dynamic( ctx e offset used )))
));

compile-expr-dynamic := λctx e offset used . (tail(
   (assert-typeof( 'compile-expr-dynamic::ctx ctx Context ))
   (local e1)
   (local e2)
   (local e3)
   (local prog)
   (local tail_safe)
   (local return)
   (set return (match e (
      ()
      ( (Variable '$_) (tail(
         ()
         (Expr( () () () () () ctx offset ))
      )))
      ( (Variable 'argv) (tail(
         (compile-include-argv())
         (set prog (prog ( \t 'mov \s '$argv, \s '%r8 \n )))
         (set prog (prog ( \t 'mov \s '0 \[ '%r8 \] , '%r12 \n )))
         (set prog (prog ( \t 'mov \s '8 \[ '%r8 \] , '%r13 \n )))
         (Expr( () prog () () () ctx offset ))
      )))
      ( (Variable vname) (tail(
         ()
         (Expr( () (get-local(ctx vname)) () () () ctx offset ))
      )))
      ( (App( (App( (Literal :) (Literal lval) )) ltype )) (tail(
         ()
         (fail (TODO CompileExpressionLiteral e))
      )))
      ( (App( (App( (Literal :) inner_expr )) (Literal ltype) )) (tail(
         ()
         (compile-expr( ctx inner_expr offset used ))
      )))
      ( (App ((Variable 'local) (Variable fname))) (tail(
         (set e1 (declare-local(ctx fname offset)))
         (Expr( (expr::get-frame e1) ((yield-nil()) (expr::get-prog e1)) (expr::get-unframe e1) (expr::get-text e1) (expr::get-data e1) (expr::get-context e1) (expr::get-offset e1) ))
      )))
      ( (App ((Variable 'gensym-label) (Variable fname))) (tail(
         (set ctx (context::bind( ctx fname (Label (uuid())) )))
         (Expr( () () () () () ctx offset ))
      )))
      ( (App( (Lambda( (Variable lhs) rhs )) bind )) (tail(
         (set e1 (declare-local(ctx lhs offset)))
         (set e2 (compile-expr((expr::get-context e1) bind (expr::get-offset e1) Used)))
         (set e3 (compile-expr((expr::get-context e2) rhs (expr::get-offset e2) Used)))
         (set prog ( prog (yield-nil()) (expr::get-prog e1) ))
         (set prog ( prog (expr::get-prog e2) (set-local((expr::get-context e2) lhs)) ))
         (set prog ( prog (expr::get-prog e3) ))
         (Expr(
            ((expr::get-frame e1) (expr::get-frame e2) (expr::get-frame e3))
            prog
            ((expr::get-unframe e1) (expr::get-unframe e2) (expr::get-unframe e3))
            ((expr::get-text e1) (expr::get-text e2) (expr::get-text e3))
            ((expr::get-data e1) (expr::get-data e2) (expr::get-data e3))
            (expr::get-context e3) (expr::get-offset e3)
         ))
      )))
      ( (App( (App( (Variable 'set) (Variable vname) )) x )) (tail(
         (set e1 (compile-expr(ctx x offset Used)))
         (set prog ((expr::get-prog e1) (set-local(ctx vname))))
         (Expr( (expr::get-frame e1) prog (expr::get-unframe e1) (expr::get-text e1) (expr::get-data e1) (expr::get-context e1) (expr::get-offset e1) ))
      )))
      ( (App( (App( (Variable 'while) c )) d )) (tail(
         (set e1 (compile-expr(ctx c offset Used)))
         (set e2 (compile-expr((expr::get-context e1) d (expr::get-offset e1) Unused)))
         (local label_while_start)
         (set label_while_start (uuid()))
         (local label_while_end)
         (set label_while_end (uuid()))
         (set prog (prog (label_while_start ': \n)))
         (set prog (prog (expr::get-prog e1)))
         (set prog (prog (\t 'cmp \s '$0, \s '%r12 \n)))
         (set prog (prog (\t 'je \s label_while_end \n)))
         (set prog (prog (expr::get-prog e2)))
         (set prog (prog (\t 'jmp \s label_while_start \n)))
         (set prog (prog (label_while_end ': \n)))
         (set tail_safe True)
         (Expr( ((expr::get-frame e1) (expr::get-frame e2)) prog ((expr::get-unframe e1) (expr::get-unframe e2)) ((expr::get-text e1) (expr::get-text e2)) ((expr::get-data e1) (expr::get-data e2)) (expr::get-context e2) (expr::get-offset e2) ))
      )))
      ( (App( (Variable 'exit) (Literal _) )) (tail(
         (set tail_safe True)
         (set prog (
            \t 'mov \s '$60, \s '%rax          \n
            \t 'mov \s '$1 , \s '%rdi          \n
            \t 'syscall                        \n
         ))
         (Expr( () prog () () () ctx offset ))
      )))
      ( (App( (Variable 'eq) (App( l r )) )) (tail(
         (set e1 (compile-expr(ctx l offset Used)))
         (set e2 (compile-expr(ctx r offset Used)))
         (local eq-yield-end)
         (set eq-yield-end (uuid()))
         (local eq-yield-false)
         (set eq-yield-false (uuid()))
         (set prog (
            (expr::get-prog e1)
            \t 'cmp \s '$0, \s '%r12 \n
            \t 'je \s eq-yield-false \n
            \t 'cmp \s '$0, \s '%r13 \n
            \t 'jne \s eq-yield-false \n
            \t 'push \s '%r12 \n
            (expr::get-prog e2)
            \t 'cmp \s '$0, \s '%r12 \n
            \t 'je \s eq-yield-false \n
            \t 'cmp \s '$0, \s '%r13 \n
            \t 'jne \s eq-yield-false \n
            \t 'mov \s '%r12 , \s '%rax \n
            \t 'pop \s '%rbx         \n
            \t 'call \s 'streq       \n
            \t 'jmp \s eq-yield-end  \n
            eq-yield-false ':        \n
            \t 'mov \s '$0 , \s '%r12 \n
            \t 'mov \s '$0 , \s '%r13 \n
            eq-yield-end ':          \n
         ))
         (Expr(
            ((expr::get-frame e1) (expr::get-frame e2))
            prog
            ((expr::get-unframe e1) (expr::get-unframe e2))
            ((expr::get-text e1) (expr::get-text e2))
            ((expr::get-data e1) (expr::get-data e2))
            ctx
            offset
         ))
      )))
      ( (App( (Variable 'is) (App( l r )) )) (tail(
         (set e1 (compile-expr(ctx l offset Used)))
         (set e2 (compile-expr(ctx r offset Used)))
         (local is-yield-end)
         (set is-yield-end (uuid()))
         (local is-yield-false)
         (set is-yield-false (uuid()))
         (set prog (
            (expr::get-prog e1)
            \t 'push \s '%r12 \n
            \t 'push \s '%r13 \n
            (expr::get-prog e2)
            \t 'pop \s '%r15 \n
            \t 'pop \s '%r14 \n
            \t 'cmp \s '%r14, \s '%r12 \n
            \t 'jne \s is-yield-false \n
            \t 'cmp \s '%r15, \s '%r13 \n
            \t 'jne \s is-yield-false \n
            \t 'mov \s '$true, \s '%r12 \n
            \t 'mov \s '$0, \s '%r13 \n
            \t 'jmp \s is-yield-end  \n
            is-yield-false ':        \n
            \t 'mov \s '$0 , \s '%r12 \n
            \t 'mov \s '$0 , \s '%r13 \n
            is-yield-end ':          \n
         ))
         (Expr(
            ((expr::get-frame e1) (expr::get-frame e2))
            prog
            ((expr::get-unframe e1) (expr::get-unframe e2))
            ((expr::get-text e1) (expr::get-text e2))
            ((expr::get-data e1) (expr::get-data e2))
            ctx
            offset
         ))
      )))
      ( (App( (App( (App( (Variable 'if) c )) t )) f )) (tail(
         (set e1 (compile-expr(ctx c offset Used)))
         (set e2 (compile-expr( (expr::get-context e1) t (expr::get-offset e1) Used )))
         (set e3 (compile-expr( (expr::get-context e2) f (expr::get-offset e2) Used )))
         (local label_if_true)
         (set label_if_true (uuid()))
         (local label_if_end)
         (set label_if_end (uuid()))
         (set prog (prog (expr::get-prog e1)))
         (set prog (prog ( \t 'cmp \s '$0, '%r12 \n )))
         (set prog (prog ( \t 'jne \s label_if_true \n )))
         (set prog (prog ( \t 'cmp \s '$0, '%r13 \n )))
         (set prog (prog ( \t 'jne \s label_if_true \n )))
         (set prog (prog (expr::get-prog e3)))
         (set prog (prog ( \t 'jmp \s label_if_end \n )))
         (set prog (prog ( label_if_true ': \n )))
         (set prog (prog (expr::get-prog e2)))
         (set prog (prog ( label_if_end ': \n )))
         (Expr(
            ((expr::get-frame e1) (expr::get-frame e2) (expr::get-frame e3))
            prog
            ((expr::get-unframe e1) (expr::get-unframe e2) (expr::get-unframe e3))
            ((expr::get-text e1) (expr::get-text e2) (expr::get-text e3))
            ((expr::get-data e1) (expr::get-data e2) (expr::get-data e3))
            (expr::get-context e3)
            (expr::get-offset e3)
         ))
      )))
      ( (App( (App( (Variable 'match) t )) p )) (tail(
         (set e1 (compile-expr(ctx t offset Used)))
         (set e2 (yield-patterns((expr::get-context e1) p (expr::get-offset e1))))
         (local label_skip)
         (set label_skip (uuid()))
         (set prog ((expr::get-prog e1) (expr::get-prog e2)))
         (set prog (prog ( \t 'cmp \s '$0, \s '%r14 \n )))
         (set prog (prog ( \t 'jne \s label_skip \n )))
         (set prog (prog (yield-nil())))
         (set prog (prog ( label_skip ': \n )))
         (Expr(
            ((expr::get-frame e1) (expr::get-frame e2))
            prog
            ((expr::get-unframe e1) (expr::get-unframe e2))
            ((expr::get-text e1) (expr::get-text e2))
            ((expr::get-data e1) (expr::get-data e2))
            (expr::get-context e2)
            (expr::get-offset e2)
         ))
      )))
      ( (App ((Variable fname) arg)) (tail(
         ()
         (apply-calling-convention-s( ctx e fname arg offset used ))
      )))
      ( (Literal l) (tail(
         ()
         (yield-atom (ctx l offset))
      )))
      ( Nil (tail(
         (set prog (prog ( \t 'mov \s '$0 , '%r12 \n )))
         (set prog (prog ( \t 'mov \s '$0 , '%r13 \n )))
         (set tail_safe True)
         (Expr( () prog () () () ctx offset ))
      )))
      ( (App (l r)) (tail(
         (set tail_safe True)
         (yield-cons(ctx l r e offset used))
      )))
      ( (GlobalVariable mangled-name) (tail(
         (set prog (
            \t 'mov \s '$ mangled-name , \s '%r15   \n
            \t 'mov \s '0 \[ '%r15 \] , \s '%r12     \n
            \t 'mov \s '8 \[ '%r15 \] , \s '%r13     \n
         ))
         (Expr( () prog () () () ctx offset ))
      )))
      ( u (fail (UnrecognizedExpression u)) )
   )))
   (if tail_safe return (tail(
      ()
      (if (eq(used Tail)) (tail(
         (set prog (expr::get-prog return))
         (set prog (prog (\t 'call \s 'tail \n)))
         (Expr( (expr::get-frame return) prog (expr::get-unframe return) (expr::get-text return) (expr::get-data return) (expr::get-context return) (expr::get-offset return) ))
      )) (
         return
      ))
   )))
));

define-calling-convention-s := λ ctx fname lmb . (tail(
   ()
   (match lmb (
      ()
      ( (Lambda( lhs rhs )) (tail(
         (local e1)
         (local e2)
         (local text)
         (set e1 (destructure-args(ctx lhs ())))
         (set e2 (compile-expr((expr::get-context e1) rhs (expr::get-offset e1) Used)))
         (set text ( text (mangle-identifier( fname (typecheck-lookup lmb) )) ': \n ))
         (set text ( text (enter-function()) ))
         (set text ( text (expr::get-frame e1) (expr::get-frame e2) ))
         (set text ( text (expr::get-prog e1) (expr::get-prog e2) ))
         (set text ( text (exit-function()) ))
         (set text ( text (expr::get-text e1) (expr::get-text e2) ))
         (Expr(
            () () () text ( (expr::get-data e1) (expr::get-data e2) ) () ()
         ))
      )))
   ))
));

apply-calling-convention-s := λ ctx e fname arg offset used . (tail(
   (local e1)
   (local prog)
   (if (is-builtin fname) (tail(
      ()
      (if (eq(fname 'tail)) (tail(
         (set e1 (compile-expr(ctx arg offset Tail)))
         (Expr( (expr::get-frame e1) (expr::get-prog e1) (expr::get-unframe e1) (expr::get-text e1) (expr::get-data e1) (expr::get-context e1) (expr::get-offset e1) ))
      )) (tail(
         (set e1 (compile-expr(ctx arg offset Used)))
         (set prog ((expr::get-prog e1) (\t 'call \s (label-case fname) \n)))
         (Expr( (expr::get-frame e1) prog (expr::get-unframe e1) (expr::get-text e1) (expr::get-data e1) (expr::get-context e1) (expr::get-offset e1) ))
      )))
   )) (tail(
      ()
      (match (get-maybe-function(ctx fname (typecheck-lookup arg))) (
         ()
         ( (GlobalFunction mangledname) (tail(
            (set e1 (compile-expr(ctx arg offset Used)))
            (set prog ((expr::get-prog e1) (\t 'call \s mangledname \n)))
            (Expr( (expr::get-frame e1) prog (expr::get-unframe e1) (expr::get-text e1) (expr::get-data e1) (expr::get-context e1) (expr::get-offset e1) ))
         )))
         ( () (
            (fail (ReferenceToUndefinedVariable fname (typecheck-lookup arg)))
         ))
         ( v (tail(
            ()
            (yield-cons(ctx v arg e offset Used))
         )))
      ))
   )))
));

get-local := λctx v . (tail(
   ()
   (match (context::lookup( ctx v () )) (
      ()
      ( (Local( getter setter )) (
         getter
      ))
      ( (GlobalVariable( body )) (
         (
            \t 'mov \s '$ (label-case v) , \s '%r15 \n
            \t 'mov \s '0 \[ '%r15 \] , \s '%r12     \n
            \t 'mov \s '8 \[ '%r15 \] , \s '%r13     \n
         )
      ))
      ( () (
         fail (ReferenceToUndefinedVariable v)
      ))
      ( u (
         fail (UnexpectedLocal u)
      ))
   ))
));

set-local := λctx v . (tail(
   ()
   (match (context::lookup( ctx v () )) (
      ()
      ( (Local( getter setter )) (
         setter
      ))
      ( (GlobalVariable( body )) (
         (
            \t 'mov \s '$ (label-case v) , \s '%r15 \n
            \t 'mov \s '%r12 , \s '0 \[ '%r15 \] \n
            \t 'mov \s '%r13 , \s '8 \[ '%r15 \] \n
         )
      ))
      ( () (
         fail (AssignToUndefinedVariable v)
      ))
      ( u (
         fail (UnexpectedLocal u)
      ))
   ))
));

get-maybe-function := λctx v arg_type . (
   (match (context::lookup( ctx v () )) (
      ()
      ( (Local(getter setter)) (
         (Variable v)
      ))
      ( (GlobalFunction( body )) (
         (GlobalFunction( (mangle-identifier( v (typecheck-lookup body) )) ))
      ))
      ( (GlobalVariable( body )) (
         (GlobalVariable( label-case v ))
      ))
      (u (fail (UnexpectedFunction v u)))
   ))
);

declare-local := λctx vname offset . (tail(
   (local frame_this)
   (set frame_this (push-zero()))
   (local refer)
   (local eight)
   (set eight (s2i 8))
   (local sixteen)
   (set sixteen (s2i 16))
   (local set_this)
   (set set_this (
      \t 'mov \s '%r12, \s (
         (i2s( (mul( (add( (mul( sixteen offset )) eight)) (dec(())) )) ))
      ) \[ '%rbp \] \n
      \t 'mov \s '%r13, \s (
         (i2s( (mul( (add( (mul( sixteen offset )) sixteen)) (dec(())) )) ))
      ) \[ '%rbp \] \n
   ))
   (set refer (
      \t 'mov \s (
         (i2s( (mul( (add( (mul( sixteen offset )) eight)) (dec(())) )) ))
      ) \[ '%rbp \] , '%r12 \n
      \t 'mov \s (
         (i2s( (mul( (add( (mul( sixteen offset )) sixteen)) (dec(())) )) ))
      ) \[ '%rbp \] , '%r13 \n
   ))
   (set ctx (context::bind( ctx vname (Local( refer set_this )) )))
   (Expr(frame_this set_this () () () ctx (inc offset)))
));

destructure-args := λctx e offset . (match e (
   ()
   ( Nil (tail(
      ()
      (Expr( () () () () () ctx offset ))
   )))
   ( (App( (App( (Literal :) (Variable n) )) nt )) (tail(
      ()
      (declare-local(ctx n offset))
   )))
   ( (Variable n) (tail(
      ()
      (declare-local(ctx n offset))
   )))
   ( ( (App( h t )) ) (tail(
      (local ta)
      (set ta (destructure-args(ctx t offset)))
      (set offset (expr::get-offset ta))
      (set ctx (expr::get-context ta))
      (local ha)
      (set ha (destructure-args(ctx h offset)))
      (set offset (expr::get-offset ha))
      (set ctx (expr::get-context ha))
      (local prog)
      (set prog (prog (push-this()) ))
      (set prog (prog (\t 'call \s 'tail \n) ))
      (set prog (prog (expr::get-prog ta) ))
      (set prog (prog (pop-this()) ))
      (set prog (prog (\t 'call \s 'head \n) ))
      (set prog (prog (expr::get-prog ha) ))
      (Expr(
         ( (expr::get-frame ta) (expr::get-frame ha) )
         prog
         ( (expr::get-unframe ta) (expr::get-unframe ha) )
         () () ctx offset
      ))
   )))
   ( _ (
      (fail (InvalidFunctionParameters DidYouForgetToSeparateADotTokenInFunctionParam? e))
   ))
));

yield-cons-s := λctx l r ce offset used . (tail(
   (local e1)
   (local e2)
   (local prog)
   (local rused)
   (match used (
      ()
      ( Used (tail(
         (set e1 (compile-expr( ctx l offset Used )))
         (set offset (expr::get-offset e1))
         (set e2 (compile-expr( (expr::get-context e1) r (expr::get-offset e1) Used )))
         (set offset (expr::get-offset e2))
         (set prog (prog (expr::get-prog e1)))
         (set prog (prog (push-this())))
         (set prog (prog (expr::get-prog e2)))
         (set prog (prog (close())))
         (set prog (prog (pop-this())))
         (set prog (prog (\t 'push \s '%r8 \n)))
         (set prog (prog (close())))
         (set prog (prog (\t 'mov \s '%r8, \s '%r12 \n)))
         (set prog (prog (\t 'pop \s '%r13 \n)))
         (Expr( ((expr::get-frame e1) (expr::get-frame e2)) prog ((expr::get-unframe e1) (expr::get-unframe e2)) ((expr::get-text e1) (expr::get-text e2)) ((expr::get-data e1) (expr::get-data e2)) (expr::get-context e2) (expr::get-offset e2) ))
      )))
      ( _ (tail(
         (set rused ( if (eq(used Tail)) Used Unused ))
         (set e1 (compile-expr( ctx l offset Unused )))
         (set e2 (compile-expr( (expr::get-context e1) r (expr::get-offset e1) rused )))
         (Expr( ((expr::get-frame e1) (expr::get-frame e2)) ((expr::get-prog e1) (expr::get-prog e2)) ((expr::get-unframe e1) (expr::get-unframe e2)) ((expr::get-text e1) (expr::get-text e2)) ((expr::get-data e1) (expr::get-data e2)) (expr::get-context e2) (expr::get-offset e2) ))
      )))
   ))
));

yield-cons := λctx l r ce offset used . (tail(
   (local e1)
   (local e2)
   (yield-cons-s( ctx l r ce offset used ))
));

yield-atom-cache := ();
yield-atom := λctx a offset . (tail(
   (local cache)
   (set cache yield-atom-cache)
   (local id)
   (local data)
   (while cache (tail(
      (if (eq( (head (head cache)) a )) (
         (set id (tail (head cache)))
      ) ())
      (set cache (tail cache))
   )))
   (if id () (
      (set id (uuid()))
      (set data (id ': \n \t '.ascii \s " (escape-sequences a) " \n \t '.zero \s 1 \n))
      (set yield-atom-cache ((a id) yield-atom-cache))
   ))
   (Expr(
      ()
      (\t 'mov \s '$ id , \s '%r12 \n # $_.$1 = 0, .atom
       \t 'mov \s '$0, \s '%r13    \n # $_.$2 = 0, .head
      )
      ()
      ()
      data
      ctx
      offset
   ))
));

escape-sequences := λt . match t (
   ()
   ( () () )
   ( \\ \\\\ )
   ( \\: \: )
   ( \\o \o )
   ( \\n (\\ 'n))
   ( \\t \t )
   ( \\s \s )
   ( \\l \l )
   ( \\[ \[ )
   ( \\] \] )
   ( (l r) ( (escape-sequences l) (escape-sequences r) ) )
   ( s (tail(
      (local c)
      (local cs)
      (local is_escape)
      (while s (tail(
         (set c (head-string s))
         (set s (tail-string s))
         (match c (
            ()
            ( " (
               (set cs (cs \\ "))
               (set is_escape ())
            ))
            (\\ (
               (if is_escape (
                 (set cs ( cs \\\\ ))
                 (set is_escape ())
               ) (set is_escape True))
            ))
            (_ (
               (if is_escape (
                  (match c (
                     ()
                     ( , (set cs (cs .)))
                     ( _ (fail( InvalidEscapeCharacter (c s) )))
                  ))
               ) (set cs (cs (clone-rope c))))
               (set is_escape ())
            ))
         ))
      )))
      (clone-rope cs)
   )))
);

yield-patterns := λctx p offset . match p (
   ()
   ( Nil (tail(
      ()
      (Expr( () (\t 'mov \s '$0, \s '%r14 \n) () () () ctx offset ))
   )))
   ( (App( prev (App( lhs rhs )) )) (tail(
      (local e1)
      (local e2)
      (local e3)
      (local prog)
      (set e1 (yield-patterns(ctx prev offset)))
      (set e2 (destructure-pattern-lhs((expr::get-context e1) lhs (expr::get-offset e1))))
      (set e3 (compile-expr((expr::get-context e2) rhs (expr::get-offset e2) Used)))
      (local label_skip)
      (set label_skip (uuid()))
      (set prog (prog (expr::get-prog e1)))
      (set prog (prog (\t 'cmp \s '$0, \s '%r14 \n)))
      (set prog (prog (\t 'jne \s label_skip \n)))
      (set prog (prog (expr::get-prog e2)))
      (set prog (prog (\t 'cmp \s '$0, \s '%r14 \n)))
      (set prog (prog (\t 'je \s label_skip \n)))
      (set prog (prog (expr::get-prog e3)))
      (set prog (prog (\t 'mov \s '$1, \s '%r14 \n)))
      (set prog (prog (label_skip ': \n)))
      (Expr( 
         ((expr::get-frame e1) (expr::get-frame e2) (expr::get-frame e3))
         prog
         ((expr::get-unframe e1) (expr::get-unframe e2) (expr::get-unframe e3))
         ((expr::get-text e1) (expr::get-text e2) (expr::get-text e3))
         ((expr::get-data e1) (expr::get-data e2) (expr::get-data e3))
         ctx
         (expr::get-offset e3)
      ))
   )))
   ( _ (
      (fail (InvalidPatternsCase p))
   ))
);

destructure-pattern-lhs := λctx lhs offset . (tail(
   (local e1)
   (local e2)
   (local prog)
   (local label_skip)
   (match lhs (
      ()
      ( (Variable '_) (tail(
         ()
         (Expr( () (\t 'mov \s '$1, \s '%r14 \n) () () () ctx offset ))
      )))
      ( (Variable v) (tail(
         (set e1 (declare-local(ctx v offset)))
         (Expr(
            (expr::get-frame e1)
            ((expr::get-prog e1) \t 'mov \s '$1, \s '%r14 \n)
            (expr::get-unframe e1)
            (expr::get-text e1)
            (expr::get-data e1)
            (expr::get-context e1)
            (expr::get-offset e1)
         ))
      )))
      ( (Literal l) (tail(
         (set e1 (yield-atom(ctx l offset)))
         (set label_skip (uuid()))
         (set prog (prog (push-this())))
         (set prog (prog (\t 'cmp \s '$0, \s '%r12 \n)))
         (set prog (prog (\t 'je \s label_skip \n)))       
         (set prog (prog (\t 'cmp \s '$0, \s '%r13 \n)))
         (set prog (prog (\t 'jne \s label_skip \n)))       
         (set prog (prog (\t 'mov \s '%r12, \s '%rax \n)))
         (set prog (prog (expr::get-prog e1)))
         (set prog (prog (\t 'mov \s '%r12, \s '%rbx \n)))
         (set prog (prog (\t 'call \s 'streq \n)))
         (set prog (prog (\t 'cmp \s '$0, \s '%r12 \n)))
         (set prog (prog (\t 'je \s label_skip \n)))
         (set prog (prog (\t 'mov \s '$1, \s '%r14 \n)))
         (set prog (prog (label_skip ': \n)))
         (set prog (prog (pop-this())))
         (Expr( (expr::get-frame e1) prog (expr::get-unframe e1) (expr::get-text e1) (expr::get-data e1) (expr::get-context e1) (expr::get-offset e1) ))
      )))
      ( Nil (tail(
         (set label_skip (uuid()))
         (set prog (prog (\t 'cmp \s '$0, \s '%r12 \n)))
         (set prog (prog (\t 'jne \s label_skip \n)))
         (set prog (prog (\t 'cmp \s '$0, \s '%r13 \n)))
         (set prog (prog (\t 'jne \s label_skip \n)))
         (set prog (prog (\t 'mov \s '$1, \s '%r14 \n)))
         (set prog (prog (label_skip ': \n)))
         (Expr( () prog () () () ctx offset ))
      )))
      ( (App( l r )) (tail(
         (set e1 (destructure-pattern-lhs(ctx l offset)))
         (set e2 (destructure-pattern-lhs((expr::get-context e1) r (expr::get-offset e1))))
         (set label_skip (uuid()))
         (set prog (prog (\t 'mov \s '$0, \s '%r14 \n)))
         (set prog (prog (push-this())))
         (set prog (prog (\t 'cmp \s '$0, \s '%r13 \n)))
         (set prog (prog (\t 'je \s label_skip \n)))
         (set prog (prog (inline-head())))
         (set prog (prog (expr::get-prog e1)))
         (set prog (prog (pop-this())))
         (set prog (prog (push-this())))
         (set prog (prog (\t 'cmp \s '$0, \s '%r14 \n)))
         (set prog (prog (\t 'je \s label_skip \n)))
         (set prog (prog (\t 'mov \s '$0, \s '%r14 \n)))
         (set prog (prog (inline-tail())))
         (set prog (prog (expr::get-prog e2)))
         (set prog (prog (label_skip ': \n)))
         (set prog (prog (pop-this())))
         (Expr( 
            ((expr::get-frame e1) (expr::get-frame e2))
            prog
            ((expr::get-unframe e1) (expr::get-unframe e2))
            ((expr::get-text e1) (expr::get-text e2))
            ((expr::get-data e1) (expr::get-data e2))
            (expr::get-context e2)
            (expr::get-offset e2)
         ))
      )))
      (_ (fail (UnexpectedPattern lhs)))
   ))
));

