Crash: Memory access out of range when getting a list of values from a rulebook

Hey everyone. I’ve encountered a bug while working in the weeds of Inform 7 and I’m not sure if it’s been reported before.

I tried checking the Mantis bug tracker at this link but I’m only seeing a blank page. I also found this thread detailing a similar bug but it doesn’t seem to have been resolved.

Background

I’m trying to rework the quest log in The Weight of a Soul to generate a list of texts, separated by subcategory. The subcategories are defined as Inform 7 objects. This is so that I can do fancy formatting tricks with the subcategories in Vorple (hyperlinks, tabbed pages) without having to hardcode them.

The solution I’m working with is creating a new kind of object – “objective subcategory” – and defining a rulebook, called the objective generation rules, which will produce different lists of texts depending on which objective subcategory it’s run on. This is what I have right now:

Defining objective subcategories

Section 2.3.12.4.2 - Objective Subcategories
	
An objective subcategory is a kind of object.

Every objective subcategory has some text called the heading name.

The objective generation rules is an objective subcategory based rulebook producing a list of texts.

To decide which list of objective subcategories is the list of applicable objective subcategories:
	let L be a list of objective subcategories;
	if the Reden-objectives are applicable, add the Reden-objectives to L;
	[...]
	decide on L.

Example of an objective subcategory

Section 2.3.12.4.3 - Reden Objectives

The Reden-objectives are an objective subcategory. The heading name of the Reden-objectives is "Reden".

To decide if Reden-objectives are applicable:
	if Reden Investigation is happening, decide yes;
	decide no.
	
An objective generation rule for Reden-objectives:
	let L be a list of texts;
	if the enabled of zoiro-mourning-alldone is false:
		add "[bullet] Go to Riggertown and ask Zoiro about his brother's activities" to L;
	if clue-reden-shack is true and Reden's Shack is unvisited:
		add "[bullet] Look for a place in Lower Riggertown where Reden could have stayed" to L;
	if clue-crowsnest is true:
		add "[bullet] Go to the Shanty Quarter and investigate the Crow's Nest pub" to L;
	otherwise:
		if Reden's Shack is visited:
			add "[bullet] Investigate Reden's shack in Lower Riggertown for clues" to L;
		if clue-reden-zoironest is true and Gangway is unvisited:
			add "[bullet] Find out more about a pub involving a [']bird's nest[']" to L;
	rule succeeds with result L.

Printing the list of objectives, with subcategory technology

To say journal-text-objectives:
	let current-objectives be the list of current objectives;
	if the number of entries in current-objectives is 0:
		say "I have no particular objective at the moment.";
	otherwise:
		repeat with current objective running through current-objectives:
			say "[line break][current objective]";
		let applicable-subcategories be the list of applicable objective subcategories;
		say "Found applicable subcategories.";
		repeat with applicable-subcategory running through applicable-subcategories:
			say "Checking applicable-subcategory: [applicable-subcategory].";
			let objectives-produced be the list of texts produced by the objective generation rules for the applicable-subcategory;
			say "Got objectives-produced.";
			if objectives-produced is not {}:
				say "[paragraph break][italic type][heading name of applicable-subcategory][roman type][line break]";
				repeat with current objective running through objectives-produced:
					say "[line break][current objective]";

Expected Output

The output is intended to look something like this:

Current objectives:

• Investigate the victims of the disease and identify the transmission vector

Found applicable subcategories.
Checking applicable-subcategory: Reden-objectives.
Got objectives-produced.

Reden
• Go to Riggertown and ask Zoiro about his brother's activities

Buggy Output

This is what I actually get when running the code:

Current objectives:

• Investigate the victims of the disease and identify the transmission vector

Found applicable subcategories.
Checking applicable-subcategory: Reden-objectives.

Glulxe fatal error: Memory access out of range (-7FFFFFD2)

Thoughts

