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.
TheoryMacro function definition is similar to macro constants one:
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.
@`:
@ (| ...another_body... |) |
To call macro function use it's name with parenthesis as with usual functions:
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
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) |
PracticeLet's write most simple macro function. We will transform `print` function into macro function to turn off all `print` calls:
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.
SnippetsCreate 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!") |
Comments
Likes: SinisterSoft
deg@(|
local t=...
return "^>"..(t[1])
|)
print(deg(3.14159265358979324))
Likes: antix
https://deluxepixel.com
https://deluxepixel.com
https://deluxepixel.com