Part 1 - open this up then copy & paste into a blank project in the IDE. (click on the little copy icon that appears top-right of the window when you 'hover' the mouse pointer over the opened-up text.)
"Scratchpad_Include_when_defining" by PB
Part - Preface
Chapter - A Warning
[ ################### STOP! ##################
facilis descensus Averno;
nocte atque dies patet atri ianua Ditis;
sed revocare gradum superasque evadere ad auras,
hoc opus, hic labor est.
Virgil, Aeneid vi. 126-29
"The descent to the Underworld is easy:
through day and night the door of black Dis lies open.
But to retrace your steps and escape to the upper air -
there is trouble, there is toil."
################### STOP! ##################
]
[
Do not proceed. You really do not want to do this. No, honestly, you really really do not want to do this. Oh, alright, if you must, but don't say
I didn't warn you when it all goes horribly wrong...
All that follows is highly deprecated and is certified to work no further than the current release of Inform 7 (10.1.2)
It mostly relates to changes introduced with version 10, so is largely not needed if you are working with 9.3/6M62.
If you think you might need any of these techniques, consider first whether to simply revert to 9.3/6M62 for your project.
But if you would be with Dante of one equal temper of heroic hearts, fellow traveller, read on...
]
Chapter - A Discursion
[
Two roads diverged in a yellow wood, and I
I took the one less traveled-by-
and that has made all the difference.
Robert Frost
]
[
Inform 7 is built upon the foundation of Inform 6, a very different, rather austere, C-like language. Inform 6 and its predecessors date back to the early 1990s. Inform 7 was a major break from this tradition, but everything written in Inform 7 (I7) source code is compiled (converted) to Inform 6 (I6) code before that I6 code is itself compiled to a story file that can be run on an interpreter.
The intermediate I6 code produced by the I7 compiler can be found as a text file in the Build directory of the <project-name>.inform folder and is called 'auto.inf'. 'auto.inf' is (re)created every time [Go] is clicked in the IDE, assuming the I7->I6 compilation process succeeds.
Prior to Inform version 10, this was the only stage of I7->I6 compilation. I7 source code was compiled to I6, the I6 code in the extensive template files defining the Library and the Standard Rules were directly pasted in alongside, and any I6 inclusions in the I7 source were also simply pasted into the 'auto.inf' file at the place directed by the Include (- ... -) directive.
All this changed with Version 10. Now there is yet another intermediate language called Inter. I7 source is compiled in the first instance to Inter. I6 inclusions in I7 source are also compiled in the first instance to Inter. What used to be the I6 template files are pre-compiled to Inter, in files called kits.
All these sources of Inter- I7 source, I6 inclusions, kit files, are then combined together. Unlike the case with I6, however, there is no persistent file holding the combined Inter code equivalent to 'auto.inf'. The combined Inter code is then compiled again to I6, finally producing the I6 'auto.inf' file.
It can be seen from this description that in the I7 context, code initially written in I6, whether template code or an inclusion, ordinarily always passes through Inter before becoming I6 once more. Sometimes the I6->Inter->I6 compilation chain leaves the resulting I6 pretty much unchanged, but sometimes it looks very different.
It is also clear that there are two different compilers that compile I6 code to something else:
(i) the I6->Inter compiler used
(a) to compile I6 template code to Inter kit files
(b) I6 inclusions in I7 code to Inter
(ii) the I6->story file compiler used to compile to a Z-machine or Glulx story file
The second of these is essentially the same compiler developed over the past 30 years to compile stories written entirely in Inform 6.
The first is a new compiler developed for Inform 7 Version 10 in order to produce Inter code.
In an ideal world, the Inform 6 language recognised and compiled by these two compilers would be identical, but that's not the case. Firstly, some of the more obscure syntaxes of the Inform 6 language are not supported by the new I6->Inter compiler. Secondly, like any new extensive and complex program, there are a small number of bugs in the new compiler, such that some I6 code that should work, and does work with the old compiler, doesn't work with the new.
In addition to this, some things written in I6 code, whether in template files or inclusions, doesn't make it through the I6->Inter->I6 compilation chain. Most obviously, comments are stripped out. More subtly, conditional compilation directives such as '#ifdef <conditionally compiled code> #endif are not passed through the compilation chain. They are instead considered at the time of I6->Inter compilation, and the decision is taken to compile <conditionally compiled code> or not at that point. This has led to some bugs even in the kit files, where code that needed to be compiled has not been. It also means that there is no mechanism for template files or I6 inclusions to put conditionally-compiled code segments into the final I6 file, to be compiled or not according to the circumstances when it is compiled from I6 to a story file.
A further complication is that a number of compiler-generated functions and data structures are only created in the final stages of the Inter->I6 compilation process. They therefore do not yet exist at the time of the I6->Inter compilation. Attempts to reference these functions and structures in I6 templates or inclusions will cause a failure of the I6->Inter compilation due to efforts to reference a not-yet-existent data structure or function. In addition, these data structures and functions cannot be replaced using the Include (- ... -) replacing <function-name> syntax, and the I6 Replace directive cannot be used in template files or I6 inclusions.
There are two other kinds of function that can cause difficulty. Built-in functions are part of the I6 language itself, common examples being random() or child() or the printing functions like name(). These are known to the I6->Inter compiler and so can be used in I6 templates or inclusions. However, should one wish to replace a system functions with a revised or enhanced version, the Include (- ... -) replacing <function-name> syntax does not work for system functions and, as previously noted, the Replace directive cannot be used in template files or I6 inclusions. Veneer functions are a handful of 'operating system' support functions that are not part of the I6 language but are compiled by the I6->story file compiler into the final story file. They are unknown to the I6->Inter compiler, and therefore unavailable to I6 templates or inclusions, but they can be called by I6 in the final 'auto.inf' file.
The developers of Inform 7 do not wish to encourage 'hybrid' coding in a combination of I7 and I6 code (see Writing with Inform §27.14. Using Inform 6 within Inform 7). An early goal of Ver 10 development was to progressively eliminate sections of I6 code that needed to be pasted verbatim into the final I6 source file, as what were called 'splats', without first passing through the I6->Inter->I6 compilation process. This endeavour was largely successful, as the I6->Inter->I6 compilers steadily improved. The facility was retained for authors to Include short sections of their own I6 code, although the practice is gently discouraged. These inclusions do however need to pass through Inter before reaching their final I6 form, introducing the restrictions alluded to previously.
However, one facility remains to inject 'splats' directly into the final I6 file without I6->Inter->I6 compilation-
Include (- ... -) when defining a/-- <kind-name>/<object-name>.
(See Writing with Inform '§27.21. Inform 6 objects and classes', which concludes with this portentious admonition: WARNING: The "Include (- ... -) when defining ..." usage still works for the moment (except in projects compiled to C at the command line, where it may fail), but it is deprecated and likely to be removed in later versions of Inform. Avoid it if at all possible.)
As things stand, the I6 code represented in the phrase above by ... is pasted directly as a 'splat' in the midst of the final I6 declaration of the kind or object, and this provides a 'back-door' or 'bridge' to do some of the things with I6 that became no longer possible in Ver 10 of I7.
This Inform 7 project illustrates some of the things you can do only with 'splats', and methods to achieve them. Will you need them? Almost certainly not. But just in case, or if you're simply curious, read on...
]
Part - Setup
Chapter - Geography
[
To me, it seems a dreadful indignity to have
a soul controlled by geography.
George Santayana
]
The Last Homely House is a room.
The Lonely Mountain is a room. North is the Withered Heath.
Chapter - Objects
[
I have a lot of objects in my space,
little things, reminders, memories.
Marc Newson
]
A dragon is a kind of animal. Smaug is a dragon. Fafnir is a dragon. The Smaug object translates into I6 as "Smaug". The Fafnir object translates into I6 as "Fafnir".
A person has a room called lair. The lair of a person is usually The Last Homely House. The lair of an animal is usually the Withered Heath. The lair of Smaug is The Lonely Mountain.
A person has a number called hit-points. The hit-points of a person is usually 8. The hit-points of an animal is usually 12. The hit-points of a dragon is usually 88. The hit-points of Smaug is 120.
Part - Include ___ when defining ___
Chapter - Credo
[
"We choose ... to do (these)... things, not because they are easy,
but because they are hard;
John F. Kennedy speech Sep 12 1962
"We choose to do these things not because they are easy,
but because we thought they were going to be easy.
Programmer's Credo
]
Chapter - Including a 'splat' when defining a kind
Section - Declaring a dummy-kind to include a 'splat'
A dummy-kind is a kind of object.
Include (- ; ! <- NB semicolon here to prematurely terminate the dummy_kind Class declaration header
! ############################### vvvvv INSERT I6 CODE BELOW HERE vvvvv #################################
! ------ Replacing System Functions -----
! System functions are a dozen or so functions built into the I6 language that are automatically compiled to be called by I6 code
! Common examples are random(); objectloop(); child(); etc.
! As noted above, they can be replaced by Replace directives placed here:
Replace random; ! replace an inbuilt system function with our own, declared hereafter
[random; return 1234; ]; ! a not-very-random replacement random number generator :)
! ------ Functions using I6 syntax not supported by the I6->Inter compiler -----
! developing Inform 7 involved the monumental task of reimplementing the I6 compiler- with a source code file for a new 'intermediate'
! language, called 'Inter' as the compilation target rather than a finalised Z-machine or Glulx story file.
! Both Inform 7 source code, and Inform 6 source code (in source for kits, or inclusions within Inform 7 source files) are compiled to Inter.
! The combined Inter file is then (usually) compiled to the unified Inform 6 source file represented by 'auto.inf'.
! I6 in Inform 7 inclusions is therefore subjected to the following compilation chain:
! I6 inclusion in I7 source -> Inter -> I6 in 'auto.inf' -> final story file e.g. 'output.ulx'
! The compiler compiling I6 inclusions to Inter is completely different to the one compiling the I6 in 'auto.inf' to 'output.ulx'
! The compiler compiling the I6 in 'auto.inf' to 'output.ulx' is essentially the same one you would use to compile stories
! which you had authored in Inform 6 code from the start.
! Mostly the two compilers recognise the same I6 code in the same way, but there are a few subtle differences,
! largely consisting of things you can do using the 'auto.inf' to 'output.ulx' compiler but not the I6 inclusion -> Inter compiler.
! some of these are documented in Writing with Inform, in the I7 documentation '§27.19. Longer extracts of Inform 6 code'
! some more are mentioned in posts on the Interactive Fiction Community Forum https://intfiction.org/c/authoring/inform-7/
! and others in submissions to the Inform 7 bug tracker https://inform7.atlassian.net/jira/software/c/projects/I7/issues
! one useful feature of switch statements in 'canonical' I6 is missing from the I6->Inter compiler:
! the ability to have cases expressed as a range of values between two constants, such as 13 to 55 or 'a' to 'z'
! the latter otherwise has to be laboriously expressed as a list of 26 constants: 'a', 'b', 'c', ... 'x', 'y', 'z'
[ ZSCII_range x; ! illustrating a switch statement using the 'to' syntax disallowed by the I6->Inter compiler.
switch (x) {
8: print "delete";
9: print "tab";
11: print "em";
13: print "newline";
32: print "space";
33 to 47, 58 to 64, 91 to 96, 123 to 126: print "punctuation/symbol";
48 to 57: print "numeral";
48 to 57: print "numeral";
'A' to 'Z' : print "uppercase";
'a' to 'z' : print "lowercase";
129 to 131 : print "cursor key";
132 to 144 : print "function key";
145 to 154 : print "numeric keypad";
155 to 223 : print "international";
252 to 254 : print "mouse";
default: print "undefined-",x;
}
];
Array ZSCIIstring string '?' 'A' 'c' '@~N'; ! '@~N' chosen as example as it is the only international character with the same value (209) in
! ZSCII and Unicode, so displays the same whether compiling to Z-machine or Glulx
[ MyFunction x; ! illustrating a switch statement using the 'to' syntax disallowed by the I6->Inter compiler.
switch (x) {
1: print "was destroyed in a fire on the launchpad";
2 to 3: print "was cancelled after the failure of Apollo 1";
4 to 6: print "was a successful test launch of the Saturn 5 rocket";
7, 9: print "completed a test-flight in Earth orbit";
8, 10: print "completed a test-flight in lunar orbit";
11, 12, 14 to 17: print "made a landing on the Moon";
13: print "made it back safely after a catastrophic explosion";
18, 19: print "was cancelled due to budgetary cuts";
20: print "was cancelled to free up a heavy launcher for the Skylab station";
}
];
! ------ Veneer Functions -----
! The veneer is a short 'invisible' segment of I6 compiled into the final story file along with the ordinary 'visible' I6 source code.
! It provides a few dozen basic 'operating system' functions that can be called by I6 code or system functions
! Any of these that are duplicated by 'visible' I6 source code are automatically overridden and replaced by that code
! so [ Print__PName; print "Fooled you!";]; would automatically replace the veneer function to print the name of a property
! The Inform Library overrides many of the veneer functions, but a few remain, as in this example which prints
! the I6 textual name associated with a given property id number.
! See the Inform 6 Technical Manual for more information about the veneer:
! https://www.ifarchive.org/if-archive/infocom/compilers/inform6/manuals/technical_manual.txt
! Veneer functions are not usually 'visible' to the I7/I6->Inter->I6 compilers, so normally can' t be referenced in I6 inclusions
! but they can be referenced within a 'splat':
[ PrintVeneerDescription p;
Print__PName(p); ! here we call a veneer function using the parameter passed to the I6 function
]; ! p should be an I6 property id number- not an I7 property, which is a reference to a
! word array of metadata relating to the property. The I6 property id number is
! given by (I7_property-->1)
! ------ Conditional Compilation ------ ------Message Directive ------ ------ Array Directive ------
#Ifdef TARGET_ZCODE; ! example of conditional compilation applied to the final I6 source
Message "Compiling greeting for Z-machine...."; ! information about the compilation (will appear in IDE Console of [Results] tab)
Array Greeting string "Welcome to Z-code!"; ! "double-quoted" Array declarations are not allowed by I6->Inter
#Ifnot; ! nor is the 'string' array declaration subtype
Message "Compiling greeting for Glulx....";
Array Greeting string "Welcome to Glulx!";
#Endif;
! ------ Verb Directive -----
! the Verb directive is recognised in normal I6 inclusions but it causes a compiler failure when loading WorldModelKit
Verb 'tabulate' ! this simply allows the command 'tabulate inventory' to evoke the inventory action
* 'inventory' -> Inv ; ! it is the equivalent of the I7 'Understand "tabulate inventory" as taking inventory.'
[ XyzzySub; "Expectantly, you cry 'xyzzy!'... but nothing happens.^"; ];
Verb ’xyzzy’
* -> Xyzzy; ! here we create a whole new action and grammar to invoke it
! equivalent to 'Xyzzying is an action applying to nothing. Understand "xyzzy" as xyzzying.
! Carry out xyzzying: say "Expectantly, you cry [']xyzzy![']... but nothing happens.".
! The xyzzying action translates into Inter as "Xyzzy". '
! Unfortunately, because this I6 code appears before all the kit-and-I7-generated Verb directives,
! we cannot use the Extend directive here, which will fail the I6 compilation with 'no previous grammar'.
! There is little that can be done using the Verb directive that isn't possible and better to do in Inform 7.
! It does provide complete authorial control over the sequence in which grammar lines appear in a verb's
! grammar table, and therefore the order in which they are considered by the parser.
! (This sequence is ordinarily controlled, according to a rather arcane set of rules, by the I7 compiler.)
! Very occasionally, when the compiler's sequencing is unhelpful, direct definition of a verb's grammar
! table with the Verb directive may be of use.
! ------ Superclass Operator -----
! The superclass operator in I6 is a somewhat obscure syntax invoking the property an object would
! have otherwise inherited from a class it belongs to, if it had not had its own property specifically defined.
! To quote from the Inform Designer's Manual 4 (p.64):
! ---------------------------------------------------------------------------------------------------------------------
! It fairly often happens that an instance of a class needs to behave almost, but not
! quite, as the class would suggest. For instance, suppose the following Treasure class:
! Class Treasure
! with deposit [;
! if (self provides deposit_points)
! score = score + self.deposit_points;
! else score = score + 5;
! move self to trophy_case;
! "You feel a sense of increased esteem and worth.";
! ];
! and we want to create an instance called Bat_Idol which flutters away, resisting
! deposition, but only if the room is dark:
! Treasure Bat_Idol "jewelled bat idol"
! with deposit [;
! if (location == thedark) {
! remove self;
! "There is a clinking, fluttering sound!";
! }
! ...
! ];
! In place of ..., what we want is all of the previous source code about depositing
! treasures. We could just copy it out again, but a much neater trick is to write:
! self.Treasure::deposit();
! Instead of sending the message deposit, we send the message Treasure::deposit,
! which means ‘‘what deposit would do if it used the value defined by Treasure’’. The
! double-colon :: is called the ‘‘superclass operator’’. (The word ‘‘superclass’’, in this
! context, is borrowed from the Smalltalk-80 language.)
! object.class::property is the value of property which the given object would
! normally inherit from the given class. (Or it gives an error if the class doesn’t provide
! that property or if the object isn’t a member of that class).
! It’s perfectly legal to write something like x = Treasure::deposit; and then to
! send Bat_Idol.x();.
! ---------------------------------------------------------------------------------------------------------------------
! I7 rebrands classes as kinds, such that 'Treasure is a kind of object.' will compile to an I6 directive similar to 'Class Treasure'.
! The superclass operator is useful in I6 predominantly because many I6 properties are elaborate routines which it might be inefficient and
! potentially poor coding practice to copy-and-paste within the specific properties of multiple objects of a class.
! In Inform 7, elaborate routines are nearly always in To... phrases or rulebooks, not an object's properties, so the inability to access
! the base properties of an object's kind is not generally missed.
! It isn't however possible to write for example 'say the printed plural name of a person'-
! Inform asks for a particular person to be specified- but by default the printed plural name of every person is "people", so if we want to
! know what is defined as the default printed plural name of the person kind we can usually write 'say the printed plural name of the player'
! But if we have "a dragon is a kind of animal", a dragon is a person too, and if our player has been declared as a man (printed plural name 'men')
! there may be no specific object with a printed plural name of "people", if we want to know what the default printed plural name of a
! dragon-as-a-person would be- "people" or "persons"?- we can use the superclass operator like so <dragon-name>.person::plural
! this turns out to be more awkward that one might think, because
! (i) we need to pass a property from I7, and there is a bug in Inform Ver 10+ that prevents property parameters being passed to I7 phrases:
! fortunately we can work around this because property parameters can still be passed from I7 to I6 inclusions
! (ii) there seems to be no obvious way to pass a kind from I7 as a parameter to either an I6 inclusion or an I7 phrase:
! we work around this by using a text parameter representing the kind name to look up the I6 representation of the kind
! and passing that- an I6 memory address- to I6 as a number parameter
!
! having finally assembled our property and kind parameters, we can pass those with our object to I6 and find <object>.<kind>::<property>
!
! see the example below where a common property is used to access the plural printed name property of a kind that an object inherits from
! ############################### ^^^^^ INSERT I6 CODE ABOVE HERE ^^^^^ #################################
Class dummy_kind_2 ! declare a 2nd dummy kind class header to absorb the orphaned remainder of the compiler's dummy-kind declaration
class K0_kind ! which will follow on after this in the final compiled I6 source file (auto.inf)
! Because the I7/I6->Inter->I6 compilers don't know about this 2nd dummy kind, although it will be compiled
! into the final story file it will not be accompanied by all the structures that the I7/I6->Inter->I6 compilers
! normally compile to accompany a kind declaration, e.g. it will not appear in the kind hierarchy array
-) when defining a dummy-kind.