Quick Links: Gideros Home | Download Gideros | Developer Guide
Macro functions in 2016.8.2
  • n1cken1cke +1 -1 (+6 / -0 )
    Maintainer
    In addition to macro constants now we have more powerful macro functions (or procedural macros). Macro functions receive a list of tokens and output a string which will be pasted into code at compile time.

    Theory
    Macro function definition is similar to macro constants one:
    name @ (| ...body... |)

    The only difference you should use parenthesis around markers. Here a marker is first character after opening parentheses (I prefer `|`). Same marker should be used to close macro body with closing parenthesis right after it.
    As with constants macro functions can be redefined only explicitly via `@@`:
    name @@ (| ...another_body... |)

    To call macro function use it's name with parenthesis as with usual functions:
    name(...arguments...)

    However macro function is different because you can pass arbitrary code to it. This code will be transformed into list of tokens (table) before passing to your macro function.
    For example `print(1 + 2)` code in
    func(print(1 + 2))

    will be transformed into
    {"print", "(", "01", "+", "02", ")"}

    For convenience tokens can be divided into 3 groups: identifiers, strings and numbers. Look at 1st character of token (with `string.sub`): strings always start with `"` (double quote), numbers start with `0` (zero) and all others are identifiers.
    Macro functions receive token list as `...`. You can look at received tokens inside macro function simply printing them with for-loop:
    for k,v in pairs(...) do print(k,v)


    Practice
    Let's write most simple macro function. We will transform `print` function into macro function to turn off all `print` calls:
    print @ (| return "" |)

    Every following `print` call after this definition will be transformed into nothing! This is handy if you often use print function for logging.
    Now little more complex function. It receives any code and pastes it as it is. It is useless but good for demonstration purposes.
    paste @ (| return table.concat(..., " ") |)

    It receives table of tokens (`...`), we concatenate them with spaces (`" "`) in between and return resulting string. Why do we use spaces in `table.concat`? Because otherwise a mess will be pasted. Each token is a string and if you pass something like `while a > b do print(a) end` and concatenate it without spaces then you get `whilea>bdoprint(a)end` which is invalid code. Add `paste` call:
    paste(
    if 1 > 0 then print "1 is bigger than 0" end
    )

    `if 1 > 0 then print "1 is bigger than 0" end` line will be pasted into resulting code and executed and you will see `1 is bigger than 0` in console.

    Snippets
    Create enumerations:
    enum @ (|
    local t = ...
    local r = {}
    for i = 1, #t, 2 do
    table.insert(r, t[i] .. " @ " .. i // 2 + 1)
    end
    print(table.concat(r, " "))
    return table.concat(r, " ")
    |)
     
    enum(apple, orange, melon)
    print(apple, orange, melon) --> 1 2 3


    Import methods your need from libraries or entire library:
    import @ (|
    local module = (...)[1]
    if not module then return "error 'import: provide a name'" end
    local r = {}
    if not _G[module] then
    r[#r+1] = "local "..module.." = require '"..module.."'"
    end
    local m = _G[module] or require(module)
    if #(...) == 1 then
    for name in pairs(m) do
    r[#r+1] = "local "..name.." = "..module.."."..name
    end
    else
    for i = 3, #(...), 2 do
    local name = (...)[i]
    if not m[name] then
    return "error 'import: "..module.."."..name.." not found'"
    end
    r[#r+1] = "local "..name.." = "..module.."."..name
    end
    end
    return table.concat(r, " ")
    |)
     
    import(math: sqrt, abs, log)
    print(sqrt(3) + log(10))
    import(table)
    print(concat({"Hello", ", ", "world!"}))


    Print lines of code before execution (separated by `;`):
    DEBUG @ (|
    local n = 1
    for i, t in ipairs(...) do
    if t == ";" then
    local r = {}
    for j = n, i - 1 do
    r[j-n+1] = (...)[j]
    end
    (...)[n] = " print[=["..table.concat(r, " ").."]=] "..(...)[n].."\n"
    n = i + 1
    end
    end
    return table.concat(..., " ")
    |)
     
    DEBUG(
    local text = TextField.new(nil, "hello", "|");
    stage:addChild(text);
    text:setText("nice");
    )


    Unroll loops:
    dotimes @ (|
    local times = table.remove(..., 1)
    return (table.concat(..., " ").." "):rep(times)
    |)
     
    local t = {}
     
    dotimes(10 print "Boom!")
  • kussakov +1 -1 (+1 / -0 )
    Member
    Very, very nice!!!! =D>

    Likes: SinisterSoft

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Login with Facebook Sign In with OpenID

In this Discussion

Top Posters