flat assembler
Message board for the users of flat assembler.
Index
> Macroinstructions > Tricky stuff in fasmg, part 3: Identifier context transfer |
Author |
|
Tomasz Grysztar 08 Oct 2016, 18:33
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: Code: 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: Code: 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: Code: macro tester arg namespace X a = 0 db a db arg end namespace end macro a = 3 tester a Code: 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: Code: macro setup local variable variable = 0 macro state db variable end macro macro switch value variable = variable xor (value) end macro end macro setup Code: macro encapsulate? Namespace local postponed,$$%,@% macro postponed end macro virtual at 0 $$% = 0 @% = 0 define $% ($$% + $ - $$) define $%% ($$% + $@ - $$ - 1/($@-$$+1)*@%) macro org? address local addr addr = address $$% = $% @% = $% - $%% end virtual virtual at addr end macro macro section? address local addr addr = address $$% = $%% @% = 0 end virtual virtual at addr end macro macro postpone?! esc macro postponed end macro macro end?.postpone?! postponed esc end macro end macro namespace Namespace macro end?.encapsulate? postponed end namespace purge org?,section?,postpone?,end?.postpone? restore $%,$%% repeat 1, Length:($$% + $ - $$) display `Namespace,': ',`Length,' bytes.',13,10 end repeat end virtual end macro end macro The context that may be carried by the text of argument includes the namespace that applied to it and also the symbol that at the time was the "parent" for the identifiers starting with dot. There is also one other place where this kind of context transfer occurs: the values of symbolic variables. When a symbol is defined with EQU or DEFINE, its entire value becomes equipped with the context that was present at the time of definition. When the text of such value is assigned to a parameter with MATCH or IRPV, the text's "colors" are preserved: Code: First: .x = 1 define LIST .x Second: .x = 2 LIST equ LIST, .x match values, LIST display `values db values end match If a MATCH cuts the text of an identifier into many parts, all parts preserve the "color" of their text. And if an identifier becomes patched up from the parts of text with many different "colors", only the context associated with the initial part has any effect on the recognition of this symbol. In case it was needed that some text is returned back to the "neutral coloring" and interpreted in the current context, the RMATCH directive does this to the text of parameters it defines. The similar effect could also be achieved by converting the value of parameter into a string with the ` operator and then interpreting this pure text with EVAL. But there is also a neat little trick based on the fact that only the context associated with the initial part of identifier has any effect. It is enough to prepend to parameter with "#" character to form an identifier interpreted in current context: Code: macro tester name namespace my name db ? ; symbol defined in its original namespace #name db ? ; symbol defined in "my" namespace end namespace end macro Last edited by Tomasz Grysztar on 11 May 2017, 13:28; edited 4 times in total |
|||
08 Oct 2016, 18:33 |
|
Tomasz Grysztar 09 Oct 2016, 18:14
Another small improvement to the encapsulation macros could be to include these definitions in the macro:
Code: define Namespace.$ $ define Namespace.$$ $$ define Namespace.$@ $@ |
|||
09 Oct 2016, 18:14 |
|
jacobly 13 Jun 2017, 10:19
I noticed a more concise way to implant preexisting symbols into the local namespace:
Code: macro test display, hello, end local space namespace space display hello, 'local namespaces', 10 end namespace end macro hello = 'Hello ' test display, hello, end This is indeed an interesting feature that I will definitely find uses for. Edit: I just realized that this only implants them inside the body of the macro, which is also useful, and even makes namespace implanting more concise: Code: macro test hello, display, end local space macro space.display? args& display args end macro namespace space hello 'local namespace' end namespace end macro macro hello name display 'Hello ', name, 10 ; display needs to be implanted in the namespace because end macro ; we don't have access to the other macro's arguments test hello, display, end Edit2: Default arguments let you only list the imports once and you can use the imports in the local namespace until you call a macro or include a file. Code: macro test macro:macro, hello:hello, display:display, end:end local space namespace space macro #display? args& ; # prevents the name from looking like the import, but display args ; you could also use a different case or rename the import end macro hello 'local namespace' end namespace end macro macro hello name display 'Hello ', name, 10 end macro test test |
|||
13 Jun 2017, 10:19 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.