[gameprogrammer] Re: More scripting engines

  • From: David Olofson <david@xxxxxxxxxxx>
  • To: gameprogrammer@xxxxxxxxxxxxx
  • Date: Fri, 30 Apr 2004 14:10:58 +0200

On Friday 30 April 2004 11.43, grant hallman wrote:
[...]
> > (In my compiler, I'd pretty much have to bypass
> >the whole symbol table logic to do anything else. The "table" is
> >actually a tree, and symbol lookups only climb towards the root.)
> >
> >However, it doesn't really avoid the problem, as it's still
> > possible to jump back and land before initializations,
>
> Well, then come the initializations, what's the problem? Gimme
> example.

If an initialization results in a reference to an object, running that 
initialization code again will brutally overwrite the reference, 
thereby causing the previous object (if it's still around) to be 
leaked.

That can be dealt with, of course, but it's not trivial... My VM 
doesn't clear registers and stuff unless it really has to, so an 
initialization is different from an assignment; the former ignores 
the previous contents of the register, whereas the latter generates 
code to properly release any object referenced by the register.


> > or even (if the
> >language supports mixing declarations with other statements, like
> > C99 or C++) jump over initializations.
>
> Far as i'm concerned, u can leave that out. That kind of stuff
> leads to "cute code", which IMO is deadlier than gotos.

Yeah, it just tends to get messy. I'm actually a bit annoyed by C99 
allowing this, especially since it makes some people (sometimes 
unknowingly) write code that's less portable for no real reason. I 
don't see the point. If one feels there is a need for this, one is 
writing way too big functions...


> >If the compiler is required to
> >insert code for automatic memory management and stuff, that means
> > a real flow analysis system is pretty much *required* to ensure,
> > not only optimal, but *correct* code generation. That sort of
> > reduces the chances of ever seeing more than approximately one
> > correct implementation, I think. ;-)
>
> In the sense that "2 + 2 = 5, for sufficiently large values of 2" ?

*hehe*

Well, what I mean is I just don't think it helps if a language is hard 
to implement by definition. It's quite bad as it is with automatic 
memory management. I want strong motivations for any increase in 
complexity.

And goto is not enough motivation for any complexity at all, IMHO. ;-)


> >> It's a mixed bag, and while gotos can be misused, a break deeply
> >> nested in a complex structure can be just as deadly.
> >
> >Sure, but what I don't quite get is how a break to a named block
> > is worse than a goto.
>
> It isn't if it's done right, neither is "bad". But if u have a
> complex wad of blocks, say a do while around several more do's
> containing multiple switch statements each containing nested do's,
> and somewhere down in the mess u say "break", i have more than a
> few times gotten wrong (or worse, compiler-dependent)

Ouch! That shouldn't happen with a properly defined language... It 
obviously has to be clearly specified which constructs support break 
and which don't.

However, even with a broken compiler (or language) it *can't* happen 
with "break <name>;", as you explicitly say which block you want to 
break out of. If that block is not part of a breakable construct, the 
code just won't compile. If compilers disagree about the number of 
breakable levels between the break and the named block, the named 
break actually "fixes" the problem.


> ideas about
> where that break was gonna take me.

Well, if you go one step further, and *require* that break refers to a 
named block, that simply can't happen... You'll land right after the 
end of the block you refer to, simple as that. If you accidentally 
try to break out of a block that isn't the body of a breakable 
construct, the code just won't compile.

Or maybe break should just plain work for any block named? Hmm... 
"Leave the current block" is a very well defined action anyway (or we 
can't have break, return and stuff like that at all), so why not?


> Sometimes it was in my own
> code, which is a bit unworthy, but $hit happens during mods.

That's *exactly* why I want them named. (I'm not talking about the 
"break <n>" found in Lua and some other languages. That one's 
outright scary, IMO.)


> >> But the bigger
> >> point is, their use is a style issue.
> >
> >Indeed. Some would even tell me to drop break altogether. ;-)
> >
> >> It's a language's job to
> >> offer features, not to try to impose a programming style. That's
> >> just annoying, and leads to even clumsier workarounds.
> >
> >Right, but as it is, I personally don't find goto very useful -
> > and I prefer to leave things out until I can prove they're
> > needed, rather than throwing in everything but the kitchen sink
> > "just in case".
> >
> >I'll reconsider goto when I see a problem that can't be handled
> > nicely with higher level constructs.
>
> Error messages,

