
require:
   "../location" ->
      <<:
   "./helpers" ->
      accum_flags
      match_error
      Body
      named_statement_matcher
      flatmacro
   "../pattern" ->
      parse_clauses
   "../util" as util


inject: mac


class MatchHandler:

   constructor{} =
      @wrapOrder = 1

   expand{info} =
      @placeholder = info.env.mark{#symbol{info.gensym{.ph}}}
      @placeholder <<: @location

   wrap{expr, info, opt} =
      parse_clauses with
         info, @placeholder, Body{expr}, opt & {wrap = null}



accum_flags! mac{"match"}! match_mac{match, info, form, expr, flags} =
   #pattern ->
      match expr:
         #void{} -> #special{MatchHandler{} <<: form} <<: form
         other -> #all{other <<: expr, #special{MatchHandler{} <<: form} <<: form}
   other ->
      opt = util.mkset{flags}
      to_match = #symbol{info.gensym{.m}} & {single_assignment = true}
      {value, body} = match expr:
         #data{Body! {*b}} -> {#value{null}, b}
         #data{v, Body! {*b}} -> {v, b}
      to_match <<: value
      `let [^to_match = ^value]: ^mbody` where mbody =
         parse_clauses with
            info, to_match, body
            opt & {
               fallback{target, pattern} =
                  match_error{target, pattern}
               wrap = null
            }

if_pattern = #seq{#multiple{_elif}, #multiple{_else, 0, 1}} where
   _elif = named_statement_matcher{.elif}
   _else = named_statement_matcher{.else}

mac{"if"}! if_mac{ctx, _, form, match} =
   #data{test, a, b} ->
      #if{test, a, b}
   #data{test, #multi! #multi{*match}} ->
      {`then{^a}`} ->
         #if{test, a, #value{undefined}}
      {`then{^a}`, `else{^b}`} ->
         #if{test, a, b}
      body ->
         match ctx:
            #expr{.multi} ->
               flatmacro{if_pattern} with
                  {#seq{#multiple{*elifs}, #multiple{*elses}}} ->
                     var rval = #value{undefined}
                     elses each `else{^body}` ->
                        rval = body
                     elifs.reverse{} each `elif{^cond, ^body}` ->
                        rval = #if{cond, body, rval}
                     #if{test, #multi{*body}, rval}
            else ->
               #if{test, #multi{*body}, #value{undefined}}

mac{"else"}! else_mac{match, _, form, _} =
   #pattern ->
      #ignore{}
   else ->
      throw E.syntax.else{msg, {node = form}} where
         msg = "'else' should be found inside an 'if' block"

mac{"not"}! not_mac{match, _, form, #data{#void{}, rhs} and arg} =
   #check or #project ->
      #nostep{form}
   #pattern ->
      #neg{rhs}
   other ->
      #send{#variable{"not"}, arg}

mac{"and"}! and_mac{match, _, form, #data{lhs, rhs} and arg} =
   #check or #project ->
      #nostep{form}
   #pattern ->
      #all{lhs, rhs}
   other ->
      #send{#variable{"and"}, arg}

mac{"or"}! or_mac{match, _, form, #data{lhs, rhs} and arg} =
   #check or #project ->
      #nostep{form}
   #pattern ->
      #any{lhs, rhs}
   other ->
      #send{#variable{"or"}, arg}

mac{"when"}! when_mac{context, _, form, #data{match, condition}} =
   #void{} -> #test{condition, #ignore{}}
   other -> #test{condition, other}

mac{"?"}! check_mac{match context, info, form, #data{chk, target}} =
   do:
      checker = match info.step{#check{context}, chk}:
         === chk -> `getChecker{^chk}`
         checker -> checker
   #pattern ->
      subp = if{target == #void, #ignore, target}
      match checker:
         #raw{checker} ->
            checker
         else ->
            #check{checker, subp}
   when target == #void ->
      checker
   other ->
      `[^checker]{^target}`

mac{"!"}! project_mac{match context, info, form, #data{proj, target}} =
   do:
      projector = match info.step{#project{context}, proj}:
         === proj -> `getProjector{^proj}`
         projector -> projector
   #pattern ->
      subp = if{target == #void, #ignore, target}
      match projector:
         #raw{projector} ->
            projector
         #unconditional{projector} ->
            #project{projector, subp, true}
         else ->
            #project{projector, subp}
   when target == #void ->
      `{x} -> [^projector]{x}[1]`
   other ->
      `[^projector]{^target}[1]`
