#
    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> ".."
        nomsu:define_compile_action("compile %actions to %lua", function(tree, \%actions, \%lua)
            local lua = Lua(tree.source, "nomsu:define_compile_action(")
            local specs = {}
            for i, action in ipairs(\%actions) do
                specs[i] = action:get_spec()
            end
            specs = repr(specs)
            if #specs > 80 then
                lua:append("\n    ",specs,",\n    ")
            else
                lua:append(specs,", ")
            end
            lua:append("function(tree")
            local args = {}
            for i,tok in ipairs(\%actions[1]) do
                if tok.type == "Var" then args[#args+1] = tok end
            end
            for i, arg in ipairs(args) do
                lua:append(", ", nomsu:tree_to_lua(arg))
            end
            local body_lua = nomsu:tree_to_lua(\%lua):as_statements("return ")
            body_lua:remove_free_vars(args)
            body_lua:declare_locals()
            lua:append(")\n    ", body_lua, "\nend);")
            return lua
        end);

# Compile-time action to make actions
immediately
    compile [action %actions %body] to
        lua> ".."
            local lua = Lua(tree.source, "nomsu:define_action(")
            local specs = {}
            for i, action in ipairs(\%actions) do
                specs[i] = action:get_spec()
            end
            specs = repr(specs)
            if #specs > 80 then
                lua:append("\n    ",specs,",\n    ")
            else
                lua:append(specs,", ")
            end
            lua:append("function(")
            local args = {}
            for i,tok in ipairs(\%actions[1]) do
                if tok.type == "Var" then args[#args+1] = tok end
            end
            for i, arg in ipairs(args) do
                lua:append(nomsu:tree_to_lua(arg))
                if i < #args then lua:append(", ") end
            end
            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);")
            return lua

# Macro to make nomsu macros
immediately
    compile [parse %shorthand as %longhand] to
        lua> ".."
            local lua = Lua(tree.source, "nomsu:define_compile_action(")
            local specs = {}
            for i, action in ipairs(\%shorthand) do
                specs[i] = action:get_spec()
            end
            specs = repr(specs)
            if #specs > 80 then
                lua:append("\n    ",specs,",\n    ")
            else
                lua:append(specs,", ")
            end
            lua:append("function(tree")
            local replacements = {}
            for i,tok in ipairs(\%shorthand[1]) do
                if tok.type == "Var" then
                    local lua_var = tostring(nomsu:tree_to_lua(tok))
                    replacements[tok.value] = lua_var
                    lua:append(", ", lua_var)
                end
            end
            MANGLE_INDEX = (MANGLE_INDEX or 0) + 1
            local function make_tree(t)
                if type(t) ~= 'table' and type(t) ~= 'userdata' then
                    return repr(t)
                elseif t.type == 'Var' and replacements[t.value] then
                    return replacements[t.value]
                elseif t.type == 'Var' then
                    return t.type.."("..repr(tostring(t.source))..", "..repr(t.value.."#"..tostring(MANGLE_INDEX))..")"
                elseif t.is_multi then
                    local bits = {repr(tostring(t.source))}
                    for i, entry in ipairs(t) do
                        bits[#bits+1] = make_tree(entry)
                    end
                    return t.type.."("..table.concat(bits, ", ")..")"
                else
                    return t.type.."("..repr(tostring(t.source))..", "..repr(t.value)..")"
                end
            end
            lua:append(")\n    local tree = ", make_tree(\%longhand), "\n    return nomsu:tree_to_lua(tree)\nend);")
            return lua

compile [remove action %action] to
    Lua ".."
        do
            local fn = ACTIONS[\(=lua "repr(\%action.stub)")]
            local stubs = ARG_ORDERS[fn]
            for stub in pairs(stubs) do
                ACTIONS[stub] = nil
            end
            ARG_ORDERS[fn] = nil
        end

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
                error("Invalid thing to convert to lua expr: "..tostring(\%tree))
            end
            return lua

    action [%tree as lua statements]
        =lua "nomsu:tree_to_lua(\%tree):as_statements()"
    
    action [%tree with vars %vars]
        =lua "\%tree:map(\%vars)"
    
    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));"

    action [%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)
    
immediately
    parse [%var <-write %code] as: lua> "\%var:append(\%code);"
    parse [to %var write %code] as: lua> "\%var: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 [nomsu] to: Lua value "nomsu"
    compile [%var as lua identifier] to: Lua value "nomsu:tree_to_lua(\(%var as lua expr))"

# Compiler tools
immediately
    compile [run %code] to
        Lua value "nomsu:run(Nomsu(\(quote "\(%code.source)"), \(%code as lua expr)))"

immediately
    compile [show lua %block] to
        lua> ".."
            local \%lua = nomsu:tree_to_lua(\%block);
            return Lua(tree.source, "print(", repr(tostring(\%lua)), ");");

immediately
    compile [say %message] to
        lua> ".."
            if \%message.type == "Text" then
                return Lua(tree.source, "print(", \(%message as lua expr), ");");
            else
                return Lua(tree.source, "print(tostring(", \(%message as lua expr), "));");
            end

# 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"

# Error functions
immediately
    compile [barf] to: Lua "error(nil, 0);"
    compile [barf %msg] to: Lua "error(\(%msg as lua expr), 0);"
    compile [assume %condition] to
        lua> "local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\%condition));"
        return
            Lua ".."
                if not \(%condition as lua expr) then
                    error(\(quote "\%assumption"), 0);
                end
    
    compile [assume %condition or barf %message] to
        Lua ".."
            if not \(%condition as lua expr) then
                error(\(%message as lua expr), 0);
            end

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