Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

I7/Glulx and memory

6 views
Skip to first unread message

Erik Temple

unread,
Nov 9, 2009, 11:11:47 PM11/9/09
to
Hi all,

I'm working on a moderately sized (about 23,000 words) Glulx project and
running into what appears to be a memory problem, one that causes the
interpreter (both glulxe and git, running under the hood in Zoom) to hang
and suck up all available CPU (more details below). This project played
very nicely before I revamped the namespaces and logic of some of the
extensions it depends on. Because I had to update so many things in the
main project file (to conform to the extension changes) before it would
compile, there aren't many clues to go on...

My question, before I start the slow, laborious process of trying to hunt
down the bug, is whether there are I6 memory settings that I should try
tweaking? (I have tried running MAX_STATIC_DATA up to 3000000 with no
luck.)

Thanks for any help!

Erik

In case it's a help to anyone, here's the trace of git trying to run the
game (from Mac OS's Activity application):

Sampling process 22841 for 3 seconds with 1 millisecond of run time
between samples
Sampling completed, processing symbols...
Analysis of sampling git (pid 22841) every 1 millisecond
Call graph:
2257 Thread_2507
2257 start
2257 _start
2257 main
2257 glk_main
2257 gitWithStream
2257 gitMain
2257 startProgram
2129 startProgram
112 accel_get_func
112 accel_get_func
16 memRead32
16 memRead32

Total number in stack (recursive counted multiple, when >=5):

Sort by top of stack, same collapsed (when >= 5):
startProgram 2129
accel_get_func 112
memRead32 16
Sample analysis of process 22841 written to file /dev/stdout

vaporware

unread,
Nov 9, 2009, 11:50:16 PM11/9/09
to
On Nov 9, 8:11 pm, "Erik Temple" <ek.tem...@gmail.com> wrote:
> Hi all,
>
> I'm working on a moderately sized (about 23,000 words) Glulx project and  
> running into what appears to be a memory problem, one that causes the  
> interpreter (both glulxe and git, running under the hood in Zoom) to hang  
> and suck up all available CPU (more details below). This project played  
> very nicely before I revamped the namespaces and logic of some of the  
> extensions it depends on. Because I had to update so many things in the  
> main project file (to conform to the extension changes) before it would  
> compile, there aren't many clues to go on...
>
> My question, before I start the slow, laborious process of trying to hunt  
> down the bug, is whether there are I6 memory settings that I should try  
> tweaking? (I have tried running MAX_STATIC_DATA up to 3000000 with no  
> luck.)

Other than one or two things (like dictionary word size), I6 memory
settings don't affect how the compiled game works, only whether or not
it will successfully compile. If the compiler isn't throwing any
errors, I6 settings aren't the problem.

What led you to call this a memory problem? You say you changed the
extension and game logic: could you have introduced an infinite loop
somewhere?

"RULES ALL" might help, but if the game is getting stuck in a loop,
you might need to run the game in a console-mode interpreter (like
glulxe + CheapGlk) in order to see any output.

vw

Erik Temple

unread,
Nov 10, 2009, 3:12:45 PM11/10/09
to
On Mon, 09 Nov 2009 22:50:16 -0600, vaporware <jmc...@gmail.com> wrote:

> Other than one or two things (like dictionary word size), I6 memory
> settings don't affect how the compiled game works, only whether or not
> it will successfully compile. If the compiler isn't throwing any
> errors, I6 settings aren't the problem.

Thanks, vw. I was afraid that'd be the answer.


> What led you to call this a memory problem?

Ignorance, more than likely! I have had this issue (see below, where I get
a bit more specific than in my original post) a number of times in the
past. Usually I have been able to fix it by changing some relatively small
thing; I have a hazy and very possibly false memory that I in one case
increasing MAX_STATIC_DATA fixed it. The odd thing is that the code that
I've changed to fix the issue has almost never been code that would be
executing at the time the interpreter hangs (which occurs, inevitably, at
startup). I can only guess that the problems I've seen stem from multiple
causes, resulting however in the same problematic behavior.
(Unfortunately, I also can't remember what was triggered the issue in most
cases. I need to start keeping notes.)


> You say you changed the
> extension and game logic: could you have introduced an infinite loop
> somewhere?

> "RULES ALL" might help, but if the game is getting stuck in a loop,
> you might need to run the game in a console-mode interpreter (like
> glulxe + CheapGlk) in order to see any output.

The problem is that the interpreter hangs (all interpreters, not just
Zoom--I've tried it on a wide range now, including cheapglulxe)
immediately after launching--I never even get to see a line of output, and
definitely not a command prompt. Most of the changes I have made to the
extensions are to things having to do with graphics display, but the hang
occurs before any windows (including the status window) have even been
opened. That said, it certainly is possible that I've introduced an
infinite loop somewhere; the changes I've made could potentially result in
that kind of thing. (At the same time, though, I find it hard to believe
that any such loop is actually executing at the time the virtual machine
starts.)

I guess I can translate these concerns into one last-ditch question before
I tear down the story file and rebuild it bit by bit: What kinds of issue
could cause a Glulx file to hang at startup? Obviously, that question is
too broad to be likely to draw any response, but a shot in the dark can't
hurt...

--Erik

Andrew Plotkin

unread,
Nov 10, 2009, 3:55:04 PM11/10/09
to
Here, Erik Temple <ek.t...@gmail.com> wrote:
>
> I guess I can translate these concerns into one last-ditch question before
> I tear down the story file and rebuild it bit by bit: What kinds of issue
> could cause a Glulx file to hang at startup? Obviously, that question is
> too broad to be likely to draw any response, but a shot in the dark can't
> hurt...

I can't think of anything. Sorry.

At startup, the VM itself does almost nothing. It loads in the string
encoding tables, but I've never seen that go wrong.

The game code starts with the "starting the virtual machine" activity,
and that sets up Glk state for windows and things. I would guess that
that's where your game is getting stuck, because the library has a lot
of hooks for you to customize that process. Also because a breakage
here would prevent windows from being created.

After that are several more setup rules (in the startup rules
rulebook). Most of them don't call game code, so they are hard to
break. The major exceptions are "when play begins" (the famous one),
preceded by "start in the correct scenes" (which must call a bunch of
scene condition tests).

--Z

--
"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the borogoves..."
*

Erik Temple

unread,
Nov 10, 2009, 4:36:27 PM11/10/09
to
On Tue, 10 Nov 2009 14:55:04 -0600, Andrew Plotkin <erky...@eblong.com>
wrote:

Thanks for the tips. I'll look at these before--and hopefully instead
of--taking apart the game source. Neither my game nor any of the
extensions I've been working on since the last time I (successfully)
compiled this project has "when play begins" or "starting the virtual
machine" rules, though of course other extensions I'm using do (Flexible
Windows comes to mind). Maybe something I've done in revising the story
file is interfering somehow with those; I'll take a look.

As an aside, you might be interested in the last thing that caused this
error for me, since it could possibly implicate the string encoding
tables--though I'm not sure what exactly they do, so even bringing this up
may be off-base. While working on a method to do what I was trying to do
in this thread from a couple of weeks ago:

http://groups.google.com/group/rec.arts.int-fiction/browse_thread/thread/cead0a3a6208a2ce/

I began by using a phrase something like this:

To debug (T - indexed text):
if using the debugging option, say T.

so that one could write DEBUG "Drawing a rectangle in [window]" and the
message would appear or not depending on whether the use option was
defined. However, passing indexed text into the phrase like this caused
the interpreter to hang at startup. Change the temporary variable to
T--that is, passing in text rather than indexed text--worked fine, though.
Unfortunately, I don't remember exactly what code I was using (the code
did employ glk_set_window to change the output stream though, probably via
the Flexible Windows phrase wrapper for that function), but hopefully this
conveys the idea of what was going on...