Exceptions.


> or string parsing,

Can you be more specific? (I usually do just fine without gotos in 
C...)


> are two examples that come to
> mind. A really squeaky-clean C coder will sometimes deal with
> multiple error conditions in a way that has the main functions of
> the module indented 12 times and pushed 'way off to the side.

Eww... :-/


> In my
> experience, that coding style may result in subtle dependencies
> between multiple errors, and is hard to read.

Yep. If you have more than three levels of indentation, the code needs 
fixing. (And yes, a tab is 8 characters wide. :-)


> I want to look at a
> module and say "look, here's what it does, and 'way down here is
> where it deals with exceptions", not "Migawd, what a clever nesting
> of if's and while's, ummm, what does it _do_?"

What I do is put that kind of stuff in a separate init function that 
gives up and returns if an error occurs. Then I make sure the cleanup 
function will work regardless of whether the init function completed 
or not, so I can just call that if there's an error.

In simple cases, I sometimes do all initialization in the "open" 
function, and do { whatever_close(); return -1; } if there's an error 
anywhere along the way.

So, essentially, I implement my generic named block + break using 
function + return in C. That's about the only "proper" excuse for 
using goto I can think of - and I don't do it any more, because 
there's no need to.


BTW, here's some weird code I found in my scripting engine. (Old stuff 
from Audiality.) WTF was in that tea I had...!? Note that the normal 
exit is in the *middle* - not at the end...! :-D

        f = fopen(m->name, "rb");
        if(!f)
                eel_error(m->owner, "Could not open file!");

        if(fseek(f, 0, SEEK_END) == 0)
        {
                m->len = ftell(f);
                if(fseek(f, 0, SEEK_SET) == 0)
                {
                        m->source = (unsigned char *)malloc(m->len + 1);
                        if(m->source)
                        {
                                if(fread(m->source, m->len, 1, f) == 1)
                                {
                                        fclose(f);
                                        return;
                                }
                                else
                                        eel_details(m->owner, "Read error!");
                        }
                        else
                                eel_details(m->owner, "Out of memory!");
                }
                else
                        eel_details(m->owner, "Rewind failed!");
        }
        else
                eel_details(m->owner, "Could not seek to EOF!");
        fclose(f);
        eel_error(m->owner, "Could not load file \"%s\"!", m->name);

Oh, and there are actually two more error cases, but fortunately, I 
wasn't stoned (or whatever the matter was) enough to handle those the 
same way. *hehe*


> To me, a good coding environment lets the programmer spend more
> time thinking about the problem, the module's mission, and less
> time thinking about the language, like how to get a fatal error
> condition noted and get the heck out.

Couldn't agree more. That's why I like language features that let you 
do what you want in a nice and clean way, with minimal risk of 
accidentally doing something else.


[...]
> >Well, I suppose that depends on what you mean by OO, but indeed,
> > the "purist" versions generally map well only to a limited set of
> > problems. OO, imperative, functional, table oriented etc all have
> > their use, and mixing them to some extent is often the optimal
> > solution.
> >
> >Problem is that if a explicitly language supports *everything*,
> > you get something that takes ages to learn, and makes it hard to
> > understand other people's code. Just look at C++ - and I believe
> > it can get h*lluva' lot worse than that if you throw in dynamic
> > typing, "call-by-name" and stuff like that as well... I believe
> > there has to be both upper and lower bounds to the level of
> > native language features to make a sensible language.
>
> Exactly what i'm saying. If u make it C-like, it will be easy to
> pick up. Heck, we're scripting, right? This is not a tool for some
> ivory-tower comp sci prof who's never had to support other people's
> code for a living. We need a bulldozer and a pickup truck, not a
> limo. "Blue Collar Coding" :)

Exactly. :-)


//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
|  Free/Open Source audio engine for games and multimedia.  |
| MIDI, modular synthesis, real time effects, scripting,... |
`-----------------------------------> http://audiality.org -'
   --- http://olofson.net --- http://www.reologica.se ---


Other related posts: