Message board for the users of flat assembler.
> Macroinstructions > Tricky stuff in fasmg, part 3: Identifier context transfer
This is the third part of a series on advanced trickery that can be done with fasmg. The other installments: part 1, part 2, part 4.
When designing a macro for a general use, it is important to ensure that it works correctly when called in different contexts. The LOCAL directive allows to create private symbols for every instance of macro without interfering with an unknown environment. But sometimes a variable may be needed to be shared across the multiple macro calls. A globally defined symbol usually works well enough for this purpose, but it is something that may be interfered with. Let's consider the following example:
These macros have a few issues. First, if someone defines symbol called GLOBAL_STATE for other purposes, it is going to interfere with them. A choice of rare and specific name for a global may help a bit, and if there are multiple global symbols needed, it is good to create a special namespace for them and attach this namespace to a global symbol with such specific name.
GLOBAL_STATE = 0 macro state db GLOBAL_STATE end macro macro switch value GLOBAL_STATE = GLOBAL_STATE xor value end macro
The other problem would show up if these macro were used inside the NAMESPACE block, because the the re-definition of GLOBAL_STATE would instead create a symbol in the other namespace. We have already seen the solution to this problem - it is enough to add the dot at the end of the identifier to make sure that the defined symbol is the same whose value is accessed on the right-hand side:
GLOBAL_STATE. = GLOBAL_STATE xor value
But there is also another option. There is a mechanism that allows the arguments to a macro to preserve the context for any identifiers inside them. The following example demonstrates this effect:
The text of both data-generating lines that get assembled is the same, "db a", but the second one interprets the "a" identifier in the context in which the macro was called and generates the 3 instead of 0. This is achieved though a mechanism that could be interpreted as a kind of text "coloring". The value of parameter "arg" has an additional property, like a color of text, that makes first "db a" different from the second "db a". Now, if we define a macro inside another macro, then the text of that inner macro is going to preserve that "coloring". The following sample uses this mechanism to improve the definition of "state"/"switch" macros that were used as an example earlier:
macro tester arg namespace X a = 0 db a db arg end namespace end macro a = 3 tester a
When the "setup GLOBAL_STATE" gets assembled, the "switch" macro is defined with a body containing the text:
macro setup variable variable = 0 macro state db variable end macro macro switch value variable = variable xor value end macro end macro setup GLOBAL_STATE namespace Program switch 8 GLOBAL_STATE = 0 state end namespace
GLOBAL_STATE = GLOBAL_STATE xor (value)
The color used above shows the parts of the text that have the global context attached to them. The parts that are not specially colored are going to be interpreted in the context in which the macro gets executed. Now when "switch 8" line is assembled, the line generated by macro is:
GLOBAL_STATE = GLOBAL_STATE xor (8)
This time there is an additional color which reflects that the contents of "value" parameter also preserves the context - this time the one in which the "switch" macro was called.
Therefore in this version both "switch" and "state" always use the global variable no matter what context they are used in. This bears a similarity to the concept of closure used in many programming languages.
The macros in the above sample correctly access their variable even though in the local namespace there is a variable defined with the same name. But, because their variable is global, the other code may still interfere with it. To make the variable completely outside the reach of others, we can use LOCAL directive. The LOCAL declaration creates a special parameter that replaces its name with the same text, but this time "colored" in such a way, that it carries the context unique to the instance of a macro:
This simple trick also allows to further improve the encapsulation macros from the previous part:
macro setup local variable variable = 0 macro state db variable end macro macro switch value variable = variable xor value end macro end macro setup
macro encapsulate? Namespace local postponed,$$%,@% macro postponed end macro virtual at 0 $$% = 0 @% = 0 define $% $$% + $ - $$ define $%% $$% + $@ - $$ - 1/