The source is compiling without any errors in the IDE and the code seems to work as intended up until the interpreter crashes. I assume this is some kind of low level compiler bug, but I really have no idea what’s going on. I’m posting this here in the hope that someone who knows Inform 7 better than I do will see it and maybe help with my problem.

1 Like

It would probably help if you could post a minimal example of the code that reproduces the error.

If you can’t do that, can you identify the point of crashing in a particular rule in your objective generation rulebook? Can you pinpoint it to a particular line of that rule? That also would probably help.

2 Likes

I put together a complete story file that reproduces the bug:

"Spelling Test"

Wizard's Tower is a room.

Chapter 1 - Spells

A spell is a kind of object.
	
Xyzzy is a spell.

Chapter 2 - A spell based rulebook producing some text

The invoking rules is a spell based rulebook producing some text.

An invoking rule for xyzzy:
	say "[italic type]Running the invoking rules for xyzzy.[roman type][line break]";
	let L be "an echoing voice that mumbles 'This joke is getting old,' before disappearing in a puff of pink smoke";
	say "[italic type]Finished generating some text. The next line of code is 'rule succeeds with result L'. After this, some flavor text should be printed.[roman type][line break]";
	rule succeeds with result L.
	
Instead of thinking:
	say "As you think, you sense that some cosmic force is considering the word 'xyzzy.'";
	let invoked-text be the text produced by the invoking rules for xyzzy;
	say "After some time, a portal opens, revealing [invoked-text]."

Chapter 3 - A spell based rulebook producing a list of texts

The spelling rules is a spell based rulebook producing a list of texts.
	
A spelling rule for xyzzy:
	say "[italic type]Running the spelling rules for xyzzy.[roman type][line break]";
	let L be {"x", "y", "z", "z", "y"};
	say "[italic type]Finished generating a list of texts. The next line of code is 'rule succeeds with result L'. After this, some flavor text should be printed.[roman type][line break]";
	rule succeeds with result L.
				
Instead of jumping: 
	say "As you jump, you sense that some cosmic force is considering the word 'xyzzy.'";
	let spelled-text be the list of texts produced by the spelling rules for xyzzy;
	say "After some time, it produces the following list of texts: [spelled-text]."

Test me with "think / jump".

This produces the following:

Spelling Test
An Interactive Fiction
Release 1 / Serial number 210409 / Inform 7 build 6M62 (I6/v6.33 lib 6/12N) SD

Wizard's Tower

>test me
(Testing.)

>[1] think
As you think, you sense that some cosmic force is considering the word "xyzzy."
Running the invoking rules for xyzzy.
Finished generating some text. The next line of code is 'rule succeeds with result L'. After this, some flavor text should be printed.
After some time, a portal opens, revealing an echoing voice that mumbles 'This joke is getting old,' before disappearing in a puff of pink smoke.

>[2] jump
As you jump, you sense that some cosmic force is considering the word 'xyzzy.'
Running the spelling rules for xyzzy.
Finished generating a list of texts. The next line of code is 'rule succeeds with result L'. After this, some flavor text should be printed.

Glulxe fatal error: Memory access out of range (-7FFFFFD2)

Based on this, it looks like the issue is with a rulebook that returns a list of values – other kinds of values seem to work fine.

EDIT:

Here’s the super-minimalist version:

"Spelling Test"

Wizard's Tower is a room.

The spelling rules is a rulebook producing a list of texts.
	
A spelling rule:
	rule succeeds with result {"x", "y", "z", "z", "y"}.
				
Instead of jumping: 
	say "As you jump, you sense that some cosmic force is at work.";
	let spelled-text be the list of texts produced by the spelling rules;
	say "After some time, it produces the following list of texts: [spelled-text]."

Test me with "jump".
Spelling Test
An Interactive Fiction
Release 1 / Serial number 210409 / Inform 7 build 6M62 (I6/v6.33 lib 6/12N) SD

Wizard's Tower

>jump
As you jump, you sense that some cosmic force is at work.

Glulxe fatal error: Memory access out of range (-7FFFFFD2)
1 Like

