Comment by mjmas
1 year ago
MicroLua for the RP2040 has a nice dsl for the PIO assembly with some special use of methods: https://github.com/MicroLua/MicroLua/blob/main/lib/pico/hard...
function pio_timer(_ENV)
public(start):
set(y, 0)
mov(y, ~y)
wrap_target()
delay:
pull()
mov(x, osr)
loop:
jmp(x_dec, loop)
mov(isr, ~y)
push()
jmp(y_dec, delay)
end
Was that really achieved with vanilla Lua or MicroLua has some language extensions?
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...]