use "core"

compile [@] to: Lua value "self"

compile [@%var] to:
    lua> ".."
        local key_lua = repr(\%var.value);
        local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
                       or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
        if key_attr then
            return Lua.value(tree.source, "self."..key_attr);
        elseif key_lua:sub(1,1) == "[" then
            key_lua = " "..key_lua;
        end
        return Lua.Value(tree.source, "self["..key_lua.."]");

compile [@%var <- %val] to:
    lua> ".."
        local val_lua = \(%val as lua expr);
        local key_lua = repr(\%var.value);
        local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
                       or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
        if key_attr then
            return Lua(tree.source, "self.", key_attr, " = ", val_lua, ";");
        elseif key_lua:sub(1,1) == "[" then
            key_lua = " "..key_lua.." ";
        end
        return Lua(tree.source, "self[", key_lua, "] = ", val_lua, ";");

compile [as %instance %body] to:
    %body_lua <- (%body as lua)
    lua> "\%body_lua:convert_to_statements();"
    return
        Lua ".."
            do
            local self = \(%instance as lua expr);
            local global_actions = ACTIONS;
            local ACTIONS = setmetatable({}, {__index=function(_,key)
                local method = self[key];
                if method then return (function(...) return method(self, ...); end); end
                return global_actions[key];
            end});
            \%body_lua
            end

compile [define object %classname %class_body] to:
    %class_identifier <- (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)")
    if: %class_identifier is ""
        %class_identifier <- "class"
    %methods <- []
    %__index <- %class_identifier
    %__newindex <- "nil"
    for %line in (%class_body's "value"):
        if: (%line's "type") is "Comment"
            do next %line
        if: ((%line's "type") is "FunctionCall") and ((%line's stub) is "slots %")
            %slot_index_clauses <- []
            %slot_newindex_clauses <- []
            %slots <- %line.value.2.value
            for %slot_index = %slot_var in %slots:
                to %slot_index_clauses add ".."
                    if key == \(repr (%slot_var's "value")) or key == \(repr (%slot_var as lua expr)) then
                        return rawget(self, \%slot_index);
                    end
                to %slot_newindex_clauses add ".."
                    if key == \(repr (%slot_var's "value")) or key == \(repr (%slot_var as lua expr)) or key == \%slot_index then
                        rawset(self, \%slot_index, value);
                    end

            %__index <- ".."
                function(self, key)
                    \(%slot_index_clauses joined with "\n")
                    return \%class_identifier[key];
                end
            %__newindex <- ".."
                function(self, key, value)
                    \(%slot_newindex_clauses joined with "\n")
                    error("Attempt to store data in "..repr(key)..", which is not a valid slot on "..tostring(self.class));
                end
            do next %line
        assume ((%line.type is "FunctionCall") and ((%line's stub) is "action % %"))
        ..or barf "Only action definitions are supported inside 'define object % %', not \(%line's "src")"
        %actions <- %line.value.2
        %body <- %line.value.3
        lua> ".."
            local stubs = {};
            for i, action in ipairs(\%actions.value) do
                stubs[i] = nomsu:tree_to_named_stub(action);
            end
            local args = {};
            for i,tok in ipairs(\%actions.value[1].value) do
                if tok.type == "Var" then args[#args+1] = nomsu:var_to_lua_identifier(tok.value); end
            end
            local arg_set = {};
            for i, arg in ipairs(args) do arg_set[arg] = true; end
            local body_lua = nomsu:tree_to_lua(\%body);
            body_lua:convert_to_statements();
            body_lua:declare_locals();
            local lua_fn_args = table.concat({"self", unpack(args)}, ", ");
            local def_tree = nomsu.compilestack[#nomsu.compilestack];

            local compiled_args = {};
            for i, arg in ipairs(args) do
                compiled_args[i] = "nomsu:tree_to_lua("..arg..")";
            end
            compiled_args = table.concat(compiled_args, "..', '..");
            table.insert(\%methods, ([==[
                %s[ %s] = function(%s)
                    %s
                end
            ]==]):format(
                \%class_identifier, repr(stubs[1]), lua_fn_args,
                    body_lua));

    return
        Lua ".."
            do -- \%class_identifier
                -- Create the class object:
                local \%class_identifier = setmetatable({
                    name=\(%classname as lua expr), instances=setmetatable({}, {__mode="k"}),
                }, {
                    __tostring=function(c) return c.name; end,
                    __call=function(cls, initial_values)
                        local inst = setmetatable({}, cls.instance_metatable);
                        for k,v in pairs(initial_values) do inst[k] = v; end
                        cls.instances[inst] = true;
                        if inst['set % up'] then
                            inst['set % up'](inst);
                        end
                        return inst;
                    end,
                });
                \%class_identifier.class = \%class_identifier;

                -- Define the methods:
            \(%methods joined with "\n")

                -- Define class methods for instantiating and accessing instances:
                \%class_identifier.instance_metatable = {
                    __index=\%__index,
                    __newindex=\%__newindex,
                    __tostring=\%class_identifier['as text'] or function(inst)
                        return "<"..inst.class.name..": "..nomsu.ids[inst]..">";
                    end,
                    __len=\%class_identifier['size of'],
                    __unm=\%class_identifier['-'],
                    __add=\%class_identifier['+ %'],
                    __sub=\%class_identifier['- %'],
                    __mul=\%class_identifier['* %'],
                    __div=\%class_identifier['/ %'],
                    __mod=\%class_identifier['wrapped around %'],
                    __pow=\%class_identifier['^ %'],
                    __band=\%class_identifier['AND %'],
                    __bor=\%class_identifier['OR %'],
                    __bxor=\%class_identifier['XOR %'],
                    __bshl=\%class_identifier['<< %'],
                    __bshr=\%class_identifier['>> %'],
                    __eq=\%class_identifier['= %'],
                    __lt=\%class_identifier['< %'],
                    __le=\%class_identifier['<= %'],
                };
                nomsu:define_action("instances of "..\%class_identifier.name, "lib/class.nom", function(__callsite)
                    return utils.keys(\%class_identifier.instances);
                end, "");
                nomsu:define_action("new "..\%class_identifier.name.." %instance", "lib/class.nom", function(__callsite, _instance)
                    return \%class_identifier(_instance);
                end, "");
                nomsu:define_action("new "..\%class_identifier.name, "lib/class.nom", function(__callsite)
                    return \%class_identifier({});
                end, "");
            end -- End of definition of \%class_identifier
            \("\n\n")