#
    This File contains actions for making actions and compile-time actions and some helper
    functions to make that easier.

# Compile-time action to make compile-time actions:
immediately
    lua> ".."
        A_give_1_nickname_2 = compile_time(function(tree, \%action, \%nickname, is_compile_time)
            local function arg_to_string(a) return tostring(nomsu:tree_to_lua(a)) end
            local action_args = table.map(\%action:get_args(), arg_to_string)
            local nickname_args = table.map(\%nickname:get_args(), arg_to_string)
            if utils.equivalent(action_args, nickname_args) then
                return Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = A", string.as_lua_id(\%action.stub)) 
            end
            local lua = Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = ")
            if is_compile_time or COMPILE_TIME[_ENV["A"..string.as_lua_id(\%action.stub)]] then
                lua:append("compile_time")
                table.insert(action_args, 1, "tree")
                table.insert(nickname_args, 1, "tree")
            end
            lua:append("(function(")
            lua:concat_append(nickname_args, ", ")
            lua:append(")\n    return A", string.as_lua_id(\%action.stub), "(")
            lua:concat_append(action_args, ", ")
            lua:append(")\nend)")
            return lua
        end)

        __MANGLE_INDEX = 0
        A_parse_1_as_2 = compile_time(function(tree, \%actions, \%body)
            local replacements = {}
            for i,arg in ipairs(\%actions[1]:get_args()) do
                replacements[arg[1]] = tostring(nomsu:tree_to_lua(arg))
            end
            local function make_tree(t)
                if not AST.is_syntax_tree(t) then
                    return repr(t)
                elseif t.type ~= 'Var' then
                    local args = table.map(t, make_tree)
                    table.insert(args, 1, repr(tostring(t.source)))
                    return t.type.."("..table.concat(args, ", ")..")"
                elseif replacements[t[1]] then
                    return replacements[t[1]]
                else
                    return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX))"
                end
            end
            local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = compile_time(function(tree")
            for _,arg in ipairs(\%actions[1]:get_args()) do
                lua:append(", ", nomsu:tree_to_lua(arg))
            end
            lua:append(")\n    __MANGLE_INDEX = __MANGLE_INDEX + 1",
                "\n    local tree = ", make_tree(\%body),
                "\n    local lua = nomsu:tree_to_lua(tree)",
                "\n    lua:remove_free_vars({")
            local vars = table.map(\%actions[1]:get_args(), function(a)
                return "Var("..repr(tostring(a.source))..", "..repr(a[1])..")"
            end)
            lua:concat_append(vars, ", ")
            lua:append("})\n    return lua\nend)")

            for i=2,#\%actions do
                lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], true))
            end
            return lua
        end)

        A_action_1_2 = compile_time(function(tree, \%actions, \%body, is_compile_time)
            local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = ")
            if is_compile_time then lua:append("compile_time") end
            lua:append("(function(")
            local args = \%actions[1]:get_args()
            local lua_args = table.map(args, function(a) return nomsu:tree_to_lua(a) end)
            if is_compile_time then table.insert(lua_args, 1, "tree") end
            lua:concat_append(lua_args, ", ")
            local body_lua = nomsu:tree_to_lua(\%body):as_statements("return ")
            body_lua:remove_free_vars(args)
            body_lua:declare_locals()
            lua:append(")\n    ", body_lua, "\nend)")
            for i=2,#\%actions do
                lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], is_compile_time))
            end
            return lua
        end)

        A_compile_1_to_2 = compile_time(function(tree, \%actions, \%body)
            return A_action_1_2(tree, \%actions, \%body, true)
        end)

compile [remove action %action] to
    Lua ".."
        A\(=lua "string.as_lua_id(\(%action.stub))") = nil
        ARG_ORDERS[fn] = nil
        COMPILE_TIME[fn] = nil

immediately
    action [%tree as nomsu]
        =lua "nomsu:tree_to_nomsu(\%tree)"

    action [%tree as inline nomsu]
        =lua "nomsu:tree_to_nomsu(\%tree, true)"

    action [%tree as lua]
        =lua "nomsu:tree_to_lua(\%tree)"

    action [%tree as lua expr]
        lua> ".."
            local lua = nomsu:tree_to_lua(\%tree)
            if not lua.is_value then
                compile_error(\%tree, "Invalid thing to convert to lua expr:\n%s")
            end
            return lua

    action [%tree as lua statements]
        =lua "nomsu:tree_to_lua(\%tree):as_statements()"

    action [%tree as lua return]
        =lua "nomsu:tree_to_lua(\%tree):as_statements('return ')"

    action [%var as lua identifier, %var as lua id]
        lua> ".."
            if type(\%var) == 'string' then return string.as_lua_id(\%var)
            elseif \%var.type == 'Var' then return string.as_lua_id(\%var[1])
            elseif \%var.type == 'Action' then return "A"..string.as_lua_id(\%var.stub)
            end
    
immediately
    compile [%tree with %t -> %replacement] to
        Lua value ".."
            \(%tree as lua expr):map(function(\(%t as lua expr))
                \(%replacement as lua return)
            end)
    
    compile [declare locals in %code] to
        Lua value "\(%code as lua expr):declare_locals()"
    
    compile [declare locals %locals in %code] to
        Lua value "\(%code as lua expr):declare_locals(\(%locals as lua expr))"
    
    compile [remove free vars %vars from %code] to
        Lua "\(%code as lua expr):remove_free_vars(\(%vars as lua expr));"

    parse [%lua <-write %code, to %lua write %code] as: lua> "\%lua:append(\%code);"

immediately
    compile [quote %s] to
        Lua value ".."
            ('"'..\(%s as lua expr):gsub("\\\\", "\\\\\\\\"):gsub("\n","\\\\n"):gsub('"', '\\\\"')..'"')
    compile [type of %obj] to: Lua value "type(\(%obj as lua expr))"

immediately
    compile [parse %text] to
        Lua value ".."
            nomsu:parse(Nomsu("\("\(%text.source)")", \(%text as lua expr)))

    compile [run %nomsu_code] to
        Lua value "nomsu:run(Nomsu(\(quote "\(%nomsu_code.source)"), \(%nomsu_code as lua expr)))"

    action [run tree %tree, %tree as value]
        lua> ".."
            if \%tree.type == 'Text' and #\%tree == 1 and type(\%tree[1]) == 'string' then
                return \%tree[1]
            end
            local lua = Lua(\%tree.source, "return ",nomsu:tree_to_lua(\%tree))
            return nomsu:run_lua(lua)

    compile [compile %block, compiled %block, %block compiled] to
        Lua value "nomsu:tree_to_lua(\(%block as lua))"

# Return
immediately
    # Return statement is wrapped in a do..end block because Lua is unhappy if you
        put code after a return statement, unless you wrap it in a block.
    compile [return] to: Lua "do return; end"
    compile [return %return_value] to: Lua "do return \(%return_value as lua expr); end"

# Literals
immediately
    compile [yes] to: Lua value "true"
    compile [no] to: Lua value "false"
    compile [nothing, nil, null] to: Lua value "nil"