← Back to context

Comment by SyrupThinker

1 year ago

While I can't speak for MicroLua, this can indeed be accomplished with vanilla Lua.

There are two things in play here:

First, the _ENV variable here is special.

It is implicitly used to lookup any identifier that has no visible binding, locally or in the surrounding scopes.

Thus a script just containing print("Hello world") is really _ENV.print("Hello world").

Usually _ENV is just implicitly defined to be the global environment (available as _G), but it can be overridden within a lexical scope to a custom value.

Second, the jump labels just make use of the alternate function call syntax of object:method(args), which is equivalent to object.method(object, args).

The whitespace is non-significant, which allows it to be written like that.

In combination with metatables you can use _ENV to track variable reads, calls etc. within a function, which you can (abuse) to create DSLs.

You can get an idea of whats possible by just tracking what we can intercept with _ENV. Adding the following code after the pio_timer function, and running the script with Lua 5.4, already gives us quite a bit for just the first three lines of the function.

local loggerMetatable = {} function loggerMetatable:__bnot() print("Called bnot on " .. rawget(self, "name")) end function loggerMetatable:__call(...) local name = rawget(self, "name") print("Invoked " .. name .. " with " .. #table.pack(...) .. " args") -- Return another logger table to visualize variable interactions return setmetatable({ name = name .. "(...)" }, loggerMetatable) end function loggerMetatable:__index(key) local name = rawget(self, "name") print("Accessed key " .. key .. " on " .. name) -- Return another logger table to visualize variable interactions return setmetatable({ name = name .. "." .. key }, loggerMetatable) end

local pio_env = setmetatable({ name = "_ENV" }, loggerMetatable) pio_timer(pio_env)

-- Output:

Accessed key public on _ENV

Accessed key start on _ENV

Invoked _ENV.public with 1 args

Accessed key set on _ENV.public(...)

Accessed key y on _ENV

Invoked _ENV.public(...).set with 3 args

Accessed key mov on _ENV

Accessed key y on _ENV

Accessed key y on _ENV

Called bnot on _ENV.y

Invoked _ENV.mov with 1 args

[...snip...]