Module:Roblox types

local apiData = mw.loadData("Module:API/data") local __ = require('Module:Underscore')

local types = {}

function types.isPrimitive(str) return (       str == 'int' or        str == 'bool' or        str == 'void' or        str == 'string' or        str == 'float' or        str == 'double' or        str:lower == 'function' or        str:lower == 'array' or        str:lower == 'dictionary'    ) end function types.isObject(str) return (str == 'Object' or str == 'Objects') end function types.isEnum(str) return not not apiData.Enums[str] --"not not" booleanifies end

local simplifiedDisplays = { Rect2D = "Rect", CoordinateFrame = "CFrame", RBXScriptSignal = "Signal" }

local function basicLink(t) if types.isPrimitive(t) then t = t:lower elseif t == "Property" then t = "string" elseif t == "Instance" then t = "Class/Instance" end local tdisp = t:gsub("^Class/","") if simplifiedDisplays[tdisp] then tdisp = simplifiedDisplays[tdisp] end return (' %s '):format(       types.isPrimitive(t) and 'Type/' or types.isEnum(t) and "Enum/" or '', -- first %s        types.isObject(t) and 'Class/Instance' or t, -- second %s        types.isPrimitive(t) and "style=\"color:green;\"" or "", -- in  (none originally #666362)        tdisp    ) end

types.link = function(ts, detuple) -- we've been passed a single type, not a list of types if #ts == 0 and ts.type then ts = {ts} end

-- tuples can often omit the Tuple<> if detuple and #ts == 1 and ts[1].type == 'Tuple' and ts[1].args then ts = ts[1].args end

return __.join(       __.map(ts, function(t) local res if t.type:lower == 'function' and t.args then if #t.args ~= 1 then error("function<> takes exactly one typearg") end if t.grouping ~= '<' then error("function typearg should be of the form 'ret(arg1, arg2)") end

local returnType = t.args[1].type if returnType == "void" then returnType = "" -- Don't display return type (it may confuse beginners) else returnType = ("&lt;%s&gt;"):format(basicLink(returnType)) end

res = ("%s%s(%s)"):format(                    basicLink(t.type),  -- function                    returnType,                    types.link(t.args[1].args or {})                ) elseif t.grouping == '<' then res = ("%s&lt;%s&gt;"):format(                    basicLink(t.type),                    types.link(t.args)                ) elseif t.grouping == '(' then               res = ("%s&lt;%s&gt;(%s)"):format( basicLink("function"), basicLink(t.type), types.link(t.args) )           else                res = basicLink(t.type)            end

if t.vararg then res = res .. ' ... '           elseif t.varName then res = res .. " " .. t.varName end return res end),       ", "    ) end

local tokens = { {'API_CLASS', 'Class/%w+'}, {'TYPE', '[a-zA-Z_]+[a-zA-Z_0-9]+'}, {'OPEN_GROUP', '[<(]'},   {'CLOSE_GROUP', '[)>]'}, {'SEPARATOR', ','}, {'WHITESPACE', '%s+'}, {'VARARG', '%.%.%.'}, }

local group_match = { ['('] = ')',   ['<'] = '>' }

local tokenize = function(s) local at = 1

return function for _, v in ipairs(tokens) do           local name, pattern = unpack(v) local ms, me = s:find(pattern, at)

if ms == at then at = me + 1 return name, s:sub(ms, me) end end -- there are characters left in the string, but we didn't recognize them -- remember that lua slices are inclusive, and are indexed from 1, hence <= if at <= #s then mw.log(at, #s) error(("Could not parse token from %q"):format(s:sub(at, at+5))) end end

end

local parse = function(tokenstream) local was_type = false local result = {} local stack = {result}

for t, s in tokenstream do       local current = stack[#stack] if t == 'WHITESPACE' then -- pass elseif t == 'TYPE' or t == 'API_CLASS' then if was_type then --error("Type names must be separated by commas") current[#current].varName = s           else table.insert(current, {type=s}) was_type = true end elseif t == 'VARARG' then if not was_type then error("Vararg must follow type") else current[#current].vararg = true end elseif t == 'SEPARATOR' then if not was_type then error("Commas must follow a type name") else was_type = false end elseif t == 'OPEN_GROUP' then if not was_type then error("groups must begin after a type name") else current[#current].grouping = s               current[#current].args = {} stack[#stack+1] = current[#current].args was_type = false end elseif t == 'CLOSE_GROUP' then local parent = stack[#stack-1] if not parent then error("Unexpected end of group found before start") end local open_s = parent[#parent].grouping if not was_type and #current ~= 0 then error("non-empty groups must end after a typename") elseif group_match[open_s] ~= s then error(("%s %s - Parens do not match: %s %s"):format(t, s, tostring(open_s), s)) else stack[#stack] = nil was_type = true end else error(("Internal error - Unrecognized token name %q"):format(t)) end end

if #stack ~= 1 then error(("There are %s argument groups that not closed"):format(#stack - 1)) end

return result end

function types.parse(s) assert(s and type(s) == 'string', ("Bad argument to tokenize: type %s"):format(type(s))) return parse(tokenize(s)) end

function types.render(frame) return types.link(types.parse(frame.args[1])) end

function types.buildFunctionArgs(frame) local vars = frame.args[1] local stack = 0 local currentVar = "" local pending = {}

for char in vars:gmatch(".") do       if char == "," and stack == 0 then table.insert(pending,currentVar) currentVar = "" else currentVar = currentVar .. char if char == "<" or char == "(" then               stack = stack + 1            elseif char == ">" or char == ")" then stack = stack - 1 end end end if #currentVar > 0 then table.insert(pending,currentVar) end

local result = "" for _,var in pairs(pending) do		local trimmed = var:gsub("^%s*(.+)%s*$","%1") -- Trims whitespace local space = trimmed:find(" ") local varType,varName if space then varType = trimmed:sub(1,space-1) varName = trimmed:sub(space+1) else varType = "void" varName = trimmed end if #result > 0 then result = result .. ", "        end result = result .. (" %s"):format(varType,varName) end

return frame:preprocess(result) end

-- Simple test case to check the code runs local t = types.link(types.parse("Dictionary), Variant>...")) mw.log(t); return types