tag:blogger.com,1999:blog-16165898724884169732023-11-15T22:39:29.234-08:00vaporware: interactive fictionI don't make IF. I make IF better.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.comBlogger18125tag:blogger.com,1999:blog-1616589872488416973.post-53638765660081156742019-08-29T23:14:00.000-07:002019-08-29T23:14:03.317-07:00ZILF 0.9 releasedVersion 0.9 of ZILF was released recently! Highlights include easier setup for non-Windows platforms, compatibility with the unearthed Infocom source, and lots of bug fixes.<br />
<br />
Get it at <a href="http://zilf.io/" target="_blank">zilf.io</a>.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-10642340988814612272017-03-19T10:31:00.004-07:002017-03-19T10:39:07.339-07:00ZILF 0.8 releasedVersion 0.8 of ZILF is here! Highlights include compiler/assembler support for V6 games, a more complete MDL interpreter (i.e. macros can be a lot more useful), better error reporting and optimization, and lots of bug fixes.<br />
<br />
<a href="https://bitbucket.org/jmcgrew/zilf/wiki/ZILF_0.8_Release_Notes">Release Notes</a><br />
<a href="https://bitbucket.org/jmcgrew/zilf/downloads/zilf-0.8.zip">Download ZILF 0.8</a>Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-6321263806766175792017-02-05T09:00:00.000-08:002017-02-05T09:00:26.239-08:00Parsing in ZILF, part 3: From noun phrase to object<i>This is the third in a series of posts describing ZILF's parser. <a href="http://vaporwareif.blogspot.com/2015/10/parsing-in-zil-part-2-noun-phrases.html">Read part 2 here.</a></i><br />
<br />
In the second post, we covered noun phrases -- phrases like "all cubes except red" and "lamp, food, and bottle" that the player can type to refer to objects.<br />
<br />
We also covered how the parser recognizes them and how it represents them in memory: the <code>PARSE-NOUN-PHRASE</code> routine scans the player's command and fills in a <code>NOUN-PHRASE</code> structure, which holds a list of adjective/noun pairs called <code>OBJSPEC</code>s.<br />
<br />
But how does the parser identify which objects the player is referring to?<br />
<br />
After <code>MATCH-SYNTAX</code> finds a syntax line that matches the player's command, the <code>FIND-OBJECTS</code> routine combines the noun phrases with the find flag and search options from the syntax line, and decides what needs to be done for each object required by the syntax line.<br />
<br />
There are a few possibilities, depending on what the player typed. It can:<br />
<br />
<ul>
<li>match a noun phrase,</li>
<li>expand a pronoun,</li>
<li>supply a missing object,</li>
<li>ask the player to clarify,</li>
<li>or fail, printing an error message.</li>
</ul>
<div>
<br />
<a name='more'></a><br /></div>
<h2>
Matching a noun phrase
</h2>
<div>
The most obvious choice: if the player typed "shiny lamp", we want to find an object whose <code>ADJECTIVE</code> and <code>SYNONYM</code> properties contain the words <code>SHINY</code> and <code>LAMP</code>, somewhere within the player's reach.
</div>
<div>
<br /></div>
<div>
The routine <code>MATCH-NOUN-PHRASE</code> does just that. It runs through the <code>OBJSPEC</code>s included by the noun phrase -- called <code>YSPEC</code>s, "Y" for "yes" -- and for each one, it uses a <code>MAP-SCOPE</code> loop to search for objects that match the <code>YSPEC</code> and aren't excluded by any <code>NSPEC</code>s ("except" clauses). The objects that match are written into a table (<code>P-PRSOS</code> or <code>P-PRSIS</code>), where they'll eventually be used to perform the action.
</div>
<div>
<br /></div>
<h3>
Scope stages
</h3>
<div>
<code>MAP-SCOPE</code> is a powerful, flexible loop statement, which chooses a set of scope stages (from scope.zil) and runs the loop body for each object encountered in those stages.
</div>
<div>
<br /></div>
<div>
In <a href="http://vaporwareif.blogspot.com/2015/09/parsing-in-zilf-part-1-ideal-sentence.html" target="_blank">part 1</a>, we saw that the syntax line contains search options telling the parser where to look for the direct and indirect objects. Those options correspond to scope stages: if we give <code>MAP-SCOPE</code> a set of search options, it'll only use the stages corresponding to those options.
</div>
<div>
<br /></div>
<div>
When it comes to interpreting a player's command, though, the search options are really only guidelines. If the player types "pick up axe" when there are two axes available, they probably don't mean the axe they're holding, so we only want to match the one they aren't holding. But if there's only one axe and they're holding it, we want to settle for that one, so we can come up with a better error message than "You don't see that here."
</div>
<div>
<br /></div>
<div>
So the parser might need to do more than one search for the same <code>YSPEC</code>: once with the scopes suggested by the syntax line, and again with a wider set of scopes if it didn't match any objects the first time.
</div>
<div>
<br /></div>
<div>
(That's an oversimplification. The way it actually works is... complicated... in order to address some special cases. It seemed like a good idea at the time.)
</div>
<div>
<br /></div>
<h3>
Match quality and <code>INVISIBLE</code>
</h3>
<code>OBJSPEC</code>s contain an adjective and a noun, and they can match an object using either or both. In fact, since a word can be both a noun and an adjective, an <code>OBJSPEC</code> that only contains a noun can match an object's adjective.<br />
<br />
If the player types "get polish" when there's a Polish sausage and a can of shoe polish available, we assume they mean the can of shoe polish, because the command makes more grammatical sense with a noun than an adjective. But if only the sausage is available, we accept a match using only the adjective.<br />
<br />
That logic is in the <code>REFERS?</code> routine, which checks an <code>OBJSPEC</code> against an object's <code>ADJECTIVE</code> and <code>SYNONYM</code> properties and returns a match score: 0 for no match, 1 for adjective-only, 2 for noun-only, and 3 if the adjective and noun both matched. Only the highest-scoring set of objects are kept as matches: a single noun match takes precedence over all the adjective-only matches.<br />
<br />
As a special case, objects with the <code>INVISIBLE</code> flag can never match. The parser skips them without even calling <code>REFERS?</code>.<br />
<br />
<h3>
<code>OBJSPEC</code>s that don't match
</h3>
<div>
Usually, it's an error if any of the <code>OBJSPEC</code>s in a <code>NOUN-PHRASE</code> returns no matches: if the player types "get lamp and sword" when there's no sword, we assume they won't settle for just the lamp.
</div>
<div>
<br /></div>
<div>
But there's an exception for "except". If every object matched by a <code>YSPEC</code> is excluded by one of the <code>NSPEC</code>s, the parser lets it slide, so "get all swords and shields except rusty" can succeed even if every sword available is rusty. If every shield is rusty too, though, the parser will complain "There are none at all available!"
</div>
<div>
<br /></div>
<h3>
<code>GENERIC-OBJECTS</code> and pseudo-objects
</h3>
<code>GENERIC-OBJECTS</code> is like <code>GLOBAL-OBJECTS</code>, but for objects that should only match as a last resort. Typically those are concepts like <code>NUMBER</code>, or conversation topics, or placeholders for objects the player can refer to when they're not present (like an NPC they might want to follow).<br />
<br />
The parser never considers <code>GENERIC-OBJECTS</code> until a <code>YSPEC</code> fails to match in all the other scopes, at which point it enables "ludicrous scope" and starts over.<br />
<br />
<h3>
<code>ALL</code> by itself
</h3>
<div>
Commands with no <code>YSPEC</code>s, like "take all" and "drop all but lamp", form a special case that skips most of the steps above.
</div>
<div>
<br /></div>
<div>
In this case, the parser calls <code>MAP-SCOPE</code> to find everything in the player's reach, but instead of <code>REFERS?</code>, it calls <code>ALL-INCLUDES?</code> to filter out objects that "all" shouldn't apply to. That means <code>INVISIBLE</code> objects and the <code>WINNER</code> for sure. For <code>TAKE</code> and <code>DROP</code>, it also skips any objects that need special care to pick up (ones with <code>TRYTAKEBIT</code>) or can't be picked up at all (ones missing <code>TAKEBIT</code>).
</div>
<div>
<br /></div>
<div>
Also, in this case, <code>GLOBAL-OBJECTS</code> and <code>GENERIC-OBJECTS</code> are always skipped. We assume the player means all the <i>nearby</i> objects, not their hands, the sun, or conversation topics. <code>LOCAL-GLOBALS</code> can still match.
</div>
<div>
<br /></div>
<h3>
Returning the matches
</h3>
<div>
If an error occurs in any step above, <code>MATCH-NOUN-PHRASE</code> returns zero after printing an error message.
</div>
<div>
<br /></div>
<div>
If a single object was matched, it returns that object.
</div>
<div>
<br /></div>
<div>
If multiple objects were matched, a few things can happen:
</div>
<div>
<ul>
<li>If the mode is "all" <i>or</i> if the player clearly asked for more than one object (by providing multiple <code>OBJSPEC</code>s), it returns the placeholder <code>MANY-OBJECTS</code>, which tells <code>PERFORM</code> to repeat the action for all the objects in <code>P-PRSOS</code> or <code>P-PRSIS</code>.</li>
<li>If the mode is "any", it picks a random object from the set of matches, prints a clarifying message, and returns that object.</li>
<li>If any of the matched objects have a <code>GENERIC</code> function, it calls them all until one of them returns an object.</li>
<li>If none of that worked, it asks the player to clarify which matched object they mean, and puts the command on hold until they answer. (This is what most languages call <i>disambiguation</i>. In ZIL, it's called <i>orphaning</i>, and we'll cover it in more detail in a later post.)</li>
</ul>
<div>
That's all for <code>MATCH-NOUN-PHRASE</code>. But what if the player didn't name an object at all?
</div>
<div>
<br /></div>
</div>
<h2>
Expanding a pronoun
</h2>
<div>
A noun phrase can also just be a pronoun. Before <code>MATCH-NOUN-PHRASE</code> is called, <code>EXPAND-PRONOUN</code> gets a chance to recognize the pronoun and load the appropriate list of objects.
</div>
<div>
<br /></div>
<div>
The pronouns are defined in pronouns.zil, and the list of objects for each pronoun is saved either by the parser (which calls <code>SET-PRONOUNS</code> after a successful parse), or by explicitly calling <code>THIS-IS-IT</code> or <code>CONTENTS-ARE-IT</code> to make the pronouns refer to something the game mentioned.
</div>
<div>
<br /></div>
<div>
Since the objects or the player may have moved since the list was written, <code>EXPAND-PRONOUN</code> also makes sure the player can still see them.
</div>
<div>
<br /></div>
<h2>
Supplying a missing object
</h2>
<div>
Sometimes the syntax line that comes closest to matching the player's command still isn't a perfect match, because the command is incomplete.
</div>
<div>
<br /></div>
<div>
For example, if the player types "enter" by itself, the only syntax line for that verb requires an object, so the parser needs to find an object to put there:
</div>
<blockquote class="tr_bq">
<code><SYNTAX ENTER OBJECT (FIND DOORBIT) (IN-ROOM) = V-ENTER></code>
</blockquote>
<div>
The <code>GWIM</code> routine ("Get What I Mean") uses the find flag and search options to look for a single object that matches. If there's <i>exactly one</i> object with that flag (<code>DOORBIT</code>) in that scope (<code>IN-ROOM</code>), <code>GWIM</code> prints a clarifying message and uses that object as the missing noun. Otherwise, it fails, and <code>FIND-OBJECTS</code> orphans the command after asking "What do you want to enter?"<br />
<br />
The find flag is optional, but important. If it's omitted, <code>GWIM</code> can only succeed if there's only one object <i>at all</i> in that scope.</div>
<div>
<br /></div>
<h3>
<code>KLUDGEBIT</code>
</h3>
<div>
<code>GWIM</code> is also called for perfectly good commands, when they happen to end with a preposition.
</div>
<div>
<br /></div>
<div>
Recall from part 1 that every preposition in a syntax line has to be associated with a noun phrase. According to ZIL's primitive concept of sentences, "turn lamp off" isn't a valid sentence, and neither is "take inventory" (since "inventory" isn't a noun phrase, it's fixed syntax). But of course we want to support those syntaxes. The solution is to put the extra object slot in the syntax line, and mark it with a special find flag:
</div>
<blockquote class="tr_bq">
<code><SYNTAX TURN OBJECT (FIND DEVICEBIT) OFF OBJECT (FIND KLUDGEBIT) = V-TURN-OFF></code>
</blockquote>
Now "turn lamp off" is parsed as if it's missing the second noun phrase, and when <code>GWIM</code> is called for it, it sees the <code>KLUDGEBIT</code> and silently fills in a special object for <code>PRSI</code>: <code>ROOMS</code>, which the player could never refer to otherwise.<br />
<br />
This doesn't stop the player from providing a second noun phrase if they want to, which can be a mixed blessing. For example, the same syntax line that uses <code>KLUDGEBIT</code> to match "put brick down" could also match "put brick down chute"... but if the verb code isn't written to handle both uses, the second noun will simply be ignored.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-32265717673250022812016-07-19T11:51:00.000-07:002016-07-19T11:51:01.443-07:00It's an amazing smile, even the suit has teeth<i>(Note: </i>vaporware: interactive fiction<i> normally sticks to technical topics, but I'm making an exception to respond to a post directed at me that doesn't allow comments.)</i><br />
<br />
In a way, it's flattering. Pillars of the IF community like Zarf and Emily Short have had detractors following them around for years, sniping at them in one forum after another. Now, I finally have one of my own.<br />
<br />
And mine knows how to hold a grudge.<br />
<br />
Last week, that grudge led him to write a number of false, demeaning things about me. Most of those were on a forum thread, which has since been expunged for code-of-conduct violations. Some were on a syndicated blog, which is why (with some regret) I'm responding here.<br />
<br />
This all started about a year ago, when I made a flippant remark in an interactive fiction chat room run by this person in response to a bit of language policing: after one person made an announcement to the room, addressing it to "you guys [and] ladies", a second person took offense to this idiom for addressing a group and chided him to be more inclusive. I responded, sarcastically, that I too was offended because the second person's language theoretically still left some people out.<br />
<br />
The moderator of this chat room warned me that such comments were unwelcome there, so I didn't repeat them. I wasn't banned, but I haven't returned there since last year, because there are a number of things I find unappealing about that environment, including a clumsy web interface. <a href="http://ifmud.port4000.com/" target="_blank">ifMUD</a> is more welcoming, more usable, <i>and</i> it has a parrot.<br />
<br />
I haven't really thought about that episode again until this weekend, when I saw his posts, which were prompted by a discussion in which other people mentioned feeling alienated in that chat room as a result of hostile interactions with the same moderator.<br />
<br />
I guess he's been stewing over it all year, because today, his account of it is couched in so much hyperbole it's barely recognizable: misrepresenting the content of our exchange, assigning me motivations that are insulting and divorced from reality, and abusing words like "deliberately" and "explicitly" to dress up false assumptions in the language of impartial observations. He even accuses me of an unexplained "pattern" of "nasty behavior" that, from what I can tell, refers to a couple times over the years when I've defended critics and competition judges against attacks very similar to the ones he's making now.<br />
<br />
I won't respond point-by-point here, partly because those attacks don't deserve any more airtime, and partly because I hope this was caused by misunderstanding rather than malice. I'd rather not burden him with being forever linked to those remarks if he chooses to retract them.<br />
<br />
He could have reached out to me at any time in the past year to clear this up, but maybe he's been busy. I know when I get into the groove working on <a href="https://www.guncho.com/" target="_blank">Guncho</a> or <a href="https://bitbucket.org/jmcgrew/zilf/wiki/Home" target="_blank">ZILF</a>, it can be hard to find time for anything else besides sleep and work, so this could be a sign that something great is in the works.<br />
<br />
<i>(Now back to the usual topics.)</i>Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-83845763211731399642015-11-30T10:50:00.000-08:002015-11-30T10:52:53.421-08:00New optimizations coming in ZILF 0.8<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86AkVtCjbcWYO3LYeCCM4eIZ8mBs6XBGSK8Rf13Ukd9W76lEyI-DE7QVDd6oFC-t5NWVMooaCuxzygamnLbaCRCAQ7jBJAakWj90QSE_8CbHYQKXGnRgZ2JQwXPAl_DZupARjYDrCFNQ/s1600/zilf-optimized-code.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86AkVtCjbcWYO3LYeCCM4eIZ8mBs6XBGSK8Rf13Ukd9W76lEyI-DE7QVDd6oFC-t5NWVMooaCuxzygamnLbaCRCAQ7jBJAakWj90QSE_8CbHYQKXGnRgZ2JQwXPAl_DZupARjYDrCFNQ/s400/zilf-optimized-code.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Left: Code generated by ZILF 0.7 (11 instructions).<br />
Right: Code after new optimizations (1 instruction).</td></tr>
</tbody></table>
<br />
<br />
<div>
How ZILF turns the nasty code on the left into a single instruction, using three new optimizations:</div>
<div>
<br /></div>
<div>
<div>
==1==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: red;">BAND STACK,2048 >STACK</span></span></div>
<div>
<span style="color: red; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>BAND STACK,32767 >STACK</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK /?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L13</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L7</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6: <span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L7:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<br /></div>
<div>
Combine sequential bitwise operations (this is new). BAND STACK,32767 goes away, because 2048 & 32767 == 2048.</div>
<div>
<br /></div>
<div>
==2==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: red;">BAND STACK,2048 >STACK</span></span></div>
<div>
<span style="color: red; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK /?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L13</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L7</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L7: <span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<br /></div>
<div>
Convert single-bit BAND+ZERO? test to BTST (this is new). BAND STACK,2048 >STACK followed by ZERO? STACK /?L12 becomes BTST STACK,2048 \?L12, because 2048 is a power of two.</div>
<div>
<br /></div>
<div>
==3==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: red;">PUSH 1</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L13</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L7</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L7: <span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<br /></div>
<div>
Bypass controlled conditional branch (this is new). PUSH 1 followed by a JUMP to ZERO? STACK \?L6 becomes JUMP ?L6, because we know the negative ZERO? branch will be taken.</div>
<div>
<br /></div>
<div>
==4==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L13</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: red;">PUSH 0</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L7</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6: <span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L7: <span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<br /></div>
<div>
Bypass controlled conditional branch. PUSH 0 followed by ZERO? STACK \?L6 becomes a JUMP to the new ?L15 (the instruction after ZERO?), because we know the negative branch won't be taken.</div>
<div>
<br /></div>
<div>
==5==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L13</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L15</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L15:<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: red;">PUSH 1</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L7</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L7:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<br /></div>
<div>
Bypass controlled conditional branch. PUSH 1 followed by a JUMP to ZERO? STACK \FALSE becomes RFALSE, because we know the negative branch will be taken.</div>
<div>
<br /></div>
<div>
==6==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L13</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L15</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L15:<span class="Apple-tab-span" style="white-space: pre;"> </span>RFALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L7</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: red;">PUSH 0</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L7:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<br /></div>
<div>
Bypass controlled conditional branch. PUSH 0 followed by ZERO? STACK \FALSE becomes a JUMP to the new ?L16, because we know the negative branch won't be taken.</div>
<div>
<br /></div>
<div>
==7==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><strike>JUMP ?L13</strike></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L15</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><strike>?L13:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \?L6</strike></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L15:<span class="Apple-tab-span" style="white-space: pre;"> </span>RFALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><strike>JUMP ?L7</strike></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:<span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L16</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><strike>?L7:</strike><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L16:<span class="Apple-tab-span" style="white-space: pre;"> </span>...</span></div>
<div>
<br /></div>
<div>
Eliminate dead code and unused labels.</div>
<div>
<br /></div>
<div>
==8==</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \?L12</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;"><strike>JUMP ?L15</strike></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><strike>?L15:</strike></span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">RFALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6: </span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">JUMP ?L16</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L16:<span class="Apple-tab-span" style="white-space: pre;"> </span>...</span></div>
</div>
<div>
<br /></div>
<div>
Eliminate branch to next instruction. JUMP ?L15 goes away; ?L15 merges with ?L12.</div>
<div>
<br /></div>
<div>
==9==</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 <span style="color: red;">\?L12</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">RFALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">JUMP ?L16</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L16:<span class="Apple-tab-span" style="white-space: pre;"> </span>...</span></div>
</div>
<div>
<br /></div>
<div>
Disintermediate branch. BTST STACK,2048 \?L12 becomes BTST STACK,2048 \FALSE, because the instruction at ?L12 is RFALSE.</div>
<div>
<br /></div>
<div>
==10==</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP <span style="color: red;">?L6</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L12:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">RFALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L6:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">JUMP ?L16</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L16:<span class="Apple-tab-span" style="white-space: pre;"> </span>...</span></div>
</div>
<div>
<br /></div>
<div>
Disintermediate branch. JUMP ?L6 becomes JUMP ?L16, because the instruction at ?L6 is JUMP ?L16.</div>
<div>
<br /></div>
<div>
==11==</div>
<div>
<br /></div>
<div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L16</span></div>
<div>
<strike><span style="font-family: "courier new" , "courier" , monospace;">?L12:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">RFALSE</span></strike></div>
<div>
<strike><span style="font-family: "courier new" , "courier" , monospace;">?L6:</span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">JUMP ?L16</span></strike></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><strike>ZERO? STACK \FALSE</strike></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L16:<span class="Apple-tab-span" style="white-space: pre;"> </span>...</span></div>
</div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<br /></div>
<div>
Eliminate dead code and unused labels.</div>
<div>
<br /></div>
<div>
==12==</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \FALSE</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><strike>JUMP ?L16</strike></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><strike>?L16:</strike></span><span class="Apple-tab-span" style="font-family: "courier new" , "courier" , monospace; white-space: pre;"> </span><span style="font-family: "courier new" , "courier" , monospace;">...</span></div>
</div>
<div>
<br /></div>
<div>
Eliminate branch to next instruction. JUMP ?L16 goes away.</div>
<div>
<br /></div>
<div>
==13==</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">?L14:<span class="Apple-tab-span" style="white-space: pre;"> </span>BTST STACK,2048 \FALSE</span></div>
</div>
<div>
<br /></div>
Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com6tag:blogger.com,1999:blog-1616589872488416973.post-28719261719292668002015-10-25T12:49:00.000-07:002017-02-05T11:01:09.322-08:00Parsing in ZIL, part 2: Noun phrases<i>This is the second in a series of posts describing ZILF's parser. <a href="http://vaporwareif.blogspot.com/2015/09/parsing-in-zilf-part-1-ideal-sentence.html">Read part 1 here</a>, or <a href="http://vaporwareif.blogspot.com/2017/02/parsing-in-zilf-part-3-from-noun-phrase.html">read part 3 here</a>.</i><br />
<br />
In the first post, we covered the basic structure of a command: verbs, prepositions, and noun phrases. Recognizing a verb or preposition is easy; it's just a word tagged with that part of speech. Once the parser has the verb and prepositions, it uses them to find a matching grammar line. But how does it recognize noun phrases, and what does it do with them?<br />
<br />
<h3>
What is a noun phrase, anyway?</h3>
Here are some sample commands, with the noun phrases in bold:<br />
<blockquote class="tr_bq">
throw <b>axe</b> at <b>dwarf</b><br />
get <b>shiny brass lamp, tasty food, and bottle</b><br />
drop <b>any</b><br />
get <b>set of keys</b><br />
get <b>all except sign</b><br />
put <b>all cubes except red and blue</b> in <b>the chute</b></blockquote>
A noun phrase is anything the player can type to identify the object of a command. It can refer to a single object, like "axe"; a list of objects, like "lamp, bottle, and food"; a set of objects answering to the same name, like "cubes"; or a set of objects implicitly defined by what the player can see, like "all" or "any". It can exclude objects with "except" or "but". And it can quantify what you mean when there's more than one similar object: do you want all of them, a specific one, or any random one?<br />
<br />
The grammar of an acceptable noun phrase is, more or less:<br />
<blockquote class="tr_bq">
[all/any] [article] [adjective...] [noun]</blockquote>
The individual parts are optional, but at least one must be given. Larger phrases can also be made by stringing small ones together with commas or words like "and", "except", "but", and "of":<br />
<blockquote class="tr_bq">
[noun phrase], [noun phrase] and [noun phrase] of [noun phrase] except [noun phrase] and [noun phrase]</blockquote>
When the parser encounters a noun phrase, it records the important parts in a data structure called <code>NOUN-PHRASE</code>, which it uses later to find matches within the set of objects available to the player. The structure itself is very simple, containing only a "mode", a list of adjective/noun pairs (called <code>OBJSPEC</code>s) to include, and another list of <code>OBJSPEC</code>s to exclude.<br />
<br />
Let's see what the parser does with some of the noun phrases from above:<br />
<br />
<h3>
"axe"</h3>
<table>
<tbody>
<tr>
<th style="text-align: left;">Mode</th>
<td>default</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Include</th>
<td>(*, <code>AXE</code>)</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Exclude</th>
<td>none</td>
</tr>
</tbody>
</table>
<br />
Simple enough: we're just looking for an object that lists <code>AXE</code> in its <code>SYNONYM</code> property. We're using the default match mode, which means if there's more than one "axe" in sight, the parser will ask which one you mean.
<br />
<br />
<h3>
"shiny brass lamp, tasty food, and bottle"</h3>
<table>
<tbody>
<tr>
<th style="text-align: left;">Mode</th>
<td>default</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Include</th>
<td>(<code>SHINY</code>, <code>LAMP</code>)<br />
(<code>TASTY</code>, <code>FOOD</code>)<br />
(*, <code>BOTTLE</code>)</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Exclude</th>
<td>none</td>
</tr>
</tbody>
</table>
<br />
This time we have a few pairs in the Include list, and two of them have adjectives. The parser will look for objects with <code>SHINY</code> in their <code>ADJECTIVE</code> properties <i>and</i> <code>LAMP</code> in their <code>SYNONYM</code> properties. Likewise for <code>TASTY</code> and <code>FOOD</code>. Notice that <span style="font-family: monospace;">BRASS</span> is nowhere to be seen -- the parser only remembers one adjective per <span style="font-family: monospace;">OBJSPEC</span>.<br />
<br />
But <code>BOTTLE</code> can be surprising in a game like Adventure where "bottle" and "bottled water" are both objects. Because of Z-machine limits, "bottle" and "bottled" are treated as the same word: if the player types "get bottled", referring to the bottled water by its adjective, to the parser it looks the same as "get bottle", referring to the bottle by its noun. The parser needs to support both.
So even though the structure lists <code>BOTTLE</code> as a noun, the parser will see there's no adjective paired with it and notice that <code>BOTTLE</code> can also be used as an adjective, and it'll look for it in the <code>ADJECTIVE</code> property as well. This is a lower-quality match, so if another nearby object has the word in its <code>SYNONYM</code> property, the parser will pick that one instead.
<br />
<br />
<h3>
"set of keys"</h3>
<table>
<tbody>
<tr>
<th style="text-align: left;">Mode</th>
<td>default</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Include</th>
<td>(*, <code>KEYS</code>)</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Exclude</th>
<td>none</td>
</tr>
</tbody>
</table>
<br />
Hey, what happened to <code>SET</code>?<br />
<br />
Object names containing "of" are special. <code>SET</code> and <code>KEYS</code> are both in the object's <code>SYNONYM</code> property, but the parser only thinks about one adjective and one noun at a time, so they can't both be used. Adjacent nouns normally belong to separate noun phrases, e.g. "give troll money".<br />
<br />
However, when the parser sees "of" in a noun phrase, it forgets the last noun it saw! So "set of keys" becomes simply "keys". This makes the parser's job easier, but at the cost of being unable to know whether the player wants the set of keys, the pile of keys, or the stack of boxes of pictures of keys. (It can still tell them apart by asking questions, as we'll see in the post on orphaning.)
<br />
<br />
<h3>
"any"</h3>
<table>
<tbody>
<tr>
<th style="text-align: left;">Mode</th>
<td>any</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Include</th>
<td>none</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Exclude</th>
<td>none</td>
</tr>
</tbody>
</table>
<br />
This time, we didn't give any nouns or adjectives, only a mode. Since the Include list is empty, the parser will choose from all available objects, and since the match mode is "any", it'll choose a random object if there's more than one match.
<br />
<br />
<h3>
"all except sign"</h3>
<table>
<tbody>
<tr>
<th style="text-align: left;">Mode</th>
<td>all</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Include</th>
<td>none</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Exclude</th>
<td>(*, <code>SIGN</code>)</td>
</tr>
</tbody>
</table>
<br />
Again, the Include list is empty, so the parser chooses from all available objects, then excludes any object with the word <code>SIGN</code> in its <code>SYNONYM</code> property. Since the match mode is "all", if there's more than one "sign" available, the parser will <strike>include</strike> exclude all of them.
<br />
<br />
<h3>
"all cubes except red and blue"</h3>
<table>
<tbody>
<tr>
<th style="text-align: left;">Mode</th>
<td>all</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Include</th>
<td>(*, <code>CUBES</code>)</td>
</tr>
<tr style="vertical-align: top;">
<th style="text-align: left;">Exclude</th>
<td>(<code>RED</code>, *)<br />
(<code>BLUE</code>, *)</td>
</tr>
</tbody>
</table>
<br />
This time, the Include and Exclude lists are both present. The parser will find all available objects with <code>CUBES</code> in their <code>SYNONYM</code> property, then exclude any that have <code>RED</code> or <code>BLUE</code> in their <code>ADJECTIVE</code> property.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com1tag:blogger.com,1999:blog-1616589872488416973.post-5385149360264504432015-10-01T12:34:00.001-07:002015-10-05T09:44:12.923-07:00ZILF 0.7 and Adventure<div dir="ltr">
<b>Update</b>: IF Archive links: <a href="http://mirror.ifarchive.org/if-archive/infocom/compilers/zilf/zilf-0.7.zip">ZILF 0.7</a>, <a href="http://mirror.ifarchive.org/if-archive/games/zcode/advent.z3">Advent.z3</a>.<br />
<br />
<strike>The IF Archive upload is still being processed, but for now you can</strike> <a href="https://bitbucket.org/jmcgrew/zilf/downloads/zilf-0.7.zip">download ZILF 0.7 from Bitbucket</a>. (Or <a href="https://bitbucket.org/jmcgrew/zilf/wiki/ZILF_0.7_Release_Notes">read the release notes</a>.)</div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Among other things, this version includes a new sample project: a port of <i>Adventure</i>, aka <i>Colossal Cave</i>. <a href="http://mirror.ifarchive.org/if-archive/games/zcode/advent.z3">Download Advent.z3</a> to play!</div>
Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-83220224853489239192015-09-20T10:12:00.000-07:002015-10-25T12:49:55.004-07:00Parsing in ZILF, part 1: The ideal sentence<div>
<i>This is the first in a series of posts describing ZILF's parser. <a href="http://vaporwareif.blogspot.com/2015/10/parsing-in-zil-part-2-noun-phrases.html">Read part 2 here.</a></i></div>
<div>
<br /></div>
<div>
Writing one's own interactive fiction language is a <a href="http://www.ifwiki.org/index.php/Category:Authoring_system">popular pastime</a>, but people who attempt it quickly find that the language itself is the easy part. The hard part is fleshing out the parser and world model to the standards that players have come to expect.</div>
<div>
<br /></div>
<div>
We've come a long way since Colossal Cave and its two-word sentences ("get lamp", "throw axe"). Most modern games understand things like:</div>
<div>
<ul>
<li>Prepositions (the "up" in "pick up lamp")</li>
<li>Different actions for the same verb word ("look at" vs. "look in" vs. "look under")</li>
<li>Commands with two or more noun phrases ("put coin in slot")</li>
<li>Complex noun phrases ("take key, lamp, and all cubes except the red cube")</li>
<li>Multiple commands per line ("north. west. open door then go in.")</li>
<li>Ordering ("watson, take the prints to the crime lab")</li>
<li>Disambiguation ("take polish" => "Which do you mean, the shoe polish or the Polish sausage?")</li>
<li>Implied nouns ("take" by itself when there's only one thing to pick up)</li>
<li>Correcting mistakes ("oops") and repeating commands ("again")</li>
</ul>
</div>
<div>
In fact, even in the 80s, Infocom's games understood those, so ZILF really should as well.</div>
<div>
<br /></div>
<div>
In this series, I'll explain how ZILF deals with all of the above, but for now let's look at a simple command like "pick up lamp". Here's the source code that defines the syntax for that command:</div>
<div>
<blockquote class="tr_bq">
<code><SYNTAX PICK UP OBJECT (FIND TAKEBIT) (MANY ON-GROUND IN-ROOM) = V-TAKE></code></blockquote>
</div>
<div>
Look at the parts in turn:</div>
<div>
<ul>
<li><code>PICK</code>: The verb word. For this syntax to take effect, the verb in the player's command has to be "pick" (or one of its synonyms, if it had any). The compiler assigns a new <i>verb number</i> to "pick", which is given the constant name <code>ACT?PICK</code>; this would also become the verb number of any synonyms.</li>
<li><code>UP</code>: A preposition. The compiler assigns a <i>preposition number</i> to "up", the constant <code>PR?UP</code>.</li>
<li><code>OBJECT</code>: Shows how many objects the command wants. <code>OBJECT</code> can appear zero, one, or two times in the syntax line; since it appears once here, this is a one-object command.</li>
<li><code>(FIND TAKEBIT)</code>: The "find flag", hinting at <i>what kind</i> of objects the command prefers.</li>
<li><code>(MANY ON-GROUND IN-ROOM)</code>: The "search options", hinting at <i>where</i> and <i>how</i> the command prefers to find its objects.</li>
<li><code>V-TAKE</code>: The name of the action routine, which contains the code that implements taking. The compiler assigns an <i>action number</i> based (mostly) on the name of the action routine, in this case the constant <code>V?TAKE</code>; the action number identifies <i>what the player wants to do</i>. There might be many different syntaxes — "get lamp", "take lamp" — that all use different verbs to represent the same action. (And yes, unfortunately, <code>V</code> for action numbers and <code>ACT</code> for verb numbers is the opposite of what you'd expect.)</li>
</ul>
</div>
<div>
Notice that nothing in that line says exactly which words have to appear in which order, and in fact the parser will accept "lamp pick up"! Unlike <a href="http://inform-fiction.org/manual/html/s30.html">Inform</a>, ZILF doesn't define a precise grammar for each command; it has a general idea of <i>what makes a sentence</i>, and it uses its rudimentary knowledge of English to extract the parts of a command and fit them into a pattern.<br />
<br />
So, what makes a sentence? A verb, a preposition, a noun phrase, another preposition, and another noun phrase. Everything except the verb is optional, but there are a few rules: you can't have a second noun phrase without a first noun phrase, each preposition needs a corresponding noun phrase, and two or more prepositions in a row collapse into one (so "look up in air" becomes "look up air").<br />
<br />
The parser looks for a verb, extracts the prepositions and noun phrases from the player's command to fit them into the ideal sentence pattern, then searches through all the syntax lines for that verb, hoping to find one that has the same prepositions and number of noun phrases that the player typed. If it finds one, it can move on to identifying which "lamp" the player is talking about.<br />
<br />
If that doesn't work, the parser may still be able to find a syntax line that can be made to work with more information, in which case it'll ask a clarifying question ("What do you want to pick up?") and <i>orphan</i> the command, keeping it around just long enough to see if the next input gives more information. Or it may decide that none of the syntaxes can possibly match and issue an error.</div>
<div>
<br /></div>
<div>
[Update: Corrected the name of the preposition number constant.]</div>
<ul><ul>
</ul>
</ul>
Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com4tag:blogger.com,1999:blog-1616589872488416973.post-89825176988384117752014-05-31T23:15:00.001-07:002014-05-31T23:18:53.455-07:00Generating better code in ZILF 0.5I've done a lot of work on the code generator and optimizer for the upcoming version of ZILF. Here's one example of how the generated code has been improved.<br />
<br />
<div>
Parser.zil has a routine called TAKE-CONT-SEARCH, which helps V-TAKE determine whether the object being taken is in a container:</div>
<div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"><ROUTINE TAKE-CONT-SEARCH (A "AUX" H) <span class="Apple-tab-span" style="white-space: pre;"> </span></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <OBJECTLOOP I .A</span><br />
<span style="font-family: Courier New, Courier, monospace;"> <COND (<OR <FSET? .I ,CONTBIT> <==? .I ,WINNER>></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <COND (<IN? ,PRSO .I></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <SET H .I></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <RETURN>)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (ELSE</span><br />
<span style="font-family: Courier New, Courier, monospace;"> <SET H <TAKE-CONT-SEARCH .I>></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <AND .H <RETURN>>)>)>></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <AND .H <RETURN .H>>></span></blockquote>
In ZILF 0.4, that turned into 14 instructions:<br />
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"> <span style="white-space: pre;">.FUNCT TAKE-CONT-SEARCH,A,H,I
FIRST? A >I /?L1
?L1: ZERO? I /?L3
FSET? I,CONTBIT /?L8
EQUAL? I,WINNER \?L12
?L8: IN? PRSO,I \?L9
SET 'H,I
JUMP ?L3
?L9: CALL TAKE-CONT-SEARCH,I >H
ZERO? H \?L3
?L12: NEXT? I >I /?L1
JUMP ?L1
?L3: ZERO? H \?L18
RETURN H
?L18: RETURN H</span></span> </blockquote>
In ZILF 0.5, it's only 11 after eliminating a ZERO?, a JUMP, and the duplicate RETURN:<br />
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.FUNCT TAKE-CONT-SEARCH,A,H,I<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>FIRST? A >I \?L3<br />?L19:<span class="Apple-tab-span" style="white-space: pre;"> </span>FSET? I,CONTBIT /?L8<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>EQUAL? I,WINNER \?L12<br />?L8:<span class="Apple-tab-span" style="white-space: pre;"> </span>IN? PRSO,I \?L9<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>SET 'H,I<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L3<br />?L9:<span class="Apple-tab-span" style="white-space: pre;"> </span>CALL TAKE-CONT-SEARCH,I >H<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? H \?L18<br />?L12:<span class="Apple-tab-span" style="white-space: pre;"> </span>NEXT? I >I /?L19<br />?L3:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? H /FALSE<br />?L18:<span class="Apple-tab-span" style="white-space: pre;"> </span>RETURN H</span></blockquote>
</div>
Here's another example. The REFERS? routine checks whether a noun, or adjective-noun pair, matches the vocabulary of some object:<br />
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"><ROUTINE REFERS? (A N O)<br /> <AND <OR <0? .A> <IN-PB/WTBL? .O ,P?ADJECTIVE .A>><br /> <IN-PWTBL? .O ,P?SYNONYM .N>>></span></blockquote>
<div>
The ZIL code is pretty simple, but ZILF 0.4 handled it poorly, needing 12 instructions and a temporary variable:</div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.FUNCT REFERS?,A,N,O,?TMP<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? A /?L5<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 0<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L6<br />?L5:<span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH 1<br />?L6:<span class="Apple-tab-span" style="white-space: pre;"> </span>POP '?TMP<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? ?TMP /?L4<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>PUSH ?TMP<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>JUMP ?L3<br />?L4:<span class="Apple-tab-span" style="white-space: pre;"> </span>CALL IN-PBTBL?,O,P?ADJECTIVE,A >STACK<br />?L3:<span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK /FALSE<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>CALL IN-PWTBL?,O,P?SYNONYM,N >STACK<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>RSTACK</span></blockquote>
<div>
Nasty! ZILF 0.5 does it in five instructions, eliminating the temp variable and a whole lot of nonsense:</div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.FUNCT REFERS?,A,N,O<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? A /?L2<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>CALL IN-PBTBL?,O,P?ADJECTIVE,A >STACK<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>ZERO? STACK /FALSE<br />?L2:<span class="Apple-tab-span" style="white-space: pre;"> </span>CALL IN-PWTBL?,O,P?SYNONYM,N >STACK<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>RSTACK</span></blockquote>
<div>
Much of the benefit comes from better understanding the relationship between values and conditions, such as knowing when a value is immediately going to be tested against zero, or which branch instructions are testing conditions that have already been tested. The Cloak example is smaller by more than 200 bytes thanks to these optimizations.</div>
Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-54941225629216774072014-05-07T07:44:00.004-07:002014-05-07T07:44:57.658-07:00Inform 7 version 6L02 is outAfter a long wait, the new version of Inform 7 is out. Pick it up at <a href="http://inform7.com/">inform7.com</a>.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-24236606191155690652013-05-31T08:43:00.002-07:002013-05-31T08:43:43.543-07:00ZLR extended version supportZLR is slowly but surely gaining support for more Z-machine versions. The latest code now works, at least partially, with all versions except V6.<br />
<br />
The differing screen models are always fun. V3 has a top window that you can't do much with, and the status line is <i>not</i> counted as one of its lines. V4 is pretty much like V5 except the bottom window scrolls from the bottom instead of the top. V6 has an entirely different model that, if I choose to implement it, will stretch the practicality of using a single IZMachineIO interface for all of these -- so far I've only had to add a few new members to support the other versions.<br />
<br />
I've also thought about adding some interpreter functionality, in order to (1) make ZLR work on platforms where Reflection.Emit isn't supported, and (2) possibly move toward a tracing JIT. No movement on that yet, though.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-91304589037479068222012-05-14T16:42:00.001-07:002012-05-14T16:50:50.303-07:00Guncho development update<div><p>I'm taking the plunge and separating the code into pluggable modules, if only so that I can test one part of this massive refactoring at a time.</p>
<p>The introduction of Playfic has encouraged me to step up my game. Supporting I7 extensions is a great idea, and while automatically importing them into Guncho is a while off, my design already includes a way to share extensions between realms, so I can at least import them by hand.</p>
</div>Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-63758398713053303482012-03-26T14:37:00.002-07:002012-03-27T00:20:17.856-07:00Guncho development update<span style="font-family: Georgia, serif; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; font-size: 100%; ">I've resumed work on Guncho and am basically working along the same </span><a href="http://vaporwareif.blogspot.com/2011/12/guncho-source-code-and-development.html" style="font-family: Georgia, serif; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; font-size: 100%; ">roadmap I outlined in December</a><span style="font-family: Georgia, serif; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; font-size: 100%; ">. The first big changes, developed in parallel, will be:</span><div><ol><li><span >A new persistence layer based on NHibernate, which will store realm and player data in a relational database in real time and keep track of revision history.</span></li><li><span >A REST API layer based on WCF, which will replace the .NET Remoting channel for communication between the control panel and game engine, and will eventually be exposed to the public.</span></li><li><span >Decoupling of "Realm" from "Instance", allowing multiple copies of a game to run side by side.</span></li></ol></div>Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-56866363366021968802012-03-12T00:36:00.004-07:002012-03-12T00:48:22.661-07:00ZLR bug fixesI've pushed<sup>(*)</sup> a few changes to the <a href="http://zlr.svn.sourceforge.net/viewvc/zlr/trunk/?view=log">ZLR Subversion repository</a> to make Infocom games more playable. So far I'm only testing with their V5 games, since ZLR only supports V5/V8. But an eventual goal is for ZLR to be able to run and debug everything ZILF can compile, so it'll be getting V3/V4 as well, and maybe V6.<br /><br />Anyway, I believe these changes will fix every Infocom V5 game -- except Beyond Zork, where the parser is still broken under ZLR. If you're brave enough to download the latest code from SVN, let me know if I missed anything in the other games!<br /><br /><small>(<sup>*</sup> yes, pushed, because I'm using Mercurial locally)</small>Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-73004765969482397652011-12-19T01:45:00.000-08:002011-12-19T02:33:28.084-08:00Guncho source code and development musingsThe <a href="https://bitbucket.org/jmcgrew/guncho">source code for Guncho</a> is now available through Bitbucket. I've added a cursory roadmap to the <a href="https://bitbucket.org/jmcgrew/guncho/wiki/Home">developer wiki</a>.<br /><br />My first priority is to make Guncho more stable and scalable: crash less often, save data more reliably, handle realm failures more gracefully, run multiple instances of realms. After that, I hope to modularize a few subsystems to allow for new languages, new virtual machines, and new communication layers (e.g. a web service API).<br /><br />I have a dream that Guncho will someday be able to "host" the IF Comp, with a dev environment for authors, a play environment with logging and access control for beta testers, and a voting/game-selecting/MPIF-matchmaking system for judges.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-59686255835450048212010-12-26T23:44:00.000-08:002010-12-26T23:44:45.529-08:00Dynamic Objects update<div>I've submitted version 6 of Dynamic Objects, which adds compatibility with Inform 7 build 6G60. There were two compatibility issues, both involving the way Dynamic Objects uses undocumented I7 properties to add cloned objects to the per-kind linked lists.</div><div><br /></div><div>First, the "IK_0" property was renamed to "KD_Count". This property contains the numeric index of each object's kind. For example, an object whose immediate kind is "person" will have a KD_Count of 8, since "person" is K8_person in I6.</div><div><br /></div><div>Second, the logic for matching a kind name to the link property had to be changed to account for new properties. This deserves more explanation.</div><div><br /></div><div>All things in an I7 game are connected via one or more linked lists, which helps to optimize kind-based loops. When we write "repeat with X running through people", the game doesn't have to loop through every object, checking each one to see if it's a person. Instead, it starts at the first person, which is referenced in the IK8_First constant, and proceeds to each subsequent person by following the IK8_Link properties.</div><div><br /></div><div>Whenever we clone an object, Dynamic Objects has to add the new object to each of the appropriate linked lists. A cloned man has to be added to the list of men, the list of people, and the list of things.</div><div><br /></div><div>The easy way to add something to a linked list is to add it at the beginning: change the head to point to the new item, then change the new item's "next" field to point to the old head. But we can't do that, because the head is stored in a compile-time constant like IK8_First. So we add it at the end, by starting from the old object (which we know is already in the list) and following the link property until we reach the end.</div><div><br /></div><div>But finding out which link property to use is tricky in itself. The I7 compiler has an easy enough time knowing that it should write "IK8_Link" for loops involving "K8_person" -- even Ayn Rand knew that 8 IS 8 -- but the *values* of IK8_Link (a property number) and K8_person (an object address) are unrelated. They're similarly named, but those names are only for compile time, right?</div><div><br /></div><div>As it turns out, no! Even in release builds, the I6 names of objects and properties are still available at runtime via the "print (object) x" and "print (property) x" syntaxes. So Dynamic Objects loops over every property, printing each one's name to a buffer and looking for the one whose name contains the same number as the name of the kind object. It's ugly but it works.</div><div><br /></div><div>That's where the compatibility issue came in. Since the names don't match exactly ("K8_person" vs. "IK8_Link"), Dynamic Objects was only comparing the part between the K and the underscore. But 6G60 added new properties with names like "IK8_Count", which threw off that comparison. The extension now checks the character after the underscore to make sure it's an L.</div><div><br /></div><div>Incidentally, if you use Dynamic Objects, please vote for <a href="https://inform7.uservoice.com/forums/57320-general/suggestions/751254-world-model-dynamic-object-creation">this suggestion</a>. A more stable way for the extension to get at this information would keep it from breaking with each new build, and adding more information for the extension to use at runtime would make it work more smoothly with indexed text properties.</div>Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-63561769688047724112010-11-30T18:58:00.001-08:002010-12-05T04:20:49.755-08:00Branch optimization in ZAPFI recently moved some unfinished changes from ZAPF's trunk into a development branch. Some have asked what these changes are all about, anyway, so here's an explanation.<br /><br />The Z-machine's branch instructions, of which there are many, can take an offset in either "long" or "short" form. Long form takes two bytes and can hold any signed 14 bit offset, whereas short form takes one byte but can only hold an unsigned 6 bit offset. In order to save space, we always want to use the short form wherever possible: that is, whenever an instruction branches forward by up to 61 bytes. (Not 63: these offsets are biased by +2.)<br /><br />But branching forward means we encounter the branch instruction before its target label, so how do we know how far it's branching? It's not easy, especially if there are other branch instructions in between this one and its target -- we can't know the true distance until we know what form <span style="font-style: italic;">those</span> will be assembled in.<br /><br />ZAPF's current approach is to make repeated passes over each function until all the label positions are discovered. When it sees a branch instruction targeting a label that hasn't been defined yet, it remembers the name of that label, then assembles the branch in long mode. Once the label's definition is encountered, ZAPF remembers its location and rewinds to the beginning of the function.<br /><br />The next time it encounters the branch instruction, it can make a better judgment of the distance, which may allow it to use short mode. The next time it encounters the label definition, it compares the new location to the old location; if it has changed, it remembers the new position and rewinds <span style="font-style: italic;">again</span>. It only reaches the end of the function once every label's final location has been discovered and every branch has been assembled correctly.<br /><br />Unfortunately, this approach doesn't always work. Some inputs cause ZAPF to get stuck in a function and never reach the end. I haven't investigated too thoroughly because the algorithm is hard to reason about, inefficient, and silly enough that I decided to just rewrite it.<br /><br />I found an interesting replacement algorithm in a 1978 paper by Thomas G. Szymanski called "Assembling Code for Machines with Span-Dependent Instructions". The paper proves that minimizing program length is NP-complete in the general case, when branch targets are allowed to be specified as arbitrary expressions of constants and labels, but presents an efficient algorithm for a restricted subset of expressions -- which works just fine for ZAPF.<br /><br />The algorithm works by assuming in a first pass that all instructions can be assembled in short form, and remembering the location of every label and every branch instruction, then studying them after that pass to trace the dependencies between branch instructions, and forcing into long mode only the instructions that need it. The label locations are adjusted -- advanced forward by one byte for every preceding branch that was changed from short to long -- and the set of adjusted locations is used in a second pass to assemble each branch in the correct mode.<br /><br />There's one more complication, though: the Z-machine's packed address system. Every routine must begin at an address divisible by 2, 4, or 8 (depending on the Z-code version), so there are usually a few padding bytes between routines. The algorithm works on all labels in the game at once, but because of the padding issue, forcing a branch into long mode doesn't actually push every following label ahead by one byte: for labels in subsequent routines, it's either zero bytes (when the extra byte can be absorbed by the next padding section) or 2/4/8 bytes (when it can't). And that's the part that isn't finished yet.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0tag:blogger.com,1999:blog-1616589872488416973.post-42582014259962002442010-11-22T17:25:00.000-08:002010-11-22T20:35:33.637-08:00Hello WorldI'm vaporware, and this is my IF blog.<br /><br />Who am I, anyway? You may have encountered some of my IF-related projects:<br /><ul><li><a href="http://fyrevm.sourceforge.net/"><span style="font-weight: bold;">FyreVM</span></a> - The modified Glulx interpreter that runs Textfyre's games.<br /></li><li><a href="http://www.guncho.com/"><span style="font-weight: bold;">Guncho</span></a> - An online multiplayer IF system based on Inform 7.<br /></li><li><a href="http://sourceforge.net/projects/snack/"><span style="font-weight: bold;">Snack</span></a> - A language similar to TADS 2 that compiles to Glulx.<br /></li><li><a href="http://sourceforge.net/projects/yomin/"><span style="font-weight: bold;">Yomin</span></a> - An integrated development environment (IDE) for Inform 6 and ZIL.</li><li><a href="http://sourceforge.net/projects/zilf/"><span style="font-weight: bold;">ZAPF</span></a> - A Z-machine assembler and disassembler.<br /></li><li><a href="http://sourceforge.net/projects/zilf/"><span style="font-weight: bold;">ZILF</span></a> - A compiler for Infocom's ZIL language.<br /></li><li><a href="http://zlr.sourceforge.net/"><span style="font-weight: bold;">ZLR</span></a> - A Z-machine interpreter.</li></ul>I've also contributed template code to <a href="http://inform7.com/"><span style="font-weight: bold;">Inform 7</span></a> and currently run the <a href="http://inform7.com/bugs/">bug tracker site</a>. I've written several <a href="http://inform7.com/extensions/authors/#Jesse_McGrew">extensions</a>, and I was the technical editor on Aaron Reed's <a href="http://inform7.textories.com/"><span style="font-style: italic;">Creating Interactive Fiction with Inform 7</span></a>.<br /><br />Why this blog? Well, occasionally I come up with something cool that I want to talk about in greater detail than one or two lines on ifMUD. Don't expect a lot of comp reviews or storytelling tips: I intend to focus on more technical topics, from the status of the projects above to Inform tricks to VM implementation details.Tarahttp://www.blogger.com/profile/06574235611152689251noreply@blogger.com0