don’t attempt to read the following code as-is on the forum website. It’ll be slightly mangled, misformatted and as a result have some words missing, crossed out etc. Copy and paste it into the IDE (or a basic text editor of your choice)
Part 2 - open this up then copy & paste into IDE at end of Part 1 (blank line between)
Section - Comments on replacing functions using an I6 Replace directive in a 'splat'
[
The Replace directive has two forms-
Replace <function-name>;
The directive must appear in the I6 source before the first declaration of <function-name>, or in the case of an inbuilt function like random(),
before the first reference to it in the source.
It may be followed by any number of declarations of <function-name>.
The compiler compiles only the *last* declaration of <function-name> in the source, discarding the rest.
In terms of ordering, declarations in files marked as system_file, or inbuilt functions from the veneer, come *before* those in normal source files,
so the last declaration in a normal source file (such as auto.inf) has highest priority.
Replace <function-name> <new-function-name>;
The directive must appear in the source before the first declaration of <function-name>
The compiler compiles the *first* declaration of <function-name> in the source as <new-function-name> instead, effectively renaming it.
This allows a kit function to be renamed rather than discarded, perhaps to be called by its replacement to implement default behaviour.
It is also possible to rename functions created and inserted directly into the I6 by the compiler, although this is unlikely to be useful
unless a means is found to inject a replacement function with the original name later in the source.
Inbuilt I6 functions like random() can't be renamed in this way, however.
These two definitions of Replace in combination mean that when two like-named declarations occur in an I6 source file, it's not possible to retain the earlier one unchanged- the earlier one must either be not compiled at all, or renamed.
This means that it's impossible to use Replace to replace functions generated by the Inter->I6 compiler, because these will be inserted into the final I6 source file later than splats from 'while defining....', or compiled functions from Include (- <function> -) phrases.
Unfortunately, in Ver 10+ the author has lost the ability to control where I6 is inserted or included in the final I6 source file.
This is a particular problem because the approved syntax of 'Include (- [<function-name>...]; -) replacing "<function-name>".' will only replace functions directly declared in kits, not inbuilt functions like random() or functions created entirely by the Inter->I6 compiler.
All this means that this despite this hack there is still no means to replace functions created entirely by the Inter->I6 compiler, although inbuilt functions can be replaced through the 'when defining...' method.
It's not possible to finesse the situation by combining multiple Replace statements, as the same <function-name> can't be used in multiple Replace statements; or by trying to insert a function later in the source by replacing an unrelated and unused late-declared function via the '...replacing "<function-name>" syntax, because in 'Include (- [<function-name>...]; -) replacing "<function-name>".' the two <function-name>s must match, otherwise the function is just included as if the ' replacing "<function-name>" ' clause did not exist.
Veneer functions (see later) do not need a Replace directive- they are automatically replaced by a like-named function declaration in I6 source.
]
Chapter - Including a common property when defining a dummy object, allowing I7 to call a function in a 'splat' or the veneer
[Directly-pasted I6 code (a 'splat') is a 'black box' to I7, meaning that it is 'invisible' to the I7/I6->Inter compiler and also to the Inter->I6 compiler, being set aside and ignored during the I7/I6->Inter->I6 compilation process, then simply pasted verbatim into the final I6 code file 'auto.inf' at the last minute. This means that any named functions (or other named entities, such as constants, arrays, or newly-declared properties) declared in the splat cannot be referred to from I7, including via ordinary I6 inclusions. Attempts to do so will fail during I7/I6->Inter compilation with a complaint that the named entity can't be found as a declaration in system functions, kits or I7 source code.
So we need a 'back-door' or 'bridge' from I7 (including normal I6 inclusions) to our 'splat' of I6 code via named entities which:
(i) are already declared and known to the I6->Inter-I6 compilers- either inherently or from kits or I7 source code (so that we can refer to these named entities from I7, or from I6 inclusions in I7 source)
(ii) we can nevertheless subvert to reference named entities in our 'black box' splat without upsetting the I7/I6->Inter->I6 compilers.
One method of prividing this bridge is by bypassing the I7/I6->Inter->I6 compilation process using the 'when defining <object-name>.' syntax for a known I7 object rather than for a kind.
This time, we define for our object one or more of the 'common' properties that are automatically declared by the compiler for all objects and are therefore known to the I6->Inter->I6 compilers. Consequently they can be referenced by I7, or by I6 inclusions in I7. On the other hand, because these property definitions are pasted directly into the final I6 source, they do not themselves trouble the I7/I6->Inter->I6 compilation process and can therefore with impunity reference named entities in our 'black box' splat.
A list of these 'common' properties is given in the Appendix, along with the I7 names and I7 kinds that are declared for some of them in Basic Inform or the Standard Rules. A few additional special compiler-declared common properties such as 'name' are not listed. These, and a few others that are created by the compiler for all objects (article; KD_Count; list_together; plural; short_name; vector) are best not interfered with.
Note that although these properties may have kinds assigned to them in I7, I6 has no concept of kinds and we are free to put any I6 entities we want in these properties- numbers, anonymous functions, I6 text, objects, dictionary words etc.
]
The dummy-object is a dummy-kind.
The dummy-object object translates into I6 as "Dummy_object".
Include (-
with capacity [ x; ! an anonymous function to print from a character array called Greeting declared in our splat.
for (x=0: x<Greeting->0:x++){
print (char) Greeting->(x+1);
}
],
add_to_scope [ x i; ! similarly, to test our ZSCII character range printing function
for (x=1: x<=ZSCIIstring->0: x++){
i=ZSCIIstring->(x);
print (char) i, " (", i ,"): ", (ZSCII_range) i, "^";
}
],
initial [x; MyFunction(x);], ! an anonymous function taking a parameter and passing it on to a named function declared in our splat
articles [p; Print__PName(p);], ! directly calling a veneer function that takes a property id number and prints a textual description of it
inside_description [o k p x; ! to print the contents of a superclass property of an unknown nature
if (~~(o provides p)) rfalse;
if (~~(o ofclass k)) rfalse;
x = o.k::p; ! getting the value of a superclass property (::p) of a given class (k) for an object (o) which inherits from it
if ( metaclass( x ) == Object) print (name) x;
else if (metaclass(x)== Class) print (I7_Kind_Name) x; ! this will only print the I7 kind names of I6 Object subclasses
else if (metaclass(x)== String) print (string) x; ! text properties created in I6
else if (x-->0 == CONSTANT_PACKED_TEXT_STORAGE) TEXT_TY_Say(x); ! text properties created in I7
else if (metaclass(x-->1) == Routine) (x-->1)(); ! if it's a routine, just run it- it may or may not print anything
else print x; ! default
],
grammar [o k p x; ! simply returning the I6 value of the contents of the superclass property.
if (~~(o provides p)) rfalse;
if (~~(o ofclass k)) rfalse;
return o.k::p;
],
-) when defining the dummy-object.
Section - Accessing a superclass property for an object
[due to a bug introduced in Ver 10, I7 phrases with a property parameter can only be declared with an I6 definition, not with a following I7 code block, so we finesse this by instead using a number parameter which is the I6 property id number, derived from an I6 definition:]
To decide what number is (p - a property) property: (- ({p}-->1) -).
[in I7, a property p is actually the address of a word array of metadata relating to p, in which the second element, -->1, holds the id of the I6 property]
[now '<property> property' e.g. 'plural printed name property' can be used for '(n - a number)' in a To.. phrase]
[in similar vein, there seems to be no obvious way to pass an I7 kind as a parameter to either an I7 or I6 phrase. Literal kinds can be pasted in as, for example (+ person +), but not a kind variable such as (+ K +)). Here are demonstrated 2 ways to finesse this.
(i) by in the first instance passing a text parameter- being the name of the kind we're interested in- to an I7 phrase. We then use that text to look up in a table (built before play begins from the I6 KindHierarchy array) the I6 memory address representing the corresponding I6 class. Finally, we pass our property-id-as-number, our kind-as-I6-class-address-as-number and our object to the Dummy_object property routine that will process the requested superclass property.
(ii) somewhat less convoluted, by representing the kind parameter from the start not by a text of its name but directly by the I6 address number that represents it, pulled from a sequence of phrases like'To decide which number is...: (- ((+ person +)) -). (see Section - Text and numeric constants for kind names, below). We could do the same using a translated kind name instead of a (+ ... +) insertion, except that translation of kinds is currently broken in Ver 10.1.2 (it has been fixed for the next release). Then, as before, we pass our property-id-as-number, our kind-as-I6-class-address-as-number and our object to the Dummy_object property routine that will process the requested superclass property.
]
[method (i)]
To say the superclass (p - a number) of (t - a text) for (o - an object):
if t is a name listed in the Table of Kind-Values:
let k be the value entry;
say the I6 superclass p of k for o.
To say the I6 superclass (p - a number) of (k - a number) for (o - an object): (- Dummy_object.inside_description({o},{k},{p}); -).
To decide which C is the superclass (name of kind of value C) for the (p - a number) of (t - a text) for (o - an object):
if t is a name listed in the Table of Kind-Values:
let k be the value entry;
let n be the I6 superclass p of k for o;
decide on n as a C. [we know the kind we're expecting, so just cast the numeric contents of the superclass property back to that kind]
To decide which number is the I6 superclass (p - a number) of (k - a number) for (o - an object): (- Dummy_object.grammar({o},{k},{p}); -).
[method (ii)]
To say the superclass (p - a number) of (k - a number) for (o - an object): (- Dummy_object.inside_description({o},{k},{p}); -).
To decide which C is the superclass (name of kind of value C) for the (p - a number) of (k - a number) for (o - an object):
let n be the I6 superclass p of k for o; [this was declared under method (i)] [We need this intermediate function and result to do the cast]
decide on n as a C. [we know the kind we're expecting, so just cast the numeric contents of the superclass property back to that kind]
Section - Other I7 phrases using subverted properties of the dummy-object
To say a/-- greeting via the/-- dummy-object: (- Dummy_object.capacity(); -).
To execute my function for (n - a number): (- Dummy_object.initial({n}); -).
To say ZSCII ranges via the/-- dummy-object: (- Dummy_object.add_to_scope(); -).
To decide which number is the/-- property id of (p - a property): (- ({p}-->1) -).
[in I7, a property p is actually the address of a word array of metadata relating to p, in which the second element, -->1, holds the id of the I6 property]
To say the/-- I7 name of (p - a property): (- print (string) {p}-->3 -).
[in I7, a property p is actually the address of a word array of metadata relating to p, in which the fourth element, -->3, holds a pointer to an I6 static string holding the textual name of the property]
To say a/-- veneer description of (p - a property): (- Dummy_object.articles(({p}-->1)); -). [ {p} is the address of a word array of metadata relating to the I7 property, in which the second element, -->1, holds the actual I6 property id to be passed to the veneer function]
Chapter - Testing Include ___ when defining ___
Section - Test phrases
When play begins:
say "[bold type]Call to a 'splat' function printing from a string Array declared with double-quoted text and with conditional compilation[roman type][line break]";
say "[a greeting via the dummy-object][paragraph break]"; [calls a splat routine to print some text]
say "[bold type]Calls to 'common property' functions printing/retrieving the value of a superclass property[roman type][paragraph break]";
say "[italic type]method (i):[roman type][paragraph break]";
[say the superclass property of a kind for an object that inherits from it, when we don't know what the nature of the superclass property is- I7 packed text, I6 String, I6 Routine, I6 Class, I6 Object), integer value]
say "The plural of person is ['][the superclass printed plural name property of person kind for Smaug]['].[paragraph break]";
[retrieve the value (of a known kind) in the superclass property of a kind for an object that inherits from it]
let obj be an object;
now obj is the superclass object for the lair property of person kind for Smaug;
say "The default lair of a person is [The obj].[line break]";
[or just retrieve the value and say it all in one go]
say "The default lair of an animal is [The superclass object for the lair property of animal kind for Smaug].[paragraph break]";
say "[italic type]method (ii):[roman type][paragraph break]";
[say the superclass property of a kind for an object that inherits from it, when we don't know what the nature of the superclass property is- I7 packed text, I6 String, I6 Routine, I6 Class, I6 Object, integer value)]
say "The usual hit-points of a person is [the superclass hit-points property of person-kind for Smaug].[line break]";
let num be a number;
now num is the superclass number for the hit-points property of animal-kind for Smaug;
say "The usual hit-points of an animal is [num].[line break]";
[retrieve the value (of a known kind) in the superclass property of a kind for an object that inherits from it]
say "The usual hit-points of a dragon is [The superclass number for the hit-points property of dragon-kind for Smaug].[paragraph break]";
[or just retrieve the value and say it all in one go]
say "[bold type]A loop calling a 'splat' routine that uses the 'to' switch syntax disallowed in I7 inclusions[roman type][line break]";
repeat with n running from 10 to 13: [ repeatedly call a splat routine that uses 'switch' syntax disallowed in I7 inclusions]
say "The mission Apollo [n] ";
execute my function for n;
say ".";
say line break;
say "[bold type]A system function (random()) replaced through the Replace directive[roman type][line break]";
say "Today's not very random number is [a random number between 1 and 10000].[paragraph break]"; [use a Replaced system function]
say "[bold type]Calling a veneer function[roman type][line break]";
say "The ['][italic type][I7 name of printed name][roman type]['] property has id [italic type][property id of printed name][roman type] and veneer description [italic type][veneer description of printed name][roman type].[paragraph break]"; [call a veneer function]
say "[bold type]Another 'splat' routine that uses the 'to' switch syntax, tabulating the ZSCII range of some ZSCII characters [roman type][line break]";
say ZSCII ranges via the dummy-object;
Section - Using a stub and other approaches that don't work
[ Using a stub, or the use of #ifndef or other conditional compilations in I6 inclusions, fails because any conditional compilation declared in normal I6 inclusions is performed by the I6->Inter compiler and so the conditional compilation directives are not passed through the I6->Inter->I6 compilation chain to the final I6 source file. This means that either the conditionally-compiled named entity is compiled by the I6->Inter compiler, in which case it will end up duplicating any separate declaration in a 'splat', or it is not, in which case the compilation will fail through the attempt to reference an undeclared named entity. So, if we try an Include such as-
Include (- #Stub ZSCII_range 1; -).
To say a/the/-- ZSCII range of (n - a number) via the/-- stub: (- print (ZSCII_range) {n}; -).
When play begins: say the ZSCII range of 209 via the stub.
-in order to allow reference in an I7 inclusion to a like-named routine ('ZSCII_range') included elsewhere 'when defining...' in a 'splat', the stub routine [ ZSCII_range x1; rfalse; ]; is duly compiled into the final I6, which ends up with duplicate 'ZSCII_range' functions with no conditional compilation.
Unfortunately, the dummy routine compiled from the stub always appears later in the final I6 than the 'splat', its position in the code being determined by the compiler with no authorial control. Consequently a Replace directive in the 'splat' can't rename the dummy stub routine, or replace it with the genuine one. A Replace directive can only rename or remove the earlier-occuring genuine routine declared in the 'splat'.
The end result is that when Inform attempts to compile the resulting 'auto.inf' file into a story file, a compiler error ensues when the stub-derived second declaration of 'ZSCII_range' is encountered: 'Error: "ZSCII_range" is a name already in use ...'
(Attempts to'trick' the compiler by including 'Constant ZSCII_range 0;' also fail. The I6->story file compiler may have little to no type recognition, but the I6->Inter compiler does, and recognises that 'ZSCII_range' should be a routine, not a constant. Compilation is aborted with ...'attempt to retrieve wrong pointer type as vanilla_function...')
Ultimately, for a 'ZSCII_range' routine to be directly referenced in an ordinary I6 inclusion, a 'ZSCII_range' routine must be compiled by the I6->Inter->I6 compilation chain, and this will inevitably result in a duplicate ZSCII_range routine in the 'auto.inf' file if one is also declared in a 'splat'.
It is of course possible as a last resort, but a bit of a nuisance, to hand-edit the resulting faulty auto.inf file to remove the duplicate dummy stub routine, then recompile auto.inf directly from the command line.
]
Part - Kinds
[
“Never lose a chance of saying a kind word.”
— William Makepeace Thackeray
]
[Some functions to facilitate the handling of kinds, which appear not to be a kind of values in Inform 7 and cannot therefore be iterated through, said, passed as parameters etc.]
Chapter - Object Kinds
Section - The KD-count property
An object has a number called KD-count.
The KD-count property translates into I6 as "KD_Count".
[This compiler-defined property is the index to the KindHierarchy array for the kind of this object]
Section - Text and numeric constants for kind names
[These are for use in phrases such as 'To say the superclass (p - a number) of (t - a text) for (o - an object):' where t is the I7 name of a kind]
[constant texts for built in kinds]
room kind is always "room".
thing kind is always "thing".
direction kind is always "direction".
door kind is always "door".
container kind is always "container".
supporter kind is always "supporter".
backdrop kind is always "backdrop".
person kind is always "person".
region kind is always "region".
man kind is always "man".
woman kind is always "woman".
animal kind is always "animal".
device kind is always "device".
vehicle kind is always "vehicle".
player's holdall kind is always "player's holdall".
[add constant texts for new kinds below (using their Inform 7 kind names)]
dragon kind is always "dragon".
dummy-kind kind is always "dummy-kind".
dummy-count-kind kind is always "dummy-count-kind".
[directly finding I6 memory addresses representing built in kinds]
To decide what number is room-kind: (- ((+ room +)) -).
To decide what number is thing-kind : (- ((+ thing +)) -).
To decide what number is direction-kind: (- ((+ direction +)) -).
To decide what number is door-kind: (- ((+ door +)) -).
To decide what number is container-kind: (- ((+ container +)) -).
To decide what number is supporter-kind: (- ((+ supporter +)) -).
To decide what number is backdrop-kind: (- ((+ backdrop +)) -).
To decide what number is person-kind: (- ((+ person +)) -).
To decide what number is region-kind: (- ((+ region +)) -).
To decide what number is man-kind: (- ((+ man +)) -).
To decide what number is woman-kind: (- ((+ woman +)) -).
To decide what number is animal-kind: (- ((+ animal +)) -).
To decide what number is device-kind: (- ((+ device +)) -).
To decide what number is vehicle-kind: (- ((+ vehicle +)) -).
To decide what number is player's-holdall-kind: (- ((+ player's holdall +)) -).
[add To decide phrases for new kinds below (using their Inform 7 kind names)]
To decide what number is dragon-kind: (- ((+ dragon +)) -).
To decide what number is dummy-kind-kind: (- ((+ dummy-kind +)) -).
To decide what number is dummy-count-kind-kind: (- ((+ dummy-count-kind +)) -).
Section - The number of kinds and the table of kind values
[This section
(i) initialises a variable to hold the total number of kinds compiled into the story file, so we can iterate through kinds
(ii) initialises a table holding low-level information about all the kinds, in a form readily-accessible to Inform 7]
The number of kinds is a number that varies.
The number of kinds variable translates into I6 as "kind_count".
Include (- Global kind_count = 0; -).
A startup rule (this is the initialise kinds rule):
count the kinds;
fill the table of kind-values.
The initialise kinds rule is listed before the update chronological records rule in the startup rules.
To count the kinds:
now the number of kinds is 0;
repeat with o running through objects:
if o provides the property KD-Count:
let kdc be the KD-Count of o;
if kdc > the number of kinds:
now the number of kinds is kdc;
[Inform does not compile a constant for the count of object kinds, so we must count them ourselves]
[We can't get this information from the KindsHierarchy array either, because it has no length entry or terminating entry.]
[NB This method only works properly if there is at least one object compiled with the highest kind id number (KD-Count)- this can be achieved by declaring a dummy kind and an object of that kind right at the end of the I7 source (see Section - Dummy-count-kind and -object later)]
To fill the table of kind-values:
let nk be the number of kinds;
while nk > 0:
choose a blank row in the Table of Kind-Values;
now the name entry is the substituted form of "[kind-name of kind-index nk]";
now the index entry is nk;
now the value entry is the value of kind nk;
now the inherits entry is the kind-index nk inherits from;
decrement nk;
[showme the contents of the Table of Kind-Values;] [comment this line out when not debugging]
Table of Kind-Values
name (text) index(number) value(number) inherits(number)
with 25 blank rows [extend this if necessary]
[name is the I7 name of the kind;
index is the kind id number, as held in KD_Count for each object and being an index into the I6 KindHierarchy array;
value is the I6 memory address of the metadata representing this kind: this can be passed to I6 phrases needing a class parameter
inherits is the kind id number of the kind that this kind most immediately inherits from]
Section - Functions from the Table of Kind-Values
To decide what text is the kind-name of the kind-address (n - a number):
if n is a value listed in the Table of Kind-Values:
decide on the name entry;
decide on "";
To decide what number is the kind-address of (o - an object):
if o is nothing:
decide on 0;
if the KD-count of o is an index listed in the Table of Kind-Values:
decide on the value entry;
decide on 0;
Section - Functions from the kind hierarchy
To say the/-- kind-name of kind-index (n - a number): (- print (I7_Kind_Name) KindHierarchy-->({n}*2); -).
To decide which number is the/-- kind-index (n - a number) inherits from: (- (KindHierarchy-->(({n}*2)+1)) -).
To decide which number is the value of kind (n - a number): (- (KindHierarchy-->({n}*2)) -).
To tabulate the kind hierarchy:
let nk be the number of kinds;
while nk > 0:
let ik be the kind-index nk inherits from;
say "[nk]: [kind-name of kind-index nk] ([value of kind nk]) -> ([kind-name of kind-index ik][if ik > 0]-[ik][end if])[line break]";
decrement nk;
To tabulate the kind hierarchy of (o - an object): (- PrintHierarchy({o}); -).
Include (-
[PrintHierarchy o i a;
print "(", (name) o, ")";
if (o provides KD_Count) {
i = o.KD_Count;
while (i > 0) {
a = i*2;
print "->", (I7_Kind_Name) KindHierarchy-->a;
i = KindHierarchy-->(a + 1);
}
}
];
-).
Chapter - Testing Kinds
When play begins:
say "[line break][bold type]----------------------- ### Testing Kinds Initialisation ### ---------------------[roman type][paragraph break]";
say "[bold type]Tabulating the kind hierarchy of an object[roman type][line break]";
tabulate the kind hierarchy of Smaug;
say paragraph break;
say "[bold type]The object kinds hierarchy (I6 KindHierarchy array)[line break]Index: name (I6 memory address) -> (inherits from name-index)[roman type][line break]";
tabulate the kind hierarchy;
say "(The number of object kinds is [number of kinds])[paragraph break]";
showme the contents of the Table of Kind-Values;
Part - Miscellaneous
Section - Casting
To decide which K is the (to-be-cast-value - a value) as a (name of kind of value K): (- {to-be-cast-value} -).
Section - Dummy-count-kind and -object (put this last in source)
dummy-count-kind is a kind.
dummy-count-object is a dummy-count-kind. [make sure there is at least one object with a kind-index above the base kinds]
Part - Appendix
Section - Common properties declared by Basic Inform and Standard Rules and their I7 equivalents
[
I6 name I7 name & kind comments
_________________ __________ ___________________________________________________________________
add_to_scope; ----
article; indefinite article (text) (do not redefine)
articles; ----
capacity; carrying capacity (number)
component_child; ----
component_parent; ----
component_sibling; ----
description; description (text)
door_dir; ----
door_to; ----
found_in; ----
grammar; ----
initial; initial appearance (text)
inside_description; ----
list_together; list grouping key (text) (do not redefine)
map_region; map region (object)
parse_name; ----
plural; printed plural name (text) (do not redefine)
regional_found_in; ----
room_index; ----
saved_short_name; ----
short_name; printed name (text) (do not redefine)
short_name_indef; ----
vector; ---- (do not redefine)
with_key; matching key (object)
KD_Count; ---- (do not redefine)
IK1_Count; ----
IK2_Count; ----
IK3_Count; ----
IK4_Count; ----
IK5_Count; ----
IK6_Count; ----
IK8_Count; ----
IK1_link; ----
IK2_link; ----
IK5_link; ----
IK6_link; ----
IK8_link; ----
]
Section - I6 Directives not recognised in kits or inclusions in V10+
[
### NOT RECOGNISED ###
Ifv3
Ifv5
Iffalse
Abbreviate
Dictionary
Import
Link
Lowstring
Message
Replace
Switches
Trace
Undef
Version
### RECOGNISED ###
#Ifdef (this and similar conditional compilation block markers will not be transmitted to the final I6 source file)
#Ifndef
#Iftrue
#Ifnot
#Endif
#OrigSource
#Stub (this and similar conditional compilation block markers will not be transmitted to the final I6 source file)
Constant
Global
Array (a number of previously-legal forms are no longer supported, in particular string type & double-quoted declarations like "Hello")
Attribute
Property
Verb (using this in an inclusion leads to "found a second definition of the name 'assim_gv' when loading '/main/WorldModelKit' "
Fake_action
Object
Default (this and similar conditional compilation block markers will not be transmitted to the final I6 source file)
]