| | 1 | #charset "us-ascii" |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright 2000, 2006 Michael J. Roberts. |
| | 5 | * |
| | 6 | * This file is part of TADS 3. |
| | 7 | * |
| | 8 | * This module defines the fundamental intrinsic classes, including Object, |
| | 9 | * String, Collection, List, and Iterator. |
| | 10 | */ |
| | 11 | |
| | 12 | #ifndef _SYSTYPE_H_ |
| | 13 | #define _SYSTYPE_H_ |
| | 14 | |
| | 15 | |
| | 16 | /* ------------------------------------------------------------------------ */ |
| | 17 | /* |
| | 18 | * TADS datatype codes. These values are returned by propType(), etc. |
| | 19 | */ |
| | 20 | #define TypeNil 1 |
| | 21 | #define TypeTrue 2 |
| | 22 | #define TypeObject 5 |
| | 23 | #define TypeProp 6 |
| | 24 | #define TypeInt 7 |
| | 25 | #define TypeSString 8 |
| | 26 | #define TypeDString 9 |
| | 27 | #define TypeList 10 |
| | 28 | #define TypeCode 11 |
| | 29 | #define TypeFuncPtr 12 |
| | 30 | #define TypeNativeCode 14 |
| | 31 | #define TypeEnum 15 |
| | 32 | |
| | 33 | |
| | 34 | /* ------------------------------------------------------------------------ */ |
| | 35 | /* |
| | 36 | * The root object class. All objects descend from this class. |
| | 37 | */ |
| | 38 | intrinsic class Object 'root-object/030004' |
| | 39 | { |
| | 40 | /* |
| | 41 | * Determine if I'm an instance or subclass of the given class 'cls'. |
| | 42 | * Note that x.ofKind(x) returns true - an object is of its own kind. |
| | 43 | */ |
| | 44 | ofKind(cls); |
| | 45 | |
| | 46 | /* get the list of direct superclasses of this object */ |
| | 47 | getSuperclassList(); |
| | 48 | |
| | 49 | /* determine if a property is defined or inherited by this object */ |
| | 50 | propDefined(prop, flags?); |
| | 51 | |
| | 52 | /* get the type of a property defined for this object */ |
| | 53 | propType(prop); |
| | 54 | |
| | 55 | /* |
| | 56 | * Get a list of my directly-defined properties. When called on |
| | 57 | * intrinsic class objects, this returns a list of properties defined |
| | 58 | * for instances of the class, as well as static properties of the |
| | 59 | * class. |
| | 60 | */ |
| | 61 | getPropList(); |
| | 62 | |
| | 63 | /* |
| | 64 | * get parameter list information for the given method - returns a |
| | 65 | * list: [minimumArgc, optionalArgc, varargs], where minimumArgc is |
| | 66 | * the minimum number of arguments, optionalArgc is the number of |
| | 67 | * additional optional arguments, and varargs is true if the function |
| | 68 | * takes a varying number of arguments greater than or equal to the |
| | 69 | * minimum, nil if not. |
| | 70 | */ |
| | 71 | getPropParams(prop); |
| | 72 | |
| | 73 | /* |
| | 74 | * determine if I'm a "class" object - returns true if the object was |
| | 75 | * defined with the "class" keyword, nil otherwise |
| | 76 | */ |
| | 77 | isClass(); |
| | 78 | |
| | 79 | /* |
| | 80 | * Determine if a property is inherited further from the given object. |
| | 81 | * definingObj is usually the value of the 'definingobj' |
| | 82 | * pseudo-variable, and origTargetObj is usually the value of the |
| | 83 | * 'targetobj' pseudo-variable. |
| | 84 | */ |
| | 85 | propInherited(prop, origTargetObj, definingObj, flags?); |
| | 86 | |
| | 87 | /* determine if this instance is transient */ |
| | 88 | isTransient(); |
| | 89 | } |
| | 90 | |
| | 91 | /* |
| | 92 | * propDefined() flags |
| | 93 | */ |
| | 94 | #define PropDefAny 1 |
| | 95 | #define PropDefDirectly 2 |
| | 96 | #define PropDefInherits 3 |
| | 97 | #define PropDefGetClass 4 |
| | 98 | |
| | 99 | |
| | 100 | /* ------------------------------------------------------------------------ */ |
| | 101 | /* |
| | 102 | * The IntrinsicClass intrinsic class. Objects of this type represent the |
| | 103 | * intrinsic classes themselves. |
| | 104 | */ |
| | 105 | intrinsic class IntrinsicClass 'intrinsic-class/030000': Object |
| | 106 | { |
| | 107 | } |
| | 108 | |
| | 109 | /* |
| | 110 | * Intrinsic class modifier object (for internal compiler use only) |
| | 111 | */ |
| | 112 | intrinsic class IntrinsicClassModifier 'int-class-mod/030000' |
| | 113 | { |
| | 114 | } |
| | 115 | |
| | 116 | /* ------------------------------------------------------------------------ */ |
| | 117 | /* |
| | 118 | * The native collection type - this is the base class for lists, vectors, |
| | 119 | * and other objects that represent collections of values. |
| | 120 | */ |
| | 121 | intrinsic class Collection 'collection/030000': Object |
| | 122 | { |
| | 123 | /* |
| | 124 | * Create an iterator for the collection. This returns a new Iterator |
| | 125 | * object that can be used to iterate over the values in the |
| | 126 | * collection. The Iterator will use a snapshot of the collection that |
| | 127 | * will never change, even if the collection is changed after the |
| | 128 | * iterator is created. |
| | 129 | */ |
| | 130 | createIterator(); |
| | 131 | |
| | 132 | /* |
| | 133 | * Create a "live iterator" for the collection. This returns a new |
| | 134 | * Iterator object that refers directly to the original collection; if |
| | 135 | * the original collection changes, the iterator will reflect the |
| | 136 | * changes in its iteration. As a result, the iterator is not |
| | 137 | * guaranteed to visit all of the elements in the collection if the |
| | 138 | * collection changes during the course of the iteration. If |
| | 139 | * consistent results are required, use createIterator() instead. |
| | 140 | */ |
| | 141 | createLiveIterator(); |
| | 142 | } |
| | 143 | |
| | 144 | /* ------------------------------------------------------------------------ */ |
| | 145 | /* |
| | 146 | * The native iterator type - this is the base class for all iterators. |
| | 147 | * This class is abstract and is thus never directly instantiated. |
| | 148 | * |
| | 149 | * Note that iterators can never be created directly with the 'new' |
| | 150 | * operator. Instead, iterators must be obtained from a collection via the |
| | 151 | * collection's createIterator() method. |
| | 152 | */ |
| | 153 | intrinsic class Iterator 'iterator/030001': Object |
| | 154 | { |
| | 155 | /* |
| | 156 | * Get the next item in the collection. This returns the next item's |
| | 157 | * value, and advances the internal state in the iterator so that a |
| | 158 | * subsequent call to getNext() returns the next item after this one. |
| | 159 | * When the iterator is first created, or after calling |
| | 160 | * resetIterator(), this returns the first item in the collection. |
| | 161 | */ |
| | 162 | getNext(); |
| | 163 | |
| | 164 | /* |
| | 165 | * Determine if the collection is out of items. Returns true if |
| | 166 | * getNext() will return a valid item, nil if no more items are |
| | 167 | * available. |
| | 168 | */ |
| | 169 | isNextAvailable(); |
| | 170 | |
| | 171 | /* |
| | 172 | * Reset to the first item. After calling this routine, the next call |
| | 173 | * to getNext() will return the first item in the collection. |
| | 174 | */ |
| | 175 | resetIterator(); |
| | 176 | |
| | 177 | /* |
| | 178 | * Get the current key. This returns the value of the key for the |
| | 179 | * current item in the collection. For an indexed collection, this |
| | 180 | * returns the index value; for a keyed collection, this returns the |
| | 181 | * current key value. |
| | 182 | */ |
| | 183 | getCurKey(); |
| | 184 | |
| | 185 | /* |
| | 186 | * Get the current value. This returns the value of the current item |
| | 187 | * in the collection. |
| | 188 | */ |
| | 189 | getCurVal(); |
| | 190 | } |
| | 191 | |
| | 192 | /* |
| | 193 | * Indexed object iterator - this type of iterator is used for lists, |
| | 194 | * vectors, and other indexed collection objects. |
| | 195 | */ |
| | 196 | intrinsic class IndexedIterator 'indexed-iterator/030000': Iterator |
| | 197 | { |
| | 198 | } |
| | 199 | |
| | 200 | |
| | 201 | /* ------------------------------------------------------------------------ */ |
| | 202 | /* |
| | 203 | * AnonFuncPtr depends on Vector |
| | 204 | */ |
| | 205 | #include "vector.h" |
| | 206 | |
| | 207 | /* |
| | 208 | * Anonymous function pointer intrinsic class |
| | 209 | */ |
| | 210 | intrinsic class AnonFuncPtr 'anon-func-ptr': Vector |
| | 211 | { |
| | 212 | } |
| | 213 | |
| | 214 | |
| | 215 | /* ------------------------------------------------------------------------ */ |
| | 216 | /* |
| | 217 | * The "TADS Object" intrinsic class. All objects that the program |
| | 218 | * defines with the "class" or "object" statements descend from this |
| | 219 | * class. |
| | 220 | */ |
| | 221 | intrinsic class TadsObject 'tads-object/030004': Object |
| | 222 | { |
| | 223 | /* |
| | 224 | * Create an instance of this object: in other words, create a new |
| | 225 | * object whose superclass is this object. The arguments provided are |
| | 226 | * passed to the new object's constructor. This method returns a |
| | 227 | * reference to the new object. |
| | 228 | */ |
| | 229 | createInstance(...); |
| | 230 | |
| | 231 | /* |
| | 232 | * Create a clone of this object. This creates an exact copy, with |
| | 233 | * the same property values, as the original. This does not call any |
| | 234 | * constructors; it merely instantiates an exact copy of the original. |
| | 235 | * |
| | 236 | * Note that the clone is a "shallow" copy, which means that any |
| | 237 | * objects it references are not themselves cloned. |
| | 238 | */ |
| | 239 | createClone(); |
| | 240 | |
| | 241 | /* |
| | 242 | * Create a transient instance of this object. This works just like |
| | 243 | * createInstance(), but creates a transient instance instead of an |
| | 244 | * ordinary (persistent) instance. |
| | 245 | */ |
| | 246 | createTransientInstance(...); |
| | 247 | |
| | 248 | /* |
| | 249 | * Create an instance of an object based on multiple superclasses. |
| | 250 | * Each argument gives a superclass, and optionally arguments for |
| | 251 | * invoking the superclass constructor. If an argument is given as |
| | 252 | * simply a class, then we don't invoke that superclass's constructor; |
| | 253 | * if the argument is given as a list, the first element of the list is |
| | 254 | * the class, and the remaining elements of the list are arguments for |
| | 255 | * that superclass's constructor. The arguments are specified in the |
| | 256 | * same order they would be to define the object, so the first argument |
| | 257 | * is the dominant superclass. |
| | 258 | * |
| | 259 | * For example, suppose we created a class definition like this: |
| | 260 | * |
| | 261 | * class D: A, B, C |
| | 262 | *. construct(x, y) |
| | 263 | *. { |
| | 264 | *. inherited A(x); |
| | 265 | *. inherited C(y); |
| | 266 | *. } |
| | 267 | *. ; |
| | 268 | * |
| | 269 | * We could obtain the same effect dynamically like so: |
| | 270 | * |
| | 271 | * local d = TadsObject.createInstanceOf([A, x], B, [C, y]); |
| | 272 | */ |
| | 273 | static createInstanceOf(...); |
| | 274 | |
| | 275 | /* |
| | 276 | * Create a transient instance based on multiple superclasses. This |
| | 277 | * works just like createInstanceOf(), but creates a transient |
| | 278 | * instance. |
| | 279 | */ |
| | 280 | static createTransientInstanceOf(...); |
| | 281 | |
| | 282 | /* |
| | 283 | * Set the superclass list. scList is a list giving the new |
| | 284 | * superclasses. The superclasses must all be TadsObject objects, with |
| | 285 | * one exception: the list [TadsObject] may be passed to create an |
| | 286 | * object based directly on TadsObject. No other intrinsic classes can |
| | 287 | * be used in the list, and objects of other types cannot be used in |
| | 288 | * the list. |
| | 289 | */ |
| | 290 | setSuperclassList(scList); |
| | 291 | } |
| | 292 | |
| | 293 | |
| | 294 | /* ------------------------------------------------------------------------ */ |
| | 295 | /* |
| | 296 | * We need CharacterSet and ByteArray (for String.mapToByteArray). (But |
| | 297 | * wait to include these until after we've defined Object, since everything |
| | 298 | * depends on Object.) |
| | 299 | */ |
| | 300 | #include "charset.h" |
| | 301 | #include "bytearr.h" |
| | 302 | |
| | 303 | /* ------------------------------------------------------------------------ */ |
| | 304 | /* |
| | 305 | * The native string type. |
| | 306 | */ |
| | 307 | intrinsic class String 'string/030005': Object |
| | 308 | { |
| | 309 | /* get the length of the string */ |
| | 310 | length(); |
| | 311 | |
| | 312 | /* extract a substring */ |
| | 313 | substr(start, len?); |
| | 314 | |
| | 315 | /* convert to upper case */ |
| | 316 | toUpper(); |
| | 317 | |
| | 318 | /* convert to lower case */ |
| | 319 | toLower(); |
| | 320 | |
| | 321 | /* find a substring */ |
| | 322 | find(str, index?); |
| | 323 | |
| | 324 | /* |
| | 325 | * convert to a list of Unicode character codes, or get the Unicode |
| | 326 | * character code for the single character at the given index |
| | 327 | */ |
| | 328 | toUnicode(idx?); |
| | 329 | |
| | 330 | /* htmlify a string */ |
| | 331 | htmlify(flags?); |
| | 332 | |
| | 333 | /* determine if we start with the given string */ |
| | 334 | startsWith(str); |
| | 335 | |
| | 336 | /* determine if we end with the given string */ |
| | 337 | endsWith(str); |
| | 338 | |
| | 339 | /* |
| | 340 | * Map to a byte array, converting to the given character set. |
| | 341 | * 'charset' must be an object of intrinsic class CharacterSet; the |
| | 342 | * characters in the string will be mapped from the internal Unicode |
| | 343 | * representation to the appropriate byte representation in the given |
| | 344 | * character set. |
| | 345 | */ |
| | 346 | mapToByteArray(charset); |
| | 347 | |
| | 348 | /* |
| | 349 | * Replace one occurrence or all occurrences of the given substring |
| | 350 | * with the given new string. |
| | 351 | */ |
| | 352 | findReplace(origStr, newStr, flags, index?); |
| | 353 | } |
| | 354 | |
| | 355 | /* |
| | 356 | * Flags for String.htmlify |
| | 357 | */ |
| | 358 | |
| | 359 | /* |
| | 360 | * translate spaces - each space in a run of multiple spaces is converted |
| | 361 | * to an sequence |
| | 362 | */ |
| | 363 | #define HtmlifyTranslateSpaces 0x0001 |
| | 364 | |
| | 365 | /* translate newlines - converts each \n character to a <br> tag */ |
| | 366 | #define HtmlifyTranslateNewlines 0x0002 |
| | 367 | |
| | 368 | /* translate tabs - converts each \t character to a <tab> tag */ |
| | 369 | #define HtmlifyTranslateTabs 0x0004 |
| | 370 | |
| | 371 | /* |
| | 372 | * Translate all whitespace characters - translate all spaces, tabs, and |
| | 373 | * newlines into their HTML equivalents: each space character becomes an |
| | 374 | * '  sequence;', each '\n' character becomes a <br> tag; and each |
| | 375 | * '\t' character becomes a <tab> tag. |
| | 376 | */ |
| | 377 | #define HtmlifyTranslateWhitespace \ |
| | 378 | (HtmlifyTranslateSpaces | HtmlifyTranslateNewlines | HtmlifyTranslateTabs) |
| | 379 | |
| | 380 | |
| | 381 | /* |
| | 382 | * Flags for String.findReplace |
| | 383 | */ |
| | 384 | |
| | 385 | /* replace only one occurrence, or replace all occurrences */ |
| | 386 | #define ReplaceOnce 0x0000 |
| | 387 | #define ReplaceAll 0x0001 |
| | 388 | |
| | 389 | |
| | 390 | /* ------------------------------------------------------------------------ */ |
| | 391 | /* |
| | 392 | * The native list type |
| | 393 | */ |
| | 394 | intrinsic class List 'list/030007': Collection |
| | 395 | { |
| | 396 | /* |
| | 397 | * Select a subset of the list: returns a new list consisting only |
| | 398 | * of the elements for which the callback function 'func' returns |
| | 399 | * true. |
| | 400 | */ |
| | 401 | subset(func); |
| | 402 | |
| | 403 | /* |
| | 404 | * Apply the callback function to each element of this list, and |
| | 405 | * return a new list consisting of the results. Effectively maps |
| | 406 | * the list to a new list using the given function. Suppose the |
| | 407 | * original list is |
| | 408 | * |
| | 409 | * [x, y, z] |
| | 410 | * |
| | 411 | * Then the result list is |
| | 412 | * |
| | 413 | * [func(x), func(y), func(z)] |
| | 414 | */ |
| | 415 | mapAll(func); |
| | 416 | |
| | 417 | /* get the number of elements in the list */ |
| | 418 | length(); |
| | 419 | |
| | 420 | /* extract a sublist */ |
| | 421 | sublist(start, len?); |
| | 422 | |
| | 423 | /* intersect with another list */ |
| | 424 | intersect(other); |
| | 425 | |
| | 426 | /* get the index of the first match for the given value */ |
| | 427 | indexOf(val); |
| | 428 | |
| | 429 | /* car/cdr - head/tail of list */ |
| | 430 | car(); |
| | 431 | cdr(); |
| | 432 | |
| | 433 | /* |
| | 434 | * Find the first element for which the given condition is true, and |
| | 435 | * return the index of the element. Applies the callback function |
| | 436 | * (which encodes the condition to evaluate) to each element in |
| | 437 | * turn, starting with the first. For each element, if the callback |
| | 438 | * returns nil, proceeds to the next element; otherwise, stops and |
| | 439 | * returns the index of the element. If the callback never returns |
| | 440 | * true for any element, we'll return nil. |
| | 441 | */ |
| | 442 | indexWhich(cond); |
| | 443 | |
| | 444 | /* |
| | 445 | * Invoke the callback func(val) on each element, in order from first |
| | 446 | * to last. No return value. |
| | 447 | */ |
| | 448 | forEach(func); |
| | 449 | |
| | 450 | /* |
| | 451 | * Find the first element for which the given condition is true, and |
| | 452 | * return the value of the element. Returns nil if no item |
| | 453 | * satisfies the condition. |
| | 454 | */ |
| | 455 | valWhich(cond); |
| | 456 | |
| | 457 | /* find the last element with the given value, and return its index */ |
| | 458 | lastIndexOf(val); |
| | 459 | |
| | 460 | /* |
| | 461 | * Find the last element for which the condition is true, and return |
| | 462 | * the index of the element. Applies the callback to each element |
| | 463 | * in turn, starting with the last element and working backwards. |
| | 464 | * For each element, if the callback returns nil, proceeds to the |
| | 465 | * previous element; otherwise, stops and returns the index of the |
| | 466 | * element. If the callback never returns true for any element, |
| | 467 | * we'll return nil. |
| | 468 | */ |
| | 469 | lastIndexWhich(cond); |
| | 470 | |
| | 471 | /* |
| | 472 | * Find the last element for which the condition is true, and return |
| | 473 | * the value of the element |
| | 474 | */ |
| | 475 | lastValWhich(cond); |
| | 476 | |
| | 477 | /* count the number of elements with the given value */ |
| | 478 | countOf(val); |
| | 479 | |
| | 480 | /* count the number of elements for which the callback returns true */ |
| | 481 | countWhich(cond); |
| | 482 | |
| | 483 | /* get a new list consisting of the unique elements of this list */ |
| | 484 | getUnique(); |
| | 485 | |
| | 486 | /* |
| | 487 | * append the elements of the list 'lst' to the elements of this |
| | 488 | * list, then remove repeated elements in the result; returns a new |
| | 489 | * list with the unique elements of the combination of the two lists |
| | 490 | */ |
| | 491 | appendUnique(lst); |
| | 492 | |
| | 493 | /* |
| | 494 | * append an element - this works almost exactly like the |
| | 495 | * concatation operator ('+'), but if the argument is a list, this |
| | 496 | * simply adds the list as a new element, rather than adding each |
| | 497 | * element of the list as a separate element |
| | 498 | */ |
| | 499 | append(val); |
| | 500 | |
| | 501 | /* |
| | 502 | * Sort the list, returning a new list. If the 'descending' flag is |
| | 503 | * provided and is not nil, we'll sort the list in descending order |
| | 504 | * rather than ascending order. |
| | 505 | * |
| | 506 | * If the 'comparisonFunction' argument is provided, it must be a |
| | 507 | * callback function; the callback takes two arguments, and returns |
| | 508 | * an integer less than zero if the first argument value is less |
| | 509 | * than the second, zero if they're equal, and an integer greater |
| | 510 | * than zero if the first is greater than the second. If no |
| | 511 | * 'comparisonFunction' argument is provided, or it's provided and |
| | 512 | * its value is nil, we'll simply compare the list elements as |
| | 513 | * ordinary values. The comparison function can be provided for |
| | 514 | * caller-defined orderings, such as ordering a set of objects. |
| | 515 | */ |
| | 516 | sort(descending?, comparisonFunction?); |
| | 517 | |
| | 518 | /* |
| | 519 | * Prepend an element - this inserts the value before the first |
| | 520 | * existing element. |
| | 521 | */ |
| | 522 | prepend(val); |
| | 523 | |
| | 524 | /* |
| | 525 | * Insert one or more elements at the given index. If the index is 1, |
| | 526 | * the elements will be inserted before the first existing element. |
| | 527 | * If the index is one higher than the number of elements, the |
| | 528 | * elements will be inserted after all existing elements. |
| | 529 | * |
| | 530 | * Note that a list value argument will simply be inserted as a single |
| | 531 | * element. |
| | 532 | * |
| | 533 | * Returns a new list with the value(s) inserted. |
| | 534 | */ |
| | 535 | insertAt(startingIndex, val, ...); |
| | 536 | |
| | 537 | /* |
| | 538 | * Delete the element at the given index, reducing the length of the |
| | 539 | * list by one element. Returns a new list with the given element |
| | 540 | * removed. |
| | 541 | */ |
| | 542 | removeElementAt(index); |
| | 543 | |
| | 544 | /* |
| | 545 | * Delete the range of elements starting at startingIndex and ending |
| | 546 | * at endingIndex. The elements at the ends of the range are included |
| | 547 | * in the deletion. If startingIndex == endingIndex, only one element |
| | 548 | * is removed. Returns a new list with the given element range |
| | 549 | * removed. |
| | 550 | */ |
| | 551 | removeRange(startingIndex, endingIndex); |
| | 552 | |
| | 553 | /* |
| | 554 | * Invoke the callback func(index, val) on each element, in order from |
| | 555 | * first to last. No return value. |
| | 556 | */ |
| | 557 | forEachAssoc(func); |
| | 558 | } |
| | 559 | |
| | 560 | /* |
| | 561 | * Sorting order flags. These can be passed in as the first argument to |
| | 562 | * List.sort() (and Vector.sort() as well) to make the meaning of the |
| | 563 | * argument clearer. |
| | 564 | */ |
| | 565 | #define SortAsc nil |
| | 566 | #define SortDesc true |
| | 567 | |
| | 568 | |
| | 569 | /* ------------------------------------------------------------------------ */ |
| | 570 | /* |
| | 571 | * 'RexPattern' intrinsic class. This class encapsulates a compiled |
| | 572 | * regular expression pattern. |
| | 573 | * |
| | 574 | * A RexPattern object can be passed to the regular expression functions |
| | 575 | * (rexMatch, rexSearch, rexReplace) in place of a string pattern. Since |
| | 576 | * compiling a regular expression takes a non-trivial amount of time, it's |
| | 577 | * more efficient to compile a pattern to a RexPattern object if the same |
| | 578 | * pattern will be used in multiple searches. |
| | 579 | */ |
| | 580 | intrinsic class RexPattern 'regex-pattern/030000': Object |
| | 581 | { |
| | 582 | /* |
| | 583 | * Constructor: |
| | 584 | * |
| | 585 | * new RexPattern(patternString) - returns a RexPattern representing |
| | 586 | * the compiled pattern string. |
| | 587 | */ |
| | 588 | |
| | 589 | /* retrieve the original pattern string used to construct the object */ |
| | 590 | getPatternString(); |
| | 591 | } |
| | 592 | |
| | 593 | #endif /* _SYSTYPE_H_ */ |