| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
note: state of the code is, still being debugged. Has random logging to std err still on. There are no showstopper bugs that I'm aware of at the moment. Also the state of this document is that I'm actively working on it. #Lua Preprocessor
I haven’t decided what to name this system, I should come up with a name that shows up in google searches as distinct from the many similar systems…
The features of this Lua preprocessor:
There are two modes for defining a macro:
macro_system.add {
head='assert_compared(?1op,?a,?b)',
body=[[ if not(?a ?op ?b) then
io.stderr:write('assertion ',@tostring(?a ?op ?b),' failed, returning ', tostring(?a), ' and ',tostring(?b),'\n')
os.exit(1)
end]]
}Now the same macro in macro directive style:
@macro {
head='assert_compared(?1op,?a,?b)',
body=[[ if not(?a ?op ?b) then
io.stderr:write('assertion ',@tostring(?a ?op ?b),' failed, returning ', tostring(?a), ' and ',tostring(?b),'\n')
os.exit(1)
end]]
}Unless you’re depending on Zerobrane integration which isn’t complete yet, you’ll need a lua file to load the macro system then to load a file you want processed.
This file needs require 'macro_require' before loading the file. Macro require patches “require” so that you include a macro file through require. You have to name your macro files specially with “.pp.lua” so that “require” knows to process them through the macro processor. So you could name your file “equal_assert.pp.lua” then require it with require 'equal_assert'.
So to make it concrete you’ll need two files, the driver file that bootstraps the macro system [for instance call it boot_macro.lua]:
require 'macro_require'
require 'assert_test'And your first macro file [call it assert_test.pp.lua]:
@macro {
head='assert_compared(?op,?a,?b)',
body=[[ if not(?a ?op ?b) then
io.stderr:write('assertion ',@tostring(?a ?op ?b),' failed, returning ', tostring(?a), ' and ',tostring(?b),'\n')
os.exit(1)
end]]
}
assert_compared(==,3+3,3+4)If you run it, it prints “assertion 3 + 3 == 3 + 4 failed, returning 6 and 7”
If you wanted to do this the pure lua way then you’d have to load the macro before you load any file that uses it, in which case your files could look like [boot_macro.lua]:
local macro_system = require 'macro_require' --the assignment isn’t necessary, since macro_require exports it as a global
macro_system.add {
head='assert_compared(?op,?a,?b)',
body=[[ if not(?a ?op ?b) then
io.stderr:write('assertion ',@tostring(?a ?op ?b),' failed, returning ', tostring(?a), ' and ',tostring(?b),'\n')
os.exit(1)
end]]
}
require 'assert_test'In this case the file to be processed is just [assert_test.pp.lua]:
assert_compared(==,3+3,3+4)But using macro directives is easier, I recommend using them except when you have to generate macros manually. If you use the Zerobrane integration and set the interpreter to the preprocessor, then you don’t need the driver files, just the preprocessor ones. In that case you’ll also be able to single step macro files, though it doesn’t work perfectly.
The system has the following preprocessor directives [the following can only appear at the beginning of a line]:
[macro directives that appear in OTHER places than the beginning of a line]:
##The Anatomy of Macros
Macros are templates that, wherever they match in the source that source is replaced with something else, and text elsewhere can be generated too. A head is a template describing the text that has to be matched to, it is a list of parameters with literal text between these parameters. The following kind of parameters exist:
Note, if the same parameter appears more than once in the head, it’s used as a “guard”. If both inputs aren’t the same then the match fails.
Here is an example of using ?, to destructure input and using @tostring to report the literals of the expression being logged.
@macro {
head='log(?a,?,b)',
body=[[io.stderr:write(@tostring(?a),'=',tostring(?a),' ') log(?b)]]
}
@macro {
head='log(?a)',
body=[[io.stderr:write(@tostring(?a),'=',tostring(?a),'\n')]]
}
local A='hello'
local B=' there'
local C='Mom'
local D=55
log(A..B,C,NOTDEFINED,D)which should return A .. B=hello there C=Mom NOTDEFINED=nil D=55
A body describes what the text that matches the head will be replaced with. There are two kinds of bodies, a body can be a template like the head or it can be a function.
In a templated body, parameters are marked with a ? in front of them. There is also a new kind of variable that doesn’t exist in the head. If you mark a name with % instead of ? then it names a local variable instead of naming an input parameter. For each local a unique name is generated, this solves the problem of “variable capture” and makes macros “hygienic”. For people not familiar with the problem, this will require some explanation, which I will give later. Note that variable names and parameter names are taken from the same namespace, it’s an error to try to reuse the name of a parameter as the name of a variable.
Macros have the following sections (all are optional except head and body):
head - the head is a string listing input parameters and the literal tokens between them. This is what the text has to match in order to trigger the macro. body - the body can either be a string or a function, th if it’s a string it’s a list of literal tokens, input parameters, new_tokens sections semantic_function
| Back | FazBrowse Home | New Git URL |