Bizarre question: emulating GOSUB
BlitzMax Forums/BlitzMax Programming/Bizarre question: emulating GOSUB
| ||
I'm working on a utility that automatically converts a simple scripting language into BMax and compiles it. It needs a 'gosub' sort of functionality: jump to a point in the code, execute whatever and then return. It needs to be able to see every variable at all times, so functions are no good. And making every variable global is right out. Is there a clever way of doing this, or have the program fallen at the silliest hurdle? |
| ||
Throw. Maybe, but Im not sure how that works. The reason surly your "Fake" gosub needs access to all the viables, was because it from a "before" namespace programming style. I would find somthing that doesnt need this, and just accept that youve not got the right solution to the problem, from within the tools you are using |
| ||
i don't know about a clever way, but you could always just inline all gosubs, ie every time you get "gosub dosomestuff" you replace it with the entire "dosomestuff" subroutine might not be elegant but it'd work |
| ||
@Dan, I dont think that would work would it? It wouldnt have access to any more of the variables than were you called it from. Where as It needs to be able to see every variable at all times, so functions are no good. And making every variable global is right out. Is implying that it need accses to more variables than the place its being called from.(Because if it only needed access to the same variables then a Function would be enough) |
| ||
since when do functions have the same scope as their calling code? |
| ||
If you store all your variables in an array you can pass that to a function. It's not elegant, but it's one way of doing it. But out of curiosity why would you need Gosub? I haven't used that since around 1994. |
| ||
@dan, My bag, Id assumed that the call would be from global scope. Youre right. |
| ||
Faking gosub: Save the gosubed part as a "block" somewhere in memory. Everywhere it gosubs to that block, just paste the gosub block in and remove the "label" and gosub return statement. Thats it, thats what gosub actually does. |
| ||
well, if you really want to blah goto func1;#endfunc1 blah end #func1 blah goto endfunc1 |
| ||
@dmaz, that only works if you want to "return" to same place. And so is infact just an inline to #endfunc1 A useful gosub implimantation would need to "return" to several different labals |
| ||
@dmaz, that only works if you want to "return" to same place. And so is infact just an inline to #endfunc1 sure, that's what he asked for. |
| ||
No it isnt. Youve just shown a goto functionality, not a gosub one Each Gosub will "Goto" one specific place, but will return to different places, dependent on were its called from. Yours will only return to the same place everytime. Edit: All you need to do to make yours work, is also allocate a variable just before the first goto, then use a case/select to goto back |
| ||
duh, I guess I wasn't thinking! |
| ||
why case select to go back? A gosub can only return to the point it came from as functions, so you can simply replace the gosub call by the content of the gosub block. No need to make it more complicated than needed! (this replacement will take care that it uses the same variables as well, just to mention) |
| ||
Yes we know that he can just put the code where ever he wants the code to be run from. Thanks for that. If on the other hand He still wants to gosub so as not to write the same code several times, the end of the gosub block would need Some selection system, and I chose Case. |
| ||
Could you explain why it would need selection? There is exactly 1 point where a gosub can go, that is the gosub call which lead it into the block, so there is no usefull reason, wasting "scripting execution time" or parsing time by selecting the "return target" if it is exactly defined. (seeing it from the more precise point of view: GoSub has no target. A GoSub call is just replaced by the content of the sub by the parser / compiler) And I did not say the scripter needs to write it several times, that would be quite stupid, there you are right :-) But the parser would and should do this when converting to BlitzMax (which is why I wrote "save the code to memory"), in the preparsing step. It makes the structure of the parser / converter lighter and cleaner. But to show what I mean here a short example: Print "This is some text" GoSub SubTest Print "Now we are nearly done" GoSub SubTest Print "finished!" End .SubTest Print "This is only a subtest" ReturnSub After preparsing, it will look like this Print "This is some text" Print "This is only a subtest" Print "Now we are nearly done" Print "This is only a subtest" Print "finished!" End This would then be converted to BlitzMax code. Hope this is clearer how my suggestion was meant. |
| ||
@Dreamora Holy shit! Well, I'll just do it that way then. I knew there had to be some lateral-thinking trick to it... ^^; Thanks for the fast answers, everyone! |
| ||
@Dream, You are confusing "What you need for a solution" and "The solution we were talking about" Could you explain why it would need selection? Yes I will. Because in the system that we were talking about, (Not the system you want to use, or the best system, or both of them if they are the same), specificaly Goto for the gosub, and goto for the return. The return would need to know which of the fake gosubs it came from. Now this is true. Maybe it wasnt the best answer to the problem, but it was true. So in the future, say things like "Oh, but I wouldnt have been doing it that way at all, so I wouldnt need a selection system" There is a major difference between a correct statment about an incorrect solution. And simply an incorrect statment |
| ||
Right My mistake. Although even then, a parser wouldn't need a select to jump in a goto. It would normally use TMap or similar to map the "goto Target" onto the line number it has to jump to. *how you would use a select which is a statically defined selection sheme with a dynamic amount of "jump targets"? Thats my major problem of understanding your select - case based solution for the goto - gotoback problem* |
| ||
Each case would be a goto back to a fixed label Case 1 goto #labal1 case 2 goto #labal2 I admit it would need work to maintain it, cos every "gosub" to it would need its own case. But this was an edit statement about a solution that Dmaz sudjested, but which wouldnt work as originaly posted. My entire take on this is that which I first posted, if you need a gosub (in a language that doesnt have gosub), its because youve designed your code badly. |
| ||
Holy shit! Well, I'll just do it that way then. I knew there had to be some lateral-thinking trick to it... ^^; tch, i told you to do it that way in the third post =p |
| ||
Strict Main() Function Main() Global a:Int = 5 'global to Add5, but not to Add10 Global b:Int = 5 Print a+" "+b Add5() Print a+" "+b Add5() Print a+" "+b Add10() Print a+" "+b Return Function Add5() a :+ 5 b :+ 5 End Function End Function Function Add10() 'Local a:Int, b:Int 'Comment out to remove compiler error a :+ 10 b :+ 10 End Function |
| ||
interesting code..i didnt think you could even declare globals within a function..or functions within functions.. all good to know - thanks ;) |
| ||
You can't declare globals within functions A global within a function is a static, this means that the variable will hold their current value between calls instead of beeing redeclared everytime again. For that reason, the code above is quite pointless. As a = 5 on each call it could as well be declared as local. On the other hand, both is the same, but you need to understand the "scope" behavior of BM that allows a lot especially quite some funny and chaotic stuff (function within function is the "least" chaotic thing you could do ;-) ) |
| ||
@Dreamora: The global value assignment only happens on the first call (or possibly at compile time), so there is nothing pointless about the code TomToad posted. |
| ||
@Fred, It is pointless, as the point of Toms code was to show how someting didnt work. |
| ||
How does that make it pointless? |
| ||
Well ok Yea. You pick a word then. (I admit that pointless is probably the wrong word in the sence that you or I would use it). But I think Dream was using pointless in the sence that it didnt solve the problem originaly posted. As appossed to it was illustrating a point about scope. |
| ||
Um, it does work. It is static within the Main() function, but acts like a Global to all the functions within Main(). It is realy no different than decalring Globals in the main App. As far as the app is conserned it is static, but functions within the App can still use it. However, my way, the Globals are still hidden from the functions outside of Main(). If you don't believe that it works, try compiling it and see. Firstly though, it will not compile "as-is" because the function Add10() cannot see the variables in Main() if you uncomment the line that says to uncomment, then it will compile. As you can see, 5 is added to a and b each time Add5() is called. If they had been declared Local instead of Global, then the function Add5() would not be able to see them either. |
| ||
@Tom, the Globals are still hidden from the functions outside of Main(). I thought that was the point for which you had originaly posted to HighLight. In that A Function outside main/namespace couldnt act as a focus for a gosub. I can see what it would do, but thought that 'Local a:Int, b:Int 'Comment out to remove compiler errorwas the salent point. If you look back we had been talking about where a focus for Gosub would need to be, but kept hitting the namespace/scope problem, so I had just assumed you were Highlighting just what the fault was with this method Sorry If I came across as a prat |
| ||
Sorry, I was responding to the OP. Trying to show a way of "emulating Gosub." I put the commented out code to show that even though the variables within Main() were declared Global, they still acted like a Local as far as functions outside of Main() were concerned. This is proven because the compiler complains of "undeclared variables." To actually compile the code and see how the functions within main actually work, you would need to remove the comment or you could remove the Strict at the top. If anyone is still confused to what I'm doing, I'm basically wrapping all the main code into a function called Main(). Then I'm declaring the variables as Global, but they will be Local to only Main() and any functions declared within Main(). All the functions within Main() are acting like a Gosub but you have a couple of advantages over normal Gosubs. One is that you can declare variables that are actually local to only the subroutines/functions themselves, and if there is something you want to hide from them, you can declare them Local within Main() So nothing except Main() can see them. You can call functions outside of Main() like regular functions, and lastly anything you want global to the whole program can be declared outside of Main(). So for an example, I'm turning this: Global a:Int Local b:Int Local c:Int Gosub Add5ToB Print b Add5ToA() Print a c = AddBAndA(a,b) Print c End #Add5ToB b :+ 5 Return Function Add5ToA() a :+ 5 End Function Function AddBAndA:Int(a:int,b:int) Return a + b End Function Into this: Global a:Int Main() Function Main() Global b:Int Local c:int Add5ToB() Print b Add5ToA() Print a c = AddBAndA(a,b) Print c End Function Add5ToB() b :+ 5 End Function End Function Function Add5ToA() a :+ 5 End Function Function AddBAndA(a:int, b:int) Return a + b End Function Notice how the variables keep the same scope in both examples, except for c which is visible to the subroutine Add5ToB in the first example but hidden to the function in the second example, demonstrating more control over scope with functions within functions instead of normal subroutines. |
| ||
@Dreamora.. i didnt mean the var was in global/file scope i simply didnt realise you could have a function within a function and that the parent functions vars would be exposed to child function.. they are declared using the global specifier..so calling a var global is not wrong in this context...it simply doesnt have the same meaning as it would if defined elsewhere.. Another thing - a static within a function in C++, which i assume is what your comparing this to..doesnt work like this..as far as i know its simply preserved, whether that means its stored on the heap instead of the stack i dont know..but it cannot be accessed outside of the function - and you cannot have functions within functions in c++ as far as i know. EDIT>> sorry, your explanation of static was correct.. - my mistake.. anyway - your "know it all" mentality is very annoying.. your nearly always impolite - words like "pointless" are offensive..your entitled to your opinion, but you could perhaps spend a little time thinking about how you present your opinion to others. anyway - rant over with... |
| ||
A global within a function is a static, this means that the variable will hold their current value between calls instead of beeing redeclared everytime again. For that reason, the code above is quite pointless. As a = 5 on each call it could as well be declared as local. Wouldn't that reset the variable to 5 each time the function is called rather than add to it each time? |
| ||
@Tonyg, You are right a local would just set it to 5 each time. But its a singleton Function "Main", so its not going to be called again, so a local would be good enough. Im not saying it should be a local, because a Static global was, (I think), The intention. We do really need to put rem statments all over the code we post. My problem with Tom above was simply because I only focused on the Remed lines |
| ||
Wouldn't that reset the variable to 5 each time the function is called rather than add to it each time? No! As mentioned above a Global variable assignment only happens once, and therefore the value will not start out as 5 every time.' ' This always returns 55 Function Hello() Global a:int a = 50 a = a+5 Return a EndFunction ' ' This will return 55, 60, 65, 70, etc. Function Hello2() Global a:int = 50 a = a+5 Return a EndFunction |
| ||
@Fred As a = 5 on each call it could as well be declared as local Wouldn't that reset the variable to 5 each time the function is called rather than add to it each time? Yes. When it is a LOCAL. Tony wasnt saying anything about the original code, he was commenting on the statement about changing the Global to Local |