--Erik


Dannii

unread,
Nov 10, 2009, 10:47:23 PM11/10/09
to
On Nov 11, 7:36 am, "Erik Temple" <ek.tem...@gmail.com> wrote:
> On Tue, 10 Nov 2009 14:55:04 -0600, Andrew Plotkin <erkyr...@eblong.com>  
> wrote:
> http://groups.google.com/group/rec.arts.int-fiction/browse_thread/thr...

>
> I began by using a phrase something like this:
>
>         To debug (T - indexed text):
>                 if using the debugging option, say T.
>
> so that one could write DEBUG "Drawing a rectangle in [window]" and the  
> message would appear or not depending on whether the use option was  
> defined. However, passing indexed text into the phrase like this caused  
> the interpreter to hang at startup. Change the temporary variable to  
> T--that is, passing in text rather than indexed text--worked fine, though.  
> Unfortunately, I don't remember exactly what code I was using (the code  
> did employ glk_set_window to change the output stream though, probably via  
> the Flexible Windows phrase wrapper for that function), but hopefully this  
> conveys the idea of what was going on...
>
> --Erik

It would be worth looking at your auto.inf to see what is in your
early rulebooks. Search for B1_startup and B5_when_play_begins. Each
individual rule will tell you where it's coming from.

Erik Temple

unread,
Nov 10, 2009, 11:39:07 PM11/10/09
to
On Tue, 10 Nov 2009 21:47:23 -0600, Dannii <curiou...@gmail.com> wrote:

> It would be worth looking at your auto.inf to see what is in your
> early rulebooks. Search for B1_startup and B5_when_play_begins. Each
> individual rule will tell you where it's coming from.

Thanks for the tip--I've filed this away for future reference. As it
happens, I got lucky and managed to isolate the problem in the first 30
minutes or so of debugging. As I suspected, it has nothing to do with
startup rules. Unfortunately, I don't understand what it does have to do
with... This is the offending block of code:


repeat with current-element running through display-active paging-buttons:
let x-coord be entry 1 of the origin of current-element;
let y-coord be entry 2 of the origin of current-element;
display image (image-ID of current-element) in paging-window at (x-coord)
by (y-coord) with dimensions (nav-size) x (nav-size);
if current-element is graphlinked:
set a graphlink in paging-window identified as current-element from
(x-coord) by (y-coord) to (x-coord + nav-size) by (y-coord + nav-size) as
the linked replacement-command of current-element;


This causes the interpreter to hang even though it is most definitely not
executed at startup. This is part of a window-drawing rule, and the window
isn't opened until later in the game. It seems as though anything having
to do with grabbing the two list entries from the origin list property of
the button object within this routine causes interpreters to hang. I do
this throughout the rest of the code, so I'm not sure what the issue with
this particular instance might be. However, it seems to do with assigning
the list entries to temporary variables. All that is needed to crash the
interpreter, in fact, is one of the first two lines of the loop (the
temporary variable assignments). On the other hand, this works, so there
is no issue with reading from the list itself:

repeat with current-element running through display-active paging-buttons:
say "[current-element]: [origin of current-element].";

I have tried variant phrasings and passing the variable assignments
through different routine, but the result is always the same. The only way
I have found to fix the issue is to used global variable instead of
temporary variables. Can this be anything but a memory issue?

I'll keep looking into this and report back on anything I find.

Thanks,
Erik

Andrew Plotkin

unread,
Nov 11, 2009, 1:05:09 AM11/11/09
to
Here, Erik Temple <ek.t...@gmail.com> wrote:
> This is the offending block of code:
>
>
> repeat with current-element running through display-active paging-buttons:
> let x-coord be entry 1 of the origin of current-element;
> let y-coord be entry 2 of the origin of current-element;
> display image (image-ID of current-element) in paging-window at (x-coord)
> by (y-coord) with dimensions (nav-size) x (nav-size);
> if current-element is graphlinked:
> set a graphlink in paging-window identified as current-element from
> (x-coord) by (y-coord) to (x-coord + nav-size) by (y-coord + nav-size) as
> the linked replacement-command of current-element;
>
>
> This causes the interpreter to hang even though it is most definitely not
> executed at startup. This is part of a window-drawing rule, and the window
> isn't opened until later in the game.

Are you saying that having this code in your game causes it to hang,
even though the code is not executed?

Does it happen if you surround that repeat statement with "if 0 is 1",
so that it's *really* not executed?

What is that repeat statement in?

> Can this be anything but a memory issue?

Sure. I7 compiler bug, generating mismatched I6 utility functions for
your properties. The thing where declaring an I7 entity causes an
unrelated part of your code to be parsed differently. Misunderstanding
of what code is executed at startup time.

Erik Temple

unread,
Nov 11, 2009, 8:06:24 AM11/11/09
to
On Wed, 11 Nov 2009 00:05:09 -0600, Andrew Plotkin <erky...@eblong.com>
wrote:

> Are you saying that having this code in your game causes it to hang,


> even though the code is not executed?
>
> Does it happen if you surround that repeat statement with "if 0 is 1",
> so that it's *really* not executed?

Yes, that's what I was saying. I just tried it with your nifty "if 0 is 1"
trick and the results are the same.


> What is that repeat statement in?

It is in the drawing rule for a graphics window (the full rule is beneath
my sig). This rule is hooked into HandleGlkEvent (both Arrange and
Redraw), so there is some possibility that it could be fired by that
routine. However, an "if" statement should prevent it from actually
firing; i.e. "if the window is present, execute this code."

A test of the status of the "g-present" flag (see Flexible Windows) that
is responsible for this test shows that it contains the correct values for
each of the windows by the time of "when play begins". All windows but the
main window are initialized as g-unpresent in any case. So, this plus the
"if 0 is 1" test seems to indicate that the problem is not in the
execution of this code.


>> Can this be anything but a memory issue?
>
> Sure. I7 compiler bug, generating mismatched I6 utility functions for
> your properties. The thing where declaring an I7 entity causes an
> unrelated part of your code to be parsed differently. Misunderstanding
> of what code is executed at startup time.

Thanks! (I don't think it's the last one though, at least not in this
particular case...)

