#charset "us-ascii"
#pragma once
/*
* Copyright 2000, 2006 Michael J. Roberts.
*
* This file is part of TADS 3.
*
* This module defines the fundamental intrinsic classes, including Object,
* String, Collection, List, and Iterator.
*/
/* ------------------------------------------------------------------------ */
/*
* TADS datatype codes. These values are returned by propType(), etc.
*/
#define TypeNil 1
#define TypeTrue 2
#define TypeObject 5
#define TypeProp 6
#define TypeInt 7
#define TypeSString 8
#define TypeDString 9
#define TypeList 10
#define TypeCode 11
#define TypeFuncPtr 12
#define TypeNativeCode 14
#define TypeEnum 15
#define TypeBifPtr 16
/* ------------------------------------------------------------------------ */
/*
* The root object class. All objects descend from this class.
*/
intrinsic class Object 'root-object/030004'
{
/*
* Determine if I'm an instance or subclass of the given class 'cls'.
* Note that x.ofKind(x) returns true - an object is of its own kind.
*/
ofKind(cls);
/* get the list of direct superclasses of this object */
getSuperclassList();
/* determine if a property is defined or inherited by this object */
propDefined(prop, flags?);
/* get the type of a property defined for this object */
propType(prop);
/*
* Get a list of my directly-defined properties. When called on
* intrinsic class objects, this returns a list of properties defined
* for instances of the class, as well as static properties of the
* class.
*/
getPropList();
/*
* get parameter list information for the given method - returns a
* list: [minimumArgc, optionalArgc, varargs], where minimumArgc is
* the minimum number of arguments, optionalArgc is the number of
* additional optional arguments, and varargs is true if the function
* takes a varying number of arguments greater than or equal to the
* minimum, nil if not.
*/
getPropParams(prop);
/*
* determine if I'm a "class" object - returns true if the object was
* defined with the "class" keyword, nil otherwise
*/
isClass();
/*
* Determine if a property is inherited further from the given object.
* definingObj is usually the value of the 'definingobj'
* pseudo-variable, and origTargetObj is usually the value of the
* 'targetobj' pseudo-variable.
*/
propInherited(prop, origTargetObj, definingObj, flags?);
/* determine if this instance is transient */
isTransient();
}
/*
* propDefined() flags
*/
#define PropDefAny 1
#define PropDefDirectly 2
#define PropDefInherits 3
#define PropDefGetClass 4
/* export the objToString method */
property objToString;
export objToString 'objToString';
/* ------------------------------------------------------------------------ */
/*
* The IntrinsicClass intrinsic class. Objects of this type represent the
* intrinsic classes themselves.
*/
intrinsic class IntrinsicClass 'intrinsic-class/030001': Object
{
/*
* Class method: is the given value an IntrinsicClass object? This
* returns true if so, nil if not.
*
* It's not possible to determine if an object is an IntrinsicClass
* object using x.ofKind(IntrinsicClass) or via x.getSuperclassList().
* This is because those methods traverse the nominal class tree:
* [1,2,3] is a List, and List is an Object. However, List and Object
* themselves are represented by IntrinsicClass instances, and it's
* occasionally useful to know if you're dealing with such an object.
* That's where this method comes in.
*
* This method returns nil for instances of an intrinsic class. For
* example, isIntrinsicClass([1,2,3]) returns nil, because [1,2,3] is a
* List instance. If you get the superclass list for [1,2,3], though,
* that will be [List], and isIntrinsicClass(List) returns true.
*/
isIntrinsicClass(obj);
}
/*
* Intrinsic class modifier object (for internal compiler use only)
*/
intrinsic class IntrinsicClassModifier 'int-class-mod/030000'
{
}
/* ------------------------------------------------------------------------ */
/*
* The native collection type - this is the base class for lists, vectors,
* and other objects that represent collections of values.
*/
intrinsic class Collection 'collection/030000': Object
{
/*
* Create an iterator for the collection. This returns a new Iterator
* object that can be used to iterate over the values in the
* collection. The Iterator will use a snapshot of the collection that
* will never change, even if the collection is changed after the
* iterator is created.
*/
createIterator();
/*
* Create a "live iterator" for the collection. This returns a new
* Iterator object that refers directly to the original collection; if
* the original collection changes, the iterator will reflect the
* changes in its iteration. As a result, the iterator is not
* guaranteed to visit all of the elements in the collection if the
* collection changes during the course of the iteration. If
* consistent results are required, use createIterator() instead.
*/
createLiveIterator();
}
/* ------------------------------------------------------------------------ */
/*
* The native iterator type - this is the base class for all iterators.
* This class is abstract and is thus never directly instantiated.
*
* Note that iterators can never be created directly with the 'new'
* operator. Instead, iterators must be obtained from a collection via the
* collection's createIterator() method.
*/
intrinsic class Iterator 'iterator/030001': Object
{
/*
* Get the next item in the collection. This returns the next item's
* value, and advances the internal state in the iterator so that a
* subsequent call to getNext() returns the next item after this one.
* When the iterator is first created, or after calling
* resetIterator(), this returns the first item in the collection.
*/
getNext();
/*
* Determine if the collection is out of items. Returns true if
* getNext() will return a valid item, nil if no more items are
* available.
*/
isNextAvailable();
/*
* Reset to the first item. After calling this routine, the next call
* to getNext() will return the first item in the collection.
*/
resetIterator();
/*
* Get the current key. This returns the value of the key for the
* current item in the collection. For an indexed collection, this
* returns the index value; for a keyed collection, this returns the
* current key value.
*/
getCurKey();
/*
* Get the current value. This returns the value of the current item
* in the collection.
*/
getCurVal();
}
/*
* Indexed object iterator - this type of iterator is used for lists,
* vectors, and other indexed collection objects.
*/
intrinsic class IndexedIterator 'indexed-iterator/030000': Iterator
{
}
/* ------------------------------------------------------------------------ */
/*
* AnonFuncPtr depends on Vector
*/
#include "vector.h"
/*
* Anonymous function pointer intrinsic class
*/
intrinsic class AnonFuncPtr 'anon-func-ptr': Vector
{
}
/* ------------------------------------------------------------------------ */
/*
* The "TADS Object" intrinsic class. All objects that the program
* defines with the "class" or "object" statements descend from this
* class.
*/
intrinsic class TadsObject 'tads-object/030005': Object
{
/*
* Create an instance of this object: in other words, create a new
* object whose superclass is this object. The arguments provided are
* passed to the new object's constructor. This method returns a
* reference to the new object.
*/
createInstance(...);
/*
* Create a clone of this object. This creates an exact copy, with
* the same property values, as the original. This does not call any
* constructors; it merely instantiates an exact copy of the original.
*
* Note that the clone is a "shallow" copy, which means that any
* objects it references are not themselves cloned.
*/
createClone();
/*
* Create a transient instance of this object. This works just like
* createInstance(), but creates a transient instance instead of an
* ordinary (persistent) instance.
*/
createTransientInstance(...);
/*
* Create an instance of an object based on multiple superclasses.
* Each argument gives a superclass, and optionally arguments for
* invoking the superclass constructor. If an argument is given as
* simply a class, then we don't invoke that superclass's constructor;
* if the argument is given as a list, the first element of the list is
* the class, and the remaining elements of the list are arguments for
* that superclass's constructor. The arguments are specified in the
* same order they would be to define the object, so the first argument
* is the dominant superclass.
*
* For example, suppose we created a class definition like this:
*
* class D: A, B, C
*. construct(x, y)
*. {
*. inherited A(x);
*. inherited C(y);
*. }
*. ;
*
* We could obtain the same effect dynamically like so:
*
* local d = TadsObject.createInstanceOf([A, x], B, [C, y]);
*
* Note that only *actual* lists are interpreted as constructor
* invokers here. A list-like object (with operator[] and length()
* methods) will be treated as a simple superclass, since otherwise it
* wouldn't be possible to specify the no-constructor format for such a
* superclass.
*/
static createInstanceOf(...);
/*
* Create a transient instance based on multiple superclasses. This
* works just like createInstanceOf(), but creates a transient
* instance.
*/
static createTransientInstanceOf(...);
/*
* Set the superclass list. scList is a list giving the new
* superclasses. The superclasses must all be TadsObject objects, with
* one exception: the list [TadsObject] may be passed to create an
* object based directly on TadsObject. No other intrinsic classes can
* be used in the list, and objects of other types cannot be used in
* the list.
*/
setSuperclassList(scList);
/*
* Get a method value. If the property is a method, this returns a
* function pointer to the method; this does NOT evaluate the method.
* If the property is not a method, this returns nil.
*
* The returned function pointer can be called like an ordinary
* function, but such a call will have no 'self' value, so the
* disembodied method won't be able to refer to properties or methods
* of 'self'. The main use of this method is to get a method of one
* object to assign as a method of another object using setMethod().
*/
getMethod(prop);
/*
* Set a method value. Assigns the given function (which must be a
* function pointer value) to the given property of 'self'. This
* effectively adds a new method to the object.
*
* The function can be an ordinary named function, or a method pointer
* retrieved from this object or from another object with getMethod().
* Anonymous functions are NOT allowed here.
*/
setMethod(prop, func);
}
/* ------------------------------------------------------------------------ */
/*
* We need CharacterSet and ByteArray (for String.mapToByteArray). (But
* wait to include these until after we've defined Object, since everything
* depends on Object.)
*/
#include "charset.h"
#include "bytearr.h"
/* ------------------------------------------------------------------------ */
/*
* The native string type.
*/
intrinsic class String 'string/030008': Object
{
/* get the length of the string */
length();
/* extract a substring */
substr(start, len?);
/* convert to upper case */
toUpper();
/* convert to lower case */
toLower();
/*
* Find a substring or pattern within the subject string (self),
* searching the string from left to right returning the index of the
* first match found. If 'str' is a string, this searches for an exact
* match to the substring. If 'str' is a RexPattern object, this
* searches for a match to the pattern. Returns the character index of
* the start of the match if found (the first character is at index 1),
* or nil if no match is found.
*
* 'index' is the optional starting index for the search. the first
* character is at index 1; a negative index specifies an offset from
* the end of the string, with -1 indicating the last character, -2 the
* second to last, and so on. If 'index' is omitted, the search starts
* at the first character. Note that the search proceeds from left to
* right even if 'index' is negative - a negative starting index is
* just a convenience to specify an offset from the end of the string,
* but the search still proceeds in the same direction.
*
* (Note: "left to right" in this context simply means from lower to
* higher character index in the string. We're using the term loosely,
* in particular ignoring anything related to the reading order or
* display direction for different languages or scripts.)
*/
find(str, index?);
/*
* convert to a list of Unicode character codes, or get the Unicode
* character code for the single character at the given index
*/
toUnicode(idx?);
/* htmlify a string */
htmlify(flags?);
/* determine if we start with the given string */
startsWith(str);
/* determine if we end with the given string */
endsWith(str);
/*
* Map to a byte array, converting to the given character set. If
* 'charset' is provided, it must be an object of intrinsic class
* CharacterSet, or a string giving the name of a character set. The
* characters in the string are mapped from the internal Unicode
* representation to the appropriate byte representation in the given
* character set. Any unmappable characters are replaced with the
* usual default/missing character for the set, as defined by the
* mapping.
*
* If 'charset' is omitted or nil, the byte array is created simply by
* treating the Unicode character code of each character in the string
* as a byte value. A byte can only hold values from 0 to 255, so a
* numeric overflow error is thrown if any character code in the source
* string is outside this range.
*
*/
mapToByteArray(charset?);
/*
* Replace one occurrence or all occurrences of the given substring
* with the given new string.
*
* 'self' is the subject string, which we search for instances of the
* replacement.
*
* 'origStr' is the string to search for within 'self'. This is
* treated as a literal text substring to find within 'self'.
* 'origStr' can alternatively be a RexPattern object, in which case
* the regular expression is matched.
*
* 'newStr' is the replacement text, as a string. 'newStr' can
* alternatively be a function (regular or anonymous) instead of a
* string. In this case, it's invoked as 'newStr(match, index, orig)'
* for each match where 'match' is the matching text, 'index' is the
* index within the original subject string of the match, and 'orig' is
* the full original subject string. This function must return a
* string value, which is used as the replacement text. Using a
* function allows greater flexibility in specifying the replacement,
* since it can vary the replacement according to the actual text
* matched and its position in the subject string.
*
* 'flags' is a combination of ReplaceXxx flags specifying the search
* options. It's optional; if omitted, the default is ReplaceAll.
*
* ReplaceOnce and ReplaceAll are mutually exclusive; they mean,
* respectively, that only the first occurrence of the match should be
* replaced, or that every occurrence should be replaced. ReplaceOnce
* and ReplaceAll are ignored if a 'limit' value is specified (this is
* true even if 'limit' is nil, which means that all occurrences are
* replaced).
*
* 'index' is the starting index within 'self' for the search. If this
* is given, we'll ignore any matches that start before the starting
* index. If 'index' is omitted, we start the search at the beginning
* of the string. If 'index' is negative, it's an index from the end
* of the string: -1 is the last character, -2 the second to last, etc.
*
* 'origStr' can be given as a list of search strings, rather than a
* single string. In this case, we'll search for each of the strings
* in the list, and replace each one with 'newStr'. If 'newStr' is
* also a list, each match to an element of the 'origStr' list is
* replaced with the corresponding element (at the same index) of the
* 'newStr' list. If there are more 'origStr' elements than 'newStr'
* elements, each match to an excess 'origStr' element is replaced with
* an empty string. This allows you to perform several replacements
* with a single call.
*
* 'limit', if specified, is an integer indicating the maximum number
* of matches to replace, or nil to replace all matches. If the limit
* is reached before all matches have been replaced, no further
* replacements are performed. If this parameter is specified, it
* overrides any ReplaceOnce or ReplaceAll flag.
*
* There are two search modes when 'origStr' is a list. The default is
* "parallel" mode. In this mode, we search for all of the 'origStr'
* elements, and replace the leftmost match. We then search the
* remainder of the string, after this first match, again searching for
* all of the 'origStr' elements. Again we replace the leftmost match.
* We repeat this until we run out of matches.
*
* The other option is "serial" mode, which you select by including
* ReplaceSerial in the flags argument. In serial mode, we start by
* searching only for the first 'origStr' element. We replace each
* occurrence throughout the string (unless we're in ReplaceOnce mode,
* in which case we stop after the first replacement). If we're in
* ReplaceOnce mode and we did a replacement, we're done. Otherwise,
* we start over with the updated string, containing the replacements
* so far, and search it for the second 'origStr' element, replacing
* each occurrence (or just the first, in ReplaceOnce mode). We repeat
* this for each 'origStr' element.
*
* The key difference between the serial and parallel modes is that the
* serial mode re-scans the updated string after replacing each
* 'origStr' element, so replacement text could itself be further
* modified. Parallel mode, in contrast, never re-scans replacement
* text.
*/
findReplace(origStr, newStr, flags?, index?, limit?);
/*
* Splice: delete 'del' characters starting at 'idx', and insert the
* string 'ins' in their place. 'ins' is optional; if omitted, this
* simply does the deletion without inserting anything.
*/
splice(idx, del, ins?);
/*
* Split the string into substrings at the given delimiter, or of a
* given fixed length.
*
* 'delim' is the delimiter. It can be one of the following:
*
* - A string or RexPattern, giving the delimiter where we split the
* string. We search 'self' for matches to this string or pattern, and
* split it at each instance we find, returning a list of the resulting
* substrings. For example, 'one,two,three'.split(',') returns the
* list ['one', 'two', 'three']. The delimiter separates parts, so
* it's not part of the returned substrings.
*
* - An integer, giving a substring length. We split the string into
* substrings of this exact length (except that the last element will
* have whatever's left over). For example, 'abcdefg'.split(2) returns
* ['ab', 'cd', 'ef', 'g'].
*
* If 'delim' is omitted or nil, the default is 1, so we'll split the
* string into one-character substrings.
*
* If 'limit' is included, it's an integer giving the maximum number of
* elements to return in the result list. If we reach the limit, we'll
* stop the search and return the entire rest of the string as the last
* element of the result list. If 'limit' is 1, we simply return a
* list consisting of the source string, since a limit of one element
* means that we can't make any splits at all.
*/
split(delim?, limit?);
/*
* Convert special characters and TADS markups to standard HTML
* markups. Returns a new string with the contents of the 'self'
* string processed as described below.
*
* 'stateobj' is an object containing the state of the output stream.
* This allows an output stream to process its contents a bit at a
* time, by maintaining the state of the stream from one call to the
* next. This object gives the prior state of the stream on entry, and
* is updated on return to contain the new state after processing this
* string. If this is omitted or nil, a default initial starting state
* is used. The function uses the following properties of the object:
*
* stateobj.flags_ is an integer with a collection of flag bits giving
* the current line state
*
* stateobj.tag_ is a string containing the text of the tag currently
* in progress. If the string ends in the middle of a tag, this will
* be set on return to the text of the tag up to the end of the string,
* so that the next call can resume processing the tag where the last
* call left off.
*
*
* The function makes the following conversions:
*
* \n ->
, or nothing at the start of a line
*
* \b ->
at the start of a line, or
within a line
*
* \ (quoted space) -> if followed by a space or another quoted
* space, or an ordinary space if followed by a non-space character
*
* \t -> a sequence of characters followed by a space, padding
* to the next 8-character tab stop. This can't take into account the
* font metrics, since that's determined by the browser, so it should
* only be used with a monospaced font.
*
* \^ -> sets an internal flag to capitalize the next character
*
* \v -> sets an internal flag to lower-case the next character
*
* ...
-> “ ... ” or ‘ ... ’,
* depending on the nesting level
*
*
-> N
tags if at the start of a line, N+1
* tags if within a line
*
*
* Note that this isn't a general-purpose HTML corrector: it doesn't
* correct ill-formed markups or standardize deprecated syntax or
* browser-specific syntax. This function is specifically for
* standardizing TADS-specific syntax, so that games can use the
* traditional TADS syntax with the Web UI.
*/
specialsToHtml(stateobj?);
/*
* Convert special characters and HTML markups to plain text, as it
* would appear if written out through the regular console output
* writer and displayed on a plain text terminal. Returns a new string
* with the contents of the 'self' string processed as described below.
* This works very much like specialsToHtml(), but rather than
* generating standard HTML output, we generate plain text output.
*
* 'stateobj' has the same meaning asin specialsToHtml().
*
* The function makes the following conversions:
*
* \n -> \n, or nothing at the start of a line
*
* \b -> \n at the start of a line, or \n\n within a line
*
* \ (quoted space) -> regular space
*
* \^ -> sets an internal flag to capitalize the next character
*
* \v -> sets an internal flag to lower-case the next character
*
* ...
-> "..." or '...' depending on the quote nesting level
*
*
-> N \n characters at the start of a line, N+1 \n
* characters within a line
*
*
-> \n at the start of a line, \n\n within a line
*
*
tag */
#define HtmlifyTranslateNewlines 0x0002
/* translate tabs - converts each \t character to a
tag; and each
* '\t' character becomes a