The error seems to occur with any attempt to return a list as the result of a rule, whatever the kind of the list, even if the list is empty, and regardless of whether the result is given as a plain constant list (‘rule succeeds with result {“x”, “y”, “z”, “z”, “y”}.’) or previously assigned to a local or global variable. ‘rule succeeds.’ compiles, runs and returns an empty list (not very helpful). This does seem to be a compiler bug.

Most obvious workaround would perhaps be to assign the outcome of the rulebook to a global variable instead of trying to return it as a value from the rulebook. (‘spelled-text is a list of texts that varies’…‘The spelling rules is a spell based rulebook.’…‘A spelling rule for xyzzy: now spelled-text is {“x”, “y”, “z”, “z”, “y”}; rule succeeds.’…‘follow the spelling rules for xyzzy;’ ).

Not so elegant, but it works.

2 Likes

Digging in the I6 weeds reveals the source of the error:

RulebookSucceeds(LIST_OF_TY,BlkValueCopy(I7SFRAME, BC_162)); 

is the line returning a list of values as the result of the rulebook, BC_162 being the constant list {“x”, “y”, “z”, “z”, “y”}, the reference to which is copied to I7SFRAME and fed via RulebookSucceeds() to the following routine:

[ RecordRuleOutcome usage rule1 rule2;
	if ((latest_rule_result-->0 == RS_SUCCEEDS or RS_FAILS) &&
		(KOVIsBlockValue(latest_rule_result-->1)))
		BlkValueFree(latest_rule_result-->2);
	if ((usage == RS_SUCCEEDS or RS_FAILS) && (KOVIsBlockValue(rule1))) {
	! #### commenting this out fixes it #### 	rule2 = BlkValueCopy(BlkValueCreate(rule1), rule2);
	}
	latest_rule_result-->0 = usage;
	latest_rule_result-->1 = rule1;
	latest_rule_result-->2 = rule2;
];

where usage == RS_SUCCEEDS; rule1 == LIST_OF_TY; rule2 == I7SFRAME

the line where the error occurs (commented out above) appears to create a fresh copy of I7SFRAME and return this as the result of the rulebook. I’m not sure why this copy attempt triggers a memory error as it seems to be a fairly basic Inform function…

returning I7SFRAME itself as the result of the rulebook by commenting out the offending line superficially works, but I suspect leads to unintended consequences… This line clearly has an intended purpose.

2 Likes

Adding the following line to the I7 code allows us to trace the inner workings of the memory management system:

Include (- Constant BLKVALUE_TRACE; -) after "ICL Commands" in "Output.i6t".

Running with this gives the output:

> jump

Created: (BVs+1FFC=f+0–>Lh+0010 2**7 1 ref = {} of kind 14)

As you jump, you sense that some cosmic force is at work.

Created: (BVs+1FF8=f+0–>Lh+0090 2**7 1 ref = {} of kind 14)

Copy: (BVs+1FF8=f+0–>Lh+0090 2**7 1 ref = {} of kind 14) to equal (BV08DD4F–>L07FEC2 2**6 resident = {x, y, z, z and y} of kind 14)

Destroying (BVs+1FF8=f+0–>Lh+0090 2**7 1 ref = {} of kind 14)

Merging: 128+256+512+1024+2048+4096+8192+16384