The problematic instance is one of perhaps 50 places (I'm estimating)
where I access entry 1 of the origin (list of numbers) property of some
object, but the only one that creates the issue. I'll look into whether
there is anything different about this particular case. I am afraid that I
may not be able to turn up the root cause, though.

--Erik


A window-drawing rule for the paging-window (this is the paging-window
drawing rule):
if the paging-window is g-present:
clear the paging-window;
resize the canvas of the paging-window to the window;
let margin be the canvas-height of the paging-canvas divided by 6;
let nav-size be (canvas-height of the paging-canvas minus margin) minus
margin;
change entry 1 of the origin of page-left_button to margin;
change entry 2 of the origin of page-left_button to margin;
change entry 2 of the origin of page-right_button to margin;
change entry 1 of the origin of page-right_button to (canvas-width of
library-canvas minus margin) minus nav-size;
prune the link-table of paging-window links;
if 0 is 1:


repeat with current-element running through display-active
paging-buttons:

let temp-coord-x be entry 1 of the origin of current-element;
let temp-coord-y be entry 2 of the origin of current-element;


display image (image-ID of current-element) in paging-window at

(temp-coord-x) by (temp-coord-y) with dimensions (nav-size) x (nav-size);


if current-element is graphlinked:
set a graphlink in paging-window identified as current-element from

(temp-coord-x) by (temp-coord-y) to (temp-coord-x + nav-size) by
(temp-coord-y + nav-size) as the linked replacement-command of
current-element;

Andrew Plotkin

unread,
Nov 11, 2009, 11:13:04 AM11/11/09
to
Here, Erik Temple <ek.t...@gmail.com> wrote:
> On Wed, 11 Nov 2009 00:05:09 -0600, Andrew Plotkin <erky...@eblong.com>
> wrote:
>
> > Are you saying that having this code in your game causes it to hang,
> > even though the code is not executed?
> >
> > Does it happen if you surround that repeat statement with "if 0 is 1",
> > so that it's *really* not executed?
>
> Yes, that's what I was saying. I just tried it with your nifty "if 0 is 1"
> trick and the results are the same.

This is fascinating. In a hurty way. :)

I think the next step is to compile it with this form, and then
comment out those lines entirely (but leave the stub function in
place), and compile it again. Compare the auto.inf files that I7 is
producing in each case.

If you're lucky, they will differ slightly. More likely, they will
differ all over, but maybe in a systematic way -- renumbered functions
or something. It could be a veritable pain in the butt.

Looking at the generated I6 for the bad function is probably useful
too.

If all of this is getting to heavy, feel free to throw the files at me.

Erik Temple

unread,
Nov 11, 2009, 3:20:12 PM11/11/09
to
On Wed, 11 Nov 2009 10:13:04 -0600, Andrew Plotkin <erky...@eblong.com>
wrote:

> This is fascinating. In a hurty way. :)

My sentiments exactly...

> I think the next step is to compile it with this form, and then
> comment out those lines entirely (but leave the stub function in
> place), and compile it again. Compare the auto.inf files that I7 is
> producing in each case.
>
> If you're lucky, they will differ slightly. More likely, they will
> differ all over, but maybe in a systematic way -- renumbered functions
> or something. It could be a veritable pain in the butt.
> Looking at the generated I6 for the bad function is probably useful
> too.

I tried this last night, and nothing jumped out at me. As you say,
functions are renumbered, as are list constants. I'm not sure what to make
of it, though... While I've found reading the generated I6 code useful in
a lot of situations, I think I just don't understand how lists work
internally (in terms of the stack, etc.) to have a feel for what might be
happening in this case.

> If all of this is getting to heavy, feel free to throw the files at me.

Thanks; I'm sending them to you now!

--Erik

Andrew Plotkin

unread,
Nov 12, 2009, 1:25:46 AM11/12/09
to
Here, Erik Temple <ek.t...@gmail.com> wrote:
> On Wed, 11 Nov 2009 00:05:09 -0600, Andrew Plotkin <erky...@eblong.com>
> wrote:
>
> > Are you saying that having this code in your game causes it to hang,
> > even though the code is not executed?
> >
> > Does it happen if you surround that repeat statement with "if 0 is 1",
> > so that it's *really* not executed?
>
> Yes, that's what I was saying. I just tried it with your nifty "if 0 is 1"
> trick and the results are the same.

After looking at the I6 source code (which Erik emailed me) I can
clear up this bit of the mystery. The code in the "0 is 1" block
contains some construct which forces I7 to compile the whole function
differently. I don't know this part of the system, but it has to do
with Inform's extra stack for block values.

I've pasted the diff below. You can see that in the crashy case, the
rule function is actually two nested functions -- the outer one
fiddles with the blockv_stack array, and the inner one does the work.
(Presumably so that if the inner one includes an early "return",
blockv_stack is still unfiddled afterwards.)

That is how the bad code can still affect the program behavior even
when it is if'ed out.

I think the rule is still being *called* at startup, though. The full
diff only showed differences in the contents of this rule, plus
changes in function and constant numbering. It's possible that there's
a bug in the latter -- I didn't try to match up all the labels and
prove they were identical. But it's much more likely that the bug is
in this I6 function, which means it can only manifest when the
function is called.

That doesn't tell us what the bug *is*, or whether it's your mistake
or a compiler bug. However, I have come to the end of what I can
figure out. You should throw this information at Graham.

You said that using global variables instead of locals works around
the problem, so that's probably the right way to go for now.

--Z

