Stack based Epoch: Epilogue

Published August 24, 2008
Advertisement
We're back on the air, baby!

As of some unholy hour this morning, I've gotten all the relevant stack-based changes made, so that the guessing game works once again. The pi computation program should be mere hours behind, but there's been a complication.


A lot of core-level guts have changed in the Fugue VM. One major change is that specific operations like if no longer bind directly to other operations; that is, previously, if (and most other builtins) would maintain a pointer to the expression that was to be used as the condition. Now, the conditional expression is evaluated separately, and its result is deposited onto the stack. Immediately afterwards, the if operation pops the value from the stack, and acts accordingly.

This simplifies the internals of the VM dramatically, which means there's probably a lot of loose ends I need to go back and tie up. It does sacrifice a tiny bit of performance since everything is shuffled on and off the stack fairly often, but that's the kind of thing I'm not going to worry about until the performance-tuning phase of the project (which is not for a long time from now). I'm putting off implementing registers in general for as long as possible for similar reasons.


Anyways, all that to say, function return values are currently broken. I was really hoping to get the pi calculator running again tonight so that I could totally sign off on the stack-based VM changes, but oh well.

The big problem appears to be in the parser end of things rather than the execution end, so it should be fairly easy to solve. The great part about solving issues in the parser is I can track all kinds of metadata about each bit of the program, allowing for some fairly nifty tricks. That kind of hackery - not to mention overhead - is something I'm trying very hard to keep away from the execution portion of the VM.

In brief, the trouble is in tracking the initialized value of return-value placeholders.

Let's look at the pi program code again for reference:

entrypoint : () -> (){	debugwritestring(cast(string, pi()))}pi : () -> (real(retval, 0.0))   // <-- this line is what we care about today{	real(denominator, 1.0)	boolean(isplus, true)	do	{		real(div, 0.0)		assign(div, divide(4.0, denominator))		if(equal(isplus, true))		{			assign(retval, add(retval, div))			assign(isplus, false)		}		else		{			assign(retval, subtract(retval, div))			assign(isplus, true)		}		assign(denominator, add(denominator, 2.0))	} while(less(denominator, 10000.0))}


The marked line shows how return values are set up in the Epoch syntax. I pretty much ripped off algebraic notation for functions. A function definition "foo : (parameters) -> (return values)" is read as "foo such that foo maps (parameters) onto (return values)."

When the function scope is entered, a variable is created for each return value, and initialized to the correct value (specified in the function definition). This variable then acts like a regular variable throughout the function's scope.

(As a side note, eventually Epoch will allow multiple named return values, via a tuple mechanism. This is likely to be the precursor to structures in the language. I've been putting it off because I can't decide on a good syntax for how to read the tuple on the caller's end.)


The problem I've run into is that, previously, I was hijacking the lexical scope container that I built to store the list of return variables and their correct initial values. Now that lexical scopes require a stack to store anything, there's no way to continue with that hijacking.

As noted earlier, this problem lies in the parser, not the execution system; so I can just build another container to handle the issue. In many ways this is a good thing, since hijacking classes and bending them to multiple purposes is Poor Engineering.


However, it is also work, and it requires some brain juice - which I am, for the night at least, totally out of. For tonight it's almost time to crash... hopefully I'll get it all sorted out tomorrow, and that'll wrap up the big changes for Release 3.

As far as minor changes go for R3, I'm going to really shore up the documentation of the VM code, and hopefully get serialization working. Once serialization (to binary form) is done, I'll bang together a little C# disassembler to aid in debugging.

I think that once the Scribble demo is done, doing something serious like a disassembler for the serialized bytecode would be a great next step. It provides a nontrivial project that has to be completed, which in turn requires that the language be pretty robust; and it also provides a much-needed tool for working with the language itself. There've been several times during the stack-based updates when I wished for a good bytecode disassembly view of what exactly the VM was doing at various points in time.

Plus, that's a decent start on the route of building an Epoch debugger that hooks directly into the VM itself. That's definitely something that will be necessary some point down the line, and it certainly would require a robust language to build it in.

Even better, building Epoch development tools in Epoch is one of those nice little closed-curve self-referential thingies that makes my spine tingle [grin]



Right... I'll shut up for now, since I can barely type anymore.
0 likes 1 comments

Comments

Daerax
Good to see Epoch back :)
August 24, 2008 10:16 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement