flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > Metaprogramming with nested CALM-instruction.

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 28 Feb 2020, 10:39
Hello!
I'm asking if there is a way to define a nested calm-code, so:
Code:
calminstruction calminstruction?.nested?
    

inside another nested calm-instruction for doing metaprog for instance.
The problem is that, I cannot do that:
Code:
calminstruction calminstruction?._poc_1?
    arrange     invoke, =calminstruction =calminstruction?.=_poc_2?
    assemble    invoke
    arrange     invoke, =end =calminstruction
    assemble    invoke
    ; and you call it as-well
    ;arrange     invoke, =_poc_2
    ;assemble    invoke
end calminstruction
    

because the instruction calminstruction is not defined inside the namespace calminstruction?.
This is the same problem if you want to include other "global" instruction inside the calminstruction namespace.
I've tried this to include for example display but it's fail:
Code:
  _macro reequ   macro
_display reequ display
    _end reequ     end
match macro display end, _macro _display _end
    namespace calminstruction?
        macro #display line?&
            display line
        end macro
    end namespace
end match
    


I think this would be great if you want to include kinda preprocessing inside the CALM compilation.
best regard Smile.


Last edited by MaoKo on 29 Feb 2020, 09:27; edited 1 time in total
Post 28 Feb 2020, 10:39
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 28 Feb 2020, 12:15
MaoKo wrote:
because the instruction calminstruction is not defined inside the namespace calminstruction?.
This is not the reason. The actual reason is that you really cannot start defining another instruction while you are defining one already. When you nest macros, for example, the inner macros do not get defined at the time when the outer one is, their definitions simply become part of the outer macro and then they get defined when that outer macro is used.

In my recent update to the fasmg overview I tried to explain the concept of CALM instruction by describing it as a specialized preprocessor. It operates on the same layer, in which classic preprocessor prepares a line from macro to be assembled by replacing parameters with their values, processing "`" operator, etc. Therefore CALM instruction can prepare a line containing a command like DISPLAY and then pass it to assembly (analogously to how macro produces lines), but the current state of assembler may be such that this command would not be executed (like when you are inside an IF block with false condition, or inside of a definition of a macro, or inside of a definition of CALM instruction).

For example, if your CALM instruction is called while a macro is being defined, the generated DISPLAY is going to become part of the definition of a macro instead of being executed immediately. And similarly, if your instruction generates a DISPLAY line while there is another CALM instruction being defined, this line is going to be treated by assembler as part of the definition and interpreted accordingly. It may end up calling some macro, if you have "calminstruction.display" defined, but after all such macros get unrolled, the assembler needs to have only compilable CALM statements. Just like when a macro is defined, normal directives are not recognized and processed at that time.
Post 28 Feb 2020, 12:15
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 28 Feb 2020, 18:37
PS. You could probably achieve the results you wanted by making a framework of customized language definitions that would collect statements and only later generate complete "calminstruction" definitions under the hood.
Post 28 Feb 2020, 18:37
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 28 Feb 2020, 18:56
Quote:

It may end up calling some macro, if you have "calminstruction.display" defined, but after all such macros get unrolled, the assembler needs to have only compilable CALM statements. Just like when a macro is defined, normal directives are not recognized and processed at that time.

Yes I agree. But what I want is not to declare calm inside another declaration. In fact, I wanted to create nested calm instruction on the fly, for executing code at "assembly time". So the calm that defined such nested calm is already defined. Something like this.
Code:
calminstruction calminstruction?._exec_once? command?*&
    ; ...
end calminstruction
holder = $00
calminstruction poc_1?
    _exec_once { compute holder, $01 }
end calminstruction
display holder+"0", $0A ; display 1 without execution poc_1
    

The only solution that I see, is to declare a nested calminstruction with random name that contain the code betweem brace and execute it.
This allow me to do a kind of preprocessing at the time of CALM compilation.
But unfortunately, I don't know if _exec_once can declare such thing.
Code:
debug? := $01
macro calminstruction?._asmcmd? invoke?*, command?*&
    local       invoke
    arrange     invoke, command
    assemble    invoke
end macro
retaincomments
calminstruction poc_1?
    _exec_once {\
            check (debug)                                                   ;\
            jyes   next                                                     ;\
            exit                                                            ;\
        next:                                                               ;\
            _amcmd invoke, =_asmcmd =invoke, ===display "DEBUG ON", $0A     ;\
    }
end calminstruction
removecomments
poc_1
    

thanks
best regard Smile


Last edited by MaoKo on 01 Mar 2020, 14:07; edited 2 times in total
Post 28 Feb 2020, 18:56
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 28 Feb 2020, 19:59
MaoKo wrote:
Yes I agree. But what I want is not to declare calm inside another declaration. In fact, I wanted to create nested calm instruction on the fly, for executing code at "assembly time". So the calm that defined such nested calm is already defined. (...)
Looking at your example, the nested instruction is certainly not defined yet, to be more precise: not compiled yet. We are during the definition of "poc_1" when the call to "_exec_once" happens, and we are not able to define (compile) another CALM instruction there, because we are in a middle of defining (compiling) the "poc_1".
Post 28 Feb 2020, 19:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 28 Feb 2020, 20:13
For your specific example of "exec_once" I can propose the following solution:
Code:
define _global