***************
*** 30647,30657 ****
rfalse;
];
! A window-drawing rule for the paging-window ( this is the paging-window drawing rule ):
! [ R_1087
t_0 ! Local variable e.g. 'margin' = NUMBER_TY
t_1 ! Local variable e.g. 'nav-size' = NUMBER_TY
t_2 ! Local variable e.g. '?-1,-1?' = OBJECT_TY
t_3 ! Local variable e.g. '?-1,-1?' = OBJECT_TY
;
if (((parameter_object == O72_paging_window))) { ! Runs only when pattern matches
if (debug_rules) DB_Rule(R_1087, 1087);
--- 30647,30668 ----
rfalse;
];
! A window-drawing rule for the paging-window ( this is the paging-window drawing rule ):
! [ R_1087 ;blockv_stack-->(blockv_sp+1) = BlkValueCreate(85,0,57);
! blockv_stack-->(blockv_sp+0) = BlkValueCreate(85,0,57);
! blockv_sp = blockv_sp + 2;
! blockv_stack-->(blockv_sp++) = R_SHELL_51(blockv_sp-2);
! blockv_sp = blockv_sp - 3;
! BlkFree(blockv_stack-->(blockv_sp+1));
! BlkFree(blockv_stack-->(blockv_sp+0));
! return blockv_stack-->(blockv_sp+2);
! ];
! [ R_SHELL_51 I7BASPL
t_0 ! Local variable e.g. 'margin' = NUMBER_TY
t_1 ! Local variable e.g. 'nav-size' = NUMBER_TY
t_2 ! Local variable e.g. '?-1,-1?' = OBJECT_TY
t_3 ! Local variable e.g. '?-1,-1?' = OBJECT_TY
+ t_4 ! Local variable e.g. 'temp-coord-x' = NUMBER_TY
+ t_5 ! Local variable e.g. 'temp-coord-y' = NUMBER_TY
;
if (((parameter_object == O72_paging_window))) { ! Runs only when pattern matches
if (debug_rules) DB_Rule(R_1087, 1087);
***************

vaporware

unread,
Nov 12, 2009, 2:12:13 PM11/12/09
to
On Nov 11, 10:25 pm, Andrew Plotkin <erkyr...@eblong.com> wrote:
> Here, Erik Temple <ek.tem...@gmail.com> wrote:
>
> > On Wed, 11 Nov 2009 00:05:09 -0600, Andrew Plotkin <erkyr...@eblong.com>  

The shell function in the diff is used to allocate two temporary list
variables (LIST_OF_TY = 85), which is why it's only generated when the
first two lines of the rule (which access lists) are included.

The generated code looks OK to me, so I would guess the rule is being
called at an inappropriate time. For example, if it's called during
the "starting the virtual machine" activity -- which opens windows, so
this may be the case -- the heap won't be initialized yet: the
"virtual machine startup rule", which triggers that activity, runs
just before the "initialise memory rule".

vw

Andrew Plotkin

unread,
Nov 12, 2009, 3:02:04 PM11/12/09
to
Here, vaporware <jmc...@gmail.com> wrote:
>
> The shell function in the diff is used to allocate two temporary list
> variables (LIST_OF_TY = 85), which is why it's only generated when the
> first two lines of the rule (which access lists) are included.
>
> The generated code looks OK to me, so I would guess the rule is being
> called at an inappropriate time. For example, if it's called during
> the "starting the virtual machine" activity -- which opens windows, so
> this may be the case -- the heap won't be initialized yet: the
> "virtual machine startup rule", which triggers that activity, runs
> just before the "initialise memory rule".

Sheesh, I would have noticed that if I'd *read* the listing of the
startup rulebook, instead of letting it dribble off my corneas.

Yes, that makes sense. Thanks.

So: either use global list variables, or set up your own wrapper rule
(which invokes the "real" rule, the one with temporary list variables,
only after the window exists).

--Z

Erik Temple

unread,
Nov 12, 2009, 4:09:09 PM11/12/09
to
On Thu, 12 Nov 2009 14:02:04 -0600, Andrew Plotkin <erky...@eblong.com>
wrote:

> Here, vaporware <jmc...@gmail.com> wrote:

Thanks to you both--with renewed focus on figuring out how the code could
possibly be called before the windows exist, I tentatively think we may
uncovered a bug in Flexible Windows. The issue arises, as vaporware
suggested, out of the startup routine, specifically VM_Initialise(), which
calls IdentifyGlkObject (phase 2 - updating). Flexible Windows slots this
code into IdentifyGlkObject:

<code>
A glulx object-updating rule (this is the updating-after-undo all rule):
follow the refresh windows rule.

This is the refresh windows rule:
let old current be the current g-window;
repeat with item running through g-present g-windows begin;
change current g-window to the item;
follow the window-drawing rules for the item;
end repeat;
if the old current is g-present
begin;
change current g-window to the old current;
set focus to the current g-window;
end if.
</code>

Comment out the glulx object-updating rule and the interpreter hang goes
away.

Zarf, the "repeat with item running through g-present g-windows" in this
should be function as the kind of wrapper you're talking about, ensuring
that the problem window drawing rule doesn't fire unless the associated
g-window is marked g-present. Since all g-windows (except the main-window
object) are initialized as g-unpresent, this rule should fire only for the
main-window at startup. Window objects are marked g-present only when
opened (which in this project doesn't occur until 2-5 turns into the
game)--or in IdentifyGlkObject (phase 2); I believe the latter should only
happen when GGRecoverObjects() has identified the object as already
existing, but I may be missing something.

In any case, it seems that somehow the window-drawing rules are called via
VMInitialize-->GGRecoverObjects-->IdentifyGlkObject despite the fact that
the windows shouldn't exist yet--perhaps they are somehow created and then
destroyed in the course of IdentifyGlkObject. As I indicated in an earlier
post, they definitely DO NOT exist by the time we get to when play begins,
when g-unpresent is properly set and GLKLIST confirms that the only window
in existence is the main window (gg_mainwin).

All that said, I do have doubts whether there isn't something else in the
code causing the problem (i.e., it may be a bug with my code rather than
Flexible Windows). The first version of this project (before I did the
makeover mentioned in the initial post) properly zeroed out references to
Glk objects on restart, while the new version apparently does not,
crashing with "Glulxe fatal error: Reference to nonexistent Glk object"
during the process of opening up the various graphics windows after
restart. Since this is also happening in InitGlkObject, there may be
something else going on here. (Flexible Windows should be handling all of
this on its own, but possibly I've intervened somehow.)

In any case, thanks much for the help! If it's warranted, I'll follow up
with a bug report to Jon Ingold.

--Erik

vaporware

unread,
Nov 12, 2009, 4:21:09 PM11/12/09
to
On Nov 12, 1:09 pm, "Erik Temple" <ek.tem...@gmail.com> wrote:
> On Thu, 12 Nov 2009 14:02:04 -0600, Andrew Plotkin <erkyr...@eblong.com>  
> wrote:

>
>
>
> > Here, vaporware <jmcg...@gmail.com> wrote:
>
> >> The shell function in the diff is used to allocate two temporary list
> >> variables (LIST_OF_TY = 85), which is why it's only generated when the
> >> first two lines of the rule (which access lists) are included.
>
> >> The generated code looks OK to me, so I would guess the rule is being
> >> called at an inappropriate time. For example, if it's called during
> >> the "starting the virtual machine" activity -- which opens windows, so
> >> this may be the case -- the heap won't be initialized yet: the
> >> "virtual machine startup rule", which triggers that activity, runs
> >> just before the "initialise memory rule".
>
> > Sheesh, I would have noticed that if I'd *read* the listing of the
> > startup rulebook, instead of letting it dribble off my corneas.
>
> > Yes, that makes sense. Thanks.
>
> > So: either use global list variables, or set up your own wrapper rule
> > (which invokes the "real" rule, the one with temporary list variables,
> > only after the window exists).
>
> Thanks to you both--with renewed focus on figuring out how the code could  
> possibly be called before the windows exist, I tentatively think we may  
> uncovered a bug in Flexible Windows.

This could be considered an Inform bug as well. The documentation for
"starting the virtual machine" mentions that you shouldn't print
anything, but doesn't mention that you can't use the heap.

vw

Erik Temple

unread,
Nov 12, 2009, 7:06:05 PM11/12/09
to
On Thu, 12 Nov 2009 15:21:09 -0600, vaporware <jmc...@gmail.com> wrote:

>> Thanks to you both--with renewed focus on figuring out how the code
>> could possibly be called before the windows exist, I tentatively think
>> we may uncovered a bug in Flexible Windows.
>
> This could be considered an Inform bug as well. The documentation for
> "starting the virtual machine" mentions that you shouldn't print
> anything, but doesn't mention that you can't use the heap.
>
> vw

Good point. I'll mention it to Graham.

--Erik

Erik Temple

unread,
Nov 12, 2009, 10:51:10 PM11/12/09
to
On Thu, 12 Nov 2009 15:09:09 -0600, Erik Temple <ek.t...@gmail.com>
wrote:

> Thanks to you both--with renewed focus on figuring out how the code
> could possibly be called before the windows exist, I tentatively think
> we may uncovered a bug in Flexible Windows.

Well, it's looking like this may be a bug in Inform after all. I'm not
sure of the exact cause, but it has to do with whether the rules for
drawing g-windows are simple rules, or whether they are object-based
rulebooks keyed to the window.

I'm going to lay out a bunch of code below in case someone wants to help
me figure out what's going on. However, I recognize that most will not, so
I'll give the gist here: the problem seems to be that an object-based
rulebook behaves differently when run through IdentifyGlkObject (Emily's
Glulx Entry Points) than does a simple rule. I'm not sure why this would
be, and I'm having trouble finding a plausible explanation for it.
Basically, if we assign a simple rule as a property of the window object,
as in the first example below, the rule doesn't fire until the window is
opened (i.e., it doesn't happen before the heap is created, which is what
causes the crash). If instead we run an object-based rulebook with a
pattern-match on the name of the window (e.g., "a window-drawing rule for
the graphics-window"), then the window-drawing rule *is* run before the
heap, regardless of the g-present flag that "protects" this from occurring
when the rule is a simple one.

My question is, then: Is there something different about object-based
rulebooks that would cause them to function differently in this situation?
(By the way, this is not the only bug I'm seeing from the interaction
between the IdentifyGlkObject routine and object-parametrized rulebooks:
there also seems to be a problem with the zeroing-reference rules--that
is, phase 0 of IdentifyGlkObject--which causes garbage collection to fail
when we restart, at least with more than three windows open.)

OK, here is code using the current version of Flexible Windows (v8) that
works fine, with a rule property attached to the g-window object:

<code>
Include Flexible Windows by Jon Ingold.

Test is a room.

The offending list is a list of numbers variable. The offending list is
{2, 4, 5}.

The graphics-window is a g-window. The position is g-placeabove. The
measurement is 60. The back-colour is g-black. The drawing rule is the
graphics-window drawing rule.

The main-window spawns the graphics-window.

This is the graphics-window drawing rule:
let T1 be entry 1 of the offending list;
let T2 be entry 2 of the offending list.

When play begins:
open up the graphics-window.
</code>

However, in its next version Flexible Windows will probably be changing to
utilize an object-based rulebook instead ("Follow the window-drawing rules
for the current window"). (The major diffs between the hacked Flexible
Windows "v9" I am using here and the current version are given beneath my
signature. Or download it here:
http://dl.dropbox.com/u/947038/Flexible%20Windows%20v9.i7x ).

So, this code, in which the only change is to the preamble of the drawing
rule, crashes the interpreter:

<code>
Test is a room.

The offending list is a list of numbers variable. The offending list is
{2, 4, 5}.

The graphics-window is a g-window. The position is g-placeabove. The
measurement is 60. The back-colour is g-black.

The main-window spawns the graphics-window.

A window-drawing rule for the graphics-window (this is the graphics-window
drawing rule): [this line is changed]
let T1 be entry 1 of the offending list;
let T2 be entry 2 of the offending list.

When play begins:
open up the graphics-window.
</code>

Any ideas?

Thanks,
Erik


v8
A g-window has a rule called drawing rule.
The drawing rule of a g-window is usually the do-nothing rule.
This is the do-nothing rule: do nothing.

"v9"
A graphics g-window is a kind of g-window. The type of a graphics g-window
is g-graphics.
A text-buffer g-window is a kind of g-window. The type of a text-buffer
g-window is g-text-buffer.
A text-grid g-window is a kind of g-window. The type of a text-grid
g-window is g-text-grid.
The window-drawing rules are an object-based rulebook.


v8


This is the refresh windows rule:
let old current be the current g-window;
repeat with item running through g-present g-windows begin;
change current g-window to the item;

follow the drawing rule of the item;


end repeat;
if the old current is g-present
begin;
change current g-window to the old current;
set focus to the current g-window;
end if.

"v9"


This is the refresh windows rule:
let old current be the current g-window;
repeat with item running through g-present g-windows begin;
change current g-window to the item;

! follow the window-drawing rules for the item;


end repeat;
if the old current is g-present
begin;
change current g-window to the old current;
set focus to the current g-window;
end if.


v8
The last After constructing a g-window :
if the pending g-window is g-present, follow the drawing rule of the
pending g-window;

"v9"
The last After constructing a g-window :
! if the pending g-window is g-present, follow the window-drawing rules
for the pending g-window;

vaporware

unread,
Nov 12, 2009, 11:51:39 PM11/12/09
to
On Nov 12, 7:51 pm, "Erik Temple" <ek.tem...@gmail.com> wrote:
> On Thu, 12 Nov 2009 15:09:09 -0600, Erik Temple <ek.tem...@gmail.com>  

> wrote:
>
> > Thanks to you both--with renewed focus on figuring out how the code  
> > could possibly be called before the windows exist, I tentatively think  
> > we may uncovered a bug in Flexible Windows.
>
> Well, it's looking like this may be a bug in Inform after all. I'm not  
> sure of the exact cause, but it has to do with whether the rules for  
> drawing g-windows are simple rules, or whether they are object-based  
> rulebooks keyed to the window.
>
> I'm going to lay out a bunch of code below in case someone wants to help  
> me figure out what's going on. However, I recognize that most will not, so  
> I'll give the gist here: the problem seems to be that an object-based  
> rulebook behaves differently when run through IdentifyGlkObject (Emily's  
> Glulx Entry Points) than does a simple rule. I'm not sure why this would  
> be, and I'm having trouble finding a plausible explanation for it.  
> Basically, if we assign a simple rule as a property of the window object,  
> as in the first example below, the rule doesn't fire until the window is  
> opened (i.e., it doesn't happen before the heap is created, which is what  
> causes the crash). If instead we run an object-based rulebook with a  
> pattern-match on the name of the window (e.g., "a window-drawing rule for  
> the graphics-window"), then the window-drawing rule *is* run before the  
> heap, regardless of the g-present flag that "protects" this from occurring  
> when the rule is a simple one.
>
> My question is, then: Is there something different about object-based  
> rulebooks that would cause them to function differently in this situation?  

Sort of. The difference is that with the rulebook, you're controlling
whether or not the rule runs with a condition in the preamble, whereas
with a rule stored in a property, you're controlling it using logic
that's entirely outside the rule.

A rulebook is a list of routines, and invoking the rulebook means
calling every routine in the list until one of them says you're done.
Normally those routines are the actual rule implementations; if a rule
has a condition in the preamble, it will be checked inside that
routine. But when Inform generates a wrapper routine to manipulate the
heap, the *wrapper* goes in the rulebook.

That means when you call a rulebook, you potentially do heap
manipulation for *all* the rules in the rulebook, even the ones that
will be skipped because of a preamble condition. So if any of your
window-drawing rules use local heap variables, the whole window-
drawing rulebook is unsafe to call from VM_Initialise!

One workaround, as you've found, is to use global variables instead so
there's no wrapper routine. Another would be to rearrange the startup
rulebook to make the heap available when this rule is called, which is
a workaround if you do it from your own code, but could be a long term
solution if done in the Standard Rules.

The problem would also go away, at least in this case, if Inform
hoisted the rule condition checking into the generated wrapper
routine. That's a little complicated and not a robust solution on its
own (what happens if the rule condition uses the heap?), but probably
a good thing to do anyway, because it's wasteful to create and destroy
a bunch of unused heap blocks for rules that are being skipped.

vw

Global Education Office

unread,
Nov 13, 2009, 10:20:40 AM11/13/09
to
On Nov 12, 10:51 pm, vaporware <jmcg...@gmail.com> wrote:

> That means when you call a rulebook, you potentially do heap
> manipulation for *all* the rules in the rulebook, even the ones that
> will be skipped because of a preamble condition. So if any of your
> window-drawing rules use local heap variables, the whole window-
> drawing rulebook is unsafe to call from VM_Initialise!

Fantastic, vw--this is just the kind of thing I was looking to find
out!


> One workaround, as you've found, is to use global variables instead so
> there's no wrapper routine. Another would be to rearrange the startup
> rulebook to make the heap available when this rule is called, which is
> a workaround if you do it from your own code, but could be a long term
> solution if done in the Standard Rules.

I will pose this rearrangement of the startup rulebook to Graham. I
*think* I may have another, Flexible Windows-specific, workaround for
this, but I'm not at a machine where I can test it just now. In any
case, I'll let Jon know about the limitation on accessing the heap
imposed by object-based rulebooks.

> The problem would also go away, at least in this case, if Inform
> hoisted the rule condition checking into the generated wrapper
> routine. That's a little complicated and not a robust solution on its
> own (what happens if the rule condition uses the heap?), but probably
> a good thing to do anyway, because it's wasteful to create and destroy
> a bunch of unused heap blocks for rules that are being skipped.

Ah, I'll mention this to Graham as well.

Thanks again!

--Erik

Dannii

unread,
Nov 13, 2009, 11:26:08 PM11/13/09
to

I've had ideas about redoing the rulebook code entirely, as it seems
very inefficient as it is now. I see no reason for each rule to be its
own function, and calling so many functions is a big performance hit.
At the very least the conditions for each rule should be outside the
functions.

vaporware

unread,
Nov 14, 2009, 12:37:48 AM11/14/09
to

Well, rulebooks can be rearranged dynamically (via procedural rules),
and the same rule can be listed in multiple rulebooks. How would that
be implemented without making each rule its own routine?

I'm not sure about the performance hit either. In a traditional
interpreter, every instruction has virtually the same running time, so
a function call is just two extra instructions. In ZLR, call
instructions end the JIT segment, but so do some other common
instructions. (I don't know about the implementation of Parchment or
any other unusual terps.)

vw

Andrew Plotkin

unread,
Nov 14, 2009, 1:12:41 AM11/14/09
to

Do it as-needed, for rules that are called from multiple places. It's
just dependency tracking.

As for procedural rules -- I'm not convinced I7 needs them. The most
common use-cases that required them have already vanished: you can now
replace and reorder rules at compile-time.

The procedural rules I see nowadays are all of the form "ignore rule X
when condition C". But this too can be done with a compile-time rule
replacement, which I leave to the reader as an exercise. :)

I'm not saying that they should be yanked out of the system
immediately. But it would not be insane for I7 to be able to generate
two code models, an efficient new-style one when there are zero
procedural rules, and the current style if there are any.

> I'm not sure about the performance hit either. In a traditional
> interpreter, every instruction has virtually the same running time, so
> a function call is just two extra instructions.

More than zero, and I'm not so sanguine about that "vrirtually".
But the real inefficiency is all the bookkeeping that the library
needs to do to track changed rulebooks.

Dannii

unread,
Nov 14, 2009, 2:10:24 AM11/14/09
to

Well one option would be to simply repeat the rule bodies! Most rules
are quite short, and the few that would be repeated would increase the
file size negligibly.

I am fairly convinced that the biggest performance problem currently
facing I7 is function calls. Many modern games will make tens of
thousands of function calls, sometimes hundreds, each and every turn.
In all interpreters function calls are expensive, and in JIT engines
they are especially expensive! Reducing the number of function calls
will probably give a slight speed increase to standard terps but also
a huge increase to JIT terps. Even if there was a slight complexity
increase I think it would be worth it.

Now the rule bodies could even remain in functions, this would be one
option for procedural rules or repeated ones. The problem with rule
books as currently implemented is that they call tens, maybe hundreds
in some cases, of functions that will almost immediately return. The
most common situation is that a function's conditions will not match,
so that is what we optimise for.

As Zarf said, procedural rules aren't as useful now as they used to
be... I'd love to see some complex examples though. I think though
that they could still be implement as just additional conditions to
try before the standard ones.

Losing the big rulebook arrays would also free some needed space in
the z-machine, which is not an issue with Glulx though.

Eliuk Blau

unread,
Nov 14, 2009, 3:21:31 AM11/14/09
to
Hello. I apologize if what I say has nothing to do with what you are
talking about because I did not understand much (my bad English).

I honestly do not see any utility to "Before Starting Virtual Machine"
because it lets you run code *before* the Glk things that are
initialized (windows, files, IO, etc.).

That is useless, because you can not "manipulate" resources that have
*not yet been created* (windows, sounds, for example). It will be
ideally something like "After Starting Virtual Machine", but although
you can use, it represents no change.

The "Virtual Machine starting" rule is running much much earlier than
GGRecoveryObjects(). That's the problem.

When I programmed Damusix, I needed something like the object
"LibraryExtensions" of Lib. 6/11, which was very useful, because
executing code (initialization of external libraries) just before
Initialise().

But when I wrote Damusix to Inform7, I found nothing that was like.
"First when play begins" is not good, because if someone changes the
execution order of rules in this rulebook, it all goes to hell, xD.
The strict order is lost. The Damusix's audio channels should be
created before the author could use the sound... and if the author
created in his own game a "FIRST when play begins", everything went to
hell. It did not work.

It would be very useful to have a rulebook that would implement rules
just before any "When play begins", as did the object
"LibraryExtensions" in Lib. 6/11.

I solved the problem as follows: at the beginning of the game is
running a series of rules in a particular order. I just got the
Damusix's initialization rule just after the system starts Glk [ie,
after running GGRecoveryObjects()]. Thus:

--------------------------------------------
Part 2 - Damusix Startup Process - Unindexed

The Damusix startup rule is listed before initialise memory rule in
the startup rules.

This is the Damusix startup rule:
damusix-initialise.

To damusix-initialise:
(- Damusix.InicializarGlk(); -)
--------------------------------------------

First run "Virtual machine starting", then is internally called
several routines, including a GGRecoveryObjects() and then run
"initialise memory". That's why it does not work use "Before/After
Starting Virtual Machine", because in any case be executed before
GGRecoveryObjects() routine.

My "hack" puts Damusix initialization just before the "initialise
memory", and therefore, just after GGRecoveryObjects(). However, the
initialization may have been positioned anywhere before "When play
begins" [but always after GGRecoveryObjects()], obtaining the same
effect.

I hope this serves to help someone. If this has nothing to do, ask for
an apology. As I said, I did not understand much of what is said in
this thread. =)

I apologize for Google's translated message. xD

Saludos! :-D
Eliuk Blau.

-----------------------------------------------------------------
P.S: Spanish text...

Hola. Yo pido disculpas si lo que voy a decir no tiene nada que ver
con lo que ustedes están hablando, porque no he entendido mucho (mi
mal inglés).

Yo, sinceramente, no le veo ninguna utilidad a "Before Starting
Virtual Machine" porque esto te permite ejecutar código *antes* que
the Glk things sean inicializados (windows, files, IO, etc.)

Eso no sirve para nada, porque no se pueden "manipular" recursos que
todavía *no han sido creados* (ventanas, sonidos, por ejemplo). Sería
ideal algo como "After Starting Virtual Machine", pero aunque se puede
usar, no representa ningún cambio.

La "Virtual Machine starting" rule se ejecuta mucho mucho antes que
GGRecoveryObjects(). Ese es el problema.

Cuando programé Damusix, yo necesitaba algo como el objeto
"LibraryExtensions" de Lib. 6/11, que era muy útil, porque ejecutaba
código (inicialización de librerías externas) justo antes de Initialise
().

Pero cuando porté Damusix a Inform7, no encontré nada que fuera
parecido. "When play begins" no sirve, porque si alguien cambia el
orden de ejecución de las reglas dentro de ese rulebook, todo se va al
carajo. xD El orden estricto se pierde. Los canales de audio de
Damusix debían crearse antes que el autor pudiera usarlos el sonido...
y si el autor creaba en el juego su propia "FIRST When play begins",
todo se iba al carajo. Ya no funcionaba.

Sería muy útil tener un rulebook que permitiera ejecutar reglas justo
antes de cualquier "When play begins", tal como lo hacía el objeto
"LibraryExtensions" en Lib. 6/11.

Yo lo solucioné el problema de la siguiente manera: al principio del
juego se ejecutan una serie de reglas en un orden concreto. Yo
simplemente metí la regla de inicialización de Damusix justo después
de que se inicie el sistema Glk [es decir, después de que se ejecute
GGRecoveryObjects()]. Así:

--------------------------------------------
Part 2 - Damusix Startup Process - Unindexed

The Damusix startup rule is listed before initialise memory rule in
the startup rules.

This is the Damusix startup rule:
damusix-initialise.

To damusix-initialise:
(- Damusix.InicializarGlk(); -)
--------------------------------------------

Primero se ejecuta "Virtual machine starting"; luego se llama
internamente a
varias rutinas, entre ellas a GGRecoveryObjects(); y luego se ejecuta
"initialise memory". Es por eso que no funciona usar "Before/After
Starting Virtual Machine", porque de todas formas se ejecutará antes
de GGRecoveryObjects().

Mi "hack" mete la inicialización de Damusix justo antes de "initialise
memory", y por lo tanto, justo después de GGRecoveryObjects(). De
todas formas, la inicializaciones podría haber estado posicionada en
cualquier parte antes de los "When play begins" [pero siempre luego de
GGRecoveryObjects()], obteniendo el mismo efecto.

Espero que esto sirva de ayuda para alguien. Si esto no tiene nada que
ver, pido una disculpa. Como lo dije, no he entendido mucho de lo que
se habla en este hilo. =)

Pido disculpas por Google's translated message. xD

Saludos!

vaporware

unread,
Nov 14, 2009, 4:37:44 AM11/14/09
to
On Nov 14, 12:21 am, Eliuk Blau <eliukb...@gmail.com> wrote:
> Hello. I apologize if what I say has nothing to do with what you are
> talking about because I did not understand much (my bad English).
>
> I honestly do not see any utility to "Before Starting Virtual Machine"
> because it lets you run code *before* the Glk things that are
> initialized (windows, files, IO, etc.).
>
> That is useless, because you can not "manipulate" resources that have
> *not yet been created* (windows, sounds, for example). It will be
> ideally something like "After Starting Virtual Machine", but although
> you can use, it represents no change.

You can manipulate a Glk window that hasn't yet been created:
glk_stylehint_set() and glk_stylehint_clear() only affect windows that
are created afterward. If you want to change display styles, but you
don't want the player to see you closing and reopening windows, you
have to be able to run code before the windows are created.

vw

vaporware

unread,
Nov 14, 2009, 6:47:55 AM11/14/09
to
On Nov 13, 10:12 pm, Andrew Plotkin <erkyr...@eblong.com> wrote:

> Here, vaporware <jmcg...@gmail.com> wrote:
> > On Nov 13, 8:26 pm, Dannii <curiousdan...@gmail.com> wrote:
>
> > > I've had ideas about redoing the rulebook code entirely, as it seems
> > > very inefficient as it is now. I see no reason for each rule to be its
> > > own function, and calling so many functions is a big performance hit.
> > > At the very least the conditions for each rule should be outside the
> > > functions.
>
> > Well, rulebooks can be rearranged dynamically (via procedural rules),
> > and the same rule can be listed in multiple rulebooks. How would that
> > be implemented without making each rule its own routine?
>
> Do it as-needed, for rules that are called from multiple places. It's
> just dependency tracking.
>
> As for procedural rules -- I'm not convinced I7 needs them. The most
> common use-cases that required them have already vanished: you can now
> replace and reorder rules at compile-time.
>
> The procedural rules I see nowadays are all of the form "ignore rule X
> when condition C". But this too can be done with a compile-time rule
> replacement, which I leave to the reader as an exercise. :)

It can be, but now you're not adding a meta-rule, you're crossing out
the old rule and writing in a more intricate version. It's a less
natural, lower level expression.

I'd like to see compile-time syntax for the remaining common use
cases: "The foo rule is inactive when eating something fixed in
place", etc. Basically conditional forms of the existing rule
rearrangement statements.

> > I'm not sure about the performance hit either. In a traditional
> > interpreter, every instruction has virtually the same running time, so
> > a function call is just two extra instructions.
>
> More than zero, and I'm not so sanguine about that "vrirtually".

Fair enough. On Linux Frotz, a function call takes about as long as
removing two objects from play. It's slower than two nops,
assignments, or additions, but faster than two equality comparisons,
divisions, or object moves.

> But the real inefficiency is all the bookkeeping that the library
> needs to do to track changed rulebooks.

Indeed. I wouldn't worry about the cost of keeping rules in functions
if the overall logic can be improved instead.

vw

Erik Temple

unread,
Nov 14, 2009, 10:59:21 AM11/14/09
to
Following up on the problems with the timing of heap initialization, as
well as the point raised by Eliuk's "hack", here are some ideas for
reordering the startup routine:

(1) Initialize memory first, before the "starting the virtual machine"
activity and VM_Initialise.
(2) "After starting the virtual machine" should perhaps really come after
VM_Initialise, rather than before it. This could be done rather easily,
because the virtual machine startup activities are not true activities
(i.e., they don't respond to procedural rules.) Alternatively, another
hook could be provided.
(3) As a response to something that's popped up a few times on raif, some
folks might find it convenient if the printing of three newlines were an
explicit rule. It is currently inline I6 and can't be changed without
hacking the library. On the other hand, if you want to add more newlines,
it's easy to do in when play begins or by listing a rule at any point
after VM_Initialise.

Are there potential pitfalls with initializing memory before
VM_Initialise? It seems to work fine in my WIP, but possibly there are
some edge cases where doing so could cause problems?

--Erik

Ron Newcomb

unread,
Nov 14, 2009, 5:29:20 PM11/14/09
to
On Nov 13, 8:26 pm, Dannii <curiousdan...@gmail.com> wrote:
> I see no reason for each rule to be its own function

A rule being its own function is required for any phrase that uses a
rule, such as in many places in the language already. For example,
scheduling the egg-timer to go off is done by a phrase like:

To (R - a rule) happens in (N - a number) turns from now:

...as well as relations...

Knowledge relates a person (called thinker) to a rule (called the
intuited principle) when the intuited principle is a principle listed
in the knowledge base of the thinker.

...as well as tables...

Table of Bob's KB
principle (a rule)
block burning rule

...as well as simple variables...

if the reason the action failed is the block burning rule,

... and all of that it just off the top of my head, and doesn't even
consider what extensions such as "Repeat through a Rulebook" can do.
While efficiency is nice, I suggest a profiler, and a proposed veneer
function, rather than removing the flexibility of the language. The
major selling point of rule-based languages is flexibility.

> At the very least the conditions for each rule should be outside the
> functions.

Though I would love some introspective features, I'm less convinced of
the feasibility of this. You could wrap-up the conditions as
(another!) boolean function and stick it in an array / table / what-
have-you, but you can't hard-code it separately into the calling
rulebook because a rulebook is NOT a function, it's just an array: a
list of function-pointers, each pointing to a rule. There's nowhere
else for the conditions to go.


> On Nov 13, 10:12 pm, Andrew Plotkin <erkyr...@eblong.com> wrote:
> > As for procedural rules -- I'm not convinced I7 needs them. The most
> > common use-cases that required them have already vanished: you can now
> > replace and reorder rules at compile-time.
> > The procedural rules I see nowadays are all of the form "ignore rule X
> > when condition C". But this too can be done with a compile-time rule

Though I too am slightly in favor of cutting out the machinery needed
for procedural rules (meaning of course the stuff that makes the
"ignore" and related procedural phrases work), they still have a
purpose in that they allow conditional modifications to extensions and
the standard rules without needing to edit those modules.

Here, vaporware <jmcg...@gmail.com> wrote:
> It can be, but now you're not adding a meta-rule, you're crossing out
> the old rule and writing in a more intricate version. It's a less
> natural, lower level expression.

Agreed...

> "The foo rule is ignored when eating something fixed in place."


> Basically conditional forms of the existing rule rearrangement statements.

This would be nice, though I wonder at the work. Many rules are
already compiled -- they exist as I6 code in templates. This means
that some of the conditions for a rule would be inside its function,
and some would be outside?

Unless Graham can stick a template command like {--here-be-any-new-
rule-conditions-for:BASIC_ACCESSIBILITY_R} into all the appropriate
places in the template code. Hm.

> > But the real inefficiency is all the bookkeeping that the library
> > needs to do to track changed rulebooks.
>
> Indeed. I wouldn't worry about the cost of keeping rules in functions
> if the overall logic can be improved instead.

True.

-Ron

Ron Newcomb

unread,
Nov 14, 2009, 5:50:22 PM11/14/09
to
As a counter-point to my own post, the current way procedural rules
work, by pushing a 'rule change' onto a stack, is a handy and
efficient way for dealing with groups of rules, such as this
theoretical line:

Before planning for Bob: ignore every rule not intuited by Bob.

But I don't think Inform uses the ability in any way.

Eliuk Blau

unread,
Nov 14, 2009, 6:20:29 PM11/14/09
to
On 14 nov, 06:37, vaporware <jmcg...@gmail.com> wrote:
> You can manipulate a Glk window that hasn't yet been created:
> glk_stylehint_set() and glk_stylehint_clear() only affect windows that
> are created afterward. If you want to change display styles, but you
> don't want the player to see you closing and reopening windows, you
> have to be able to run code before the windows are created.
>
> vw

Yeah, glk_stylehint_set() and glk_stylehint_clear(), are the only Glk
commands thar operate in a non created window. Both are exceptions.

Anyway, would then be better rename the rule as "Before Default
Windows creation" instead of "Before Starting Virtual Machine",
because, for all purposes (except for the exceptions mentioned), you
need a Glk system initialized.

Eliuk.

vaporware

unread,
Nov 14, 2009, 7:37:25 PM11/14/09
to
On Nov 14, 2:29 pm, Ron Newcomb <psc...@yahoo.com> wrote:
> Here, vaporware <jmcg...@gmail.com> wrote:
> > "The foo rule is ignored when eating something fixed in place."
> > Basically conditional forms of the existing rule rearrangement statements.
>
> This would be nice, though I wonder at the work. Many rules are
> already compiled -- they exist as I6 code in templates.  This means
> that some of the conditions for a rule would be inside its function,
> and some would be outside?

Good point.

This could be a case for generating more wrapper routines. If the
author adds a condition to the basic accessibility rule, I7 could
generate a new function:

[ BASIC_ACCESSIBILITY_WRAPPER;
if (condition) rfalse;
return BASIC_ACCESSIBILITY_R();
];

And then all other mentions of BASIC_ACCESSIBILITY_R would be replaced
with BASIC_ACCESSIBILITY_WRAPPER.

For rules written in I7, the new condition could be put directly into
the rule routine.

vw

Dannii

unread,
Nov 14, 2009, 10:24:47 PM11/14/09
to

I7 knows nothing of I6 functions. The entire thing could be written
with jumps, not that it would be wise to do that.

I am not proposing reducing any of I7's flexibility and power, I am
proposing changing the I6 code it generates to improve performance
(possibly at the expense of file size). Rulebooks may not be functions
now, but that doesn't mean they couldn't be. There may be many
situations where rules should be their own functions, but that doesn't
mean a rule must be a function if it fits none of those situations.

Anyway, talk is pointless. When I get access to the I7 compiler then
I'll see about implementing some of my ideas.

Andrew Plotkin

unread,
Nov 23, 2009, 12:02:39 PM11/23/09
to
Here, Eliuk Blau <eliu...@gmail.com> wrote:
> On 14 nov, 06:37, vaporware <jmcg...@gmail.com> wrote:
> > You can manipulate a Glk window that hasn't yet been created:
> > glk_stylehint_set() and glk_stylehint_clear() only affect windows that
> > are created afterward. If you want to change display styles, but you
> > don't want the player to see you closing and reopening windows, you
> > have to be able to run code before the windows are created.
>
> Yeah, glk_stylehint_set() and glk_stylehint_clear(), are the only Glk
> commands thar operate in a non created window. Both are exceptions.
>
> Anyway, would then be better rename the rule as "Before Default
> Windows creation" instead of "Before Starting Virtual Machine",
> because, for all purposes (except for the exceptions mentioned), you
> need a Glk system initialized.

As long as I'm backing up to old Glulx discussions... I think it makes
sense to have both spots available to the author (/ extension writer).
"Before starting the virtual machine" for memory initialization and
switching the Glk system on; "before creating windows" for that stuff.

Eliuk Blau

unread,
Nov 26, 2009, 1:43:59 AM11/26/09
to
On 23 nov, 14:02, Andrew Plotkin <erkyr...@eblong.com> wrote:
> As long as I'm backing up to old Glulx discussions... I think it makes
> sense to have both spots available to the author (/ extension writer).
> "Before starting the virtual machine" for memory initialization and
> switching the Glk system on; "before creating windows" for that stuff.
>
> --Z

=) Cool.
And a *real* "After starting the virtual machine"?
(It would be very useful for future extensions like Damusix)

Saludos!
Eliuk.

0 new messages