<commit_before>local types = require "titan-compiler.types"

local coder = {}

local codeexp, codestat

local function quotestr(s)
    s = s:gsub("\\", "\\\\")
        :gsub("\a", "\\a")
        :gsub("\b", "\\b")
        :gsub("\f", "\\f")
        :gsub("\n", "\\n")
        :gsub("\r", "\\r")
        :gsub("\t", "\\t")
        :gsub("\v", "\\v")
        :gsub("\"", "\\\"")
    return '"' .. s .. '"'
end

local function node2literal(node)
    local tag = node._tag
    if tag == "Exp_Integer" or tag == "Exp_Float" then
        return tonumber(node.value)
    elseif tag == "Exp_Unop" and node.op == "-" then
        local lexp = node2literal(node.exp)
        return lexp and -lexp
    else
        return nil
    end
end

local function getslot(t, c, s)
    c = c and c .. " =" or ""
    local tmpl
    if types.equals(t, types.Integer) then tmpl = "%s ivalue(%s)"
    elseif types.equals(t, types.Float) then tmpl = "%s fltvalue(%s)"
    elseif types.equals(t, types.Boolean) then tmpl = "%s bvalue(%s)"
    elseif types.equals(t, types.Nil) then tmpl = "%s 0"
    elseif types.equals(t, types.String) then tmpl = "%s tsvalue(%s)"
    elseif types.has_tag(t, "Array") then tmpl = "%s hvalue(%s)"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format(tmpl, c, s)
end

local function checkandget(t, c, s, lin)
    local tag
    if types.equals(t, types.Integer) then tag = "integer"
    elseif types.equals(t, types.Float) then 
        return string.format([[
            if(ttisinteger(%s)) { %s = (lua_Number)ivalue(%s); }
            else if(ttisfloat(%s)) { %s = fltvalue(%s); }
            else luaL_error(L, "type error at line %d, expected float but found %%s", lua_typename(L, ttnov(%s)));
        ]], s, c, s, s, c, s, lin, s)
        elseif types.equals(t, types.Boolean) then tag = "boolean"
    elseif types.equals(t, types.Nil) then tag = "nil"
    elseif types.equals(t, types.String) then tag = "string"
    elseif types.has_tag(t, "Array") then tag = "table"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format([[
        if(ttis%s(%s)) { %s; }
        else luaL_error(L, "type error at line %d, expected %s but found %%s", lua_typename(L, ttnov(%s)));
    ]], tag, s, getslot(t, c, s), lin, tag, s)
end

local function checkandset(t, st, sl, lin)
    local tag
    if types.equals(t, types.Integer) then tag = "integer"
    elseif types.equals(t, types.Float) then 
        return string.format([[
            if(ttisinteger(%s)) { setfltvalue(%s, ((lua_Number)ivalue(%s))); }
            else if(ttisfloat(%s)) { setobj2t(L, %s, %s); }
            else luaL_error(L, "type error at line %d, expected float but found %%s", lua_typename(L, ttnov(%s)));
        ]], sl, st, sl, sl, st, sl, lin, sl)
        elseif types.equals(t, types.Boolean) then tag = "boolean"
    elseif types.equals(t, types.Nil) then tag = "nil"
    elseif types.equals(t, types.String) then tag = "string"
    elseif types.has_tag(t, "Array") then tag = "table"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format([[
        if(ttis%s(%s)) { setobj2t(L, %s, %s); }
        else luaL_error(L, "type error at line %d, expected %s but found %%s", lua_typename(L, ttnov(%s)));
    ]], tag, sl, st, sl, lin, tag, sl)
end

local function setslot(t, s, c)
    local tmpl
    if types.equals(t, types.Integer) then tmpl = "setivalue(%s, %s);"
    elseif types.equals(t, types.Float) then tmpl = "setfltvalue(%s, %s);"
    elseif types.equals(t, types.Boolean) then tmpl = "setbvalue(%s, %s);"
    elseif types.equals(t, types.Nil) then tmpl = "setnilvalue(%s); ((void)%s);"
    elseif types.equals(t, types.String) then tmpl = "setsvalue(L, %s, %s);"
    elseif types.has_tag(t, "Array") then tmpl = "sethvalue(L, %s, %s);"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format(tmpl, s, c)
end

