<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.figuramc.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PenguinEncounter</id>
	<title>FiguraMC - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.figuramc.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PenguinEncounter"/>
	<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php/Special:Contributions/PenguinEncounter"/>
	<updated>2026-05-29T17:04:22Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Main_Page&amp;diff=903</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Main_Page&amp;diff=903"/>
		<updated>2026-04-07T19:14:49Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Notice/Warning&lt;br /&gt;
 |content=&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight: bold; margin: 0.5em 0 0 0;&amp;quot;&amp;gt;This wiki has been replaced by [https://docs.figuramc.org docs.figuramc.org].&amp;lt;/div&amp;gt;&lt;br /&gt;
Please use the documentation available there.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;min-height: min(50svh, 20em);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Main_Page&amp;diff=902</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Main_Page&amp;diff=902"/>
		<updated>2026-04-07T19:13:08Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: Remove navbox, move link to big text&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Notice/Warning&lt;br /&gt;
 |content=&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight: bold; margin: 0.5em 0 0 0;&amp;quot;&amp;gt;This wiki has been replaced by [https://docs.figuramc.org docs.figuramc.org].&amp;lt;/div&amp;gt;&lt;br /&gt;
Please use the documentation available there.&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Multiplayer_Avatar_Interaction&amp;diff=900</id>
		<title>Multiplayer Avatar Interaction</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Multiplayer_Avatar_Interaction&amp;diff=900"/>
		<updated>2025-04-14T05:12:23Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: a bit of style cleanup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Avatars can share values with one another to allow for interactions between them. An example of this would be the various petting scripts you can find in the community.&lt;br /&gt;
&lt;br /&gt;
The basic principle works like this:&lt;br /&gt;
One avatar can store a value.&lt;br /&gt;
Another avatar can get this stored value and do something based on it.&lt;br /&gt;
&lt;br /&gt;
To store a value that other avatars can read you use avatar:store(key, value)&lt;br /&gt;
&lt;br /&gt;
For example&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
avatar:store(&amp;quot;amount&amp;quot;, 1)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: It is recommended to never store any UserData objects as this could lead to security issues! Best to only store primitives like numbers, strings, or regular tables!&lt;br /&gt;
&lt;br /&gt;
Someone else can then read this value in their script by checking the avatarVars for each player.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
for uuid, vars in pairs(world.avatarVars()) do&lt;br /&gt;
    print(uuid)&lt;br /&gt;
    printTable(vars)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So for our example you could read the &amp;quot;amount&amp;quot; which we set to 1 and show the player name as well:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
for uuid, vars in pairs(world.avatarVars()) do&lt;br /&gt;
    if vars[&amp;quot;amount&amp;quot;] then&lt;br /&gt;
        local playerName = uuid&lt;br /&gt;
        for name, plr in pairs(world.getPlayers()) do&lt;br /&gt;
            if plr:getUUID() == uuid then&lt;br /&gt;
                playerName = name&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        print(playerName, &amp;quot;has amount of&amp;quot;, vars[key])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=ModelPart_Indexing&amp;diff=899</id>
		<title>ModelPart Indexing</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=ModelPart_Indexing&amp;diff=899"/>
		<updated>2025-04-12T23:58:46Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: how did i miss that what&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;{{Hatnote|For technical documentation on ModelParts, see [[ModelPart]].}}&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When Figura loads your avatar, all the model files in your avatar are organized into a tree. The starting point of this tree is called &amp;lt;code&amp;gt;models&amp;lt;/code&amp;gt;. All of the parts of your model, including cubes, meshes, and groups from your Blockbench models, as well as folders in your avatar and the model files themselves are converted to ModelParts and part of this tree.&lt;br /&gt;
&lt;br /&gt;
The name &amp;lt;code&amp;gt;models&amp;lt;/code&amp;gt; does not refer to any specific model file in your avatar. Instead, model files are created as children of the &amp;lt;code&amp;gt;models&amp;lt;/code&amp;gt; folder. &#039;&#039;&#039;If your Blockbench model is inside a subfolder of your avatar&#039;&#039;&#039;, that folder becomes a child of &amp;lt;code&amp;gt;models&amp;lt;/code&amp;gt;, and the model file becomes a child of the subfolder.&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=root|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Head|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=Head}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=HeadLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=RightArm|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArm}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArmLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=Cool Name}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Cool Group|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=cube}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
To access a child part, &#039;&#039;index&#039;&#039; the parent part with the name of the child part.&lt;br /&gt;
&lt;br /&gt;
There are two ways to &#039;&#039;index&#039;&#039; something in Lua. The first way is by using dots:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 4px 6px; border-radius: 6px; border: 3px solid var(--color-success); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root.Head&amp;lt;/code&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code sample first gets the &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; part (a file named &amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;) and then the &amp;lt;code&amp;gt;Head&amp;lt;/code&amp;gt; group inside it. The name of the cube, mesh, group, folder, or file needs to be within the quotes. To access deeper parts (for example, a &amp;lt;code&amp;gt;HeadLayer&amp;lt;/code&amp;gt; cube within the &amp;lt;code&amp;gt;Head&amp;lt;/code&amp;gt; group), add more dots and names:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 4px 6px; border-radius: 6px; border: 3px solid var(--color-success); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root.Head.HeadLayer&amp;lt;/code&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second way to index is longer, and uses square brackets and quotes (&amp;lt;code&amp;gt;[&amp;quot;&amp;quot;]&amp;lt;/code&amp;gt;) to separate the names. &#039;&#039;&#039;This method is required if the name of a part contains characters that aren&#039;t letters, numbers, or underscores&#039;&#039;&#039;; this includes &#039;&#039;names with spaces&#039;&#039;, like &amp;lt;code&amp;gt;Right Arm&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
When using brackets to index, place the name &#039;&#039;&#039;between the quotes&#039;&#039;&#039;, and &#039;&#039;&#039;don&#039;t include a period before it&#039;&#039;&#039;:&lt;br /&gt;
* &amp;lt;div style=&amp;quot;padding: 3px 4px; border-radius: 4px; border: 2px solid var(--color-success); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models[&amp;quot;model&amp;quot;][&amp;quot;root&amp;quot;][&amp;quot;Cool Name&amp;quot;]&amp;lt;/code&amp;gt; ✓ Correct&amp;lt;/div&amp;gt;&lt;br /&gt;
* &amp;lt;div style=&amp;quot;padding: 3px 4px; border-radius: 4px; border: 2px solid var(--color-success); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root[&amp;quot;Cool Name&amp;quot;]&amp;lt;/code&amp;gt; ✓ Correct - both brackets and dots can be mixed together!&amp;lt;/div&amp;gt;&lt;br /&gt;
* &amp;lt;div style=&amp;quot;padding: 3px 4px; border-radius: 4px; border: 2px solid var(--color-destructive); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root.[&amp;quot;Cool Name&amp;quot;]&amp;lt;/code&amp;gt; ✗ Incorrect: &#039;&#039;&#039;don&#039;t put a dot &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; before the bracket &amp;lt;code&amp;gt;[&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
* &amp;lt;div style=&amp;quot;padding: 3px 4px; border-radius: 4px; border: 2px solid var(--color-destructive); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root[Cool Name]&amp;lt;/code&amp;gt; ✗ Incorrect: &#039;&#039;&#039;name in brackets is missing a set of quotes&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To return to using dots for indexing after using brackets, place the dot after the closing bracket:&lt;br /&gt;
* &amp;lt;div style=&amp;quot;padding: 3px 4px; border-radius: 4px; border: 2px solid var(--color-success); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root[&amp;quot;Cool Group&amp;quot;].cube&amp;lt;/code&amp;gt; ✓ Correct&amp;lt;/div&amp;gt;&lt;br /&gt;
* &amp;lt;div style=&amp;quot;padding: 3px 4px; border-radius: 4px; border: 2px solid var(--color-destructive); width: fit-content; margin-top: 6px;&amp;quot;&amp;gt;&amp;lt;code&amp;gt;models.model.root[&amp;quot;Cool Group&amp;quot;]cube&amp;lt;/code&amp;gt; ✗ Incorrect: &#039;&#039;&#039;missing dot &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; after bracket&#039;&#039;&#039;&amp;lt;div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A note on model files in folders ===&lt;br /&gt;
&lt;br /&gt;
If your Blockbench model file is within a folder in your avatar, the &#039;&#039;folder&#039;&#039; will become a part of the ModelPart tree. That means that if a model file named &amp;lt;code&amp;gt;player.bbmodel&amp;lt;/code&amp;gt; is located in a folder named &amp;lt;code&amp;gt;models&amp;lt;/code&amp;gt;, the model file has to be accessed with &amp;lt;code&amp;gt;models.models.player&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;models[&amp;quot;models&amp;quot;][&amp;quot;player&amp;quot;]&amp;lt;/code&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
{{Notice|content=&lt;br /&gt;
If you later want to use animations from a model, folders affect the indexing into the &amp;lt;code&amp;gt;animations&amp;lt;/code&amp;gt; table too - but the system is a little different.&lt;br /&gt;
&lt;br /&gt;
Instead of indexing the folder and then the file, &#039;&#039;combine each folder name and the file name with a period &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; and index with the combined name&#039;&#039;, even if the names of the folders and files contain special characters. For example, to get the animations from the &amp;lt;code&amp;gt;model 2.bbmodel&amp;lt;/code&amp;gt; file in the &amp;lt;code&amp;gt;main models&amp;lt;/code&amp;gt; folder:&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=FSFolderClosed.png|content=&amp;lt;code&amp;gt;main models&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model 2.bbmodel&amp;lt;/code&amp;gt;}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
animations[&amp;quot;main models.model 2&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=898</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=898"/>
		<updated>2025-03-31T03:57:45Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::before {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::before {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: &#039;Roboto Mono&#039;,&#039;Menlo&#039;,&#039;Consolas&#039;,&#039;Liberation Mono&#039;,&#039;Fira Code&#039;,&#039;Courier New&#039;,monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
&lt;br /&gt;
    display: flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::before {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 4px 0;&lt;br /&gt;
    margin-right: 4px;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #c891ff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=897</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=897"/>
		<updated>2025-03-31T03:54:29Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::before {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::before {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
&lt;br /&gt;
    display: flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::before {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 4px 0;&lt;br /&gt;
    margin-right: 4px;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #c891ff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=896</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=896"/>
		<updated>2025-03-31T03:53:33Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::before {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::before {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::before {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    height: 100%;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    background-color: #c891ff;&lt;br /&gt;
    margin-right: 4px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=895</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=895"/>
		<updated>2025-03-31T03:52:14Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::after {&lt;br /&gt;
    content: &amp;quot;func&amp;quot;;&lt;br /&gt;
    font-size: 0.3em;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    border: 2px solid #c891ff;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    margin-left: 4px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=894</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=894"/>
		<updated>2025-03-31T03:50:59Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^#&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit -lua-typedef&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union lua-type-recolor-border&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local separator = [=[&amp;lt;span class=&amp;quot;lua-type-union-bar lua-type-recolor-bar&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, separator))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=893</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=893"/>
		<updated>2025-03-31T03:49:45Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 12px;&lt;br /&gt;
    height: 12px;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    border: 2px solid #c891ff;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    margin-left: 4px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=892</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=892"/>
		<updated>2025-03-31T03:48:30Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 6px;&lt;br /&gt;
    height: 6px;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    border: 2px solid #eeddff;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    margin-left: 4px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=891</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=891"/>
		<updated>2025-03-31T03:47:17Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 16px;&lt;br /&gt;
    height: 16px;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    border: 1px solid #eeddff;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=890</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=890"/>
		<updated>2025-03-31T03:46:00Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a::after {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit.-lua-typedef a::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    width: 16px;&lt;br /&gt;
    height: 16px;&lt;br /&gt;
    border: 1px solid #eeddff;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=889</id>
		<title>User:PenguinEncounter/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=889"/>
		<updated>2025-03-31T03:42:22Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;font-size: 3rem;&amp;quot;&amp;gt;[https://wiki.figuramc.org/index.php/User:PenguinEncounter/sandbox?action=purge purge it]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Media:FunctionProtocol.svg]]&lt;br /&gt;
[[File:FunctionProtocol.svg]]&lt;br /&gt;
&lt;br /&gt;
{{User:PenguinEncounter/newtype|function|Vector2|#TickEventCallback}}&lt;br /&gt;
&lt;br /&gt;
For example, to get the cube {{Emoji|BBCube.png}} RightArm:&lt;br /&gt;
&lt;br /&gt;
This is what is in your project:&lt;br /&gt;
&lt;br /&gt;
{{#invoke:MatrixFields|run|size=4}}&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=FSFolderOpen.png|content=&amp;lt;code&amp;gt;my_avatar&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=FiguraJSON.png|content=&amp;lt;code&amp;gt;avatar.json&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Head|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=Head}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=HeadLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=RightArm&amp;lt;br&amp;gt;oops|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArm}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArmLayer}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== conspicuous example code ==&lt;br /&gt;
{{#tag:syntaxhighlight|&lt;br /&gt;
function events.tick()&lt;br /&gt;
    &amp;lt;nowiki&amp;gt;--[[wiki.figuramc.org, &amp;lt;/nowiki&amp;gt;{{FULLPAGENAME}} rev. {{REVISIONID}}&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    models.model.Head:setVisible(false)&lt;br /&gt;
end&lt;br /&gt;
|lang=lua}}&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;code&amp;gt;HostAPI:setTitleTimes&amp;lt;/code&amp;gt; =&lt;br /&gt;
{{Host only|kind=method}}&lt;br /&gt;
Configures the hold, fade-in, and fade-out durations of titles displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Arguments !! Return Type&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(timesData {{type|Vector3}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(fadeInTime {{type|integer}}, stayTime {{type|integer}}, fadeOutTime {{type|integer}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
host:setTitleTimes(1, 1, 1)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;!-- idk --&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=File:FunctionProtocol.svg&amp;diff=888</id>
		<title>File:FunctionProtocol.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=File:FunctionProtocol.svg&amp;diff=888"/>
		<updated>2025-03-31T03:31:45Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: PenguinEncounter uploaded a new version of File:FunctionProtocol.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=File:FunctionProtocol.svg&amp;diff=887</id>
		<title>File:FunctionProtocol.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=File:FunctionProtocol.svg&amp;diff=887"/>
		<updated>2025-03-31T03:19:21Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: PenguinEncounter uploaded a new version of File:FunctionProtocol.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=File:FunctionProtocol.svg&amp;diff=886</id>
		<title>File:FunctionProtocol.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=File:FunctionProtocol.svg&amp;diff=886"/>
		<updated>2025-03-31T03:17:07Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=885</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=885"/>
		<updated>2025-03-31T02:52:42Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 1px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=884</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=884"/>
		<updated>2025-03-31T02:22:14Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/** DARK MODE **/&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPES **/&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external.external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** TYPE COLORS **/&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=883</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=883"/>
		<updated>2025-03-31T02:20:52Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;nil&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px solid #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;any&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffff;&lt;br /&gt;
	border: 1px dotted #808080;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;table&amp;quot;] a {&lt;br /&gt;
	background-color: #ffffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;function&amp;quot;] a {&lt;br /&gt;
	background-color: #eeddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;string&amp;quot;] a {&lt;br /&gt;
	background-color: #ddffdd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;boolean&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name=&amp;quot;number&amp;quot;] a, .lua-type-explicit[data-name=&amp;quot;integer&amp;quot;] {&lt;br /&gt;
	background-color: #ddddff;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Vector&amp;quot;] a {&lt;br /&gt;
	background-color: #ffeedd;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit[data-name^=&amp;quot;Matrix&amp;quot;] a {&lt;br /&gt;
	background-color: #ffddee;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=882</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=882"/>
		<updated>2025-03-31T02:14:44Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=881</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=881"/>
		<updated>2025-03-31T02:14:08Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:hover {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: underline;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=880</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=880"/>
		<updated>2025-03-31T02:10:46Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit &amp;gt; a {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit &amp;gt; a {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=879</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=879"/>
		<updated>2025-03-31T02:08:59Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents; /* flatten &#039;a&#039; as flex element */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    color: #000000;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a:visited {&lt;br /&gt;
    color: #000000;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=878</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=878"/>
		<updated>2025-03-31T02:01:52Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit -lua-typedef&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union lua-type-recolor-border&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local separator = [=[&amp;lt;span class=&amp;quot;lua-type-union-bar lua-type-recolor-bar&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, separator))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=877</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=877"/>
		<updated>2025-03-31T02:01:45Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #b0b0b0;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=876</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=876"/>
		<updated>2025-03-31T02:01:16Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .lua-type-explicit {&lt;br /&gt;
		filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
	}&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-border {&lt;br /&gt;
        border: 1px solid #404040 !important;&lt;br /&gt;
    }&lt;br /&gt;
    html.skin-theme-clientpref-os .lua-type-recolor-bar {&lt;br /&gt;
        background-color: #b0b0b0 !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-explicit {&lt;br /&gt;
	filter: invert() hue-rotate(180deg) brightness(110%);&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-border {&lt;br /&gt;
    border: 1px solid #404040 !important;&lt;br /&gt;
}&lt;br /&gt;
html.skin-theme-clientpref-night .lua-type-recolor-bar {&lt;br /&gt;
    background-color: #b0b0b0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #eeeeee;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #eeeeee;&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=875</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=875"/>
		<updated>2025-03-31T01:23:39Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid var(--color-surface-2); /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: var(--color-surface-2); /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid var(--color-surface-2); /* FIXME: theming */&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=874</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=874"/>
		<updated>2025-03-31T01:20:49Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 3px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    margin: 3px 0;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=873</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=873"/>
		<updated>2025-03-31T01:20:03Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local separator = [=[&amp;lt;span class=&amp;quot;lua-type-union-bar&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, separator))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=872</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=872"/>
		<updated>2025-03-31T01:19:26Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local separator = [=[&amp;lt;span class=&amp;quot;lua-type-union-bar&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, &amp;quot;&amp;quot;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=871</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=871"/>
		<updated>2025-03-31T01:19:20Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 2px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
.lua-type-union &amp;gt; .lua-type-union-bar {&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 2px;&lt;br /&gt;
    align-self: stretch;&lt;br /&gt;
    background-color: #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
    padding: 0 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=870</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=870"/>
		<updated>2025-03-31T01:17:26Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 1px;&lt;br /&gt;
    gap: 4px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; .external::after {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
.lua-type-explicit &amp;gt; a {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
    padding: 2px 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=869</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=869"/>
		<updated>2025-03-31T01:15:58Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 4px;&lt;br /&gt;
    gap: 4px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
    padding: 2px 4px;&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
    display: contents;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=868</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=868"/>
		<updated>2025-03-31T00:51:20Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 1px 4px;&lt;br /&gt;
    gap: 4px;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.lua-type-explicit {&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    border: 1px solid #404040; /* FIXME: theming */&lt;br /&gt;
    padding: 2px 4px;&lt;br /&gt;
    font-family: monospace;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=867</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=867"/>
		<updated>2025-03-31T00:50:04Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 2px 4px;&lt;br /&gt;
    gap: 4px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=866</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=866"/>
		<updated>2025-03-31T00:49:57Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, &amp;quot;&amp;quot;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/newtype&amp;diff=865</id>
		<title>User:PenguinEncounter/newtype</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/newtype&amp;diff=865"/>
		<updated>2025-03-31T00:47:13Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;templatestyles src=&amp;quot;Module:Signatures/Signatures.css&amp;quot;/&amp;gt;{{#invoke:Signatures|type}}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=864</id>
		<title>Module:Signatures/Signatures.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures/Signatures.css&amp;diff=864"/>
		<updated>2025-03-31T00:46:47Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: new CSS&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;.lua-type-union {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-flow: row nowrap;&lt;br /&gt;
    padding: 3px 1px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=863</id>
		<title>User:PenguinEncounter/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=863"/>
		<updated>2025-03-31T00:44:29Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;font-size: 3rem;&amp;quot;&amp;gt;[https://wiki.figuramc.org/index.php/User:PenguinEncounter/sandbox?action=purge purge it]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{User:PenguinEncounter/newtype|function|Vector2|#TickEventCallback}}&lt;br /&gt;
&lt;br /&gt;
For example, to get the cube {{Emoji|BBCube.png}} RightArm:&lt;br /&gt;
&lt;br /&gt;
This is what is in your project:&lt;br /&gt;
&lt;br /&gt;
{{#invoke:MatrixFields|run|size=4}}&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=FSFolderOpen.png|content=&amp;lt;code&amp;gt;my_avatar&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=FiguraJSON.png|content=&amp;lt;code&amp;gt;avatar.json&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Head|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=Head}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=HeadLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=RightArm&amp;lt;br&amp;gt;oops|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArm}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArmLayer}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== conspicuous example code ==&lt;br /&gt;
{{#tag:syntaxhighlight|&lt;br /&gt;
function events.tick()&lt;br /&gt;
    &amp;lt;nowiki&amp;gt;--[[wiki.figuramc.org, &amp;lt;/nowiki&amp;gt;{{FULLPAGENAME}} rev. {{REVISIONID}}&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    models.model.Head:setVisible(false)&lt;br /&gt;
end&lt;br /&gt;
|lang=lua}}&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;code&amp;gt;HostAPI:setTitleTimes&amp;lt;/code&amp;gt; =&lt;br /&gt;
{{Host only|kind=method}}&lt;br /&gt;
Configures the hold, fade-in, and fade-out durations of titles displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Arguments !! Return Type&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(timesData {{type|Vector3}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(fadeInTime {{type|integer}}, stayTime {{type|integer}}, fadeOutTime {{type|integer}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
host:setTitleTimes(1, 1, 1)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;!-- idk --&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=862</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=862"/>
		<updated>2025-03-31T00:44:04Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, &amp;quot; / &amp;quot;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=861</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=861"/>
		<updated>2025-03-31T00:43:22Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local renderType&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.ExplicitType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeExplicit(typeM)&lt;br /&gt;
    local formatstr&lt;br /&gt;
    if not typeM.link then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;$TypeName&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    elseif typeM.link:find(&amp;quot;^https?://&amp;quot;) then&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[$Link $TypeName]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    else&lt;br /&gt;
        formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-explicit&amp;quot; data-name=&amp;quot;$TypeName&amp;quot; $ExtraAttr&amp;gt;[[$Link|$TypeName]]&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    end&lt;br /&gt;
    local replacements = {&lt;br /&gt;
        Link = typeM.link,&lt;br /&gt;
        TypeName = typeM.name,&lt;br /&gt;
        ExtraAttr = &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    local formatted = formatstr:gsub(&amp;quot;%$([a-zA-Z]+)&amp;quot;, replacements)&lt;br /&gt;
    return formatted&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.UnionType&lt;br /&gt;
---@return string&lt;br /&gt;
local function renderTypeUnion(typeM)&lt;br /&gt;
    if #typeM.anyOf == 1 then return renderType(typeM.anyOf[1]) end&lt;br /&gt;
    if #typeM.anyOf == 2 then&lt;br /&gt;
        -- todo: add ? for optionals&lt;br /&gt;
    end&lt;br /&gt;
    local formatstr = [=[&amp;lt;span class=&amp;quot;lua-type-union&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]=]&lt;br /&gt;
    local chunks = {}&lt;br /&gt;
    for _, t in ipairs(typeM.anyOf) do&lt;br /&gt;
        chunks[#chunks+1] = renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
    return formatstr:format(table.concat(chunks, &amp;quot; &amp;quot;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
---@return string&lt;br /&gt;
function renderType(typeM)&lt;br /&gt;
    if typeM.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.ExplicitType&lt;br /&gt;
        return renderTypeExplicit(typeM)&lt;br /&gt;
    elseif typeM.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
        ---@cast typeM docs.UnionType&lt;br /&gt;
        return renderTypeUnion(typeM)&lt;br /&gt;
    end&lt;br /&gt;
    error((&amp;quot;An internal error occured: unsure how to render a %s type&amp;quot;):format(tostring(typeM.typeKind)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=860</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=860"/>
		<updated>2025-03-30T23:54:09Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
local function renderType(typeM)&lt;br /&gt;
    return tostring(typeM)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        local s = spool.new(table.concat(tabulated, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=859</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=859"/>
		<updated>2025-03-30T23:53:48Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
local function renderType(typeM)&lt;br /&gt;
    return tostring(typeM)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    local nargs = 0&lt;br /&gt;
    while input_args[nargs + 1] do nargs = nargs + 1 end&lt;br /&gt;
    if nargs == 0 then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if nargs == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        local tabulated = {}&lt;br /&gt;
        for i = 1, nargs do tabulated[i] = input_args[i] end&lt;br /&gt;
        do return table.concat(tabulated, &amp;quot;|&amp;quot;) end&lt;br /&gt;
        local s = spool.new(table.concat(input_args, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=858</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=858"/>
		<updated>2025-03-30T23:51:08Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
local function renderType(typeM)&lt;br /&gt;
    return tostring(typeM)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    do return table.maxn(input_args) end&lt;br /&gt;
    if not input_args[1] then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    &lt;br /&gt;
    if not input_args[2] then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        do return table.concat(input_args, &amp;quot;|&amp;quot;) end&lt;br /&gt;
        local s = spool.new(table.concat(input_args, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=857</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=857"/>
		<updated>2025-03-30T23:50:25Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
local function renderType(typeM)&lt;br /&gt;
    return tostring(typeM)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    do return #input_args end&lt;br /&gt;
    if not input_args[1] then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    if #input_args == 1 then&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        do return table.concat(input_args, &amp;quot;|&amp;quot;) end&lt;br /&gt;
        local s = spool.new(table.concat(input_args, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=856</id>
		<title>User:PenguinEncounter/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=856"/>
		<updated>2025-03-30T23:48:57Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;font-size: 3rem;&amp;quot;&amp;gt;[https://wiki.figuramc.org/index.php/User:PenguinEncounter/sandbox?action=purge purge it]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{User:PenguinEncounter/newtype|boolean|other}}&lt;br /&gt;
&lt;br /&gt;
For example, to get the cube {{Emoji|BBCube.png}} RightArm:&lt;br /&gt;
&lt;br /&gt;
This is what is in your project:&lt;br /&gt;
&lt;br /&gt;
{{#invoke:MatrixFields|run|size=4}}&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=FSFolderOpen.png|content=&amp;lt;code&amp;gt;my_avatar&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=FiguraJSON.png|content=&amp;lt;code&amp;gt;avatar.json&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Head|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=Head}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=HeadLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=RightArm&amp;lt;br&amp;gt;oops|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArm}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArmLayer}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== conspicuous example code ==&lt;br /&gt;
{{#tag:syntaxhighlight|&lt;br /&gt;
function events.tick()&lt;br /&gt;
    &amp;lt;nowiki&amp;gt;--[[wiki.figuramc.org, &amp;lt;/nowiki&amp;gt;{{FULLPAGENAME}} rev. {{REVISIONID}}&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    models.model.Head:setVisible(false)&lt;br /&gt;
end&lt;br /&gt;
|lang=lua}}&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;code&amp;gt;HostAPI:setTitleTimes&amp;lt;/code&amp;gt; =&lt;br /&gt;
{{Host only|kind=method}}&lt;br /&gt;
Configures the hold, fade-in, and fade-out durations of titles displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Arguments !! Return Type&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(timesData {{type|Vector3}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(fadeInTime {{type|integer}}, stayTime {{type|integer}}, fadeOutTime {{type|integer}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
host:setTitleTimes(1, 1, 1)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;!-- idk --&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=855</id>
		<title>Module:Signatures</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=Module:Signatures&amp;diff=855"/>
		<updated>2025-03-30T23:48:53Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- a MediaWiki module&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
Syntax description:&lt;br /&gt;
&amp;lt;name&amp;gt; &amp;lt;attr...&amp;gt; ...;;&lt;br /&gt;
&amp;lt;modName&amp;gt; [modSyn];;&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
---@alias docs.ModelKind &amp;quot;field&amp;quot; | &amp;quot;method&amp;quot; | &amp;quot;type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---Origin of this documentation item.&lt;br /&gt;
---@alias docs.ModelOrigin&lt;br /&gt;
---| &amp;quot;Lua&amp;quot; Lua 5.2 built-in types and APIs.&lt;br /&gt;
---| &amp;quot;Figura&amp;quot; Figura types and APIs. Usually the default.&lt;br /&gt;
---| &amp;quot;abstract&amp;quot; Describes ideas, but no concrete implementation exists.&lt;br /&gt;
---| &amp;quot;typedef&amp;quot; Custom on-the-spot types, like function protocols or array types.&lt;br /&gt;
&lt;br /&gt;
---@alias Set&amp;lt;T&amp;gt; {[T]: true}&lt;br /&gt;
&lt;br /&gt;
---@class docs.DocModel&lt;br /&gt;
---@field kind docs.ModelKind&lt;br /&gt;
---@field name string?&lt;br /&gt;
---@field prefixHint string?&lt;br /&gt;
---@field attributes Set&amp;lt;string&amp;gt;&lt;br /&gt;
---@field origin docs.ModelOrigin&lt;br /&gt;
&lt;br /&gt;
---@class docs.FieldModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;field&amp;quot;&lt;br /&gt;
---@field canRead boolean&lt;br /&gt;
---@field canWrite boolean&lt;br /&gt;
---@field readType docs.TypeModel?&lt;br /&gt;
---@field writeType docs.TypeModel?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;method&amp;quot;&lt;br /&gt;
---@field signatures docs.MethodSignature[]&lt;br /&gt;
---@field isStatic boolean&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature.Parameter&lt;br /&gt;
---@field name string&lt;br /&gt;
---@field type docs.TypeModel&lt;br /&gt;
---@field vararg boolean?&lt;br /&gt;
&lt;br /&gt;
---@class docs.MethodSignature&lt;br /&gt;
---@field returns docs.TypeModel[]&lt;br /&gt;
---@field parameters docs.MethodSignature.Parameter[]&lt;br /&gt;
&lt;br /&gt;
---@class docs.TypeModel : docs.DocModel&lt;br /&gt;
---@field kind &amp;quot;type&amp;quot;&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot; | &amp;quot;union&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@class docs.ExplicitType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;explicit&amp;quot;&lt;br /&gt;
---@field link string?&lt;br /&gt;
&lt;br /&gt;
---@class docs.UnionType : docs.TypeModel&lt;br /&gt;
---@field typeKind &amp;quot;union&amp;quot;&lt;br /&gt;
---@field anyOf docs.TypeModel[]&lt;br /&gt;
&lt;br /&gt;
---@type metatable&lt;br /&gt;
local type_meta = {&lt;br /&gt;
    __tostring = function (t)&lt;br /&gt;
        return (&amp;quot;&amp;lt;type %s from %s&amp;gt;&amp;quot;):format(t.name or &#039;(anonymous)&#039;, t.origin)&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@class docs.spool : docs.DocModel&lt;br /&gt;
---@field src string&lt;br /&gt;
---@field cursor integer&lt;br /&gt;
---@field cursorNext integer?&lt;br /&gt;
local spool = {}&lt;br /&gt;
&lt;br /&gt;
---Create a new spool.&lt;br /&gt;
---@param src string&lt;br /&gt;
---@return docs.spool&lt;br /&gt;
function spool.new(src)&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        src = src,&lt;br /&gt;
        cursor = 1&lt;br /&gt;
    }, {__index = spool})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return integer cursor&lt;br /&gt;
function spool:consumeSpace()&lt;br /&gt;
    self.cursor = self.src:match(&amp;quot;%s*()&amp;quot;, self.cursor)&lt;br /&gt;
    return self.cursor&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local unpack = unpack or table.unpack -- 5.2 compat&lt;br /&gt;
&lt;br /&gt;
---Starts matching from the current cursor position, and recieves the next cursor position from a position capture.&lt;br /&gt;
---@param pattern string&lt;br /&gt;
---@param noMatchMsg string Format pattern with 1 %d to hold the cursor value.&lt;br /&gt;
---@return any ...&lt;br /&gt;
function spool:matchc(pattern, noMatchMsg)&lt;br /&gt;
    local results = {self.src:match(pattern, self.cursor)}&lt;br /&gt;
    if #results == 0 then error(noMatchMsg:format(self.cursor)) end&lt;br /&gt;
    self.cursorNext = results[#results]&lt;br /&gt;
    results[#results] = nil&lt;br /&gt;
    return unpack(results)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@return string&lt;br /&gt;
function spool:peek()&lt;br /&gt;
    return self.src:sub(self.cursor, self.cursor)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:adv()&lt;br /&gt;
    if self:isAtEOF() then return end&lt;br /&gt;
    self.cursor = self.cursor + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:matchadv(pattern)&lt;br /&gt;
    local _, to = self.src:find(pattern, self.cursor)&lt;br /&gt;
    if to then&lt;br /&gt;
        self.cursor = to + 1&lt;br /&gt;
        return true&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:commit()&lt;br /&gt;
    if not self.cursorNext then error &amp;quot;spool:commit(): Nothing to commit&amp;quot; end&lt;br /&gt;
    self.cursor = self.cursorNext&lt;br /&gt;
    self.cursorNext = nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function spool:isAtEOF() return self.cursor &amp;gt; #self.src end&lt;br /&gt;
&lt;br /&gt;
local LUA_TYPES_URL = &amp;quot;https://www.lua.org/manual/5.2/manual.html#2.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, docs.ExplicitType&amp;gt;&lt;br /&gt;
local typeDefinitions = {}&lt;br /&gt;
---@type table&amp;lt;string, string&amp;gt;&lt;br /&gt;
local typeAlias = {}&lt;br /&gt;
local types = setmetatable({}, {&lt;br /&gt;
    __index = function (t, k)&lt;br /&gt;
        local resolvedName = k&lt;br /&gt;
        local i = 0&lt;br /&gt;
        while typeAlias[resolvedName] do&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            if i &amp;gt; 10 then error &amp;quot;internal: alias chain too long&amp;quot; end&lt;br /&gt;
            resolvedName = typeAlias[resolvedName]&lt;br /&gt;
        end&lt;br /&gt;
        return typeDefinitions[resolvedName]&lt;br /&gt;
    end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@param link string?&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function newExplicitType(name, origin, link)&lt;br /&gt;
    if typeDefinitions[name] then error &amp;quot;Type already exists&amp;quot; end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = name,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = origin,&lt;br /&gt;
        link = link&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[name] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param anyOf docs.TypeModel[]&lt;br /&gt;
---@return docs.UnionType&lt;br /&gt;
local function newUnionType(anyOf)&lt;br /&gt;
    local function flatten(typ)&lt;br /&gt;
        if not typ.kind or typ.typeKind == &amp;quot;union&amp;quot; then&lt;br /&gt;
            local iterator&lt;br /&gt;
            if not typ.kind then&lt;br /&gt;
                iterator = typ&lt;br /&gt;
            else&lt;br /&gt;
                iterator = typ.anyOf&lt;br /&gt;
            end&lt;br /&gt;
            local seen = {}&lt;br /&gt;
            local res = {}&lt;br /&gt;
            for _, typ2 in ipairs(iterator) do&lt;br /&gt;
                for _, v in ipairs(flatten(typ2)) do&lt;br /&gt;
                    if not seen[v] then&lt;br /&gt;
                        res[#res + 1] = v&lt;br /&gt;
                        seen[v] = true&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            return res&lt;br /&gt;
        elseif typ.typeKind == &amp;quot;explicit&amp;quot; then&lt;br /&gt;
            return { typ }&lt;br /&gt;
        end&lt;br /&gt;
        error &amp;quot;No type&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    ---@type docs.UnionType&lt;br /&gt;
    return setmetatable({&lt;br /&gt;
        anyOf = flatten(anyOf),&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;abstract&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;union&amp;quot;&lt;br /&gt;
    }, {&lt;br /&gt;
        __tostring = function (t)&lt;br /&gt;
            local chunks = {}&lt;br /&gt;
            for _, type in ipairs(t.anyOf) do&lt;br /&gt;
                chunks[#chunks+1] = tostring(type)&lt;br /&gt;
            end&lt;br /&gt;
            return table.concat(chunks, &#039; | &#039;)&lt;br /&gt;
        end&lt;br /&gt;
    })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param name string&lt;br /&gt;
---@param origin docs.ModelOrigin&lt;br /&gt;
---@return docs.ExplicitType&lt;br /&gt;
local function getOrDefineType(name, origin)&lt;br /&gt;
    local t = types[name]&lt;br /&gt;
    if t then return t end&lt;br /&gt;
    return newExplicitType(name, origin, name)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create a new type definition type.&lt;br /&gt;
---@param typedefname string without #&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function newTypedef(typedefname)&lt;br /&gt;
    local prefixed = &amp;quot;#&amp;quot;..typedefname&lt;br /&gt;
    if typeDefinitions[prefixed] then return typeDefinitions[prefixed] end&lt;br /&gt;
    ---@type docs.ExplicitType&lt;br /&gt;
    local t = setmetatable({&lt;br /&gt;
        kind = &amp;quot;type&amp;quot;,&lt;br /&gt;
        typeKind = &amp;quot;explicit&amp;quot;,&lt;br /&gt;
        name = typedefname,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        origin = &amp;quot;typedef&amp;quot;,&lt;br /&gt;
        link = &amp;quot;#typedef_&amp;quot;..typedefname&lt;br /&gt;
    }, type_meta)&lt;br /&gt;
    typeDefinitions[prefixed] = t&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
do -- builtin types&lt;br /&gt;
&lt;br /&gt;
    -- currying&lt;br /&gt;
    ---@param sourceName string&lt;br /&gt;
    ---@return fun(list: string[])&lt;br /&gt;
    local function alias(sourceName)&lt;br /&gt;
        return function(list)&lt;br /&gt;
            for _, v in ipairs(list) do&lt;br /&gt;
                typeAlias[v] = sourceName&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    newExplicitType(&amp;quot;any&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    alias &amp;quot;any&amp;quot; { &amp;quot;AnyType&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;void&amp;quot;, &amp;quot;abstract&amp;quot;)&lt;br /&gt;
    newExplicitType(&amp;quot;nil&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;nil&amp;quot; { &amp;quot;null&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;boolean&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;boolean&amp;quot; { &amp;quot;Boolean&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;number&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;number&amp;quot; { &amp;quot;Number&amp;quot;, &amp;quot;double&amp;quot;, &amp;quot;float&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;integer&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;integer&amp;quot; { &amp;quot;Integer&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;string&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;string&amp;quot; { &amp;quot;String&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;function&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;function&amp;quot; { &amp;quot;Function&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;userdata&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;userdata&amp;quot; { &amp;quot;Userdata&amp;quot; }&lt;br /&gt;
    newExplicitType(&amp;quot;table&amp;quot;, &amp;quot;Lua&amp;quot;, LUA_TYPES_URL)&lt;br /&gt;
    alias &amp;quot;table&amp;quot; { &amp;quot;Table&amp;quot; }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseComment(s)&lt;br /&gt;
    s:matchc(&amp;quot;%*/()&amp;quot;, &amp;quot;No end of comment found&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
---@return docs.TypeModel&lt;br /&gt;
local function parseSimpleType(s)&lt;br /&gt;
    if s:isAtEOF() then error(&amp;quot;Expected a type at position &amp;quot; .. s.cursor .. &amp;quot; but instead found nothing&amp;quot;) end&lt;br /&gt;
    local prefix = s:peek()&lt;br /&gt;
    if prefix == &amp;quot;#&amp;quot; then&lt;br /&gt;
        s:adv()&lt;br /&gt;
        local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a typedef name at position %d, but found nothing after the #&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        return newTypedef(typename)&lt;br /&gt;
    end&lt;br /&gt;
    local typename = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected a type name at position %d, but found nothing&amp;quot;)&lt;br /&gt;
    s:commit()&lt;br /&gt;
    local t = getOrDefineType(typename, &amp;quot;Figura&amp;quot;)&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param s docs.spool&lt;br /&gt;
local function parseType(s)&lt;br /&gt;
    local t = parseSimpleType(s)&lt;br /&gt;
    s:consumeSpace()&lt;br /&gt;
    local nextC = s:peek()&lt;br /&gt;
    if nextC == &#039;|&#039; then&lt;br /&gt;
        local unions = {t}&lt;br /&gt;
        while nextC == &#039;|&#039; do&lt;br /&gt;
            s:adv()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            unions[#unions+1] = parseSimpleType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            nextC = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        return newUnionType(unions)&lt;br /&gt;
    else&lt;br /&gt;
        return t&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function noop() end&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedModifiers = {&lt;br /&gt;
    prefixHint = function (line, model)&lt;br /&gt;
        if model.prefixHint then error &amp;quot;duplicate prefixHint declaration&amp;quot; end&lt;br /&gt;
        model.prefixHint = line&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.DocModel)&amp;gt;&lt;br /&gt;
local sharedAttributes = {&lt;br /&gt;
    global = noop&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodModifiers = {&lt;br /&gt;
}&lt;br /&gt;
methodModifiers = setmetatable(methodModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.MethodModel)&amp;gt;&lt;br /&gt;
local methodAttributes = {&lt;br /&gt;
    static = function (model)&lt;br /&gt;
        model.isStatic = true&lt;br /&gt;
    end,&lt;br /&gt;
    hostOnly = noop&lt;br /&gt;
}&lt;br /&gt;
methodAttributes = setmetatable(methodAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@type table&amp;lt;string, fun(line: string, model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldModifiers = {&lt;br /&gt;
    [&amp;quot;type&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field type&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;readType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field readType&amp;quot; end&lt;br /&gt;
        model.readType = t&lt;br /&gt;
    end,&lt;br /&gt;
    [&amp;quot;writeType&amp;quot;] = function (line, model)&lt;br /&gt;
        local s = spool.new(line)&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error &amp;quot;expected end of statement after field writeType&amp;quot; end&lt;br /&gt;
        model.writeType = t&lt;br /&gt;
    end,&lt;br /&gt;
}&lt;br /&gt;
fieldModifiers = setmetatable(fieldModifiers, {__index = sharedModifiers})&lt;br /&gt;
---@type table&amp;lt;string, fun(model: docs.FieldModel)&amp;gt;&lt;br /&gt;
local fieldAttributes = {&lt;br /&gt;
    readOnly = function (model)&lt;br /&gt;
        model.canWrite = false&lt;br /&gt;
    end,&lt;br /&gt;
    writeOnly = function (model)&lt;br /&gt;
        model.canRead = false&lt;br /&gt;
    end&lt;br /&gt;
}&lt;br /&gt;
fieldAttributes = setmetatable(fieldAttributes, {__index = sharedAttributes})&lt;br /&gt;
&lt;br /&gt;
---@param sigtext string&lt;br /&gt;
local function parseMethodSignature(sigtext)&lt;br /&gt;
    local s = spool.new(sigtext)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodSignature&lt;br /&gt;
    local model = {&lt;br /&gt;
        returns = {},&lt;br /&gt;
        parameters = {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local function parseParameter(assumeV)&lt;br /&gt;
        ---@type docs.MethodSignature.Parameter&lt;br /&gt;
        local param = setmetatable({&lt;br /&gt;
            name = &amp;quot;&amp;quot;,&lt;br /&gt;
            type = typeDefinitions.void,&lt;br /&gt;
            vararg = false&lt;br /&gt;
        }, {&lt;br /&gt;
            __tostring = function (t)&lt;br /&gt;
                return (&amp;quot;&amp;lt;param %s (%s)&amp;gt;&amp;quot;):format(t.name, tostring(t.type))&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
        if assumeV then&lt;br /&gt;
            param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
            param.vararg = true&lt;br /&gt;
        else&lt;br /&gt;
            -- already consumed: &#039;parameter &#039;&lt;br /&gt;
            if s:matchadv(&amp;quot;^%.%.%. ?&amp;quot;) then&lt;br /&gt;
                param.name = &amp;quot;...&amp;quot;&lt;br /&gt;
                param.vararg = true&lt;br /&gt;
            else&lt;br /&gt;
                param.name = s:matchc(&amp;quot;^([a-zA-Z0-9]+) ?()&amp;quot;, &amp;quot;Expected parameter name at %d but found nothing&amp;quot;)&lt;br /&gt;
                s:commit()&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        param.type = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end parameter declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        model.parameters[#model.parameters+1] = param&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseReturns()&lt;br /&gt;
        -- already consumed: &#039;returns &#039;&lt;br /&gt;
        model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while s:matchadv(&#039;^,&#039;) do&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            model.returns[#model.returns+1] = parseType(s)&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
        if not s:matchadv(&#039;^;&#039;) then error(&amp;quot;Expected a semicolon to end returns declaration at position &amp;quot; .. s.cursor) end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parseStmt()&lt;br /&gt;
        if s:matchadv &amp;quot;^/%*&amp;quot; then&lt;br /&gt;
            return parseComment(s)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^returns &amp;quot; then&lt;br /&gt;
            return parseReturns()&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter%.%.%.&amp;quot; then&lt;br /&gt;
            return parseParameter(true)&lt;br /&gt;
        end&lt;br /&gt;
        if s:matchadv &amp;quot;^parameter &amp;quot; then&lt;br /&gt;
            return parseParameter()&lt;br /&gt;
        end&lt;br /&gt;
        error(&amp;quot;No valid signature statement at &amp;quot; .. s.cursor)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
local function parseMethod(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.MethodModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;method&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        isStatic = false,&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        signatures = {},&lt;br /&gt;
    }&lt;br /&gt;
    local parseTopLevelStmt, parseMethodHeader, parseSignatureOuter&lt;br /&gt;
    local blockKw = {}&lt;br /&gt;
&lt;br /&gt;
    function parseSignatureOuter()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local block_content = s:matchc(&amp;quot;^(%b{})%s*;()&amp;quot;, &amp;quot;Expected a bracketed block after &#039;signature&#039; at position %d&amp;quot;)&lt;br /&gt;
        local inside = block_content:sub(2, -2)&lt;br /&gt;
        local sig = parseMethodSignature(inside)&lt;br /&gt;
        model.signatures[#model.signatures+1] = sig&lt;br /&gt;
        s:commit()&lt;br /&gt;
    end&lt;br /&gt;
    blockKw.signature = parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseMethodHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected method name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not methodAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown method attribute &#039;%s&#039; at position %d in method header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            methodAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of method documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier or block keyword, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if methodModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return methodModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        if blockKw[action] then return blockKw[action]() end&lt;br /&gt;
        error((&amp;quot;Expected a modifier or block keyword, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseMethodHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param docstr string&lt;br /&gt;
---@return docs.FieldModel&lt;br /&gt;
local function parseField(docstr)&lt;br /&gt;
    local s = spool.new(docstr)&lt;br /&gt;
&lt;br /&gt;
    ---@type docs.FieldModel&lt;br /&gt;
    local model = {&lt;br /&gt;
        kind = &amp;quot;field&amp;quot;,&lt;br /&gt;
        attributes = {},&lt;br /&gt;
        name = &amp;quot;&amp;lt;unnamed&amp;gt;&amp;quot;,&lt;br /&gt;
        origin = &amp;quot;Figura&amp;quot;,&lt;br /&gt;
        canRead = true,&lt;br /&gt;
        canWrite = true&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    local parseTopLevelStmt, parseFieldHeader, parseSignatureOuter&lt;br /&gt;
&lt;br /&gt;
    function parseFieldHeader()&lt;br /&gt;
        local name = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected field name at %d&amp;quot;)&lt;br /&gt;
        model.name = name&lt;br /&gt;
        s:commit()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        local peek = s:peek()&lt;br /&gt;
        while #peek ~= 0 and peek ~= &#039;;&#039; do&lt;br /&gt;
            local attr = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected something else!&amp;quot;)&lt;br /&gt;
            if not fieldAttributes[attr] then&lt;br /&gt;
                error((&amp;quot;Unknown field attribute &#039;%s&#039; at position %d in field header&amp;quot;):format(attr, s.cursor))&lt;br /&gt;
            end&lt;br /&gt;
            fieldAttributes[attr](model)&lt;br /&gt;
            model.attributes[attr] = true&lt;br /&gt;
            s:commit()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
            peek = s:peek()&lt;br /&gt;
        end&lt;br /&gt;
        if peek == &#039;;&#039; then&lt;br /&gt;
            s:adv() -- advance past the ;&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;Reached end of field documentation while parsing header&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    function parseTopLevelStmt()&lt;br /&gt;
        local startAt = s.cursor&lt;br /&gt;
        ---@type string&lt;br /&gt;
        local action = s:matchc(&amp;quot;^([^%s;]+) ?()&amp;quot;, &amp;quot;Expected a modifier, but got nothing instead at position %d&amp;quot;)&lt;br /&gt;
        s:commit()&lt;br /&gt;
        if fieldModifiers[action] then&lt;br /&gt;
            ---@type string&lt;br /&gt;
            local remainder = s:matchc(&amp;quot;^(.-);%s*()&amp;quot;, &amp;quot;Expected semicolon to end statement around %d&amp;quot;)&lt;br /&gt;
            s:commit()&lt;br /&gt;
            return fieldModifiers[action](remainder, model)&lt;br /&gt;
        end&lt;br /&gt;
        error((&amp;quot;Expected a modifier, but got &#039;%s&#039; instead at position %d&amp;quot;):format(action, startAt))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function parse()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        parseFieldHeader()&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        while not s:isAtEOF() do&lt;br /&gt;
            parseTopLevelStmt()&lt;br /&gt;
            s:consumeSpace()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    parse()&lt;br /&gt;
    return model&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param typeM docs.TypeModel&lt;br /&gt;
local function renderType(typeM)&lt;br /&gt;
    return tostring(typeM)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---@param o any&lt;br /&gt;
---@param i number?&lt;br /&gt;
local function printFancy(o, i)&lt;br /&gt;
    i = i or 2&lt;br /&gt;
    local indent = (&amp;quot; &amp;quot;):rep(i)&lt;br /&gt;
    if type(o) == &amp;quot;table&amp;quot; then&lt;br /&gt;
        if getmetatable(o) and getmetatable(o).__tostring then&lt;br /&gt;
            return tostring(o)&lt;br /&gt;
        end&lt;br /&gt;
        local b = &amp;quot;{\n&amp;quot;&lt;br /&gt;
        for k, v in pairs(o) do&lt;br /&gt;
            b = b .. indent .. printFancy(k, i + 2) .. &amp;quot; = &amp;quot; .. printFancy(v, i + 2) .. &amp;quot;,\n&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        b = b .. (&amp;quot; &amp;quot;):rep(i - 2) .. &amp;quot;}&amp;quot;&lt;br /&gt;
        return b&lt;br /&gt;
    else&lt;br /&gt;
        return tostring(o)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseAndRenderType(frame)&lt;br /&gt;
    local input_args = frame:getParent().args&lt;br /&gt;
    do return printFancy(input_args) end&lt;br /&gt;
    if not input_args[1] then error &amp;quot;Need to specify at least one type&amp;quot; end&lt;br /&gt;
    if #input_args == 1 then&lt;br /&gt;
        do return input_args[1] end&lt;br /&gt;
        local s = spool.new(input_args[1])&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    else&lt;br /&gt;
        do return table.concat(input_args, &amp;quot;|&amp;quot;) end&lt;br /&gt;
        local s = spool.new(table.concat(input_args, &amp;quot;|&amp;quot;))&lt;br /&gt;
        local t = parseType(s)&lt;br /&gt;
        s:consumeSpace()&lt;br /&gt;
        if not s:isAtEOF() then error(&amp;quot;Expected end of type at position &amp;quot; .. s.cursor) end&lt;br /&gt;
        return renderType(t)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- local m = parseField [[&lt;br /&gt;
--     player global readOnly;&lt;br /&gt;
--     type PlayerAPI;&lt;br /&gt;
-- ]]&lt;br /&gt;
-- print(printFancy(m))&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
    type = parseAndRenderType&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=854</id>
		<title>User:PenguinEncounter/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=854"/>
		<updated>2025-03-30T23:47:11Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;font-size: 3rem;&amp;quot;&amp;gt;[https://wiki.figuramc.org/index.php/User:PenguinEncounter/sandbox?action=purge purge it]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{User:PenguinEncounter/newtype|boolean}}&lt;br /&gt;
&lt;br /&gt;
For example, to get the cube {{Emoji|BBCube.png}} RightArm:&lt;br /&gt;
&lt;br /&gt;
This is what is in your project:&lt;br /&gt;
&lt;br /&gt;
{{#invoke:MatrixFields|run|size=4}}&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=FSFolderOpen.png|content=&amp;lt;code&amp;gt;my_avatar&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=FiguraJSON.png|content=&amp;lt;code&amp;gt;avatar.json&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Head|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=Head}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=HeadLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=RightArm&amp;lt;br&amp;gt;oops|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArm}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArmLayer}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== conspicuous example code ==&lt;br /&gt;
{{#tag:syntaxhighlight|&lt;br /&gt;
function events.tick()&lt;br /&gt;
    &amp;lt;nowiki&amp;gt;--[[wiki.figuramc.org, &amp;lt;/nowiki&amp;gt;{{FULLPAGENAME}} rev. {{REVISIONID}}&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    models.model.Head:setVisible(false)&lt;br /&gt;
end&lt;br /&gt;
|lang=lua}}&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;code&amp;gt;HostAPI:setTitleTimes&amp;lt;/code&amp;gt; =&lt;br /&gt;
{{Host only|kind=method}}&lt;br /&gt;
Configures the hold, fade-in, and fade-out durations of titles displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Arguments !! Return Type&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(timesData {{type|Vector3}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(fadeInTime {{type|integer}}, stayTime {{type|integer}}, fadeOutTime {{type|integer}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
host:setTitleTimes(1, 1, 1)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;!-- idk --&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
	<entry>
		<id>https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=853</id>
		<title>User:PenguinEncounter/sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.figuramc.org/index.php?title=User:PenguinEncounter/sandbox&amp;diff=853"/>
		<updated>2025-03-30T23:46:07Z</updated>

		<summary type="html">&lt;p&gt;PenguinEncounter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{User:PenguinEncounter/newtype|boolean}}&lt;br /&gt;
&lt;br /&gt;
For example, to get the cube {{Emoji|BBCube.png}} RightArm:&lt;br /&gt;
&lt;br /&gt;
This is what is in your project:&lt;br /&gt;
&lt;br /&gt;
{{#invoke:MatrixFields|run|size=4}}&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=FSFolderOpen.png|content=&amp;lt;code&amp;gt;my_avatar&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=FiguraJSON.png|content=&amp;lt;code&amp;gt;avatar.json&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{tree|icon=BBModel.png|content=&amp;lt;code&amp;gt;model.bbmodel&amp;lt;/code&amp;gt;|inner=&lt;br /&gt;
{{tree|icon=BBGroup.png|content=Head|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=Head}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=HeadLayer}}&lt;br /&gt;
}}&lt;br /&gt;
{{tree|icon=BBGroup.png|content=RightArm&amp;lt;br&amp;gt;oops|inner=&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArm}}&lt;br /&gt;
{{tree|icon=BBCube.png|content=RightArmLayer}}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== conspicuous example code ==&lt;br /&gt;
{{#tag:syntaxhighlight|&lt;br /&gt;
function events.tick()&lt;br /&gt;
    &amp;lt;nowiki&amp;gt;--[[wiki.figuramc.org, &amp;lt;/nowiki&amp;gt;{{FULLPAGENAME}} rev. {{REVISIONID}}&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    models.model.Head:setVisible(false)&lt;br /&gt;
end&lt;br /&gt;
|lang=lua}}&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;code&amp;gt;HostAPI:setTitleTimes&amp;lt;/code&amp;gt; =&lt;br /&gt;
{{Host only|kind=method}}&lt;br /&gt;
Configures the hold, fade-in, and fade-out durations of titles displayed on the screen.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Arguments !! Return Type&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(timesData {{type|Vector3}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setTitleTimes(fadeInTime {{type|integer}}, stayTime {{type|integer}}, fadeOutTime {{type|integer}})&amp;lt;/code&amp;gt;&lt;br /&gt;
| self {{type|HostAPI}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
host:setTitleTimes(1, 1, 1)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;!-- idk --&amp;gt;&lt;/div&gt;</summary>
		<author><name>PenguinEncounter</name></author>
	</entry>
</feed>