Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·originates fromworld 07158574-5…

luau_introspect

Parses Luau/asset source TEXT into structured declarations. Pure string/pattern parsing (`string.find` / `string.match` / `string.gmatch`) over the literal source — no compiling, no VM spawn, no runtime state, no engine FFI. Given source text, it extracts what is written in the f…

by◐lumi·posted 2mo ago
What it does

luau_introspect (module)

Parses Luau/asset source TEXT into structured declarations. Pure string/pattern parsing (string.find / string.match / string.gmatch) over the literal source — no compiling, no VM spawn, no runtime state, no engine FFI. Given source text, it extracts what is written in the file.

This is the shared parser every per-assetType M.ref.inspect hook uses to turn a component/module/tool's own source into the structured record asset.inspect returns.

Exports

  • docstrings(src) -> DocMap — scans --!desc / --!arg / --!return / --!example comment runs and binds each run to the name of the declaration on the next non-comment line (the identifier after function, public:, local function, or M.).
  • publicFields(src) -> { FieldEntry } — parses public = { name = Field.<kind>(default, mode), ... } table-literal entries and module-scope public.<name> = Field.<kind>(...) assignments.
  • events(src) -> { EventEntry } — parses events = { name = Event(payloadSchema?, syncMode?), ... } table-literal entries.
  • methods(src) -> { MethodEntry } — parses function public:<name>(...) / typed function public:<name>(...) declarations (params + optional return type).
  • lifecycleHooks(src, catalog) -> { string } — top-level function <name>( declarations whose name is in the caller-supplied catalog array.
  • moduleExports(src) -> { ExportEntry } — parses M.<name> = function assignments, function M.<name>(...) declarations, and a trailing return { foo = foo, ... } export table.
  • forSource(src, computeFn) -> any — memoises computeFn(src) keyed on src itself in a module-local table. The same source text returns the cached result without re-running computeFn; different source text recomputes. Keyed on the source content (not an asset checksum) because composite/container assets (component folders, scene folders, etc.) all hash to the empty-string checksum — a checksum-keyed cache collapses every such asset's detail onto whichever one was inspected first.

Usage

local Introspect = require("modules.luau_introspect")

local src = self:getInitScript()
local detail = Introspect.forSource(src, function(s)
    return {
        fields = Introspect.publicFields(s),
        events = Introspect.events(s),
        methods = Introspect.methods(s),
        hooks = Introspect.lifecycleHooks(s, lifecycleCatalog),
    }
end)

Notes

  • Every function is a pure string→table transform: same source text in, same structured table out, every time. No reads of live component state, no entity/world queries.
  • Structural scanning runs over a comment- and string-aware mask of the source (a same-length copy where comment bodies and string-literal contents are blanked to spaces, delimiters and newlines preserved). Pattern matches and brace/paren balancing run on the mask — so a brace, Field., Event(, or a whole entry that appears inside a comment or a string literal never registers — while captured values are sliced from the original source at the same (aligned) indices. This makes the parser robust to apostrophes in comments, commented-out entries, string defaults containing } / Field. / Event(, and [[ ]] long strings.
  • default / category are raw trimmed source-text slices (with any trailing comment stripped), not evaluated values.
    • For a ref constructor (Field.assetRef / dataRef / resource / componentRef) the FIRST positional argument is the category / contract / componentType selector, captured under category; the SECOND argument is captured under default.
    • For every other Field kind the first argument is the default and category is nil.
  • A trailing Sync / NoSync identifier is captured as sync, tolerating a trailing comma or a trailing comment after the last argument.
  • Field/Event entries whose key is a computed or indirect expression ([expr] = Field.number(...)) are omitted — only literal identifier keys are recognised.
  • methods captures a ... variadic parameter (as name = "..."), with its : <type> annotation when present.
  • A --!desc written above a field/event declaration (<name> = Field.<kind>(...) / <name> = Event(...)) or a public.<name> = assignment binds to that name in docstrings, and is folded into the corresponding publicFields / events entry's desc.
  • lifecycleHooks never hardcodes the lifecycle-callback catalog; the caller passes the list of names to match against.
  • forSource's memo is bounded (oldest-key eviction) so a long session touching many distinct asset versions can't grow it without limit.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

luau_introspect Module Parses Luau/asset source TEXT into structured declarations. Pure string/pattern parsing (string.find / string.match / string.gmatch) over the literal source — no compiling, no VM spawn, no runtime state, no engine FFI. Given source text, it extracts what is written in the literals. This is the shared parser every per-assetType `M.ref.inspect` hook uses to turn a component/module/tool's own source into the structured record `asset.inspect` returns. Structural scanning runs over a COMMENT- and STRING-aware "mask" of the source: `maskNonCode` produces a same-length copy where comment bodies and string-literal contents are blanked to spaces (delimiters and newlines preserved). Because the mask is index-aligned with the source, pattern matches and brace/paren balancing run on the mask (so a brace, `Field.`, or `Event(` inside a comment or string never registers), while captured values are sliced from the ORIGINAL source at the same indices. Consumers: local Introspect = require("modules.luau_introspect") local docs = Introspect.docstrings(src) local fields = Introspect.publicFields(src)

trim(s: string) → string

Internal: trim leading/trailing whitespace.

argtypedescription
sstring

stripCR(line: string) → string

Internal: strip a trailing carriage return so \r\n source still lines up.

argtypedescription
linestring

longBracketOpen(s: string, i: number) → void

Internal: if a long-bracket opener (`[`, then zero+ `=`, then `[`) starts at `i`, return (level, contentStart) — the `=` count and the index just past the opener. Otherwise (nil, nil).

argtypedescription
sstring
inumber

longBracketClose(s: string, contentStart: number, level: number) → number

Internal: index just past the close of a long bracket at `level` whose content begins at `contentStart`. Returns #s + 1 when unterminated.

argtypedescription
sstring
contentStartnumber
levelnumber

maskNonCode(s: string) → string

Internal: a same-length copy of `s` where comment bodies and string-literal contents are replaced by spaces. Newlines and string delimiters are preserved so line structure and "this is a string" cues survive; every structural character in real CODE stays intact. All entry detection and brace/paren balancing scans this — captured values are sliced from the original source at the same (aligned) indices.

argtypedescription
sstring

stripComments(s: string) → string

Internal: `s` with comment bodies removed but string literals kept verbatim. Used to clean a captured value slice (which may carry a trailing comment) without destroying a real string default.

argtypedescription
sstring

cleanArg(s: string) → string

Internal: clean a captured value slice — drop any comment, trim edges.

argtypedescription
sstring

balancedParenEnd(mask: string, openIdx: number) → number

Internal: index of the ")" that balances the "(" at `openIdx` in the mask (nil if unbalanced). The mask has no string/comment content, so a plain paren-depth count suffices.

argtypedescription
maskstring
openIdxnumber

balancedBraceEnd(mask: string, openIdx: number) → number

Internal: index of the "}" that balances the "{" at `openIdx` in the mask.

argtypedescription
maskstring
openIdxnumber

argRanges(mask: string, from: number, to: number) → void

Internal: split masked text [from..to] on top-level commas; return the inclusive [a,b] ranges (full-string coordinates) of each argument. Nesting inside (), {}, [] is respected. An all-whitespace span yields an empty list (so "no arguments" reads as zero args), but a trailing comma still produces its (possibly empty) final range so a caller scanning from the end can skip it.

argtypedescription
maskstring
fromnumber
tonumber

splitTopLevel(mask: string) → void

Internal: split masked text into top-level comma-separated substrings.

argtypedescription
maskstring

callArgsAt(mask: string, src: string, openIdx: number) → void

Internal: given the "(" index in `mask`/`src` (which are aligned), return the argument list and the matching ")" index (nil if unbalanced).

argtypedescription
maskstring
srcstring
openIdxnumber

extractSync(args: { Arg }) → string

Internal: the trailing `Sync` / `NoSync` identifier among the args, or nil. Scans from the end, skipping empty (trailing-comma) args.

argtypedescription
args{ Arg }

argSrc(args: { Arg }, i: number) → string

Internal: the source text of argument `i`, or nil when it is absent or empty.

argtypedescription
args{ Arg }
inumber

fieldFromCall(name: string, kind: string, args: { Arg }) → FieldEntry

Internal: build a FieldEntry from a parsed `Field.<kind>(...)` call.

argtypedescription
namestring
kindstring
args{ Arg }

parseFieldEntries(bodyMask: string, bodySrc: string) → void

Internal: scan a masked body (with its aligned source) for `name = Field.<kind>(<args>)` entries. Only literal identifier keys match — a computed `[expr] = Field...` key is never captured.

argtypedescription
bodyMaskstring
bodySrcstring

parseEventEntries(bodyMask: string, bodySrc: string, docs: DocMap) → void

Internal: scan a masked events-block body for `name = Event(schema?, syncMode?)` entries. `docs` (from `docstrings(src)`) supplies each event's `desc` when a `--!desc` run sits above its declaration.

argtypedescription
bodyMaskstring
bodySrcstring
docsDocMap

extractAnchor(line: string) → string

Internal: the identifier a doc-comment run binds to, read from the next code line — the name after `function` / `public:` / `local function` / `M.` / `public.<name>`, or the key of a `<name> = Field.<...>` / `<name> = Event(...)` field/event declaration.

argtypedescription
linestring

docstrings(src: string) → DocMap

Scans `src` for `--!desc` / `--!arg` / `--!return` / `--!example` / `--!deprecated` / `--!hidden` doc-comment runs and binds each run to the name of the declaration on the next code line (the identifier after `function`, `public:`, `local function`, `M.`, `public.<name>`, or a `<name> = Field.<...>` / `<name> = Event(...)` field/event declaration). A continuation line ("--! " with no tag word) extends whichever field the run's last tag opened. Blank and plain `--` comment lines between the run and the declaration are skipped without terminating the run.

argtypedescription
srcstringLuau/asset source text.

examples

local d = I.docstrings(src); print(d.takeDamage.desc)

newRun( ) → DocEntry

appendDesc(text: string) → void

argtypedescription
textstring

lastTarget(t: string) → void

argtypedescription
tstring

lastTarget(t: string) → void

argtypedescription
tstring

lastTarget(t: string) → void

argtypedescription
tstring

lastTarget(t: string) → void

argtypedescription
tstring

literalTableFields(mask: string, src: string, docs: DocMap, tableName: string, results: { FieldEntry }) →

Parses `public = { name = Field.<kind>(...), ... }` table-literal entries and module-scope `public.<name> = Field.<kind>(...)` assignments. For a ref constructor (`assetRef` / `dataRef` / `resource` / `componentRef` / `taggedRef`) the first positional argument is captured as `category` and the second as `default`. An `enum` reports its option list as `values`, a `range` its `min` and `max`, a `bitmask` its width as `bits` — each ahead of its `default` — and an `alias` the fields it stands for as `targets`, with no default. For every other kind the first argument is the `default`. All are raw trimmed source-text slices, comments stripped. A trailing `Sync`/`NoSync` identifier becomes `sync`. Entries whose key is a computed/indirect expression (`[expr] = Field...`) are omitted. Comments and string literals never register as entries. Folds in `desc` from `docstrings(src)`.

argtypedescription
maskstring
srcstringLuau/asset source text.
docsDocMap
tableNamestring
results{ FieldEntry }

examples

local f = I.publicFields(src); print(f[1].name, f[1].category)

publicFields(src: string) → void

argtypedescription
srcstring

privateFields(src: string) →

Parses the `private = { name = Field.<kind>(...), ... }` table literal the way `publicFields` parses `public`: each entry's kind, its default and the arguments ahead of it, and its `sync`. A private field a component marks `Serialized` is saved with the scene like a public one, so a reader comparing saved state against declared defaults needs both.

argtypedescription
srcstringLuau/asset source text.

examples

local f = I.privateFields(src); print(f[1].name, f[1].default)

events(src: string) →

Parses `events = { name = Event(payloadSchema?, syncMode?), ... }` table-literal entries. The payload schema — a `{ k = Field.<kind>(...) }` table — is parsed like `publicFields` and reduced to `{name, type}` pairs; a payloadless `Event()` yields an empty payload. `sync` is true only when the SECOND positional `Event` argument is the literal identifier `Sync`. Comments and string literals never register. Folds in `desc` from `docstrings(src)`.

argtypedescription
srcstringLuau/asset source text.

examples

local e = I.events(src); print(e[1].name, e[1].sync)

signatureFrom(mask: string, openIdx: number) → void

Internal: parse the parameter list + return annotation of a function whose `(` is at `openIdx` in the mask. Returns the param entries, the return-type string (nil when unannotated), and the index just past the close (or `openIdx + 1` when the parens are unbalanced). A `...` variadic is captured with `name = "..."`.

argtypedescription
maskstring
openIdxnumber

methods(src: string) →

Parses `function public:<name>(<params>)` and `typed function public:<name>(<params>)` declarations — with an optional `: <ret>` return-type annotation immediately after the closing paren — into `{name, params, returns}`. Each param is split on top-level commas into `{name, type?}`; a `...` variadic is captured with `name = "..."`. Folds in `desc` from `docstrings(src)`.

argtypedescription
srcstringLuau/asset source text.

examples

local m = I.methods(src); print(m[1].name, m[1].returns)

refMethods(src: string) →

Parses the methods a `behavior.luau`-style module exposes on a `M.ref = { name = fn, ... }` table — the assetType's per-asset behavior surface (`ref:method(...)`). Each entry's key maps to its backing `local function <fn>(self, <params>): <ret>` (or `function <fn>(...)`) declaration: the leading `self` parameter is dropped (it is the ref the method is called on), the remaining params + return annotation + the declaration's `--!desc` are captured, with `hidden` and `deprecated` from its `--!hidden` / `--!deprecated` lines, so a listing can leave a hidden method out while a type shape still carries it. A key whose value is a table literal or an expression other than a bare function name is skipped. Runs over the comment/string-aware mask, so a commented `M.ref` never registers.

argtypedescription
srcstringLuau/asset source text.

examples

local api = I.refMethods(behaviorSrc); print(api[1].name, api[1].returns)

lifecycleHooks(src: string, catalog: { string }) →

Finds top-level `function <name>(` declarations whose name is in the caller-supplied `catalog`. Excludes `local function`, `public:` methods, and `M.` exports — only a bare top-level `function NAME(` declaration matches. Never hardcodes the lifecycle-callback catalog; the caller passes the list of names to match against. Commented-out declarations never match.

argtypedescription
srcstringLuau/asset source text.
catalog{ string }Array of lifecycle-callback names to match against.

examples

local hooks = I.lifecycleHooks(src, { "awake", "update" })

moduleExports(src: string) →

Parses `M.<name> = function(...)` assignments, `function M.<name>(...)` declarations, and a trailing `return { foo = foo, ... }` export table into `{name, kind, signature?, desc?}`. `kind` is `"function"` for a function assignment/declaration, `"value"` for any other `M.<name> = <expr>` assignment (a `==` comparison is not an assignment). A name that appears only in the `return { ... }` table infers its kind from whether a same-named `function <name>(` / `local function <name>(` declaration exists. Folds in `desc` from `docstrings(src)`.

argtypedescription
srcstringLuau/asset source text.

examples

local exports = I.moduleExports(src); print(exports[1].name)

ensure(name: string) → ExportEntry

argtypedescription
namestring

signatureAt(openIdx: number) → string

argtypedescription
openIdxnumber

maskNonCode(src: string) → string

A same-length copy of `src` where comment bodies and string-literal contents are replaced by spaces (delimiters and newlines preserved), so structural pattern matching over the result never registers a comment or string body. Index-aligned with `src` — a match position in the mask is the same position in `src`.

argtypedescription
srcstringLuau/asset source text.

examples

local mask = I.maskNonCode(src); string.find(mask, "operations%s*=%s*{")

tableLiteralKeys(src: string, assignmentName: string) →

Finds `<assignmentName> = { ... }` in `src` and returns the top-level literal-identifier keys of that table (e.g. `tableLiteralKeys(src, "operations")` on `operations = { generate = {...}, from_image = {...} }` returns `{"generate","from_image"}`). Runs over the comment/string-aware mask, so a `--` comment or string literal mentioning the assignment name never registers. Comment/string/nesting inside a value never breaks the scan (balanced brace matching, same as `publicFields`/`events`). A computed `["key"]` entry is omitted — only bare identifier keys are recognised. Returns `{}` when the assignment is absent.

argtypedescription
srcstringLuau/asset source text.
assignmentNamestringThe table's assignment name (e.g. "operations").

examples

local ops = I.tableLiteralKeys(src, "operations")

parseTableBody(mask: string, src: string, from: number, to: number) → TableNode

Internal: parse the top-level `key = value` entries of a table body whose masked/source slices span the full-coordinate range [from, to]. A value that starts with `{` recurses into a nested `TableNode`; any other value is captured as its trimmed source-text `scalar` (quotes and all). Computed (`[expr] =`) and positional entries are skipped — only bare identifier keys.

argtypedescription
maskstring
srcstring
fromnumber
tonumber

tableLiteral(src: string, assignmentName: string) → TableNode

Parses a named table literal `assignmentName = { ... }` into a nested `TableNode` — each top-level `key = value` becomes an entry whose value is either a `scalar` (the trimmed source text of a non-table value, quotes included) or a nested `table` (`TableNode`) when the value is itself a `{ ... }`. Runs over the comment/string-aware mask, so a `key`, `{`, or `}` inside a comment or string never registers. Computed (`[expr] =`) and positional entries are skipped. Returns nil when the assignment is absent. "operations" or "M%.tokens").

argtypedescription
srcstringLuau/asset source text.
assignmentNamestringThe table's assignment name (a Lua pattern, e.g.

examples

local ops = I.tableLiteral(src, "operations")

forSource(src: string, computeFn: (string) →

Memoises `computeFn(src)` keyed on `src` itself in a bounded module-local table. The same source text returns the cached result without re-invoking `computeFn`; different source text recomputes. The cache is capped (oldest key evicted first), so an evicted key recomputes on its next request. `computeFn`.

argtypedescription
srcstringSource text — both the memo key and the argument passed to
computeFn(stringCalled as `computeFn(src)` on a cache miss.

examples

local detail = I.forSource(src, buildDetail)
⌬ Types
DocArg = { name: string, desc: string }DocEntry = { desc: string?, args: { DocArg }, returns: string?, examples: { string }, deprecated: string?, hidden: boolean? }DocMap = { [string]: DocEntry }FieldEntry = {EventPayloadEntry = { name: string, type: string }EventEntry = { name: string, payload: { EventPayloadEntry }, sync: boolean, desc: string? }MethodParam = { name: string, type: string? }MethodEntry = { name: string, params: { MethodParam }, returns: string?, desc: string?, deprecated: string?, hidden: boolean? }ExportEntry = { name: string, kind: string, signature: string?, desc: string? }TableEntry = { key: string, scalar: string?, table: TableNode? }TableNode = { entries: { TableEntry } }

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

3items
·
other · born here
▤file
▲ 0↑ born
backing path · modules/luau_introspect.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.