local function ctype(t)
    if types.equals(t, types.Integer) then return "lua_Integer"
    elseif types.equals(t, types.Float) then return "lua_Number"
    elseif types.equals(t, types.Boolean) then return "int"
    elseif types.equals(t, types.Nil) then return "int"
    elseif types.equals(t, types.String) then return "TString*"
    elseif types.has_tag(t, "Array") then return "Table*"
    else error("invalid type " .. types.tostring(t))
    end
end

-- creates a new code generation context for a function
local function newcontext()
    return {
        tmp = 1,    -- next temporary index (for generating temporary names)
        nslots = 0, -- number of slots needed by function
        depth = 0,  -- current stack depth
        dstack = {} -- stack of stack depths
    }
end

local function newslot(ctx, name)
    local sdepth = ctx.depth
    ctx.depth = ctx.depth + 1
    if ctx.depth > ctx.nslots then ctx.nslots = ctx.depth end
    return string.format([[
        TValue *%s = _base + %d;
    ]], name, sdepth)
end

local function newtmp(ctx, typ, isgc)
    local tmp = ctx.tmp
    ctx.tmp = ctx.tmp + 1
    if isgc then
        return newslot(ctx, "_tmp_" .. tmp .. "_slot") .. string.format([[
            %s _tmp_%d = 0;
        ]], ctype(typ), tmp), "_tmp_" .. tmp, "_tmp_" .. tmp .. "_slot"
    else
        return string.format([[
            %s _tmp_%d = 0;
        ]], ctype(typ), tmp), "_tmp_" .. tmp
    end
end

local function pushd(ctx)
    table.insert(ctx.dstack, ctx.depth)
end

local function popd(ctx)
    ctx.depth = table.remove(ctx.dstack)
end

-- All the code generation functions for STATEMENTS take
-- the function context and the AST node and return the
-- generated C code for the statement, as a string

local function codeblock(ctx, node)
    local stats = {}
    pushd(ctx)
    for _, stat in ipairs(node.stats) do
        table.insert(stats, codestat(ctx, stat))
    end
    popd(ctx)
    return "    {\n    " .. table.concat(stats, "\n    ") .. "\n    }"
end

local function codewhile(ctx, node)
    pushd(ctx)
    local cstats, cexp = codeexp(ctx, node.condition, true)
    local cblk = codestat(ctx, node.block)
    popd(ctx)
    local restoretop = "" -- FIXME
    return string.format([[
        while(1) {
            %s
            if(!(%s)) {
                %s
                break;
            }
            %s
        }
    ]], cstats, cexp, restoretop, cblk)
end

local function coderepeat(ctx, node)
    pushd(ctx)
    local cstats, cexp = codeexp(ctx, node.condition, true)
    local cblk = codestat(ctx, node.block)
    popd(ctx)
    return string.format([[
        while(1) {
            %s
            %s
            if(%s) {
                break;
            }
        }
    ]], cblk, cstats, cexp)
end

local function codeif(ctx, node, idx)
    idx = idx or 1
    local cstats, cexp, cthn, cels
    if idx == #node.thens then -- last condition
        cstats, cexp = codeexp(ctx, node.thens[idx].condition, true)
        cthn = codestat(ctx, node.thens[idx].block)
        cels = node.elsestat and "else " .. codestat(ctx, node.elsestat) or ""
    else
        cstats, cexp = codeexp(ctx, node.thens[idx].condition, true)
        cthn = codestat(ctx, node.thens[idx].block)
        cels = codeif(ctx, node, idx + 1)
    end
    return string.format([[
        {
            %s
            if(%s) {
                %s
            } %s
        }
    ]], cstats, cexp, cthn, cels)
end