calminstruction calminstruction?._exec_once? command?*&
        take _global._exec_once, command
end calminstruction

calminstruction calminstruction.end? instruction
        local command, buffer
        arrange command, =end instruction
        assemble command
        arrange command, =calminstruction =__tmp
        assemble command
    reverse:
        take buffer, _global._exec_once
        jyes reverse
    exec:
        take command, buffer
        jno done
        assemble command
        jump exec
    done:
        arrange command, =end =calminstruction
        assemble command
        arrange command, =__tmp
        assemble command
end calminstruction



holder = $00
calminstruction poc_1?
  _exec_once  compute holder, $01
end calminstruction


display holder+"0", $0A ; display 1 without execution poc_1    
It collects the lines passed to "_exec_once" in a global variable and then after the definition of instruction is done it defines another temporary instruction with the collected statements. This is not easily extendable to handle other cases, though.

As I noted earlier, the only general solution would be to define a customized language for definitions with set of macros that would only collect and prepare statements, and at the end construct the actual CALM definitions and pass them to assembler.
Post 28 Feb 2020, 20:13
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 29 Feb 2020, 09:48
Thank you. As always, you find a solution Smile.
I'm not thinking about redefining calminstruction?.end? instruction.
But what do you mean by a set of macro for definition?
Also I'm going to be off topics, for the remaining line.
I'v seen that you can do stuff like that:
Code:
calminstruction abc?
    arrange     A, =I, =J, =K
    compute     B, $02
    publish     A, B
end calminstruction
    


I think that it's a great feature to assign to the first identifier.
But I've not seen this in the man. Do you think that I can reliably use this feature?
Thanks
best regard Smile
Post 29 Feb 2020, 09:48
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 29 Feb 2020, 10:06
MaoKo wrote:
But I've not seen this in the man. Do you think that I can reliably use this feature?
This actually looks more like a bug and probably should be fixed.
Post 29 Feb 2020, 10:06
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 12:06
Hello!
Currently, I'm trying go further the above solution for allowing _execonce to do execution on the "fly".
The solution given above work pretty fine with many situation, but postponed execution may include subtle behavior.
Code:
debug = $00
calminstruction calminstruction?._initsym? target?*, value?&
    publish     target, value
end calminstruction

calminstruction calminstruction?._incdebg?
    compute      debug, debug + $01
end calminstruction

calminstruction calminstruction?._ensure?
    local       target, holder, A
    _initsym    _A, A
    arrange     A, =INT3
    arrange     holder, =_execonce =arrange _A, ===compute ===variable, $01
    assemble    holder
    assemble    A
end calminstruction

calminstruction poc?
    _incdebg
    _initsym    random, $00
    _ensure
end calminstruction
    

The problem here is that the instruction INT3 will be executed because arrange A, compute ... is executed at end.
I'm also trying to use almost everytime the bare bone calm instruction set.
I've thinking about sandboxing the instructions one by one the see if the assemble of _execonce can occur.
Something like this (simplified version):
Code:
calminstruction calminstruction? prefix?*, arguments?&
    ; ...
    ; create calminstruction ?! line?&
    ; if end calminstruction encounter
    ; create calminstruction prefix with all line collected
    ; purge of ?
    ; exit
    ; calminstruction sandbox?
    ;   line
    ; end calminstruction
    ; purge of sandbox?
end calminstruction
    

The problem with this is that such calmcode like _incdedb is executed 2 time and therefore inc debug 2 time.
Another theoritical solution will be:
Code:
calminstruction calminstruction?._execonce? command?*&
    _asmcmd invoke, =end =calminstruction
    _asmcmd invoke, =calminstruction =calminstruction?.=__tmp?
    _asmcmd invoke, command
    _asmcmd invoke, =end =calminstruction
    _asmcmd invoke, =calminstruction ; current calminstruction name
    _asmcmd invoke, =__tmp
end calminstruction
    

But the problem here is that previous declaration (eg: "init symbl, $01") will be lost.
I've no idea how to do to that. I can declare a subset mini language but I don't known if it's will be useful.
If someone have an idea?
regard
Post 02 Mar 2020, 12:06
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 02 Mar 2020, 12:29
I'm not sure what is your actual goal here, but once the macros start to resemble a multi-layered spaghetti, it is often a signal that it might not be a good way to approach the problem (at least not in case of fasmg, as opposed to fasm 1 where limitations of language really pushed the obscure solutions). What are you trying to accomplish with this?
Post 02 Mar 2020, 12:29
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 13:01
The ideal solution would be to replace in a dynamic fashion each _execonce with some nested calminstruction.
In an instruction that "assemble" _execonce, I would like to execute it not a the end.
Because possibly an "assembly time" variable after the instruction can use some value created by the _execonce instruction.
But as you stated, it's impossible to declare an instruction while another instruction is currently declared.
So, it's pretty difficult but I hope it's not impossible.
Code:
calminstruction calminstruction?._poc_1?
    local A
    init build, _execonce arrange A, =compute =B, $01
    init A, ERROR
    assemble build
    assemble A ; Here ERROR (but I want to "compute" to be executed)
