The built-in Lua Debugger ( → ) Watch panel evaluates
paths into Lua’s variable scopes rather than arbitrary expressions. A path
is one identifier followed by any number of .name or [key] segments,
optionally qualified by a section prefix. Expressions, method calls, and
arithmetic are not accepted — only variable indexing.
A Watch spec may start with one of the three debugger section prefixes:
Locals.name — a local in the current stack frame.
Upvalues.name — an upvalue of the current stack frame’s function.
Globals.name — a global, resolved through _G first and then
through the current frame’s _ENV upvalue. The _ENV fallback matters for
scripts loaded with a file environment (e.g. via dofile), where
top-level assignments land in _ENV rather than raw _G.
The exact tokens Locals, Upvalues, and Globals (no trailing .) are
also accepted and select a whole section’s contents.
Additionally:
_G is aliased to Globals.
_G. is aliased to Globals..
Locals, then Upvalues, then Globals, in that order.
After canonicalization, the Watch grammar is:
watch := section "." body
| section
| body
section := "Locals" | "Upvalues" | "Globals"
body := ident ( "." ident | "[" key "]" )*
ident := [A-Za-z_] [A-Za-z0-9_]*
key := integer | boolean | string
integer := "-"? ( decimal-digits | ("0x" | "0X") hex-digits )
boolean := "true" | "false"
string := '"' ( char-except-"\"\\ | escape )* '"'
| "'" ( char-except-'\\ | escape )* "'"
Subscripts mirror Lua’s own short-literal syntax and accept:
t[0], t[-1], t[0x1F], t[-0X1f].
t[true], t[false]. True and FALSE
are parsed as identifiers, not booleans.
\a \b \f \n \r \t \v \\ \" \' \?, decimal bytes \NNN
(1–3 digits, value ≤ 255), hex bytes \xHH, Unicode codepoints
\u{H…} (1–8 hex digits, value ≤ 0x7FFFFFFF, UTF-8 encoded), and \z
which skips following whitespace. Raw newlines inside a string key are
rejected; use \n.
The decoded key is pushed onto the Lua stack and the value is fetched with
lua_gettable, so any matching table key type is reachable.
For userdata (Wireshark class instances, e.g. Proto, Field), a bracket
key must be a string and is looked up as an attribute name, the same as
ud.name.
Before validation the Watch engine applies:
_G and _G. are rewritten to Globals and Globals..
. are collapsed outside bracket literals, so
Globals . foo . bar becomes Globals.foo.bar.
[ … ] around the key is tolerated.
["…"] / ['…'] are decoded before lookup, so
t["a\tb"] indexes the key a<TAB>b.
Path depth is capped at 32 boundaries: the count of each '.' and '[' in
the canonical path (the same limit as the Variables / Watch UI). The watch
engine rejects paths that reach this limit.
The following shapes are intentionally rejected or not aliased, to keep Watch strictly a path evaluator:
[[…]]) as bracket keys — short literals only.
t[1.5]).
_G["weird-key"]: the _G[…] form is not aliased because a bracket key
cannot generally be turned into a Globals.ident path. Use
Globals.name for identifier-named globals.
-- A local 'pkt' in the current frame: Locals.pkt -- A field on a local userdata: pkt.src -- An upvalue table indexed by string: Upvalues.cache["session-42"] -- A numbered entry in a globals table, plus a nested field: Globals.sessions[1].name -- Hex and boolean keys: Globals.opcode_table[0x11] Globals.flags[true] -- Unqualified — tried in locals, then upvalues, then globals: my_proto -- _G aliases: _G.wireshark_version -- same as Globals.wireshark_version _G -- same as Globals (whole section)