Created: (BVh+011C–>Lh+0090 2**7 1 ref = {

At which point the memory error occurs. The copy operation is presumably the one occurring for the invocation of RulebookSucceeds (since the right side is our constant list). But then that value gets immediately destroyed again, before RecordRuleOutcome has a chance to make its own copy. Which is weird, because, as far as I can tell, no function capable of destroying BlockValues is called along the way.

(EDIT: Those last two sentences are nonsense, see below.)

2 Likes

The problem actually seems to be with BlkValueCreate(rule1) because rule1 is LIST_OF_TY, which is a weak kind representing a list of unspecified type, whereas BlkValueCreate() wants a strong kind as a parameter (in this case KD2_list_of_texts) as a parameter. Substituting ‘KD2_list_of_texts’ for ‘rule1’ here in the I6 source eliminates the error. I’m not yet able to easily see the way of finding ‘KD2_list_of_texts’ as the correct parameter programmatically…

3 Likes

With some more debugging I’ve come to a similar conclusion: The crash seems to happen because at some point, the list being created doesn’t know the kind of values it holds. (Turns out I was digging rather too deep into BlockValues.i6t… as you say, the answer is right there in the generated code.)

Debug output

(A mixture of my own print statement and Glulxe’s call stack)

RulebookSucceeds called
RecordRuleOutcome called
BlkValueCreate called
LIST_OF_TY_Create called for skov 34
BlkValueWrite called for h+0090+0 with -2147483602
BlkValueWrite called for h+0090+1 with 0
BlkValueCreateSB1 called
Created: (BVh+011C-->Lh+0090 2**7 1 ref = {BlkValueRead called for h+011C+1
BlkValueRead called for h+011C+0
Debug: Glulxe fatal error: Memory access out of range
Debug: - RT__ChLDW()  (pc=$23EB)
Debug:    x=-2147483602 ($8000002E); y=0
Debug: - KindAtomic()  (pc=$5630)
Debug:    kind=-2147483602 ($8000002E)
Debug: - LIST_OF_TY_Say()  (pc=$5DE3F)
Debug:    list=535215 ($82AAF); format=0; no_items=0; v=0; i=0; bk=0
Debug: - LIST_OF_TY_Support()  (pc=$5DA90)
Debug:    task=15 ($F); arg1=535215 ($82AAF); arg2=0; arg3=0
Debug: - BlkValueDebug()  (pc=$56CFB)
Debug:    bv=535215 ($82AAF); flag=0; refc=1; long_block=535075 ($82A23); kovs=383404 ($5D9AC), LIST_OF_TY_Support()
Debug: - BlkValueCreate()  (pc=$565BE)
Debug:    strong_kind=34 ($22); short_block=535215 ($82AAF); kovs=383404 ($5D9AC), LIST_OF_TY_Support()
Debug: - RecordRuleOutcome()  (pc=$2A62C)
Debug:    usage=1; rule1=34 ($22); rule2=507566 ($7BEAE)
Debug: - RulebookSucceeds()  (pc=$2A840)
Debug:    weak_kind=34 ($22); value=507566 ($7BEAE)
Debug: - KERNEL_4()  (pc=$1BF9F)
Debug:    (no locals)
Debug: - R_799()  (pc=$1BF5F)
Debug:    I7RBLK=0
Debug: - B362_spelling()  (pc=$2224F)
Debug:    forbid_breaks=1; rv=0
Debug: - FollowRulebook()  (pc=$2A71A)
Debug:    rulebook=362 ($16A); parameter=0; no_paragraph_skips=1; rv=139842 ($22242), B362_spelling(); ss=0; spv=0
Debug: - ResultOfRule()  (pc=$2A8E3)
Debug:    RB=362 ($16A); V=0; F=1; K=534799 ($8290F), KD2_list_of_texts[3]; a=0
Debug: - KERNEL_1()  (pc=$1276B)
Debug:    tmp_0=507570 ($7BEB2)
Debug: - R_802()  (pc=$126F3)
Debug:    I7RBLK=0
Debug: - B20_instead()  (pc=$1E54B)
Debug:    forbid_breaks=0; rv=0
... and so on ...

As for fixing this… it does seem like an I7 compiler bug as well as a potential design flaw in the template layer (and I’m surprised that nobody seems to have run into this earlier), so we’re out of luck until a new version is released. (And with Mantis off-line, there’s no telling if Graham is even aware of this issue.)

3 Likes

This goes all the way back to the original compiled call to RulebookSucceeds

RulebookSucceeds(LIST_OF_TY,BlkValueCopy(I7SFRAME, BC_162));

from which the first parameter weak kind LIST_OF_TY propagates down to BlkValueCreate(rule1) & creates the error. There would be an easy fix if there was a function to retrieve the strong kind from a list of values, since all that is required is to retrieve this from rule2 & use that as the parameter to BlkValueCreate. Surely there must be such a function, but I can’t presently see it…

3 Likes

There is, sort of. Create the typeless list first, then use LIST_OF_TY_CopyKind.

Including the following in the I7 code works around the issue, but it’s more of a hack than a proper solution in my opinion:

Include (-
Constant RS_NEITHER = 0;
Constant RS_SUCCEEDS = 1;
Constant RS_FAILS = 2;

Array latest_rule_result --> 3;

[ RecordRuleOutcome usage rule1 rule2  tmp;
    if ((latest_rule_result-->0 == RS_SUCCEEDS or RS_FAILS) &&
        (KOVIsBlockValue(latest_rule_result-->1)))
        BlkValueFree(latest_rule_result-->2);
    if ((usage == RS_SUCCEEDS or RS_FAILS) && (KOVIsBlockValue(rule1))) {
        if (rule1 == LIST_OF_TY) {
            tmp = BlkValueCreate(rule1);
            LIST_OF_TY_CopyKind(tmp, rule2); 
            BlkValueCopy(tmp, rule2);
        } else { tmp = BlkValueCopy(BlkValueCreate(rule1), rule2); }
    } else { tmp = rule2; }
    latest_rule_result-->0 = usage;
    latest_rule_result-->1 = rule1;
    latest_rule_result-->2 = tmp;
];
-) instead of "Latest Rule Result" in "Rulebooks.i6t".

(Well, it seems to work around the issue, anyways. I haven’t tested this extensively.)

(Edit: see below for a solution that is perhaps less hacky.)

3 Likes

Aha, I had tried that and thought it didn’t work, but of course in retrospect it was because I still had the Block Value trace on, which generated the memory error while it was announcing the creation of a typeless list, before the type was copied in. Doh! :roll_eyes:

I can’t see any reason why this fix wouldn’t be fully functional, unless it creates a problem to return a typeless list as latest_rule_result–>1, in which case we’re back to trying to work out how to retrieve a strong type from a list of values…

However, the only place I can see latest_rule_result–>1 is used is within RecordRuleOutcome(), to check if it is a Block Value (which still works fine for a typeless list), and to check that it’s non-zero when deciding what value to return from a rule in:

[ ResultOfRule RB V F K a;
	if (RB) FollowRulebook(RB, V, F);
	a = latest_rule_result-->0;
	if ((a == RS_FAILS) || (a == RS_SUCCEEDS)) {
		a = latest_rule_result-->1;
		if (a) return latest_rule_result-->2;
	}
	if (K) return DefaultValueOfKOV(K);
	return 0;
];

So, I think you might genuinely have fixed this :grinning:

Yeah, I did the same initially. (Which is why I called it a workaround rather than a proper solution.)

Well, if you hadn’t spelled out the root cause for me, I’d probably still be chasing down non-existent double-free bugs… :wink:

If @chinkeeyong can confirm that this works in a larger game, then I’ll see if I can get this incorporated into the 6M62 Patches extension.

2 Likes

Wow! I hadn’t expected this to be fixed so quickly.

When I have time, I’ll throw this into my dev build of The Weight of a Soul and see how it performs.

I still can’t help feeling there must be a way to retrieve the strong kind of a given list of values, which would neaten things up and allow the trace to run without crashing the interpreter, but I’m damned if I can work out how to do it…

I had another look at this and devised what seemed like a ‘cleaner’ if slightly more complex solution by moving down another layer into the undergrowth and modifying LIST_OF_TY_Create() so that it could respond to a customised call to create a list using a weak kind of LIST_OF_TY and a kind specified by the ‘spare’ short_block parameter, which is not used when creating a fresh list.

This seemed to work fine, and it got rid of the out-of-range memory crash when block value tracing is turned on, but all kinds of other weird stuff was happening when block value tracing was on. I assumed that this was a side-effect of my tinkering, but then came to realise that block value tracing is itself broken. A minimalist version of my test game without any I6 hacking or returns from rules still goes ape when block value tracing is turned on…

First the working hack and test game:

Summary
"Spelling Test"

Wizard's Tower is a room.

The larking rules is a rulebook producing a list of texts.
The thinking rules is a rulebook producing a list of numbers.
The chilling rules is a rulebook producing a list of lists of texts.

Line1 is always {"picture", "yourself", "on", "a", "boat", "on", "a", "river"}.
Line2 is always {"with", "tangerine", "trees", "and", "marmalade", "skies"}.
Line3 is always {"somebody", "calls", "you", ",", "you", "answer", "quite", "slowly"}.
Line4 is always {"a",  "girl", "with", "kaleidoscope", "eyes"}.
Lyrics is always {Line1, Line2, Line3, Line4}.

A chilling rule:
	rule succeeds with result Lyrics.

A larking rule:
	let L be a list of texts;
	let A be "[the action name part of the current action]";
	repeat with N running from 1 to the number of characters in A:
		add character number N in A to L;
	rule succeeds with result L.

A thinking rule:
	let L be a list of numbers;
	repeat with N running from 1 to 5:
		add N * N to L;
	rule succeeds with result L.
	
Instead of chilling:
	say "As conscious thought slowly drifts and dissolves, you sense that some cosmic force is at work.";
	let spelled-text be the list of lists of texts produced by the chilling rules;
	say "After some time, images begin to swirl, condense and writhe across your mind's eye:[line break]";
	repeat with N running from 1 to the number of entries in spelled-text:
		say line break;
		repeat with M running from 1 to the number of entries in entry N in spelled-text:
			say "[entry M in entry N in spelled-text]...[run paragraph on]";
	say "[paragraph break]You wake, as if from a dream...[paragraph break]";

Instead of thinking: 
	say "As you concentrate, you sense that some cosmic force is at work.";
	let spelled-text be the list of numbers produced by the thinking rules;
	say "After some time, it produces the following list of numbers: [spelled-text]."

				
Instead of jumping, waving hands or singing: 
	say "As you lark about, you sense that some cosmic force is at work.";
	let spelled-text be the list of texts produced by the larking rules;
	say "After some time, it produces the following list of texts: [spelled-text]."
	
Singing is an action applying to nothing.
Understand "sing" as singing.
Chilling is an action applying to nothing.
Understand "chill" as chilling.





Test me with "jump  / wave / sing / think / chill".

Include (-
Constant RS_NEITHER = 0;
Constant RS_SUCCEEDS = 1;
Constant RS_FAILS = 2;
! Constant BLKVALUE_TRACE=1;

Array latest_rule_result --> 3;

[ RecordRuleOutcome usage rule1 rule2;
	if ((latest_rule_result-->0 == RS_SUCCEEDS or RS_FAILS) &&
		(KOVIsBlockValue(latest_rule_result-->1)))
		BlkValueFree(latest_rule_result-->2);
	if ((usage == RS_SUCCEEDS or RS_FAILS) && (KOVIsBlockValue(rule1))) {
		if (rule1 == LIST_OF_TY) {
			rule2 =BlkValueCopy(BlkValueCreate(rule1,BlkValueRead(rule2, LIST_ITEM_KOV_F)), rule2);
		! print "Alternate creation runs...^";
		}
		else { rule2 = BlkValueCopy(BlkValueCreate(rule1), rule2); 
		! print "Standard creation runs...^";
		}
	} 
	latest_rule_result-->0 = usage;
	latest_rule_result-->1 = rule1;
	latest_rule_result-->2 = rule2;
];
-) instead of "Latest Rule Result" in "Rulebooks.i6t".

Include (-

! Fix to allow alternate creation of Lists of kind K through combination of
! 1st parameter – weak kind LIST_OF_TY
! 2nd parameter – kind K e.g. TEXT_TY
! in addition to standard call where
! 1st parameter – strong kind e.g. KD_2_list_of_texts
! 2nd parameter – zero or short block address to recycle
 
[ LIST_OF_TY_Create skov sb list;
	! print "(1) skov=", skov, " sb=",sb,"^";
	if ((skov == LIST_OF_TY)) {
		if (sb == 0){ print "**** Programming error- tried to create List of unspecified type ****^"; 
		}
		else {skov = sb; sb=0;  ! need to set sb to 0 to get new short block allocated later from BlkValueCreateSB1(sb, list)
		! print "Parameters set...^";
		jump List_of_ty_create_continue;
		} 
	} 
	skov = KindBaseTerm(skov, 0); ! standard call parameters
	.List_of_ty_create_continue;
	! print "(2) skov=", skov, " sb=",sb,"^";
	list = FlexAllocate(27*WORDSIZE, LIST_OF_TY, BLK_FLAG_MULTIPLE + BLK_FLAG_WORD);
	BlkValueWrite(list, LIST_ITEM_KOV_F, skov, true);
	BlkValueWrite(list, LIST_LENGTH_F, 0, true);
	sb = BlkValueCreateSB1(sb, list);
	return sb;
];

-) instead of "Creation" in "Lists.i6t".

(Obviously, remove commenting out of BLKVALUE_TRACE=1 to achieve an even more psychedelic experience)

And a minimalist demonstration of broken block value tracing:

Summary
"Spelling Test"

Wizard's Tower is a room.

The skylarking rules is a rulebook.

A skylarking rule:
	let L be a list of texts;
	let A be "[the action name part of the current action]";
	repeat with N running from 1 to the number of characters in A:
		add character number N in A to L;
	now lark-text is L.

lark-text is a list of texts that varies.				
Instead of jumping, waving hands or singing: 
	say "As you lark about, you sense that some cosmic force is at work.";
	Follow the skylarking rules;
	say "After some time, it produces the following list of texts: [lark-text]."
	
Singing is an action applying to nothing.
Understand "sing" as singing.

Test me with "jump  / wave / sing".

Include (-
Constant BLKVALUE_TRACE =0;
-) after "Definitions.i6t".
2 Likes

Sounds the same as this:

1 Like

I feel a need to express my admiration for this community. This is clearly a thorny problem. But @chinkeeyong gave a really excellent example of how to reproduce it, and then within one day @drpeterbatesuk and @ArdiMaster 's public collaboration generated an understanding and multiple workarounds. Yay everyone!

(OK, I’m taking on faith the understanding part, 'cause I’m going to need to learn more before I understand it!)

2 Likes

Looking into the Standard Rules, we find the following (slightly funny sounding) definition, which is the source of all our weak kind woes:

To rule succeeds with result (val - a value)
	(documented at ph_succeedswith):
	(- RulebookSucceeds({-weak-kind:rule-return-kind},{-return-value-from-rule:val}); rtrue; -) - in to only.

So we can fashion a replacement phrase that passes in the strong kind instead:

To rule succeeds with repaired result (val - a value):
	(- RulebookSucceeds({-strong-kind:rule-return-kind},{-return-value-from-rule:val}); rtrue; -) - in to only.

I’ve adapted your example from above, using the “Section in place of” syntax to replace the relevant part of the standard rules rather than actually introducing this “with repaired result” phrase. I’ve added a test case for producing just a plain number, to make sure we don’t accidentally break that.

Adapted test case
"Spelling Test"

Section 1 - Our stuff

Wizard's Tower is a room.

The larking rules is a rulebook producing a list of texts.
The thinking rules is a rulebook producing a list of numbers.
The chilling rules is a rulebook producing a list of lists of texts.
The consideration rules is a rulebook producing a number.

Line1 is always {"picture", "yourself", "on", "a", "boat", "on", "a", "river"}.
Line2 is always {"with", "tangerine", "trees", "and", "marmalade", "skies"}.
Line3 is always {"somebody", "calls", "you", ",", "you", "answer", "quite", "slowly"}.
Line4 is always {"a",  "girl", "with", "kaleidoscope", "eyes"}.
Lyrics is always {Line1, Line2, Line3, Line4}.

A consideration rule:
	rule succeeds with result 42.

A chilling rule:
	rule succeeds with result Lyrics.

A larking rule:
	let L be a list of texts;
	let A be "[the action name part of the current action]";
	repeat with N running from 1 to the number of characters in A:
		add character number N in A to L;
	rule succeeds with result L.

A thinking rule:
	let L be a list of numbers;
	repeat with N running from 1 to 5:
		add N * N to L;
	rule succeeds with result L.
	
Instead of chilling:
	say "As conscious thought slowly drifts and dissolves, you sense that some cosmic force is at work.";
	let spelled-text be the list of lists of texts produced by the chilling rules;
	say "After some time, images begin to swirl, condense and writhe across your mind's eye:[line break]";
	repeat with N running from 1 to the number of entries in spelled-text:
		say line break;
		repeat with M running from 1 to the number of entries in entry N in spelled-text:
			say "[entry M in entry N in spelled-text]...[run paragraph on]";
	say "[paragraph break]You wake, as if from a dream...[paragraph break]";

Instead of thinking: 
	say "As you concentrate, you sense that some cosmic force is at work.";
	let spelled-text be the list of numbers produced by the thinking rules;
	say "After some time, it produces the following list of numbers: [spelled-text]."

				
Instead of jumping, waving hands or singing: 
	say "As you lark about, you sense that some cosmic force is at work.";
	let spelled-text be the list of texts produced by the larking rules;
	say "After some time, it produces the following list of texts: [spelled-text]."
	
Singing is an action applying to nothing.
Understand "sing" as singing.
Chilling is an action applying to nothing.
Understand "chill" as chilling.

Considering is an action applying to nothing.
Understand "consider" as considering.

Carry out considering:
	let R be the number produced by the consideration rules;
	say "You consider [R]."

Test me with "jump  / wave / sing / think / chill / consider".

Section 2 - List Type fixes - in place of Section SR5/4/8 in Standard Rules by Graham Nelson

To make no decision
	(documented at ph_nodecision): (- rfalse; -) - in to only.
To rule succeeds
	(documented at ph_succeeds):
	(- RulebookSucceeds(); rtrue; -) - in to only.
To rule fails
	(documented at ph_fails):
	(- RulebookFails(); rtrue; -) - in to only.
To rule succeeds with result (val - a value)
	(documented at ph_succeedswith):
	(- RulebookSucceeds({-strong-kind:rule-return-kind},{-return-value-from-rule:val}); rtrue; -) - in to only.
To decide if rule succeeded
	(documented at ph_succeeded):
	(- (RulebookSucceeded()) -).
To decide if rule failed
	(documented at ph_failed):
	(- (RulebookFailed()) -).
To decide which rulebook outcome is the outcome of the rulebook
	(documented at ph_rulebookoutcome):
	(- (ResultOfRule()) -).

This seems to work fine (despite the first parameter for RulebookSucceeds being explicitly called weak_kind) as well. So, yay, another workaround, for the sake of it!

(Also, props to your poetic test cases. Mine tend to be much more utilitarian. Also also, good to know that this has already been reported.)

4 Likes

Wow, wasn’t expecting this to be emanating from the Standard Rules rather than the compiler- didn’t even think of looking there!

If it doesn’t unexpectedly break anything else, far from ‘for the sake of it’ this looks like by far the best fix yet, probably could be considered the definitive one as it seems to repair the root cause :+1:t2:

1 Like

Me neither – I just sort of assumed that this would be a phrase built into the compiler. I just stumbled across it while looking for something else.

1 Like