end calminstruction
calminstruction _poc_2?
    _poc_1
end calminstruction
    
Post 02 Mar 2020, 13:01
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 02 Mar 2020, 13:36
I was really asking about what original problem you had that you are trying to solve this way. Because seeing this line:
Code:
arrange     holder, =_execonce =arrange _A, ===compute ===variable, $01    
made me think that perhaps this "execonce" is not an actual solution to your problem, just an imperfect tool that you try to build upon.
Post 02 Mar 2020, 13:36
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 14:20
Yes I agree with you that this line is pretty awful. But I really need this feature for my lib.
I made two distinction about what is executed at (assembly/compile time) and runtime in my lib.
The tilde say "at assembly time". (_xcalm_exec ~ = _execonce)
So for instance:
Code:
calminstruction test?
    _xcalm_init ~ [symbl] A, B ; declare a symbol A that point to B (like init in calm package)
    _xcalm_init ~ [symbl] B, C
    _xcalm_init ~ [symbl] C, D
    ; so now we are a chain like this A -> B -> C -> D
    ; I want to transform A so that A -> D (so 2 time transform)
    _xcalm_exec ~ [rpeat:2] { transform A }
    ; This is why I need execonce because I don't want generate code here
    _xcalm_init ~ [symbl|const] E, [expnd] A ; E = D ([expnd] expand behave like arrange does)
end calminstruction
    

_execonce need to create dynamicly a nested calminstruction and afterward call it.
I can't wait the end of test for exec the "_execonce" otherwise E would have the B value.
But sometime I use _execonce in nested calm in more complex form:
Code:
calminstruction calminstruction?.duptop_5? target?*
    arrange     duplicate, target
    transform   duplicate
    _xcalm_exec { _xcalm_exec ~ [rpeat:5] { publish :target, duplicate } }
    ; exec without ~ is the normal exec that generate an "assemble" statement
end calminstruction
    

The problem is to implement (_xcalm_exec ~).
Post 02 Mar 2020, 14:20
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 02 Mar 2020, 14:26
Perhaps instead you should implement _xcalm_init in such way, that it would only get executed at the end of definition, together with _xcalm_exec? In fact, you could probably make a wrapper that would convert _xcalm_init into _xcalm_exec line, and then all these statements would be _xcalm_exec statements, solving the problem of out-of-sync execution.
Post 02 Mar 2020, 14:26
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 14:33
Yes, I've thinking about that too. But if an user want to make it's own init calminstruction?
This make portability with other libs more difficult.

What do you think?
Post 02 Mar 2020, 14:33
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 14:34
But peraphs for now, it would be suffice.
Post 02 Mar 2020, 14:34
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 02 Mar 2020, 14:39
_xcalm_exec is more general than _xcalm_init, you can always customize the internal instruction that gets executed with _xcalm_exec. You _xcalm_init could just be a shortcut to something like "_xcalm_exec _xcalm__init ...".
Post 02 Mar 2020, 14:39
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 14:49
Ok. Thank you, you help me alot. I think that I will try to create a calminstruction ?! that look for predefined instruction and if not it will prefix by _xcalm_exec. I need also to check if a user redefined predefined instruction too but this will not be so difficult.
Post 02 Mar 2020, 14:49
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 02 Mar 2020, 14:51
Once you use an "?!" interceptor, you may collect the lines directly and not even need a separate _xcalm_exec macro then.
Post 02 Mar 2020, 14:51
View user's profile Send private message Visit poster's website Reply with quote
MaoKo



Joined: 07 May 2019
Posts: 100
Location: Paris/French
MaoKo 02 Mar 2020, 17:40
A problem that arise too is if you want to include some conditional block.
If the collected line is executed in front or back the order may be broken:
Code:
debug_v1 = $00
debug_v2 = $01
retaincomments
calminstruction abc?
    local buffer
    _exec_xcalm ~ {\
            check   (debug_v1)  ;\
            jyes    next_1      ;\
            exit                ;\
        next_1:                 ;\
            _exec_xcalm { display "DEBUG V1 ON } ;\
    }
    arrange     buffer, =display "MIDDLE"
    assemble    buffer
    _exec_xcalm ~ {\
            check   (debug_v2)  ;\
            jyes    next_2      ;\
            exit                ;\
        next_2:                 ;\
            _exec_xcalm { display "DEBUG V2 ON } ;\
    }
    arrange     buffer, =display "END"
    assemble    buffer
end calminstruction
removecomments
    

Peraphs I need to take 3 pass. The first pass to collect all instruction.
The second pass to collect all instruction issue with _execonce.
And the latter to build the real calminstruction with good order. But if somme instruction update somme global variable, it's update twice Sad.
Post 02 Mar 2020, 17:40
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.