Module:API/data

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 },		{ "^]+)/>", function(name, attrs)			local t = { xmltype = "selfclose", xmlname = name }			parseAttributes(attrs, t)			return t		end},		{ "^", function(name, attrs)			local t = { xmltype = "start", xmlname = name }			parseAttributes(attrs, t)			return t		end },		{ "^", 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  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  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.ClassCategory = getProperty(rfmClass,"ClassCategory") 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) changeClass("SelectionLasso","tags","deprecated",true) changeClass("InsertService","Properties","AllowInsertFreeModels","tags","deprecated",true) changeClass("MotorFeature","tags","deprecated",true) changeClass("TextBox","Properties","FontSize","tags","deprecated",true) changeClass("TextButton","Properties","FontSize","tags","deprecated",true) changeClass("TextLabel","Properties","FontSize","tags","deprecated",true)

return apiData