Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Macro functions in 2016.8.2 — Gideros Forum

Macro functions in 2016.8.2

n1cken1cke Maintainer
edited September 2016 in Announcements
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.
@`:
@ (| ...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!")
+1 -1 (+7 / -0 )Share on Facebook

Comments

Sign In or Register to comment.