Extensions like "inlinemacro" that come with fasm2 header set require a custom global preprocessor, which idiomatically looks like:
calminstruction ?! &line&
and is able to transparently analyse all the lines of the source and possibly mutate them before passing to the assembly.
This also requires some additional handling around includes, because such preprocessing macros are non-recursive, so they are not active during the processing of included contents, as it all happens within a single call to interceptor macro, with the line being the "include" statement.
Because of this detail, while multiple preprocessors can be easily stacked on top of each other, it is harder to get them to cooperate nicely inside included files. To provide a possible solution, I designed a modular preprocessor, which could be a standardised base for interoperating packages need to add custom preprocessors for all the lines.
A prototype looks like this:
; Modular preprocessor.
if ~ defined Preprocessor?
define Preprocessor?
Preprocessor? = 'Modular'
calminstruction Preprocessor? &line&
match =include any, line
jyes outside
assemble line
exit
outside:
take Preprocessor?.outside, line
end calminstruction
macro Preprocessor?.init? chain
chain
macro include? file*, head
include file, Preprocessor?.init head
purge ?, include?
end macro
calminstruction ?! &line&
call Preprocessor?, line
take line, Preprocessor?.outside
jyes outside
exit
outside:
assemble line
end calminstruction
end macro
define Preprocessor?.start? Preprocessor?.start?
macro Preprocessor?.start?
end macro
macro Preprocessor?.start?
purge Preprocessor?.start?
Preprocessor?.init?
end macro
end if
To add a new preprocessor into the chain, it is enough to define it as:
calminstruction Preprocessor?! &line&
But instead of passing the line directly to assembly, it should call the "Preprocessor?" macro with the text of the line as only argument, to pass the line down the chain.
The preprocessors do not start right away. To start processing all the subsequent lines, it is necessary to call the macro:
There is also a symbolic link that allows to call it just as easily from CALM:
assemble Preprocessor?.start
Repeated calls to this macro are allowed and have no additional effect.
For example, this is a simple implementation of FIX command (similar to fasm's directive):
define fix? fix?
calminstruction (name) fix? &value&
arrange name, fix.name
publish name, value
assemble Preprocessor?.start
done:
end calminstruction
calminstruction Preprocessor?! &line&
local any
match any =fix? any?, line
jyes skip
transform line, fix
skip:
call Preprocessor?, line
end calminstruction
And the inline macro package can be adapted to use this engine, allowing multiple preprocessors to cooperate:
define inlinemacro? inlinemacro
calminstruction inlinemacro?! declaration&
local name
match name(arguments?), declaration
jyes define
match name= arguments?, declaration
jyes define
match name arguments?, declaration
define:
arrange tmp, =__inline__.name
arrange name, =inlinemacro.name
publish name, tmp
arrange tmp, =struc (=return?) name arguments
assemble tmp
end calminstruction
macro end?.inlinemacro?!
end struc
end macro
calminstruction Preprocessor?! &text&
local head, tail, name, arguments, more, i
init i, 0
match =inlinemacro? more, text
jyes ready
transform text, inlinemacro
jno ready
match =else? more, text
jno preprocess
compute i, i+1
arrange text, =__inline__.(=else =if 1) =__inline__.(=__return__.i==1) text =__inline__.(=end =if) =__inline__.(=if ~=definite =__return__.i)
preprocess:
match head? =__inline__.name?(tail?, text
jno ready
match arguments?) tail?, tail
jno ready ; syntax error
collect:
match arguments?, arguments, ()
jyes inline
match more?) tail?, tail
jno ready ; syntax error
arrange arguments, arguments) more
jump collect
inline:
match , name
jyes special
local tmp, return
compute i, i+1
arrange return, =__return__.i
arrange tmp, return =inlinemacro.name arguments
arrange text, head return tail
take text, tmp
jump preprocess
special:
arrange text, head tail
take text, arguments
jump preprocess
ready:
call Preprocessor?, text
take , text
take text, text
jyes preprocess
end calminstruction
Preprocessor.start