local function codefor(ctx, node)
    pushd(ctx)
    node.decl._cvar = "_local_" .. node.decl.name
    local cdecl = ctype(node.decl._type) .. " " .. node.decl._cvar
    local csstats, csexp = codeexp(ctx, node.start)
    local cfstats, cfexp = codeexp(ctx, node.finish)
    local cinc = ""
    local cstart, cfinish, cstep, ccmp
    if types.equals(node.decl._type, types.Integer) then
        cstart = string.format([[
            %s
            lua_Integer _forstart = %s;
        ]], csstats, csexp)
        cfinish = string.format([[
            %s
            lua_Integer _forlimit = %s;
        ]], cfstats, cfexp)
    else
        cstart = string.format([[
            %s
            lua_Number _forstart = %s;<commit_msg>coder.lua: removing spaces between 'else' and '{' and avoiding '}' '{' in the same line<commit_after>local types = require "titan-compiler.types"

local coder = {}

local codeexp, codestat

local function quotestr(s)
    s = s:gsub("\\", "\\\\")
        :gsub("\a", "\\a")
        :gsub("\b", "\\b")
        :gsub("\f", "\\f")
        :gsub("\n", "\\n")
        :gsub("\r", "\\r")
        :gsub("\t", "\\t")
        :gsub("\v", "\\v")
        :gsub("\"", "\\\"")
    return '"' .. s .. '"'
end

local function node2literal(node)
    local tag = node._tag
    if tag == "Exp_Integer" or tag == "Exp_Float" then
        return tonumber(node.value)
    elseif tag == "Exp_Unop" and node.op == "-" then
        local lexp = node2literal(node.exp)
        return lexp and -lexp
    else
        return nil
    end
end

local function getslot(t, c, s)
    c = c and c .. " =" or ""
    local tmpl
    if types.equals(t, types.Integer) then tmpl = "%s ivalue(%s)"
    elseif types.equals(t, types.Float) then tmpl = "%s fltvalue(%s)"
    elseif types.equals(t, types.Boolean) then tmpl = "%s bvalue(%s)"
    elseif types.equals(t, types.Nil) then tmpl = "%s 0"
    elseif types.equals(t, types.String) then tmpl = "%s tsvalue(%s)"
    elseif types.has_tag(t, "Array") then tmpl = "%s hvalue(%s)"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format(tmpl, c, s)
end

local function checkandget(t, c, s, lin)
    local tag
    if types.equals(t, types.Integer) then tag = "integer"
    elseif types.equals(t, types.Float) then 
        return string.format([[
            if(ttisinteger(%s)) { %s = (lua_Number)ivalue(%s); }
            else if(ttisfloat(%s)) { %s = fltvalue(%s); }
            else luaL_error(L, "type error at line %d, expected float but found %%s", lua_typename(L, ttnov(%s)));
        ]], s, c, s, s, c, s, lin, s)
        elseif types.equals(t, types.Boolean) then tag = "boolean"
    elseif types.equals(t, types.Nil) then tag = "nil"
    elseif types.equals(t, types.String) then tag = "string"
    elseif types.has_tag(t, "Array") then tag = "table"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format([[
        if(ttis%s(%s)) { %s; }
        else luaL_error(L, "type error at line %d, expected %s but found %%s", lua_typename(L, ttnov(%s)));
    ]], tag, s, getslot(t, c, s), lin, tag, s)
end

local function checkandset(t, st, sl, lin)
    local tag
    if types.equals(t, types.Integer) then tag = "integer"
    elseif types.equals(t, types.Float) then 
        return string.format([[
            if(ttisinteger(%s)) { setfltvalue(%s, ((lua_Number)ivalue(%s))); }
            else if(ttisfloat(%s)) { setobj2t(L, %s, %s); }
            else luaL_error(L, "type error at line %d, expected float but found %%s", lua_typename(L, ttnov(%s)));
        ]], sl, st, sl, sl, st, sl, lin, sl)
        elseif types.equals(t, types.Boolean) then tag = "boolean"
    elseif types.equals(t, types.Nil) then tag = "nil"
    elseif types.equals(t, types.String) then tag = "string"
    elseif types.has_tag(t, "Array") then tag = "table"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format([[
        if(ttis%s(%s)) { setobj2t(L, %s, %s); }
        else luaL_error(L, "type error at line %d, expected %s but found %%s", lua_typename(L, ttnov(%s)));
    ]], tag, sl, st, sl, lin, tag, sl)
end

local function setslot(t, s, c)
    local tmpl
    if types.equals(t, types.Integer) then tmpl = "setivalue(%s, %s);"
    elseif types.equals(t, types.Float) then tmpl = "setfltvalue(%s, %s);"
    elseif types.equals(t, types.Boolean) then tmpl = "setbvalue(%s, %s);"
    elseif types.equals(t, types.Nil) then tmpl = "setnilvalue(%s); ((void)%s);"
    elseif types.equals(t, types.String) then tmpl = "setsvalue(L, %s, %s);"
    elseif types.has_tag(t, "Array") then tmpl = "sethvalue(L, %s, %s);"
    else
        error("invalid type " .. types.tostring(t))
    end
    return string.format(tmpl, s, c)
end

local function ctype(t)
    if types.equals(t, types.Integer) then return "lua_Integer"
    elseif types.equals(t, types.Float) then return "lua_Number"
    elseif types.equals(t, types.Boolean) then return "int"
    elseif types.equals(t, types.Nil) then return "int"
    elseif types.equals(t, types.String) then return "TString*"
    elseif types.has_tag(t, "Array") then return "Table*"
    else error("invalid type " .. types.tostring(t))
    end
end

-- creates a new code generation context for a function
local function newcontext()
    return {
        tmp = 1,    -- next temporary index (for generating temporary names)
        nslots = 0, -- number of slots needed by function
        depth = 0,  -- current stack depth
        dstack = {} -- stack of stack depths
    }
end

local function newslot(ctx, name)
    local sdepth = ctx.depth
    ctx.depth = ctx.depth + 1
    if ctx.depth > ctx.nslots then ctx.nslots = ctx.depth end
    return string.format([[
        TValue *%s = _base + %d;
    ]], name, sdepth)
end

local function newtmp(ctx, typ, isgc)
    local tmp = ctx.tmp
    ctx.tmp = ctx.tmp + 1
    if isgc then
        return newslot(ctx, "_tmp_" .. tmp .. "_slot") .. string.format([[
            %s _tmp_%d = 0;
        ]], ctype(typ), tmp), "_tmp_" .. tmp, "_tmp_" .. tmp .. "_slot"
    else
        return string.format([[
            %s _tmp_%d = 0;
        ]], ctype(typ), tmp), "_tmp_" .. tmp
    end
end

local function pushd(ctx)
    table.insert(ctx.dstack, ctx.depth)
end

local function popd(ctx)
    ctx.depth = table.remove(ctx.dstack)
end

-- All the code generation functions for STATEMENTS take
-- the function context and the AST node and return the
-- generated C code for the statement, as a string

local function codeblock(ctx, node)
    local stats = {}
    pushd(ctx)
    for _, stat in ipairs(node.stats) do
        table.insert(stats, codestat(ctx, stat))
    end
    popd(ctx)
    return "    {\n    " .. table.concat(stats, "\n    ") .. "\n    }"
end

local function codewhile(ctx, node)
    pushd(ctx)
    local cstats, cexp = codeexp(ctx, node.condition, true)
    local cblk = codestat(ctx, node.block)
    popd(ctx)
    local restoretop = "" -- FIXME
    return string.format([[
        while(1) {
            %s
            if(!(%s)) {
                %s
                break;
            }
            %s
        }
    ]], cstats, cexp, restoretop, cblk)
end

local function coderepeat(ctx, node)
    pushd(ctx)
    local cstats, cexp = codeexp(ctx, node.condition, true)
    local cblk = codestat(ctx, node.block)
    popd(ctx)
    return string.format([[
        while(1) {
            %s
            %s
            if(%s) {
                break;
            }
        }
    ]], cblk, cstats, cexp)
end

local function codeif(ctx, node, idx)
    idx = idx or 1
    local cstats, cexp, cthn, cels
    if idx == #node.thens then -- last condition
        cstats, cexp = codeexp(ctx, node.thens[idx].condition, true)
        cthn = codestat(ctx, node.thens[idx].block)
        cels = node.elsestat and "else " .. codestat(ctx, node.elsestat):match("^[ \t]*(.*)") or ""
    else
        cstats, cexp = codeexp(ctx, node.thens[idx].condition, true)
        cthn = codestat(ctx, node.thens[idx].block)
        cels = codeif(ctx, node, idx + 1)
    end
    return string.format([[
        {
            %s
            if(%s) {
                %s
            }
			%s
        }
    ]], cstats, cexp, cthn, cels)
end

local function codefor(ctx, node)
    pushd(ctx)
    node.decl._cvar = "_local_" .. node.decl.name
    local cdecl = ctype(node.decl._type) .. " " .. node.decl._cvar
    local csstats, csexp = codeexp(ctx, node.start)
    local cfstats, cfexp = codeexp(ctx, node.finish)
    local cinc = ""
    local cstart, cfinish, cstep, ccmp
    if types.equals(node.decl._type, types.Integer) then
        cstart = string.format([[
            %s
            lua_Integer _forstart = %s;
        ]], csstats, csexp)
        cfinish = string.format([[
            %s
            lua_Integer _forlimit = %s;
        ]], cfstats, cfexp)
    else
        cstart = string.format([[
            %s
            lua_Number _forstart = %s;
{'fraction_non_alphanumeric': 0.1506606312698801, 'fraction_numerical': 0.0019574259848299485, 'mean_word_length': 2.336530612244898, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5253266', 'n_tokens_mistral': 5717, 'n_tokens_neox': 5636, 'n_words': 1599}