← Back to context

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...]