Bizarre question: emulating GOSUB

BlitzMax Forums/BlitzMax Programming/Bizarre question: emulating GOSUB

Ninjacrat(Posted 2006) [#1]
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?


H&K(Posted 2006) [#2]
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


dan_upright(Posted 2006) [#3]
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


H&K(Posted 2006) [#4]
@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)


dan_upright(Posted 2006) [#5]
since when do functions have the same scope as their calling code?


fredborg(Posted 2006) [#6]
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.


H&K(Posted 2006) [#7]
@dan,

My bag, Id assumed that the call would be from global scope. Youre right.


Dreamora(Posted 2006) [#8]
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.


dmaz(Posted 2006) [#9]
well, if you really want to

blah
goto func1;#endfunc1
blah

end

#func1
blah
goto endfunc1


H&K(Posted 2006) [#10]
@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(Posted 2006) [#11]
@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.


H&K(Posted 2006) [#12]
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


dmaz(Posted 2006) [#13]
duh, I guess I wasn't thinking!


Dreamora(Posted 2006) [#14]
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)


H&K(Posted 2006) [#15]
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.


Dreamora(Posted 2006) [#16]
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.


Ninjacrat(Posted 2006) [#17]
@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!


H&K(Posted 2006) [#18]
@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


Dreamora(Posted 2006) [#19]
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*


H&K(Posted 2006) [#20]
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.


dan_upright(Posted 2006) [#21]
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


TomToad(Posted 2006) [#22]
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



Defoc8(Posted 2006) [#23]
interesting code..i didnt think you could even declare
globals within a function..or functions within functions..
all good to know - thanks ;)


Dreamora(Posted 2006) [#24]
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 ;-) )


fredborg(Posted 2006) [#25]
@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.


H&K(Posted 2006) [#26]
@Fred,

It is pointless, as the point of Toms code was to show how someting didnt work.


fredborg(Posted 2006) [#27]
How does that make it pointless?


H&K(Posted 2006) [#28]
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.


TomToad(Posted 2006) [#29]
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.


H&K(Posted 2006) [#30]
@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 error
was 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


TomToad(Posted 2006) [#31]
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.


Defoc8(Posted 2006) [#32]
@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...


tonyg(Posted 2006) [#33]
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?


H&K(Posted 2006) [#34]
@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


fredborg(Posted 2006) [#35]
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



H&K(Posted 2006) [#36]
@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