require "lib/metaprogramming.nom"
require "lib/operators.nom"
require "lib/utils.nom"

# Conditionals
macro statement [if %condition %if_body] =:
    ".."|if \%condition as lua\ then
        |    \(lua expr "vars.if_body.value") as lua\
        |end

macro statement [if %condition %if_body else %else_body] =:
    ".."|if \%condition as lua\ then
        |    \(lua expr "vars.if_body.value") as lua\
        |else
        |    \(lua expr "vars.else_body.value") as lua\
        |end

# Return
macro block [return] =: "return nil"
macro block [return %return-value] =: ".."
    |return \%return-value as lua\

macro [do %action] =: ".."
    |(\%action as lua\)(compiler, setmetatable({}, {__index=vars}))


# GOTOs
macro statement [-> %label] =: ".."
    |::label_\compiler "var_to_lua_identifier" [%label]\::
macro statement [go to %label] =: ".."
    |goto label_\compiler "var_to_lua_identifier" [%label]\

# Loop control flow
macro statement [break] =: "break"
macro statement [break for] =: "goto break_for"
macro statement [break for-all] =: "goto break_for_all"
macro statement [break repeat] =: "goto break_repeat"
macro statement [break repeat-until] =: "goto break_repeat_until"
macro statement [break repeat-while] =: "goto break_repeat_while"
macro statement [break %var, stop getting %var, no more %var] =: ".."
    |goto break_\compiler "var_to_lua_identifier" [%var]\

macro statement [continue] =: "continue"
macro statement [continue for] =: "goto continue_for"
macro statement [continue for-all] =: "goto continue_for_all"
macro statement [continue repeat] =: "goto continue_repeat"
macro statement [continue repeat-until] =: "goto continue_repeat_until"
macro statement [continue repeat-while] =: "goto continue_repeat_while"
macro statement [continue %var, go to next %var, on to the next %var] =: ".."
    |goto continue_\compiler "var_to_lua_identifier" [%var]\
# TODO: add labeled break/continue?

# While loops
macro block [repeat %body] =:
    ".."|while true do
        |    \(lua expr "vars.body.value") as lua\
        |    ::continue_repeat::
        |end
        |::break_repeat::
macro block [repeat while %condition %body] =:
    ".."|while \%condition as lua\ do
        |    \(lua expr "vars.body.value") as lua\
        |    ::continue_repeat_while::
        |end
        |::break_repeat_while::
macro block [repeat until %condition %body] =:
    ".."|while not (\%condition as lua\) do
        |    \(lua expr "vars.body.value") as lua\
        |    ::continue_repeat_until::
        |end
        |::break_repeat_until::

# For loops
macro block [for %var in %iterable %body] =:
    %var-type =: lua expr "vars.var.type"
    assert (%var-type == "Var") ".."
        |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\
    # This trashes the loop variables, just like in Python.
    ".."
        |for i,value in ipairs(\%iterable as lua\) do
        |    \%var as lua\ = value
        |    \(lua expr "vars.body.value") as lua\
        |    ::continue_for::
        |    ::continue_\compiler "var_to_lua_identifier" [%var]\::
        |end
        |::break_for::
        |::break_\compiler "var_to_lua_identifier" [%var]\::

macro block [for all %iterable %body] =:
    pass # TODO: fix compiler bug
    # This trashes the loop variables, just like in Python.
    ".."|for i,value in ipairs(\%iterable as lua\) do
        |    vars.it = value
        |    \(lua expr "vars.body.value") as lua\
        |    ::continue_for_all::
        |end
        |::break_for_all::

# Switch statement/multi-branch if
macro block [when %body] =:
    %result =: ""
    for %statement in (lua expr "vars.body.value.value"):
        %func-call =: lua expr "vars.statement.value"
        assert ((lua expr "vars['func-call'].type") == "FunctionCall") ".."
            |Invalid format for 'when' statement. Only '?' blocks are allowed.
        %tokens =: lua expr "vars['func-call'].value"
        %q =: lua expr "vars.tokens[1]"
        assert (((lua expr "vars.q.type") == "Word") and ((lua expr "vars.q.value") == "?")) ".."
            |Invalid format for 'when' statement. Lines must begin with '?'
        %thunk =: lua expr "vars.tokens[#vars.tokens]"
        assert ((lua expr "vars.thunk.type") == "Thunk") ".."
            |Invalid format for 'when' statement. Lines must have a body.
        %condition_bits =: []
        for %i in (2 up to (lua expr "#vars.tokens")):
            lua block "table.insert(vars['condition_bits'], vars.tokens[vars.i])"

        if (lua expr "#vars.condition_bits == 0"):
            %result join=: ".."
                |
                |do
                |    local ret
                |    \(lua expr "vars.thunk.value") as lua\
                |    return ret
                |end
        ..else:
            if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
                %condition =: lua expr "vars.condition_bits[1]"
            ..else:
                %condition =: dict [..]
                    ["type",lua expr "vars['func-call'].type"]
                    ["src",lua expr "vars['func-call'].src"]
                    ["value", %condition_bits]
            %result join=: ".."
                |
                |if \%condition as lua\ then
                |    local ret
                |    \(lua expr "vars.thunk.value") as lua\
                |    return ret
                |end

    %result

# Switch statement
macro block [when %branch-value %body] =:
    %result =: ".."|local branch_value = \%branch-value as lua\
    for %statement in (lua expr "vars.body.value.value"):
        %func-call =: lua expr "vars.statement.value"
        assert ((lua expr "vars['func-call'].type") == "FunctionCall") ".."
            |Invalid format for 'when' statement. Only == blocks are allowed.
        %tokens =: lua expr "vars['func-call'].value"
        %eq =: lua expr "vars.tokens[1]"
        assert (((lua expr "vars.eq.type") == "Word") and ((lua expr "vars.eq.value") == "==")) ".."
            |Invalid format for 'when' statement. Lines must begin with '=='
        %thunk =: lua expr "vars.tokens[#vars.tokens]"
        assert ((lua expr "vars.thunk.type") == "Thunk") ".."
            |Invalid format for 'when' statement. Lines must have a body.
        %condition_bits =: []
        for %i in (2 up to (lua expr "#vars.tokens")):
            lua block "table.insert(vars.condition_bits, vars.tokens[vars.i])"
        if (lua expr "#vars.condition_bits == 0"):
            %result join=: ".."
                |
                |do
                |    local ret
                |    \(lua expr "vars.thunk.value") as lua\
                |    return ret
                |end
        ..else:
            if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
                %condition =: (lua expr "vars.condition_bits[1]")
            ..else:
                %condition =: dict [..]
                    ["type",lua expr "vars['func-call'].type"]
                    ["src",lua expr "vars['func-call'].src"]
                    ["value", %condition_bits]
            %result join=: ".."
                |
                |if compiler.utils.equivalent(branch_condition, \%condition as lua\) then
                |    local ret
                |    \(lua expr "vars.thunk.value") as lua\
                |    return ret
                |end
    %result