#..
    This File contains rules for making rules and macros and some helper functions to make
    that easier.

# Rule to make macros:
immediately:
    lua> ".."
        nomsu:defmacro("compile %macro_def to %body", function(nomsu, vars)
            nomsu:assert(\%macro_def.type == "List",
                "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
            nomsu:assert(\%body.type == "Block",
                "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
            local signature = {};
            for i, alias in ipairs(\%macro_def.value) do
                signature[i] = alias.src;
            end
            local body_lua = nomsu:tree_to_lua(\%body);
            body_lua = body_lua.statements or ("return "..body_lua.expr..";");
            local lua = ([[
        do
            local function macro(nomsu, vars)
                %s
            end
            local function macro_wrapper(...) return {expr=macro(...)}; end
            nomsu:defmacro(%s, macro_wrapper, %s);
        end]]):format(body_lua, nomsu:repr(signature), nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src)));
            return {statements=lua};
        end, \(__src__ 1));

    lua> ".."
        nomsu:defmacro("compile %macro_def to code %body", function(nomsu, vars)
            nomsu:assert(\%macro_def.type == "List",
                "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
            nomsu:assert(\%body.type == "Block",
                "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
            local signature = {};
            for i, alias in ipairs(\%macro_def.value) do
                signature[i] = alias.src;
            end
            local body_lua = nomsu:tree_to_lua(\%body);
            body_lua = body_lua.statements or ("return "..body_lua.expr..";");
            local lua = ([[
        do
            local function macro(nomsu, vars)
                %s
            end
            local function macro_wrapper(...) return {statements=macro(...)}; end
            nomsu:defmacro(%s, macro_wrapper, %s);
        end]]):format(body_lua, nomsu:repr(signature), nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src)));
            return {statements=lua};
        end, \(__src__ 1));

compile [rand] to: "math.random()"

# Rule to make rules:
immediately:
    compile [rule %signature = %body] to code:
        lua> ".."
            nomsu:assert(\%signature.type == "List",
                "Invalid type for rule definition signature. Expected List, but got: "..tostring(\%signature.type));
            nomsu:assert(\%body.type == "Block",
                "Invalid type for rule definition body. Expected Block, but got: "..tostring(\%body.type));
            local signature = {};
            for i, alias in ipairs(\%signature.value) do
                signature[i] = alias.src;
            end
            local body_lua = nomsu:tree_to_lua(\%body);
            body_lua = body_lua.statements or ("return "..body_lua.expr..";");
            local src = nomsu:dedent(nomsu:source_code(0));
            local def_lua = ([[
            nomsu:def(%s, function(nomsu, vars)
                %s
            end, %s);]]):format(nomsu:repr(signature), body_lua, nomsu:repr(src));
            return def_lua;

# Rule to make nomsu macros:
immediately:
    lua> ".."
        nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, vars)
            nomsu:assert(\%shorthand.type == "List",
                "Invalid type for parse definition signature. Expected List, but got: "..tostring(\%shorthand.type));
            nomsu:assert(\%longhand.type == "Block",
                "Invalid type for parse definition body. Expected Block, but got: "..tostring(\%longhand.type));
            local signature = {};
            for i, alias in ipairs(\%shorthand.value) do
                signature[i] = alias.src;
            end
            local template = {};
            for i, line in ipairs(\%longhand.value) do
                template[i] = nomsu:dedent(line.src);
            end
            signature, template = nomsu:repr(signature), nomsu:repr(table.concat(template, "\\n"));
            return {expr=([[
        nomsu:defmacro(%s, (function(nomsu, vars)
            local template = nomsu:parse(%s, %s);
            if #template.value == 1 then template = template.value[1]; end
            local replacement = nomsu:replaced_vars(template, vars);
            return nomsu:tree_to_lua(replacement);
        end), %s)]]):format(signature, template, nomsu:repr(\%shorthand.line_no), nomsu:repr(nomsu:source_code(0)))};
        end), \(__src__ 1));

rule [remove rule %stub] =:
    lua> ".."
        local def = nomsu.defs[\%stub];
        for _, alias in ipairs(def.aliases) do
            nomsu.defs[alias] = false;
        end

immediately:
    rule [%tree as lua] =:
        =lua "nomsu:tree_to_lua(\%tree).expr"
    rule [%tree as lua statements] =:
        lua> ".."
            local lua = nomsu:tree_to_lua(\%tree);
            return lua.statements or (lua.expr..";");
    rule [%tree as value] =:
        =lua "nomsu:tree_to_value(\%tree, vars)"
    compile [repr %obj] to:
        "nomsu:repr(\(%obj as lua))"
    compile [indented %obj] to:
        "nomsu:indent(\(%obj as lua))"
    compile [dedented %obj] to:
        "nomsu:dedent(\(%obj as lua))"
    compile [type %obj, type of %obj] to:
        "type(\(%obj as lua))"

parse [lua do> %block] as:
    lua> "do"
    lua> %block
    lua> "end"

compile [nomsu] to: "nomsu"
compile [nomsu's %key] to: "nomsu[\(%key as lua)]"
compile [nomsu %method %args] to: "nomsu[\(%method as lua)](nomsu, unpack(\(%args as lua)))"
compile [tree %tree with %replacements] to: ".."
    nomsu:replaced_vars(\(%tree as lua), \(%replacements as lua))

parse [rule %signature] as:
    (nomsu's "defs")->(nomsu "get_stub" [\%signature])

# Get the source code for a function
rule [help %rule] =:
    lua> ".."
        local fn_def = nomsu.defs[nomsu:get_stub(\%rule)]
        if not fn_def then
            nomsu:writeln("Rule not found: "..nomsu:repr(\%rule));
        else
            nomsu:writeln(fn_def.src or "<unknown source code>");
        end

# Compiler tools
parse [eval %code, run %code] as: nomsu "run" [%code]
rule [source code from tree %tree] =:
    lua> ".."
        local _,_,leading_space = \%tree.src:find("\\n(%s*)%S");
        if leading_space then
            local chunk1, chunk2 = \%tree.src:match(":%s*([^\\n]*)(\\n.*)");
            chunk2 = chunk2:gsub("\\n"..leading_space, "\\n");
            return chunk1..chunk2.."\\n";
        else
            return \%tree.src:match(":%s*(%S.*)").."\\n";
        end
parse [source code %body] as: source code from tree \%body

parse [parse tree %code] as: nomsu "tree_to_str" [\%code]

parse [enable debugging] as: lua> "nomsu.debug = true"
parse [disable debugging] as: lua> "nomsu.debug = false"