FANDOM


parse = require "Module:LexAPI"
 
--good enough xml parsing for the roblox file format
do
	local function parseAttributes(attrs, t)
		for k, v in attrs:gmatch('(%w+)="(.-)"') do
			t[k] = v
		end
	end
 
	local lexRules = {
		{ "^%s+", function() return nil end },
		{ "^([^<]+)", function(s)
			s = s:gsub("%s+", " "):gsub("^%s+", ""):gsub("%s+$", ""):gsub("&(%w+);", {
				quot='"', apos="'", lt = "<", gt = ">", amp = "&"
			})
			if s ~= "" then return s end
		end },
		{ "^<!%-%-.-%-%->", function() return nil end },
		{ "^<(%w+)([^>]+)/>", function(name, attrs)
			local t = { xmltype = "selfclose", xmlname = name }
			parseAttributes(attrs, t)
			return t
		end},
		{ "^<(%w+)(.-)>", function(name, attrs)
			local t = { xmltype = "start", xmlname = name }
			parseAttributes(attrs, t)
			return t
		end },
		{ "^</(%w+)>", function(name) return { xmltype = "close", xmlname = name } end }
	}
 
	local function lexXml(src)
		local i = 1
		return function()
			local token
			while i <= #src and not token do
				local success = false
				for _, rule in ipairs(lexRules) do
					local _, stop, a, b = src:find(rule[1], i)
					if stop then
						token = rule[2](a, b)
						i = stop + 1
						success = true
						break
					end
				end
				assert(success, "Can't parse")
			end
			return token
		end
	end
 
	function parseXml(src)
		local stack = {}
		local current = { xmltype = "document" }
 
		for token in lexXml(src) do
			if type(token) == "string" then
				current[#current + 1] = token
			elseif token.xmltype == "start" then
				token.xmltype = "element"
				current[#current + 1] = token
				stack[#stack + 1] = current
				current = token
			elseif token.xmltype == "selfclose" then
				token.xmltype = "element"
				current[#current + 1] = token
			elseif token.xmltype == "close" then
				assert(token.xmlname == current.xmlname, "mismatched close tag")
				current = stack[#stack]
				stack[#stack] = nil
			else
				assert(false)
			end
		end
 
		assert(#stack == 0, "unexpected end of file")
		return current
	end
 
	--gets a roblox property of an Instance described by an <Item> tag
	function getProperty(node, name)
		local properties
		for _, child in ipairs(node) do
			if child.xmlname == "Properties" then
				properties = child
				break
			end
		end
 
		if not properties then return end
 
		for _, property in ipairs(properties) do
			if property.name == name then
				return property[1], property.name
			end
		end
	end
 
	--gets the roblox children of an Instance described by an <Item> tag
	function getChildren(node)
		local t = {}
		for _, child in ipairs(node) do
			if child.xmlname == "Item" then
				t[#t+1] = child
			end
		end
		return t
	end
end
 
function merge(...)
	local data = {}
	local lists = {...}
	for _,t in pairs(lists) do
		for _,v in pairs(t) do
			table.insert(data,v)
		end
	end
	return data
end
 
function organize(data, reflectionMetadata)
    local Classes = {}
    local Enums = {}
    for i=0,#data,1 do
        local item = data[i]
        if item then 
            if(item.type == "Class") then
                if not Classes[item.Name] then
                    Classes[item.Name] = item
                    Classes[item.Name].Properties = {}
                    Classes[item.Name].Functions = {}
                    Classes[item.Name].YieldFunctions = {}
                    Classes[item.Name].Events = {}
                    Classes[item.Name].Callbacks = {}
                    Classes[item.Name].Subclasses = {}
                end
            elseif(item.type == "Property") then
                local parentClass = Classes[item.Class]
                parentClass.Properties[item.Name] = item
            elseif(item.type == "Function") then
                local parentClass = Classes[item.Class]
                parentClass.Functions[item.Name] = item
            elseif(item.type == "YieldFunction") then
                local parentClass = Classes[item.Class]
                parentClass.YieldFunctions[item.Name] = item
            elseif(item.type == "Event") then
                local parentClass = Classes[item.Class]
                parentClass.Events[item.Name] = item
            elseif(item.type == "Callback") then
                local parentClass = Classes[item.Class]
                parentClass.Callbacks[item.Name] = item
            elseif(item.type == "Enum") then
                if not Enums[item.Name] then Enums[item.Name] = item end
            elseif(item.type == "EnumItem") then
                local parentEnum = Enums[item.Enum]
                if not parentEnum.EnumItems then
                    parentEnum.EnumItems = {} 
                end
                parentEnum.EnumItems[item.Name] = item   
            end
        end
    end
 
    for _, item in pairs(Classes) do
        if Classes[item.Superclass] then
            Classes[item.Superclass].Subclasses[item.Name] = item
        end
    end
 
	local typeLookup = {
		ReflectionMetadataCallbacks = "Callbacks",
		ReflectionMetadataEvents = "Events",
		ReflectionMetadataFunctions = "Functions",
		ReflectionMetadataProperties = "Properties",
		ReflectionMetadataYieldFunctions = "YieldFunctions"
	}
 
    local reflectionMetadataClasses = reflectionMetadata[1][1]
	assert(reflectionMetadataClasses.class == "ReflectionMetadataClasses")
	for _, rfmClass in ipairs(getChildren(reflectionMetadataClasses)) do
		assert(rfmClass.class == "ReflectionMetadataClass")
		local className = getProperty(rfmClass, "Name")
		local class = Classes[className]
 
		--does this look like enough error handling in this next part?
		--I had to insert !every single one! of these checks in response to a situation that actually happens in the ReflectionMetadata.xml file
		if not class then
			mw.log(className.." in RFM but not in dump")
		else
    		class.HasReflectionMetadataEntry = true
			class.ExplorerOrder = tonumber((getProperty(rfmClass, "ExplorerOrder")))
			class.ExplorerImageIndex = tonumber((getProperty(rfmClass, "ExplorerImageIndex")))
			class.ReflectionMetadataSummary = getProperty(rfmClass, "summary")
            class.UIMaximum = tonumber((getProperty(rfmClass, "UIMaximum")))
            class.UIMinimum = tonumber((getProperty(rfmClass, "UIMinimum")))
			class.IsBackend = getProperty(rfmClass, "IsBackend")
            class.Insertable = getProperty(rfmClass, "Insertable")
            class.PreferredParent = getProperty(rfmClass,"PreferredParent")
			for _, rfmMembersGroup in ipairs(getChildren(rfmClass)) do
				local membersGroup = class[typeLookup[rfmMembersGroup.class]]
				if membersGroup then
					for _, rfmMember in ipairs(getChildren(rfmMembersGroup)) do
						local member = membersGroup[getProperty(rfmMember, "Name")]
						if not member then
							mw.log(className.."."..(getProperty(rfmMember, "Name") or "NAMELESS").." in RFM but not in dump ("..rfmMembersGroup.class..")")
						else
							member.ReflectionMetadataSummary = getProperty(rfmMember, "summary")
						end
					end
				else
					mw.log(string.format("%s has entry %s (%s) in the wrong place?", className, getProperty(rfmMembersGroup, "Name") or "", rfmMembersGroup.class))
				end
			end
		end
	end
 
    local ret = {}
    ret.Classes = Classes
    ret.Enums = Enums
    ret.Items = data
    return ret
end
 
local apiDump = mw.title.new("API:Class_reference/Dump/raw", ""):getContent()
local libraries = mw.title.new("API:Class_reference/Libraries", ""):getContent()
local unscriptables = (mw.title.new("API:Class_reference/UnscriptableMembers",""):getContent().."\n"):gsub("\n"," [unscriptable]\n")
local rmd = mw.title.new("API:Class_reference/ReflectionMetadata/raw", ""):getContent()
local apiData = organize(merge(parse(apiDump),parse(unscriptables),parse(libraries)), parseXml(rmd))
 
local function changeClass(className,...)
    local class = apiData.Classes[className]
    if class then
        local args = {...}
        local process = args[#args]
        local destination = args[#args-1]
        local traverse = class
        for i = 1,#args-2 do
            local nextPart = args[i]
            if traverse[nextPart] then
                traverse = traverse[nextPart]
            else
                return
            end
        end
        if type(process) == "function" then
            traverse[destination] = process(traverse[destination])
        else
            traverse[destination] = process
        end
    end
end
 
local function hideAndDeprecate(tags)
    tags.deprecated = true
    tags.hidden = true
    return tags
end
 
changeClass("Model","Subclasses",{Workspace = apiData.Classes.Workspace})
changeClass("Workspace","Superclass","Model")
changeClass("FormFactorPart","tags","deprecated",true)
changeClass("Feature","tags","deprecated",true)
changeClass("BasePart","Properties","Friction","tags",hideAndDeprecate)
changeClass("BasePart","Properties","Elasticity","tags",hideAndDeprecate)
changeClass("PVInstance","Properties","CoordinateFrame","tags","unscriptable",true)
changeClass("Pose","Properties","MaskWeight","tags","deprecated",true)
changeClass("NetworkReplicator","Functions","SendMarker","tags","deprecated",true)
changeClass("NetworkMarker","tags","deprecated",true)
changeClass("AdService","tags","deprecated",true)
changeClass("AdService","Functions","ShowVideoAd","tags","deprecated",true)
changeClass("AdService","Events","VideoAdClosed","tags","deprecated",true)
changeClass("Part","Superclass","BasePart")
changeClass("WedgePart","Superclass","BasePart")
changeClass("FormFactorPart","Subclasses",{})
changeClass("BasePart","Subclasses",function (t)
    t.Part = apiData.Classes.Part
    t.WedgePart = apiData.Classes.WedgePart
    t.FormFactorPart = nil
    return t
end)
changeClass("UserSettings","Functions","Reset","tags","RobloxScriptSecurity",true)
changeClass("PluginManager","tags","deprecated",true)
changeClass("PluginManager","Functions","CreatePlugin","tags","deprecated",true)
changeClass("Lighting","Functions","GetMoonPhase","tags","deprecated",true)
changeClass("Instance","Properties","DataCost","tags","deprecated",true)
changeClass("ClickDetector","Events","mouseClick","tags","deprecated",true)
 
return apiData

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.