Luau C Preprocesor. Add more features to the Luau language

Features

pre values

pre x = math.sqrt(32474278874217828742598412787427624675241676742)

rather than calculating math.sqrt(3247427887…) on runtime it will calculate than on preprocess time and render:

local x = tonumber("1.802062120855378e+23")

(the tonumber is automatically added for numbers that are too big)

#if, #endif, #elif, #else

#if 1+1 == 2
print("According to the preprocessor 1+1 is 2")
#elif 1+1==3
print("According to the preprocessor 1+1 is 3 (wrong)")
#else
print("According to the preprocessor 1+1 is not 2 or 3 (wrong)")
#endif

will generate

print("According to the preprocessor 1+1 is 2")

#define, #ifdef, #ifndef

#define DEBUGMODE false
print(`debug mode is: {DEBUGMODE}`) -- the code can access defined variables normally
-- code..
#ifdef DEBUGMODE -- #ifndef does the means if not defined
print("On debug mode")
#endif
-- code..

#error, #warn

errors or warns from the preprocessor

#if 1+1 ~= 2
#warn uh oh
#error 1+1 is not 2!!!!!
#endif

#include

includes the source code of another file (script does not work)

#include game.Workspace.file

Source

locktostring = function(val)
    if typeof(val) == "string" then
        return val
    elseif typeof(val) == "number" then
        if val == math.huge then
            return "math.huge"
        elseif val == -math.huge then
            return "-math.huge"
        end
        if not tonumber(typeof(val)) then
            return "tonumber(\"" .. tostring(val) .. "\")"
        end
        return tostring(val)
    elseif typeof(val) == "boolean" then
        return tostring(val)
    elseif typeof(val) == "table" then
        local str = "{"
        for i, v in pairs(val) do
            str = str .. locktostring(i) .. "=" .. locktostring(v) .. ","
        end
        str = str .. "}"
        return str
    elseif typeof(val) == "Instance" then
        error("Instances are not supported yet for pre-values")
    elseif typeof(val) == "function" then
        error("Functions are not supported yet for pre-values")
    elseif typeof(val) == "userdata" then
        error("Userdata is not supported yet for pre-values")
    elseif typeof(val) == "nil" then
        return "nil"
    else
        error("Unhandled type: " .. typeof(val))
    end
end
loadenvstring = function(env, str)
    local fn = loadstring(str)
    if not fn then
        print(fn, str)
        error("Failed to load stringenv")
    end
    setfenv(fn, env)
    return fn
end
fn = function(code)
    lines = code:split("\n")
    on = true
    env = {}
    for i, line in ipairs(lines) do
        if not on then
            lines[i] = ""
            continue
        end

        if line:sub(1, 1) == "#" then
            if line:sub(1, 3) == "#if" and (line:sub(4, 4) == " ") then
                local var = line:sub(5)
                if not loadenvstring(env, "return " .. var)() then
                    on = false
                end
                line = ""
            elseif line:sub(1, 6) == "#endif" then
                on = true
                line = ""
            elseif line:sub(1, 5) == "#else" then
                on = not on
                line = ""
            elseif line:sub(1, 7) == "#elif" then
                local var = line:sub(9)
                if loadenvstring(env, "return " .. var)() then
                    on = true
                end
                line = ""
            elseif line:sub(1, 7) == "#define" then
                local var = line:sub(9)
                local name, val = var:match("(.*) (.*)")
                name = name or var
                val = val or "true"
                env[name] = val
                line = "local " .. name .. " = " .. val
            elseif line:sub(1, 6) == "#ifdef" then
                local var = line:sub(8)
                if not _G[var] then
                    on = false
                end
                line = ""
            elseif line:sub(1, 7) == "#ifndef" then
                local var = line:sub(9)
                if _G[var] then
                    on = false
                end
                line = ""
            elseif line:sub(1, 6) == "#error" then
                error(line:sub(8))
            elseif line:sub(1, 6) == "#warn " then
                warn(line:sub(7))
                line = ""
            elseif line:sub(1, 8) == "#include" then
                local var = line:sub(10)
                line = loadstring("return " .. var)().Source
            else
                error("Unknown preprocessor directive: " .. line)
            end
        end
        
        --// look for a match like this: pre x = 5
        --// if found, replace with local x = 5 (but x will be calculated)

        lines[i] = line:gsub("pre (.*) = (.*)", function(var,val)
            --// replace line with new line
            return ("local "..var .. " = " .. locktostring(loadstring("return "..val)()))
        end)
    end

    return table.concat(lines, "\n")
end

return fn

Todo

  • Allow # commands to be run with whitespace before it
-- code..
  #directive

doesn’t work,

-- code..
#directive

works.

Credits/License

All of the code is written by me, No credits is needed for products/games that use it but if the source code of another project includes this that requires credits.

1 Like

A preprocessor is a program that takes in code with extensions (like in C preprocessor directives) and compiles it to normal code (normal Luau code in this case).

1 Like

I believe this post belongs to #feature-requests:engine-features. Kindly move it there.

It could be a feature request, but I doubt it would ever get added. Regardless, that’s not the intention of this thread.

1 Like
  1. I don’t have access to feature requests
  2. I included source code for the Preprocessor

Oh right my bad. I misinterpreted the thread.

Nevermind :slight_smile:

So is this a plugin or a feature request?

I’m pretty sure this ain’t a feature request cuz of the above. I guess they forgot to include a link to the module

The source code is already provided. You have to create a ModuleScript and insert the source code given by the OP. You can then reference it like this:

local Module = require(script.Module)
local NewScript = Module.fn("code / source reference when using command bars or plugins")
print(NewScript)
1 Like

I think I’m going blind lol… just forget that